增加ai配置和ai广场功能

This commit is contained in:
hero920103 committed 2026-11-09 16:44:11 +08:00
1 parent 244b4bd9fd
commit d9a50e23dd
27 files changed
+3808 -57

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
import request from "@/utils/request";
/** OCR 识别(配置在平台「AI管理 → OCR识别配置」) */
export function ocrRecognize(data) {
return request({ url: "/backend/ocr/recognize", method: "post", data });
}
+215
View File
@@ -0,0 +1,215 @@
<template>
<div class="container-box ocr-page">
<div class="header-bar">
<h2>OCR 识别</h2>
<p class="page-tip">
上传图片进行文字识别。识别服务由平台统一配置(平台端「系统 - AI管理 - OCR识别配置」),
当前识别能力待接入,已保存的配置即将生效。
</p>
</div>
<el-divider />
<el-row :gutter="20">
<el-col :span="10">
<el-card shadow="never">
<template #header><span>上传图片</span></template>
<el-upload
class="ocr-uploader"
drag
:show-file-list="false"
accept="image/*"
:auto-upload="false"
:on-change="handleFile"
>
<div class="ocr-uploader__inner">
<el-icon :size="40"><UploadFilled /></el-icon>
<p>点击或拖拽图片到此处</p>
<p class="sub">支持 png / jpg / webp 等常见格式</p>
</div>
</el-upload>
<div v-if="previewUrl" class="ocr-preview">
<img :src="previewUrl" alt="待识别图片" />
<p class="ocr-preview__name">{{ fileName }}</p>
</div>
<el-input
v-model="prompt"
type="textarea"
:rows="3"
class="ocr-prompt"
placeholder="识别提示词(可选,留空使用平台默认配置)"
/>
<el-button
type="primary"
:loading="recognizing"
:disabled="!imageBase64"
style="width: 100%"
@click="doRecognize"
>
开始识别
</el-button>
</el-card>
</el-col>
<el-col :span="14">
<el-card shadow="never" class="ocr-result-card">
<template #header><span>识别结果</span></template>
<div v-if="resultText" class="ocr-result">
<pre>{{ resultText }}</pre>
</div>
<el-empty v-else description="识别结果将显示在这里" />
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
import { ocrRecognize } from "@/api/ocr";
const previewUrl = ref("");
const fileName = ref("");
const imageBase64 = ref("");
const prompt = ref("");
const recognizing = ref(false);
const resultText = ref("");
const MAX_SIZE = 10 * 1024 * 1024;
const handleFile = (file) => {
const raw = file?.raw;
if (!raw) return;
if (!String(raw.type || "").startsWith("image/")) {
ElMessage.warning("请选择图片文件");
return;
}
if (raw.size > MAX_SIZE) {
ElMessage.warning("图片不能超过 10MB");
return;
}
fileName.value = raw.name || "image";
const reader = new FileReader();
reader.onload = () => {
const dataUrl = String(reader.result || "");
previewUrl.value = dataUrl;
imageBase64.value = dataUrl.includes(",") ? dataUrl.split(",")[1] : dataUrl;
resultText.value = "";
};
reader.readAsDataURL(raw);
};
const doRecognize = async () => {
recognizing.value = true;
resultText.value = "";
try {
const res = await ocrRecognize({
image_base64: imageBase64.value,
prompt: prompt.value.trim(),
});
if (res?.code === 200) {
const d = res.data || {};
if (d.recognized && d.text) {
resultText.value = d.text;
} else {
ElMessage.info(d.message || "OCR 识别服务待接入");
resultText.value = d.message || "OCR 识别服务待接入";
}
} else {
ElMessage.error(res?.msg || "识别失败");
}
} finally {
recognizing.value = false;
}
};
</script>
<style lang="less" scoped>
.ocr-page {
padding: 20px;
}
.header-bar {
h2 {
margin: 0 0 8px;
font-size: 18px;
font-weight: 600;
color: var(--el-text-color-primary);
}
}
.page-tip {
margin: 0;
font-size: 13px;
color: var(--el-text-color-secondary);
line-height: 1.7;
}
.ocr-uploader {
:deep(.el-upload-dragger) {
padding: 36px 0;
}
&__inner {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: var(--el-text-color-secondary);
p {
margin: 0;
font-size: 13px;
}
.sub {
font-size: 12px;
color: var(--el-text-color-placeholder);
}
}
}
.ocr-preview {
margin-top: 16px;
img {
width: 100%;
max-height: 260px;
object-fit: contain;
border-radius: 6px;
border: 1px solid var(--el-border-color-lighter);
}
&__name {
margin: 8px 0 0;
font-size: 12px;
color: var(--el-text-color-secondary);
word-break: break-all;
}
}
.ocr-prompt {
margin-top: 16px;
}
.ocr-result-card {
min-height: 420px;
pre {
margin: 0;
padding: 16px;
border-radius: 6px;
background: var(--el-fill-color-light);
font-size: 14px;
line-height: 1.9;
white-space: pre-wrap;
word-break: break-word;
color: var(--el-text-color-primary);
}
}
</style>