Compare commits
25 Commits
ac580599b3
...
0.2.5
| Author | SHA1 | Date | |
|---|---|---|---|
| 45005fcc92 | |||
| df18bdfa7e | |||
| 281ade6ac9 | |||
| 835426b133 | |||
| d001fec21e | |||
| 253de3639c | |||
| a0507b8054 | |||
| 9f803880fa | |||
| 71fe964476 | |||
| 0f5c8c08ff | |||
| e032253327 | |||
| 3f108e2138 | |||
| 2d913e397f | |||
| 74bc8aa498 | |||
| 5d747faee1 | |||
| 7bd8eeca77 | |||
| 782b2dd82e | |||
| f9c0abb3a0 | |||
| c5300591e6 | |||
| f96629566f | |||
| 8d581ac638 | |||
| acc80074ea | |||
| 62d28be032 | |||
| 5b956e1365 | |||
| 7485ba16e6 |
129
.gitea/workflows/ci.yml
Normal file
129
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
# Required Secrets:
|
||||||
|
# - DJANGO_SECRET_KEY: Django Secret Key
|
||||||
|
# - token: Gitea API token for creating releases
|
||||||
|
# - ALIST_PUBLIC_URL: Public URL for AList download (e.g., http://alist.example.com/d/ci)
|
||||||
|
# - WEBDAV_URL: WebDAV upload URL (e.g., http://alist.example.com/dav/ci/)
|
||||||
|
# - WEBDAV_USER: WebDAV username
|
||||||
|
# - WEBDAV_PASSWORD: WebDAV password
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- Django
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 版本号(如 0.2.2),为空则自动生成
|
||||||
|
required: false
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-ci:
|
||||||
|
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && contains(github.event.head_commit.message, '[ci]'))
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
timeout-minutes: 40
|
||||||
|
env:
|
||||||
|
DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }}
|
||||||
|
DJANGO_DEBUG: "False"
|
||||||
|
DJANGO_ALLOWED_HOSTS: "127.0.0.1,localhost"
|
||||||
|
IMAGE_NAME: achievement_inputing_ci
|
||||||
|
ARTIFACT_DIR: artifacts
|
||||||
|
# 请在 Secrets 中配置 ALIST_PUBLIC_URL,例如 http://139.224.69.213:8080/d/ci
|
||||||
|
DOWNLOAD_BASE: ${{ secrets.ALIST_PUBLIC_URL }}
|
||||||
|
GITEA_SERVER: ${{ github.server_url }}
|
||||||
|
GITEA_REPO: ${{ github.repository }}
|
||||||
|
RELEASE_TOKEN: ${{ secrets.token }}
|
||||||
|
steps:
|
||||||
|
- name: Ensure source present
|
||||||
|
env:
|
||||||
|
SERVER: ${{ github.server_url }}
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
REF: ${{ github.ref }}
|
||||||
|
SHA: ${{ github.sha }}
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
if [ -f "$GITHUB_WORKSPACE/Dockerfile" ]; then exit 0; fi
|
||||||
|
mkdir -p "$GITHUB_WORKSPACE"
|
||||||
|
cd "$GITHUB_WORKSPACE"
|
||||||
|
git init .
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
git fetch --depth=1 "$SERVER/$REPO.git" "$REF"
|
||||||
|
else
|
||||||
|
git -c http.extraHeader="Authorization: Bearer $TOKEN" fetch --depth=1 "$SERVER/$REPO.git" "$REF"
|
||||||
|
fi
|
||||||
|
git checkout FETCH_HEAD
|
||||||
|
- name: Derive version
|
||||||
|
run: |
|
||||||
|
msg="${{ github.event.head_commit.message }}"
|
||||||
|
ver_input="${{ github.event.inputs.version }}"
|
||||||
|
ver=""
|
||||||
|
if [ -n "$ver_input" ]; then
|
||||||
|
ver="$ver_input"
|
||||||
|
else
|
||||||
|
ver=$(echo "$msg" | grep -Eo "\[[0-9]+(\.[0-9]+){1,}\]" | head -n1 | tr -d '[]')
|
||||||
|
fi
|
||||||
|
if [ -z "$ver" ]; then
|
||||||
|
ver="$(date +%Y%m%d%H%M)-${GITHUB_SHA:0:7}"
|
||||||
|
fi
|
||||||
|
echo "VERSION=$ver" >> $GITHUB_ENV
|
||||||
|
- name: Build application image
|
||||||
|
run: |
|
||||||
|
docker build -t "$IMAGE_NAME:$VERSION" -f "$GITHUB_WORKSPACE/Dockerfile" "$GITHUB_WORKSPACE"
|
||||||
|
- name: Output image info
|
||||||
|
run: |
|
||||||
|
docker image inspect "$IMAGE_NAME:$VERSION" --format '{{.Id}} {{.Size}}'
|
||||||
|
- name: Export image tar
|
||||||
|
run: |
|
||||||
|
ART="achievement_inputing_ci_${VERSION}.tar"
|
||||||
|
docker save -o "$GITHUB_WORKSPACE/$ART" "$IMAGE_NAME:$VERSION"
|
||||||
|
echo "$ART" > "$GITHUB_WORKSPACE/.artifact_name"
|
||||||
|
- name: Publish artifact locally
|
||||||
|
run: |
|
||||||
|
ART=$(cat "$GITHUB_WORKSPACE/.artifact_name")
|
||||||
|
mkdir -p "$GITHUB_WORKSPACE/$ARTIFACT_DIR"
|
||||||
|
mv "$GITHUB_WORKSPACE/$ART" "$GITHUB_WORKSPACE/$ARTIFACT_DIR/"
|
||||||
|
echo "artifact: $GITHUB_WORKSPACE/$ARTIFACT_DIR/$ART"
|
||||||
|
- name: Publish to WebDAV
|
||||||
|
env:
|
||||||
|
WEBDAV_URL: ${{ secrets.WEBDAV_URL }}
|
||||||
|
WEBDAV_USER: ${{ secrets.WEBDAV_USER }}
|
||||||
|
WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
ART=$(cat "$GITHUB_WORKSPACE/.artifact_name")
|
||||||
|
FILE_PATH="$GITHUB_WORKSPACE/$ARTIFACT_DIR/$ART"
|
||||||
|
|
||||||
|
# 检查必要的 secrets 是否存在
|
||||||
|
if [ -z "$WEBDAV_URL" ]; then
|
||||||
|
echo "Error: WEBDAV_URL secret is not set."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 确保 URL 以 / 结尾
|
||||||
|
case "$WEBDAV_URL" in
|
||||||
|
*/) ;;
|
||||||
|
*) WEBDAV_URL="${WEBDAV_URL}/" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Uploading $ART to $WEBDAV_URL..."
|
||||||
|
curl -f -u "$WEBDAV_USER:$WEBDAV_PASSWORD" -T "$FILE_PATH" "${WEBDAV_URL}${ART}"
|
||||||
|
echo "Upload success."
|
||||||
|
- name: Create release with download link
|
||||||
|
if: env.RELEASE_TOKEN != ''
|
||||||
|
run: |
|
||||||
|
ART=$(cat "$GITHUB_WORKSPACE/.artifact_name")
|
||||||
|
BRANCH=${GITHUB_REF#refs/heads/}
|
||||||
|
TAG="$VERSION"
|
||||||
|
NAME="$VERSION"
|
||||||
|
BASE="${DOWNLOAD_BASE%/}"
|
||||||
|
DL="$BASE/$ART"
|
||||||
|
echo "download: $DL"
|
||||||
|
JSON=$(printf '{"tag_name":"%s","target_commitish":"%s","name":"%s","body":"%s"}' "$TAG" "$BRANCH" "$NAME" "$DL")
|
||||||
|
curl -sS -X POST "$GITEA_SERVER/api/v1/repos/$GITEA_REPO/releases" -H "Content-Type: application/json" -H "Authorization: token $RELEASE_TOKEN" -d "$JSON"
|
||||||
@@ -166,3 +166,4 @@ ELASTICSEARCH_INDEX_NAMES = {
|
|||||||
# AI Studio/OpenAI client settings
|
# AI Studio/OpenAI client settings
|
||||||
AISTUDIO_API_KEY = os.environ.get('AISTUDIO_API_KEY', '')
|
AISTUDIO_API_KEY = os.environ.get('AISTUDIO_API_KEY', '')
|
||||||
OPENAI_BASE_URL = os.environ.get('OPENAI_BASE_URL', 'https://aistudio.baidu.com/llm/lmapi/v3')
|
OPENAI_BASE_URL = os.environ.get('OPENAI_BASE_URL', 'https://aistudio.baidu.com/llm/lmapi/v3')
|
||||||
|
OPENAI_MODEL_NAME = os.environ.get('OPENAI_MODEL_NAME', 'ernie-4.5-turbo-vl-32k')
|
||||||
|
|||||||
@@ -167,6 +167,9 @@ let currentId = '';
|
|||||||
let currentWriter = '';
|
let currentWriter = '';
|
||||||
let currentImage = '';
|
let currentImage = '';
|
||||||
let allDataCache = []; // 缓存所有数据,避免重复请求
|
let allDataCache = []; // 缓存所有数据,避免重复请求
|
||||||
|
let currentSearchQuery = ''; // 记录当前搜索查询
|
||||||
|
let isFuzzySearch = false; // 记录当前是否为模糊搜索
|
||||||
|
let isDeleting = false; // 标记是否正在删除
|
||||||
|
|
||||||
// 图片缩放相关变量
|
// 图片缩放相关变量
|
||||||
let currentScale = 1;
|
let currentScale = 1;
|
||||||
@@ -186,6 +189,8 @@ async function performSearch(type) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentSearchQuery = query;
|
||||||
|
isFuzzySearch = type === 'fuzzy';
|
||||||
showSearchLoading();
|
showSearchLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -244,6 +249,7 @@ function showSearchMessage(message, type = '') {
|
|||||||
|
|
||||||
// 加载所有数据
|
// 加载所有数据
|
||||||
async function loadAllData() {
|
async function loadAllData() {
|
||||||
|
currentSearchQuery = '';
|
||||||
showSearchLoading();
|
showSearchLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -282,6 +288,7 @@ function displayAllData(data) {
|
|||||||
function clearSearch() {
|
function clearSearch() {
|
||||||
searchQueryInput.value = '';
|
searchQueryInput.value = '';
|
||||||
searchResultDiv.style.display = 'none';
|
searchResultDiv.style.display = 'none';
|
||||||
|
currentSearchQuery = '';
|
||||||
|
|
||||||
// 如果有缓存数据,显示全部
|
// 如果有缓存数据,显示全部
|
||||||
if (allDataCache.length > 0) {
|
if (allDataCache.length > 0) {
|
||||||
@@ -488,15 +495,9 @@ async function saveEdit(){
|
|||||||
alert('保存成功');
|
alert('保存成功');
|
||||||
closeModal();
|
closeModal();
|
||||||
// 重新加载数据以显示更新
|
// 重新加载数据以显示更新
|
||||||
if (searchResultDiv.style.display !== 'none') {
|
if (currentSearchQuery) {
|
||||||
// 如果当前显示的是搜索结果,重新执行搜索
|
// 如果当前显示的是搜索结果,重新执行搜索
|
||||||
const query = searchQueryInput.value.trim();
|
performSearch(isFuzzySearch ? 'fuzzy' : 'exact');
|
||||||
if (query) {
|
|
||||||
const isFuzzy = document.querySelector('.search-result').textContent.includes('模糊');
|
|
||||||
performSearch(isFuzzy ? 'fuzzy' : 'exact');
|
|
||||||
} else {
|
|
||||||
loadAllData();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
loadAllData();
|
loadAllData();
|
||||||
}
|
}
|
||||||
@@ -506,8 +507,20 @@ async function saveEdit(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function doDelete(id){
|
async function doDelete(id){
|
||||||
|
if (isDeleting) {
|
||||||
|
alert('正在处理删除操作,请稍候...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!confirm('确认删除该记录?此操作不可撤销')) return;
|
if(!confirm('确认删除该记录?此操作不可撤销')) return;
|
||||||
|
|
||||||
|
isDeleting = true;
|
||||||
|
const deleteButton = document.querySelector(`button[onclick="doDelete('${id}')"]`);
|
||||||
|
if (deleteButton) {
|
||||||
|
deleteButton.disabled = true;
|
||||||
|
deleteButton.textContent = '删除中...';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/elastic/data/${id}/delete/`, {
|
const response = await fetch(`/elastic/data/${id}/delete/`, {
|
||||||
method:'DELETE',
|
method:'DELETE',
|
||||||
@@ -519,20 +532,32 @@ async function doDelete(id){
|
|||||||
if(data.status!=='success') throw new Error(data.message || '删除失败');
|
if(data.status!=='success') throw new Error(data.message || '删除失败');
|
||||||
|
|
||||||
alert('删除成功');
|
alert('删除成功');
|
||||||
// 重新加载数据
|
// 清空缓存,确保下次加载获取最新数据
|
||||||
if (searchResultDiv.style.display !== 'none') {
|
allDataCache = [];
|
||||||
const query = searchQueryInput.value.trim();
|
|
||||||
if (query) {
|
// 根据当前显示状态重新加载数据
|
||||||
const isFuzzy = document.querySelector('.search-result').textContent.includes('模糊');
|
if (currentSearchQuery) {
|
||||||
performSearch(isFuzzy ? 'fuzzy' : 'exact');
|
// 如果当前显示的是搜索结果,重新执行搜索
|
||||||
} else {
|
performSearch(isFuzzySearch ? 'fuzzy' : 'exact');
|
||||||
loadAllData();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
loadAllData();
|
// 修复:重新加载所有数据时,强制刷新缓存
|
||||||
|
const response = await fetch('/elastic/all-data/');
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.status === 'success') {
|
||||||
|
allDataCache = data.data || [];
|
||||||
|
displayAllData(allDataCache);
|
||||||
|
} else {
|
||||||
|
showSearchMessage(`加载数据失败: ${data.message || '未知错误'}`, 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e.message||'删除失败');
|
alert(e.message||'删除失败');
|
||||||
|
} finally {
|
||||||
|
isDeleting = false;
|
||||||
|
if (deleteButton) {
|
||||||
|
deleteButton.disabled = false;
|
||||||
|
deleteButton.textContent = '删除';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,17 +43,21 @@
|
|||||||
.preview-box h3 {margin-top: 0;color: #334155; }
|
.preview-box h3 {margin-top: 0;color: #334155; }
|
||||||
.preview-box img { max-width: 100%;max-height: 300px;border: 1px solid #e2e8f0;border-radius: 8px;object-fit: contain;}
|
.preview-box img { max-width: 100%;max-height: 300px;border: 1px solid #e2e8f0;border-radius: 8px;object-fit: contain;}
|
||||||
.result-box {flex: 1;}
|
.result-box {flex: 1;}
|
||||||
.result-box h3 { margin-top: 0; color: #334155;}
|
.result-box h3 { margin-top: 0; color: #334155;}
|
||||||
.form-controls { display: flex;gap: 8px;margin-bottom: 12px;flex-wrap: wrap;}
|
.form-controls { display: flex;gap: 8px;margin-bottom: 12px;flex-wrap: wrap;}
|
||||||
#kvForm {border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; max-height: 300px; overflow: auto;margin-bottom: 12px;background: white;}
|
#kvForm {border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; max-height: 300px; overflow: auto;margin-bottom: 12px;background: white;}
|
||||||
.form-row {display: grid;grid-template-columns: 1fr 1fr auto;gap: 8px; margin-bottom: 6px; }
|
.form-row {display: grid;grid-template-columns: 1fr 1fr auto;gap: 8px; margin-bottom: 6px; }
|
||||||
.form-row input {padding: 8px;border: 1px solid #cbd5e1;border-radius: 4px;}
|
.form-row input {padding: 8px;border: 1px solid #cbd5e1;border-radius: 4px;}
|
||||||
#resultBox { width: 100%;min-height: 200px;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size: 14px; padding: 12px; border: 1px solid #e2e8f0;
|
#resultBox { width: 100%;min-height: 200px;font-family: ui-monospace, SFMono-Regular, Menlo, monospace;font-size: 14px; padding: 12px; border: 1px solid #e2e8f0;
|
||||||
border-radius: 8px; resize: vertical;box-sizing: border-box; }
|
border-radius: 8px; resize: vertical;box-sizing: border-box; }
|
||||||
.status-message { padding: 10px; margin: 10px 0; border-radius: 6px; display: none; }
|
.status-message { padding: 10px; margin: 10px 0; border-radius: 6px; display: none; }
|
||||||
.status-message.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
.status-message.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
||||||
.status-message.error { background-color: #f8d7da;color: #721c24; border: 1px solid #f5c6cb; }
|
.status-message.error { background-color: #f8d7da;color: #721c24; border: 1px solid #f5c6cb; }
|
||||||
.action-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
|
.action-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||||
|
.progress {position: relative; height: 12px; background: #e2e8f0; border-radius: 8px; overflow: hidden;}
|
||||||
|
.progress-bar {height: 100%; width: 0; background: linear-gradient(90deg, #4f46e5 0%, #60a5fa 100%); transition: width .2s ease;}
|
||||||
|
.progress-wrap {display:none; margin-top: 8px;}
|
||||||
|
.progress-text {margin-top: 6px; font-size: 12px; color: #334155;}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -90,6 +94,10 @@
|
|||||||
<button type="submit" class="btn btn-primary">上传并识别</button>
|
<button type="submit" class="btn btn-primary">上传并识别</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="status-message" id="uploadMsg"></div>
|
<div class="status-message" id="uploadMsg"></div>
|
||||||
|
<div class="progress-wrap" id="progressWrap">
|
||||||
|
<div class="progress"><div class="progress-bar" id="progressBar"></div></div>
|
||||||
|
<div class="progress-text" id="progressText"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-container">
|
<div class="preview-container">
|
||||||
@@ -136,9 +144,51 @@ const kvForm = document.getElementById('kvForm');
|
|||||||
const addFieldBtn = document.getElementById('addFieldBtn');
|
const addFieldBtn = document.getElementById('addFieldBtn');
|
||||||
const syncFromTextBtn = document.getElementById('syncFromTextBtn');
|
const syncFromTextBtn = document.getElementById('syncFromTextBtn');
|
||||||
const dropArea = document.getElementById('dropArea');
|
const dropArea = document.getElementById('dropArea');
|
||||||
|
const progressWrap = document.getElementById('progressWrap');
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
const progressText = document.getElementById('progressText');
|
||||||
|
|
||||||
let currentImageRel = '';
|
let currentImageRel = '';
|
||||||
|
|
||||||
|
function setProgress(p, text){
|
||||||
|
const v = Math.max(0, Math.min(100, Math.round(p||0)));
|
||||||
|
progressBar.style.width = v + '%';
|
||||||
|
progressText.textContent = (text||'') + (text? ' ' : '') + v + '%';
|
||||||
|
}
|
||||||
|
function showProgress(){
|
||||||
|
progressWrap.style.display = 'block';
|
||||||
|
}
|
||||||
|
function hideProgress(){
|
||||||
|
progressWrap.style.display = 'none';
|
||||||
|
setProgress(0, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function convertToJpeg(file){
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
let img;
|
||||||
|
try{
|
||||||
|
const blob = await fetch(url).then(r=>r.blob());
|
||||||
|
img = await createImageBitmap(blob);
|
||||||
|
}catch(e){
|
||||||
|
img = await new Promise((resolve,reject)=>{const i=new Image();i.onload=()=>resolve(i);i.onerror=reject;i.src=url;});
|
||||||
|
}
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
const maxDim = 2000;
|
||||||
|
const w = img.width;
|
||||||
|
const h = img.height;
|
||||||
|
const scale = Math.min(1, maxDim/Math.max(w,h));
|
||||||
|
const nw = Math.round(w*scale);
|
||||||
|
const nh = Math.round(h*scale);
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = nw;
|
||||||
|
canvas.height = nh;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0, nw, nh);
|
||||||
|
const blob = await new Promise(resolve=>canvas.toBlob(resolve,'image/jpeg',0.82));
|
||||||
|
const name = (file.name||'image').replace(/\.[^/.]+$/, '') + '.jpg';
|
||||||
|
return new File([blob], name, {type:'image/jpeg'});
|
||||||
|
}
|
||||||
|
|
||||||
// 拖拽上传功能
|
// 拖拽上传功能
|
||||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
dropArea.addEventListener(eventName, preventDefaults, false);
|
dropArea.addEventListener(eventName, preventDefaults, false);
|
||||||
@@ -289,20 +339,39 @@ uploadForm.addEventListener('submit', async (e) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showProgress();
|
||||||
|
setProgress(5, '转换为JPG');
|
||||||
|
let jpegFile = file;
|
||||||
|
try {
|
||||||
|
jpegFile = await convertToJpeg(file);
|
||||||
|
setProgress(50, '转换为JPG');
|
||||||
|
preview.src = URL.createObjectURL(jpegFile);
|
||||||
|
} catch (_) {
|
||||||
|
jpegFile = file;
|
||||||
|
setProgress(50, '转换为JPG');
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', jpegFile);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let prog = 50;
|
||||||
|
setProgress(prog, '识别中');
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
prog = Math.min(95, prog + 1);
|
||||||
|
setProgress(prog, '识别中');
|
||||||
|
}, 120);
|
||||||
const resp = await fetch('/elastic/upload/', {
|
const resp = await fetch('/elastic/upload/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: { 'X-CSRFToken': getCookie('csrftoken') || '' },
|
headers: { 'X-CSRFToken': getCookie('csrftoken') || '' },
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
|
clearInterval(timer);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!resp.ok || data.status !== 'success') {
|
if (!resp.ok || data.status !== 'success') {
|
||||||
throw new Error(data.message || '上传识别失败');
|
throw new Error(data.message || '上传识别失败');
|
||||||
}
|
}
|
||||||
|
setProgress(100, '识别完成');
|
||||||
uploadMsg.textContent = data.message || '识别成功';
|
uploadMsg.textContent = data.message || '识别成功';
|
||||||
uploadMsg.className = 'status-message success';
|
uploadMsg.className = 'status-message success';
|
||||||
uploadMsg.style.display = 'block';
|
uploadMsg.style.display = 'block';
|
||||||
@@ -310,10 +379,12 @@ uploadForm.addEventListener('submit', async (e) => {
|
|||||||
renderFormFromObject(data.data || {});
|
renderFormFromObject(data.data || {});
|
||||||
currentImageRel = data.image;
|
currentImageRel = data.image;
|
||||||
confirmBtn.disabled = false;
|
confirmBtn.disabled = false;
|
||||||
|
setTimeout(hideProgress, 800);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uploadMsg.textContent = e.message || '发生错误';
|
uploadMsg.textContent = e.message || '发生错误';
|
||||||
uploadMsg.className = 'status-message error';
|
uploadMsg.className = 'status-message error';
|
||||||
uploadMsg.style.display = 'block';
|
uploadMsg.style.display = 'block';
|
||||||
|
progressText.textContent = '识别失败';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -381,4 +452,4 @@ document.getElementById('logoutBtn').addEventListener('click', async () => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ def ocr_and_extract_info(image_path: str):
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
model="ernie-4.5-turbo-vl-32k",
|
model=getattr(settings, "OPENAI_MODEL_NAME", "ernie-4.5-turbo-vl-32k"),
|
||||||
)
|
)
|
||||||
|
|
||||||
response_text = chat_completion.choices[0].message.content
|
response_text = chat_completion.choices[0].message.content
|
||||||
|
|||||||
Reference in New Issue
Block a user