Files
chongming-web/src/views/DoubaoView.vue
T
2026-08-27 14:45:10 +08:00

268 lines
6.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="doubao-container">
<el-card class="main-card" shadow="hover">
<template #header>
<div class="card-header">
<el-icon :size="24"><Cpu /></el-icon>
<span style="font-weight: 600; font-size: 18px; margin-left: 8px"
>豆包视觉理解测试</span
>
</div>
</template>
<el-form label-position="top">
<el-form-item label="提示词 (prompt)">
<el-input
v-model="prompt"
type="textarea"
:rows="3"
placeholder="请输入对图片的提问(可选)"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<el-upload
class="upload-area"
drag
:auto-upload="false"
:show-file-list="false"
:on-change="handleFileChange"
accept="image/*"
>
<div v-if="!imagePreview" class="upload-placeholder">
<el-icon :size="48" class="el-icon--upload"><Upload /></el-icon>
<div class="el-upload__text">
拖拽图片到此处,或 <em>点击选择</em>
</div>
<div class="el-upload__tip">支持 JPG / PNG / WebP</div>
</div>
<div v-else class="preview-wrapper">
<img :src="imagePreview" class="preview-image" />
<div class="preview-actions">
<el-button
type="danger"
size="small"
circle
@click.stop="clearImage"
>
<el-icon><Close /></el-icon>
</el-button>
</div>
</div>
</el-upload>
<div v-if="fileInfo" style="margin: 12px 0">
<el-tag type="info"
><el-icon><Document /></el-icon> {{ fileInfo }}</el-tag
>
</div>
<div class="action-bar">
<el-button
type="primary"
size="large"
:loading="loading"
:disabled="!imageBase64"
@click="analyze"
>
{{ loading ? "分析中..." : "🚀 分析图片" }}
</el-button>
<el-button v-if="resultText" size="large" @click="copyResult">
<el-icon><CopyDocument /></el-icon> 复制结果
</el-button>
</div>
<div v-if="statusText" style="margin-top: 16px">
<el-alert
:title="statusText"
:type="statusType"
:closable="false"
show-icon
/>
</div>
<div v-if="resultText" style="margin-top: 20px">
<el-divider content-position="left">分析结果</el-divider>
<div class="result-content" v-html="resultHtml"></div>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { ElMessage } from "element-plus";
import {
Cpu,
Upload,
Close,
Document,
CopyDocument,
} from "@element-plus/icons-vue";
// 导入生成的 SDK 函数及类型
import { endpointApiV1DoubaoAnalyzePost } from "@/api/generated/sdk.gen";
import type { EndpointApiV1DoubaoAnalyzePostData } from "@/api/generated/types.gen";
const prompt = ref("描述这张图片的内容");
const imageBase64 = ref("");
const imagePreview = ref("");
const loading = ref(false);
const statusText = ref("");
const resultText = ref("");
const fileInfo = ref("");
const statusType = computed(() => {
if (statusText.value.includes("✅")) return "success";
if (statusText.value.includes("❌")) return "error";
return "info";
});
const resultHtml = computed(() => resultText.value.replace(/\n/g, "<br>"));
const handleFileChange = (file: any) => {
const raw = file.raw || file;
if (!raw) return;
if (!raw.type.startsWith("image/")) {
ElMessage.warning("请选择图片文件");
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const data = e.target?.result as string;
imageBase64.value = data;
imagePreview.value = data;
fileInfo.value = `${raw.name} (${(raw.size / 1024).toFixed(1)} KB)`;
statusText.value = "";
resultText.value = "";
};
reader.readAsDataURL(raw);
};
const clearImage = () => {
imageBase64.value = "";
imagePreview.value = "";
fileInfo.value = "";
statusText.value = "";
resultText.value = "";
};
const analyze = async () => {
if (!imageBase64.value) {
ElMessage.warning("请先选择一张图片");
return;
}
// 构造请求参数
const requestData: EndpointApiV1DoubaoAnalyzePostData = {
body: {
image_base64: imageBase64.value,
prompt: prompt.value.trim() || "描述这张图片的内容",
},
url: "/api/v1/doubao/analyze",
};
loading.value = true;
statusText.value = "⏳ 正在分析中...";
resultText.value = "";
try {
// 调用生成的 API 函数 (默认 throwOnError = false)
const response = await endpointApiV1DoubaoAnalyzePost(requestData);
// 检查 HTTP 错误
if (response.error) {
const errMsg = response.error || "请求失败";
resultText.value = `❌ 错误: ${errMsg}`;
statusText.value = "❌ 分析失败";
return;
}
// 获取业务数据
const data = response.data;
if (data?.error) {
resultText.value = `❌ 错误: ${data.error}`;
statusText.value = "❌ 分析失败";
} else {
resultText.value = data?.result || "(无返回内容)";
statusText.value = "✅ 分析完成";
}
} catch (err: any) {
// 网络错误或其他异常
resultText.value = `❌ 请求异常: ${err.message}`;
statusText.value = "❌ 请求失败";
} finally {
loading.value = false;
}
};
const copyResult = () => {
const text = resultText.value.replace(/<[^>]+>/g, "");
navigator.clipboard
.writeText(text)
.then(() => {
ElMessage.success("已复制");
})
.catch(() => {
ElMessage.error("复制失败");
});
};
</script>
<style scoped>
.doubao-container {
max-width: 700px;
margin: 0 auto;
}
.main-card {
border-radius: 12px;
}
.card-header {
display: flex;
align-items: center;
}
.upload-area {
width: 100%;
}
.upload-placeholder {
padding: 30px 0;
text-align: center;
}
.preview-wrapper {
position: relative;
display: inline-block;
width: 100%;
}
.preview-image {
max-width: 100%;
max-height: 400px;
border-radius: 8px;
border: 1px solid var(--cm-border);
}
.preview-actions {
position: absolute;
top: 8px;
right: 8px;
background: rgba(22, 27, 34, 0.75);
border-radius: 50%;
backdrop-filter: blur(4px);
}
.action-bar {
margin-top: 16px;
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.result-content {
background: var(--el-fill-color-lighter);
padding: 16px;
border-radius: 8px;
white-space: pre-wrap;
word-break: break-word;
line-height: 1.8;
color: var(--cm-text);
}
</style>