修改缩略图

This commit is contained in:
sjy
2026-07-28 17:33:48 +08:00
parent 7077d05d6a
commit 15deed1cf4
8 changed files with 141 additions and 132 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -27,7 +27,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>民众智创</title> <title>民众智创</title>
<script type="module" crossorigin src="/assets/index-9q5zWXAc.js"></script> <script type="module" crossorigin src="/assets/index-BioNCQJL.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-IrbjYl3V.css"> <link rel="stylesheet" crossorigin href="/assets/index-IrbjYl3V.css">
</head> </head>
<body> <body>
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { LoadingOutlined, PlayCircleFilled, WarningOutlined } from '@ant-design/icons'; import { LoadingOutlined, PlayCircleFilled, WarningOutlined } from '@ant-design/icons';
import { resolveGenerationUiState } from '../../utils/generationTaskStatus'; import { resolveGenerationUiState } from '../../utils/generationTaskStatus';
import { buildImagePreviewUrl } from '../../utils/previewUrl';
export interface GenerationTaskResourceItem { export interface GenerationTaskResourceItem {
id?: string; id?: string;
@@ -273,9 +274,9 @@ const GenerationTaskResourceGrid: React.FC<Props> = ({ task, onPreview, resolveU
{items.slice(0, 5).map((item, index) => { {items.slice(0, 5).map((item, index) => {
const uiState = resolveGenerationUiState(item); const uiState = resolveGenerationUiState(item);
const displayStatus = uiState.effectiveKey; const displayStatus = uiState.effectiveKey;
const imageUrl = item.imageUrl ? resolveUrl(`/static${item.imageUrl}&w=300&q=50`) : ''; const imageUrl = item.imageUrl ? resolveUrl(buildImagePreviewUrl(item.imageUrl)) : '';
const videoUrl = resolveUrl(item.videoUrl); const videoUrl = resolveUrl(item.videoUrl);
const coverUrl = item.videoCoverUrl ? resolveUrl(`/static${item.videoCoverUrl}&w=300&q=50`) : ''; const coverUrl = item.videoCoverUrl ? resolveUrl(buildImagePreviewUrl(item.videoCoverUrl)) : '';
const isVideo = (item.genType || task.genType) === 'video'; const isVideo = (item.genType || task.genType) === 'video';
const hasResource = isVideo ? !!videoUrl : !!imageUrl; const hasResource = isVideo ? !!videoUrl : !!imageUrl;
const pending = uiState.isActive; const pending = uiState.isActive;
@@ -59,7 +59,7 @@ const getAssetPreviewUrl = (asset: PrivatePortraitSelectableAsset) => {
if (asset.assetType === 'Video') { if (asset.assetType === 'Video') {
return getPreviewUrl(asset.videoCoverUrl || asset.previewUrl || asset.displayUrl || asset.providerUrl); return getPreviewUrl(asset.videoCoverUrl || asset.previewUrl || asset.displayUrl || asset.providerUrl);
} }
return getPreviewUrl(asset.previewUrl || asset.displayUrl || asset.videoCoverUrl || asset.providerUrl); return getPreviewUrl('static' + (asset.previewUrl || asset.displayUrl || asset.videoCoverUrl || asset.providerUrl)+"?w=300&q=50");
}; };
const formatDuration = (value?: number | null) => { const formatDuration = (value?: number | null) => {
@@ -339,6 +339,7 @@ const PrivatePortraitAssetPicker: React.FC<PrivatePortraitAssetPickerProps> = ({
isVideo ? ( isVideo ? (
<video src={previewUrl} muted style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> <video src={previewUrl} muted style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
) : ( ) : (
<img src={previewUrl} alt={asset.name || meta.fallbackName} style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> <img src={previewUrl} alt={asset.name || meta.fallbackName} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
) )
) : isVideo ? ( ) : isVideo ? (
@@ -414,7 +414,8 @@ const UploadResourceHistoryPicker: React.FC<UploadResourceHistoryPickerProps> =
const selectedDisabled = selectedIdSet.has(item.id); const selectedDisabled = selectedIdSet.has(item.id);
const validationError = getItemValidationError(item); const validationError = getItemValidationError(item);
const disabled = selectedDisabled || !!validationError; const disabled = selectedDisabled || !!validationError;
const preview = buildPreviewUrl(item.previewUrl || item.displayUrl || item.resourceUrl); const baseUrl = item.previewUrl || item.displayUrl || item.resourceUrl;
const preview = buildPreviewUrl(item.resourceType === 'image' ? 'static' + baseUrl + '?w=300&q=50' : baseUrl);
return ( return (
<div <div
key={item.id} key={item.id}
+5 -5
View File
@@ -24,6 +24,7 @@ import { useSearchParams } from 'react-router-dom';
import { PrivatePortraitLibraryPanel } from '../components/privatePortrait'; import { PrivatePortraitLibraryPanel } from '../components/privatePortrait';
import { UploadResourceHistoryPanel } from '../components/uploadResource'; import { UploadResourceHistoryPanel } from '../components/uploadResource';
import { gethistory, gethistoryItems, getOAuthList, asyncBatchUploadMaterial, updateFilename, getUploadHistory, getAllOAuthAccountList, getOpenTypeAll, getPreTestList, getDefaultPreTest, deleteHistory, deleteResourcesMaterial } from '../api'; import { gethistory, gethistoryItems, getOAuthList, asyncBatchUploadMaterial, updateFilename, getUploadHistory, getAllOAuthAccountList, getOpenTypeAll, getPreTestList, getDefaultPreTest, deleteHistory, deleteResourcesMaterial } from '../api';
import { buildImagePreviewUrl } from '../utils/previewUrl';
const { Search } = Input; const { Search } = Input;
const { Text } = Typography; const { Text } = Typography;
@@ -160,11 +161,11 @@ const GeneratedRecord: React.FC = () => {
}; };
const buildUrl = (path: string, isImage: boolean = false): string => { const buildUrl = (path: string, isImage: boolean = false): string => {
const baseUrl = import.meta.env.VITE_API_BASE || "http://localhost:8000"; const baseUrl = import.meta.env.VITE_API_BASE || "http://localhost:8000";
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
const cleanBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; const cleanBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
if (isImage) { if (isImage) {
return `${cleanBase}/static/${cleanPath}&w=300&q=50`; return `${cleanBase}${buildImagePreviewUrl(path)}`;
} }
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
return `${cleanBase}/${cleanPath}`; return `${cleanBase}/${cleanPath}`;
}; };
// 下载文件 // 下载文件
@@ -1671,7 +1672,7 @@ const GeneratedRecord: React.FC = () => {
/> />
) : ( ) : (
<img <img
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${previewItem.imageUrl}&w=300&q=50`} src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${buildImagePreviewUrl(previewItem.imageUrl)}`}
alt="预览" alt="预览"
style={{ style={{
maxWidth: '100%', maxWidth: '100%',
@@ -1945,9 +1946,8 @@ const GeneratedRecord: React.FC = () => {
return <Typography.Text style={{ fontSize: 12, color: '#94a3b8' }}></Typography.Text>; return <Typography.Text style={{ fontSize: 12, color: '#94a3b8' }}></Typography.Text>;
} }
const baseUrl = import.meta.env.VITE_API_BASE || "http://localhost:8000"; const baseUrl = import.meta.env.VITE_API_BASE || "http://localhost:8000";
const cleanPath = coverField.startsWith('/') ? coverField.slice(1) : coverField;
const cleanBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; const cleanBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
const coverUrl = `${cleanBase}/static/${cleanPath}&w=300&q=50`; const coverUrl = `${cleanBase}${buildImagePreviewUrl(coverField)}`;
return ( return (
<img <img
src={coverUrl} src={coverUrl}
@@ -24,6 +24,7 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { uploadHotOpeningVideo, uploadHotOpeningImage, generateReplication, getReplicationList, deleteHotOpeningReplicationTask, getEngine, calculateCredits } from '../api'; import { uploadHotOpeningVideo, uploadHotOpeningImage, generateReplication, getReplicationList, deleteHotOpeningReplicationTask, getEngine, calculateCredits } from '../api';
import { buildImagePreviewUrl } from '../utils/previewUrl';
import bg1 from '../assets/bg1.png'; import bg1 from '../assets/bg1.png';
import UploadSelector from '../components/UploadSelector'; import UploadSelector from '../components/UploadSelector';
import { useAuthStore } from '../store/useAuthStore'; import { useAuthStore } from '../store/useAuthStore';
@@ -609,7 +610,7 @@ const GenerateConver: React.FC = () => {
<img <img
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}/static${item.finalVideoCoverUrl}&w=300&q=50`} src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${buildImagePreviewUrl(item.finalVideoCoverUrl)}`}
alt={item.targetProjectName} alt={item.targetProjectName}
style={{ width: '100%', height: '100%', objectFit: 'cover' }} style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/> />
+5
View File
@@ -0,0 +1,5 @@
/** 构建图片预览 URL,自动拼接 /static 前缀和缩放参数 */
export function buildImagePreviewUrl(url: string): string {
const cleanPath = url.startsWith('/') ? url.slice(1) : url;
return `/static/${cleanPath}&w=300&q=50`;
}