Merge pull request #71 from solar2ain/fix/webui-improvements (closes #70, closes #72)

fix: 修复媒体文件路径缺失目录前缀 & WebUI 请求历史改进
This commit is contained in:
FoxHui
2026-04-16 01:48:20 +08:00
committed by GitHub
Unverified
7 changed files with 75 additions and 10 deletions
+13
View File
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.6.6] - 2026-04-12
### 🐛 Fixed
- **WebUI**
- 修复媒体文件服务接口未拼接目录前缀导致图片无法预览的问题 (ref #70)
- 历史模块路径改为绝对路径,避免非项目根目录启动时路径解析失败
### ✨ Added
- **WebUI**
- 请求历史 Prompt 列支持点击弹窗预览完整内容
- 预览弹窗新增「复制全文」按钮
- 重发失败记录时自动删除旧的失败条目
## [3.6.5] - 2026-04-09
### ✨ Added
+4 -2
View File
@@ -43,7 +43,8 @@ import {
deleteByDateRange as deleteHistoryByDateRange,
retryMediaDownload,
getStats as getHistoryStats,
getModelList as getHistoryModelList
getModelList as getHistoryModelList,
getMediaDir
} from '../../../utils/history.js';
import path from 'path';
import fs from 'fs/promises';
@@ -519,7 +520,8 @@ export function createAdminRouter(context) {
}
try {
const data = await fs.readFile(filepath);
const fullPath = path.join(getMediaDir(), filepath);
const data = await fs.readFile(fullPath);
const ext = path.extname(filepath).toLowerCase();
const mimeTypes = {
'.png': 'image/png',
+1 -1
View File
@@ -8,7 +8,7 @@ import fs from 'fs/promises';
import path from 'path';
import { logger } from './logger.js';
const DATA_DIR = 'data/history';
const DATA_DIR = path.join(process.cwd(), 'data', 'history');
const DB_PATH = path.join(DATA_DIR, 'history.db');
const MEDIA_DIR = path.join(DATA_DIR, 'media');
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+54 -4
View File
@@ -15,7 +15,8 @@ import {
RocketOutlined,
RedoOutlined,
InboxOutlined,
LoadingOutlined
LoadingOutlined,
CopyOutlined
} from '@ant-design/icons-vue';
import { message, Modal } from 'ant-design-vue';
@@ -52,6 +53,7 @@ const previewModalVisible = ref(false);
const previewContent = ref('');
const previewMediaType = ref('text'); // text, image, video
const previewMediaUrl = ref('');
const previewTitle = ref('快速预览');
// 媒体数据缓存 (blob URLs)
const mediaCache = ref({});
@@ -466,6 +468,7 @@ const handleRefresh = () => {
const previewResponse = async (record) => {
previewModalVisible.value = true;
previewMediaType.value = 'text';
previewTitle.value = '响应预览';
if (record.status === 'failed') {
previewContent.value = record.error_message || '未知错误';
} else {
@@ -473,6 +476,24 @@ const previewResponse = async (record) => {
}
};
// 快速预览 Prompt 内容
const previewPrompt = (record) => {
previewModalVisible.value = true;
previewMediaType.value = 'text';
previewTitle.value = 'Prompt 预览';
previewContent.value = record.prompt || '无内容';
};
// 复制预览内容到剪贴板
const copyPreviewContent = async () => {
try {
await navigator.clipboard.writeText(previewContent.value);
message.success('已复制到剪贴板');
} catch (e) {
message.error('复制失败');
}
};
// 快速预览媒体
const previewMedia = async (record) => {
const media = getFirstMedia(record);
@@ -510,6 +531,7 @@ const closePreview = () => {
previewContent.value = '';
previewMediaUrl.value = '';
previewMediaType.value = 'text';
previewTitle.value = '快速预览';
};
// 多选变化
@@ -641,6 +663,20 @@ const sendRequest = () => {
}, 1000);
};
// 静默删除记录(不弹确认框)
const silentDeleteRecord = async (id) => {
try {
await fetch('/admin/history', {
method: 'DELETE',
headers: {
...settingsStore.getHeaders(),
'Content-Type': 'application/json'
},
body: JSON.stringify({ ids: [id] })
});
} catch (e) { /* 静默失败 */ }
};
// 从历史记录重发
const resendFromRecord = (record) => {
const modelId = record.model_id || record.model_name;
@@ -651,7 +687,15 @@ const resendFromRecord = (record) => {
sendPrompt.value = record.prompt;
}
sendImageList.value = [];
// 如果原记录是失败状态(没有生成回复或图片),重发后删除旧记录
const shouldDelete = record.status === 'failed';
sendRequest();
if (shouldDelete) {
silentDeleteRecord(record.id);
}
};
// === 自动刷新 ===
@@ -877,9 +921,9 @@ onUnmounted(() => {
@change="handleTableChange"
>
<template #bodyCell="{ column, record }">
<!-- Prompt 支持多行 -->
<!-- Prompt 支持多行点击弹出预览 -->
<template v-if="column.key === 'prompt'">
<div class="multiline-text">
<div class="multiline-text clickable" @click="previewPrompt(record)" title="点击查看完整内容">
{{ truncateText(record.prompt, 120) }}
</div>
</template>
@@ -1067,7 +1111,13 @@ onUnmounted(() => {
@cancel="closePreview"
>
<template #title>
<span>快速预览</span>
<div style="display: flex; align-items: center; gap: 8px;">
<span>{{ previewTitle }}</span>
<a-button v-if="previewMediaType === 'text'" type="text" size="small" @click="copyPreviewContent">
<template #icon><CopyOutlined /></template>
复制全文
</a-button>
</div>
</template>
<div v-if="previewMediaType === 'text'" class="preview-text-content">
{{ previewContent }}