feat: 以图搜图集成拍照功能,支持直接调起摄像头搜图

This commit is contained in:
DXC
2026-06-05 15:47:50 +08:00
parent 1def8c7747
commit 93b9846fc6

View File

@ -33,6 +33,17 @@
</div>
</el-upload>
<!-- 拍照按钮 -->
<el-button
v-if="!previewUrl"
type="primary"
class="camera-btn"
@click="openCamera"
>
<el-icon><VideoCamera /></el-icon>
调起摄像头拍照
</el-button>
<div v-if="searching" class="loading-tip">
<el-icon class="is-loading"><Loading /></el-icon>
<span>正在识别图片并检索...</span>
@ -94,15 +105,33 @@
<template #footer>
<el-button @click="handleClose">关闭</el-button>
</template>
<!-- 拍照弹窗 -->
<el-dialog
v-model="cameraVisible"
title="拍照"
width="95%"
style="max-width: 480px; height: 80vh; padding: 0;"
append-to-body
destroy-on-close
:close-on-click-modal="false"
@close="closeCamera"
>
<WebRtcCamera
@cancel="closeCamera"
@photo-submit="handleCameraSubmit"
/>
</el-dialog>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { Camera, Loading, Picture, WarningFilled } from '@element-plus/icons-vue'
import { Camera, Loading, Picture, WarningFilled, VideoCamera } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { imageSearch, type ImageSearchItem } from '@/api/common/upload'
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
const router = useRouter()
@ -126,6 +155,9 @@ const searching = ref(false)
const searched = ref(false)
const results = ref<ImageSearchItem[]>([])
// 拍照相关
const cameraVisible = ref(false)
watch(() => props.modelValue, (val) => {
visible.value = val
if (!val) {
@ -137,6 +169,27 @@ watch(visible, (val) => {
emit('update:modelValue', val)
})
// 拍照相关方法
const openCamera = () => {
cameraVisible.value = true
}
const closeCamera = () => {
cameraVisible.value = false
}
const handleCameraSubmit = (file: File) => {
// 关闭拍照弹窗
closeCamera()
// 生成预览
currentFile.value = file
previewUrl.value = URL.createObjectURL(file)
// 立即触发搜图
doSearch(file)
}
const handleFileChange = (uploadFile: any) => {
const file = uploadFile.raw
if (!file) return
@ -179,6 +232,9 @@ const doSearch = async (file: File) => {
}
const clearImage = () => {
if (previewUrl.value) {
URL.revokeObjectURL(previewUrl.value)
}
previewUrl.value = ''
currentFile.value = null
results.value = []
@ -188,7 +244,6 @@ const clearImage = () => {
const fullImageUrl = (path: string) => {
if (!path) return '';
// 直接原样返回,完全信任后端传过来的 image_url
return path.startsWith('http') ? path : path;
}
@ -219,6 +274,9 @@ const handleClose = () => {
}
const resetState = () => {
if (previewUrl.value) {
URL.revokeObjectURL(previewUrl.value)
}
previewUrl.value = ''
currentFile.value = null
searching.value = false
@ -234,6 +292,12 @@ const resetState = () => {
min-height: 380px;
}
/* 拍照按钮 */
.camera-btn {
width: 100%;
margin-top: 8px;
}
/* ── 左侧上传区 ── */
.upload-section {
flex: 0 0 220px;