| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- <template>
- <view class="page">
- <view class="bg-gradient"></view>
- <van-nav-bar :title="inputType === 'vision' ? '视力录入' : '屈光度录入'" left-arrow @click-left="onBack" fixed :z-index="999" />
- <view class="header">
- <view class="student-info">
- <view class="info-row">
- <image class="gender-icon" :src="studentInfo.gender === '男' ? '/static/images/icon/man.png' : '/static/images/icon/woman.png'" mode="aspectFit"></image>
- <text class="student-name">{{ studentInfo.name }}</text>
- </view>
- <view class="detail-row">
- <text>学校:{{ schoolName }}</text>
- </view>
- <view class="detail-row">
- <text>班级:{{ studentInfo.class }}</text>
- </view>
- <view class="detail-row">
- <text>身份证:{{ studentInfo.idCard }}</text>
- </view>
- </view>
- </view>
- <view class="content">
- <view class="section-header">
- <text class="section-title">眼科检查</text>
- </view>
- <view class="form-item" v-if="inputType === 'vision'">
- <text class="label">左眼远视力</text>
- <input class="value-input" v-model="formData.leftVision" type="digit" placeholder="请输入" @blur="formatVision('leftVision')" />
- </view>
- <view class="form-item" v-if="inputType === 'vision'">
- <text class="label">右眼远视力</text>
- <input class="value-input" v-model="formData.rightVision" type="digit" placeholder="请输入" @blur="formatVision('rightVision')" />
- </view>
- <view class="form-item" v-if="inputType === 'refraction'">
- <text class="label">左眼屈光度</text>
- <input class="value-input" v-model="formData.leftRefraction" type="number" placeholder="请输入" />
- </view>
- <view class="form-item" v-if="inputType === 'refraction'">
- <text class="label">右眼屈光度</text>
- <input class="value-input" v-model="formData.rightRefraction" type="number" placeholder="请输入" />
- </view>
- </view>
- <view class="footer">
- <view class="footer-btns">
- <van-button plain type="primary" round size="large" @click="onBack" icon="replay">返回</van-button>
- <van-button type="warning" round size="large" @click="handleScan" icon="scan">扫一扫</van-button>
- <van-button type="primary" round size="large" icon="success" @click="handleSave">保存</van-button>
- </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 { inputVisionData, inputRefractionData } from '@/services/common'
- import jsQR from 'jsqr'
- const STORAGE_KEY = 'eye_examine_user_info'
- const userInfo = ref({})
- const studentInfo = ref({})
- const inputType = ref('')
- const visionDataId = ref('')
- const schoolName = ref('')
- const showScanner = ref(false)
- const videoRef = ref(null)
- const canvasRef = ref(null)
- let stream = null
- let animationId = null
- const formData = ref({
- leftVision: '',
- rightVision: '',
- leftRefraction: '',
- rightRefraction: ''
- })
- onLoad((options) => {
- studentInfo.value = {
- id: options.studentId || '',
- name: decodeURIComponent(options.studentName || ''),
- gender: decodeURIComponent(options.studentGender || ''),
- idCard: decodeURIComponent(options.studentIdCard || ''),
- classId: options.classId || '',
- class: `${decodeURIComponent(options.grade || '')} ${decodeURIComponent(options.className || '')}`
- }
-
- inputType.value = options.type || ''
- visionDataId.value = options.visionDataId || ''
- schoolName.value = decodeURIComponent(options.schoolName || '')
-
- userInfo.value = {
- name: decodeURIComponent(options.userName || ''),
- phone: decodeURIComponent(options.userPhone || ''),
- organization: decodeURIComponent(options.userOrg || '')
- }
- })
- const onBack = () => {
- uni.navigateBack({
- delta: 1
- })
- }
- const handleScan = 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
- }
- const formatVision = (field) => {
- const value = parseFloat(formData.value[field])
- if (!isNaN(value)) {
- formData.value[field] = value.toFixed(1)
- }
- }
- const handleSave = async () => {
- if (inputType.value === 'vision') {
- if (!formData.value.leftVision || !formData.value.rightVision) {
- return uni.showToast({
- title: '请填写完整视力信息',
- icon: 'none'
- })
- }
-
- try {
- const params = {
- student_id: parseInt(studentInfo.value.id),
- left_eye_vision: parseFloat(formData.value.leftVision),
- right_eye_vision: parseFloat(formData.value.rightVision),
- input_user: userInfo.value.name,
- operator_phone: userInfo.value.phone
- }
-
- if (visionDataId.value) {
- params.vision_data_id = visionDataId.value
- }
-
- const res = await inputVisionData(params)
- if (res && res.code === 200) {
- uni.showToast({
- title: '保存成功',
- icon: 'success',
- duration: 1000
- })
- setTimeout(() => {
- uni.navigateBack({ delta: 2 })
- }, 1000)
- }
- } catch (error) {
- console.error('保存视力数据失败', error)
- }
- } else if (inputType.value === 'refraction') {
- if (!formData.value.leftRefraction || !formData.value.rightRefraction) {
- return uni.showToast({
- title: '请填写完整屈光度信息',
- icon: 'none'
- })
- }
-
- try {
- const params = {
- student_id: parseInt(studentInfo.value.id),
- left_eye_refraction: parseFloat(formData.value.leftRefraction),
- right_eye_refraction: parseFloat(formData.value.rightRefraction),
- input_user: userInfo.value.name,
- operator_phone: userInfo.value.phone
- }
-
- if (visionDataId.value) {
- params.vision_data_id = visionDataId.value
- }
-
- const res = await inputRefractionData(params)
- if (res && res.code === 200) {
- uni.showToast({
- title: '保存成功',
- icon: 'success',
- duration: 1000
- })
- setTimeout(() => {
- uni.navigateBack({ delta: 2 })
- }, 1000)
- }
- } catch (error) {
- console.error('保存屈光度数据失败', error)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .page {
- min-height: 100vh;
- position: relative;
- }
- .bg-gradient {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(180deg, #4A9FF5 0%, #E8F5FF 100%);
- z-index: 0;
- pointer-events: none;
- }
- .header {
- padding: 20rpx 30rpx;
- padding-top: calc(20rpx + 46px);
- position: relative;
- z-index: 1;
- }
- .student-info {
- background: rgba(255, 255, 255, 0.9);
- border-radius: 20rpx;
- padding: 30rpx;
- }
- .info-row {
- display: flex;
- align-items: center;
- margin-bottom: 20rpx;
- }
- .gender-icon {
- width: 40rpx;
- height: 40rpx;
- margin-right: 10rpx;
- }
- .student-name {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .detail-row {
- font-size: 26rpx;
- color: #666;
- margin-top: 10rpx;
- }
- .content {
- padding: 30rpx;
- position: relative;
- z-index: 1;
- padding-bottom: 150rpx;
- }
- .section-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 20rpx;
- }
- .section-title {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- .form-item {
- background: #fff;
- border-radius: 20rpx;
- padding: 30rpx;
- margin-bottom: 20rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .label {
- font-size: 28rpx;
- color: #333;
- }
- .value {
- font-size: 32rpx;
- font-weight: bold;
- color: #0063F5;
- flex: 1;
- text-align: center;
- }
- .value-input {
- font-size: 32rpx;
- font-weight: bold;
- color: #0063F5;
- flex: 1;
- text-align: center;
- }
- .edit-icon {
- width: 40rpx;
- height: 40rpx;
- }
- .footer {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 30rpx;
- background: #fff;
- z-index: 10;
- }
- .footer-btns {
- display: flex;
- gap: 20rpx;
-
- button {
- flex: 1;
- }
- }
- .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>
|