23 Commits

Author SHA1 Message Date
281ade6ac9 增加了进度条,提升等待感知[ci][0.2.4]
All checks were successful
CI / docker-ci (push) Successful in 34s
2025-11-27 12:21:08 +08:00
835426b133 修复了不支持webp格式的图片上传的问题
All checks were successful
CI / docker-ci (push) Has been skipped
2025-11-27 12:11:58 +08:00
d001fec21e 搞定(应该)😅[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 35s
2025-11-27 11:39:15 +08:00
253de3639c 😅😅😅😅[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 32s
2025-11-27 11:33:49 +08:00
a0507b8054 😅😅😅[ci][0.2.3]
Some checks failed
CI / docker-ci (push) Failing after 28s
2025-11-27 11:31:38 +08:00
9f803880fa 😅😅[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 32s
2025-11-27 11:25:13 +08:00
71fe964476 😅[ci][0.2.3]
Some checks failed
CI / docker-ci (push) Failing after 59s
2025-11-27 11:22:19 +08:00
0f5c8c08ff 再试一次[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 31s
2025-11-27 11:18:00 +08:00
e032253327 使用act_runner的服务器以提供下载[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 31s
2025-11-27 11:08:34 +08:00
3f108e2138 调整了一下yml进行构建和发布[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 5m31s
2025-11-26 22:33:12 +08:00
2d913e397f 调整了一下yml进行构建和发布[ci][0.2.3]
All checks were successful
CI / docker-ci (push) Successful in 4m45s
2025-11-26 22:24:15 +08:00
74bc8aa498 调整了一下yml进行构建和发布[ci][0.2.3] 2025-11-26 22:11:34 +08:00
5d747faee1 调整了一下yml进行构建和发布[ci][0.2.3]
Some checks failed
CI / docker-ci (push) Failing after 30s
2025-11-26 22:07:50 +08:00
7bd8eeca77 调整了一下yml进行构建和发布[ci][0.2.3] 2025-11-26 22:01:14 +08:00
782b2dd82e 调整了一下yml进行构建和发布[ci][0.2.3]
Some checks failed
CI / docker-ci (push) Failing after 30s
2025-11-26 21:58:10 +08:00
f9c0abb3a0 调整了一下yml进行构建和发布[ci][0.2.3]
Some checks failed
CI / docker-ci (push) Failing after 29s
2025-11-26 21:55:50 +08:00
c5300591e6 调整了一下yml进行构建和发布[ci][0.2.3] 2025-11-26 21:51:52 +08:00
f96629566f 调整了一下yml[ci]
All checks were successful
CI / docker-ci (push) Successful in 13m56s
2025-11-26 18:12:03 +08:00
8d581ac638 不尝试对镜像进行测试[ci]
Some checks failed
CI / docker-ci (push) Failing after 4s
2025-11-26 18:09:13 +08:00
acc80074ea 使用[ci]触发工作流
Some checks failed
CI / docker-ci (push) Failing after 3m0s
2025-11-26 18:00:35 +08:00
DSQ
62d28be032 数据管理页面删除时刷新页面 2025-11-22 15:59:31 +08:00
DSQ
5b956e1365 数据管理页面删除时刷新页面 2025-11-22 13:05:29 +08:00
DSQ
7485ba16e6 修复了数据管理页面删除时不能及时刷新页面的BUG 2025-11-22 12:10:01 +08:00
3 changed files with 228 additions and 31 deletions

101
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,101 @@
name: CI
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
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
SERVER_DEST_DIR: /srv/ci
DOWNLOAD_BASE: http://139.224.69.213:8080
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 /srv/ci
run: |
set -e
ART=$(cat "$GITHUB_WORKSPACE/.artifact_name")
cat "$GITHUB_WORKSPACE/$ARTIFACT_DIR/$ART" | docker run --rm -i -v "$SERVER_DEST_DIR:/srvci" "$IMAGE_NAME:$VERSION" sh -c "cat > /srvci/$ART && ls -l /srvci"
echo "published: $SERVER_DEST_DIR/$ART"
- 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%/}"
BASE="${BASE%/ci}"
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"

View File

@@ -167,6 +167,9 @@ let currentId = '';
let currentWriter = '';
let currentImage = '';
let allDataCache = []; // 缓存所有数据,避免重复请求
let currentSearchQuery = ''; // 记录当前搜索查询
let isFuzzySearch = false; // 记录当前是否为模糊搜索
let isDeleting = false; // 标记是否正在删除
// 图片缩放相关变量
let currentScale = 1;
@@ -186,6 +189,8 @@ async function performSearch(type) {
return;
}
currentSearchQuery = query;
isFuzzySearch = type === 'fuzzy';
showSearchLoading();
try {
@@ -244,6 +249,7 @@ function showSearchMessage(message, type = '') {
// 加载所有数据
async function loadAllData() {
currentSearchQuery = '';
showSearchLoading();
try {
@@ -282,6 +288,7 @@ function displayAllData(data) {
function clearSearch() {
searchQueryInput.value = '';
searchResultDiv.style.display = 'none';
currentSearchQuery = '';
// 如果有缓存数据,显示全部
if (allDataCache.length > 0) {
@@ -488,15 +495,9 @@ async function saveEdit(){
alert('保存成功');
closeModal();
// 重新加载数据以显示更新
if (searchResultDiv.style.display !== 'none') {
if (currentSearchQuery) {
// 如果当前显示的是搜索结果,重新执行搜索
const query = searchQueryInput.value.trim();
if (query) {
const isFuzzy = document.querySelector('.search-result').textContent.includes('模糊');
performSearch(isFuzzy ? 'fuzzy' : 'exact');
} else {
loadAllData();
}
performSearch(isFuzzySearch ? 'fuzzy' : 'exact');
} else {
loadAllData();
}
@@ -506,8 +507,20 @@ async function saveEdit(){
}
async function doDelete(id){
if (isDeleting) {
alert('正在处理删除操作,请稍候...');
return;
}
if(!confirm('确认删除该记录?此操作不可撤销')) return;
isDeleting = true;
const deleteButton = document.querySelector(`button[onclick="doDelete('${id}')"]`);
if (deleteButton) {
deleteButton.disabled = true;
deleteButton.textContent = '删除中...';
}
try {
const response = await fetch(`/elastic/data/${id}/delete/`, {
method:'DELETE',
@@ -519,20 +532,32 @@ async function doDelete(id){
if(data.status!=='success') throw new Error(data.message || '删除失败');
alert('删除成功');
// 重新加载数据
if (searchResultDiv.style.display !== 'none') {
const query = searchQueryInput.value.trim();
if (query) {
const isFuzzy = document.querySelector('.search-result').textContent.includes('模糊');
performSearch(isFuzzy ? 'fuzzy' : 'exact');
} else {
loadAllData();
}
// 清空缓存,确保下次加载获取最新数据
allDataCache = [];
// 根据当前显示状态重新加载数据
if (currentSearchQuery) {
// 如果当前显示的是搜索结果,重新执行搜索
performSearch(isFuzzySearch ? 'fuzzy' : 'exact');
} 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) {
alert(e.message||'删除失败');
} finally {
isDeleting = false;
if (deleteButton) {
deleteButton.disabled = false;
deleteButton.textContent = '删除';
}
}
}

View File

@@ -43,17 +43,21 @@
.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;}
.result-box {flex: 1;}
.result-box h3 { margin-top: 0; color: #334155;}
.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;}
.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;}
#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; }
.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.error { background-color: #f8d7da;color: #721c24; border: 1px solid #f5c6cb; }
.action-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
.result-box h3 { margin-top: 0; color: #334155;}
.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;}
.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;}
#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; }
.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.error { background-color: #f8d7da;color: #721c24; border: 1px solid #f5c6cb; }
.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>
</head>
<body>
@@ -90,6 +94,10 @@
<button type="submit" class="btn btn-primary">上传并识别</button>
</form>
<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 class="preview-container">
@@ -136,9 +144,51 @@ const kvForm = document.getElementById('kvForm');
const addFieldBtn = document.getElementById('addFieldBtn');
const syncFromTextBtn = document.getElementById('syncFromTextBtn');
const dropArea = document.getElementById('dropArea');
const progressWrap = document.getElementById('progressWrap');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
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 => {
dropArea.addEventListener(eventName, preventDefaults, false);
@@ -289,20 +339,39 @@ uploadForm.addEventListener('submit', async (e) => {
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();
formData.append('file', file);
formData.append('file', jpegFile);
try {
let prog = 50;
setProgress(prog, '识别中');
const timer = setInterval(() => {
prog = Math.min(95, prog + 1);
setProgress(prog, '识别中');
}, 120);
const resp = await fetch('/elastic/upload/', {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-CSRFToken': getCookie('csrftoken') || '' },
body: formData,
});
clearInterval(timer);
const data = await resp.json();
if (!resp.ok || data.status !== 'success') {
throw new Error(data.message || '上传识别失败');
}
setProgress(100, '识别完成');
uploadMsg.textContent = data.message || '识别成功';
uploadMsg.className = 'status-message success';
uploadMsg.style.display = 'block';
@@ -310,10 +379,12 @@ uploadForm.addEventListener('submit', async (e) => {
renderFormFromObject(data.data || {});
currentImageRel = data.image;
confirmBtn.disabled = false;
setTimeout(hideProgress, 800);
} catch (e) {
uploadMsg.textContent = e.message || '发生错误';
uploadMsg.className = 'status-message error';
uploadMsg.style.display = 'block';
progressText.textContent = '识别失败';
}
});
@@ -381,4 +452,4 @@ document.getElementById('logoutBtn').addEventListener('click', async () => {
});
</script>
</body>
</html>
</html>