app: Adapt API

This commit is contained in:
LittleChest 2025-09-27 10:10:17 +08:00
parent be40451a76
commit 56aa260e60

View File

@ -15,25 +15,26 @@
muted
width="320"
height="240"
style="border-radius: 8px; background: #000"
class="rounded-lg bg-black"
v-show="cameraActive && !photoData"
></video>
<img
v-if="photoData"
:src="photoData"
alt="自拍预览"
alt="预览"
width="320"
height="240"
style="border-radius: 8px; object-fit: cover"
class="rounded-lg object-cover"
/>
</div>
<div v-if="errorMsg" class="text-red-600 text-center mt-2">
{{ errorMsg }}
</div>
<div class="mt-4 flex gap-3 justify-center">
<v-btn color="primary" @click="startCamera" v-if="!cameraActive"
>打开摄像头</v-btn
>
<v-btn color="primary" @click="takePhoto" v-if="cameraActive && !photoData"
<v-btn color="primary" @click="takePhoto" :disabled="!cameraActive" v-if="!photoData"
>拍照</v-btn
>
<v-btn color="secondary" @click="retake" v-if="photoData">重拍</v-btn>
@ -50,15 +51,22 @@
</label>
</div>
<v-switch v-model="saveConsent" inset hide-details color="primary" class="mt-4" label="允许 littlew.top 将此信息与你的账户关联" />
<v-progress-linear
:indeterminate="true"
v-if="loading"
class="mt-4"
></v-progress-linear>
<div v-if="responseText" class="mt-4">
<div v-if="debug" class="mt-4">
<div>调试信息</div>
<pre class="bg-gray-100 p-3 rounded overflow-auto">{{ responseText }}</pre>
<pre class="bg-gray-100 p-3 rounded overflow-auto">{{ debug }}</pre>
</div>
<div v-if="age !== null || gender !== null" class="mt-4 text-center">
<div>年龄{{ ageDisplay }}</div>
<div>性别{{ genderDisplay }}</div>
</div>
</v-card-text>
</v-card>
@ -78,11 +86,16 @@ export default {
photoData: null,
photoBlob: null,
loading: false,
responseText: '',
debug: '',
errorMsg: '',
saveConsent: true,
age: null,
gender: null,
}
},
methods: {
async startCamera() {
try {
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user' },
audio: false,
@ -92,8 +105,12 @@ export default {
const videoEl = this.$refs.video
videoEl.srcObject = this.stream
await videoEl.play().catch(() => {})
} catch (e) {
this.errorMsg = e
}
},
takePhoto() {
try {
const video = this.$refs.video
const canvas = document.createElement('canvas')
canvas.width = video.videoWidth || 320
@ -109,12 +126,19 @@ export default {
0.9
)
this.stopCamera()
} catch (e) {
this.errorMsg = e
}
},
retake() {
try {
if (this.photoData) URL.revokeObjectURL(this.photoData)
this.photoData = null
this.photoBlob = null
this.startCamera()
} catch (e) {
this.errorMsg = e
}
},
stopCamera() {
if (this.stream) {
@ -124,40 +148,69 @@ export default {
this.cameraActive = false
},
onFileChange(e) {
try {
const file = e.target.files && e.target.files[0]
if (!file) return
this.photoBlob = file
this.photoData = URL.createObjectURL(file)
} catch (e) {
this.errorMsg = e
}
},
async uploadPhoto() {
if (!this.photoBlob) return alert('请先拍照或选择图片')
if (!this.photoBlob) {
this.errorMsg = '请先拍照或选择图片'
return
}
this.loading = true
this.responseText = ''
this.debug = ''
try {
const form = new FormData()
form.append('face', this.photoBlob, 'selfie.jpg')
const resp = await fetch('https://api.littlew.top/age', {
let url = 'https://api.littlew.top/age'
if (this.saveConsent) url += '?save=true'
const resp = await fetch(url, {
method: 'POST',
body: form,
}, {
credentials: 'include',
})
const text = await resp.text()
try {
const obj = JSON.parse(text)
this.responseText = JSON.stringify(obj, null, 2)
const data = JSON.parse(text)
this.debug = data.raw
this.age = typeof data.age === 'number' ? data.age : -1
this.gender = typeof data.gender === 'number' ? data.gender : -1
} catch (e) {
this.responseText = text
this.debug = text
this.age = -1
this.gender = -1
}
} catch (e) {
this.responseText = e.message
this.errorMsg = e
} finally {
this.loading = false
}
},
},
computed: {
ageDisplay() {
if (this.age === null) return '未知'
return this.age > -1 ? String(this.age) : '未知'
},
genderDisplay() {
if (this.gender === null) return '未知'
if (this.gender === 0) return 'Female'
if (this.gender === 1) return 'Male'
return '未知'
},
},
beforeUnmount() {
this.stopCamera()
},
mounted() {
this.startCamera()
}
}
</script>