增加ai配置和ai广场功能
This commit is contained in:
1 parent
244b4bd9fd
commit
d9a50e23dd
27 files changed
+3808
-57
No files matched your search
@@ -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 });
|
||||
}
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user