| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import React, { useState } from 'react';
- import { Card, CardContent } from '@/components/ui/card';
- import { Button } from '@/components/ui/button';
- import { Input } from '@/components/ui/input';
- import { Badge } from '@/components/ui/badge';
- import { Trash2, Edit, X } from 'lucide-react';
- // 获取图片URL的函数
- const getImageUrl = (item) => {
- if (!item.stored_path) return '';
-
- // 从完整路径中提取文件名
- const filename = item.stored_path.split(/[\\/]/).pop();
- if (!filename) return '';
-
- // 构建完整的图片URL
- const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
- return `${baseURL}/materials/${filename}`;
- };
- // 获取素材类型标签配置
- const getMaterialTypeConfig = (imageType) => {
- switch (imageType) {
- case 'face':
- return {
- label: 'IP',
- className: 'bg-red-500 text-white border-red-500'
- };
- case 'cloth':
- return {
- label: '服装',
- className: 'bg-blue-500 text-white border-blue-500'
- };
- case 'original':
- return {
- label: '原图',
- className: 'bg-purple-500 text-white border-purple-500'
- };
- default:
- return null;
- }
- };
- export default function MaterialCard({ material, onDelete, onUpdateName }) {
- const [isEditing, setIsEditing] = useState(false);
- const [editName, setEditName] = useState(material.name || material.original_filename || '');
- const handleSaveName = async () => {
- if (editName.trim() !== material.name) {
- await onUpdateName(material.id, editName.trim());
- }
- setIsEditing(false);
- };
- const handleCancelEdit = () => {
- setEditName(material.name || material.original_filename || '');
- setIsEditing(false);
- };
- const typeConfig = getMaterialTypeConfig(material.image_type);
- return (
- <Card className="overflow-hidden">
- <div className="aspect-square bg-gray-100 relative group">
- <img
- src={getImageUrl(material)}
- alt={material.name || material.original_filename}
- className="w-full h-full object-cover"
- onError={(e) => {
- console.error('图片加载失败:', getImageUrl(material));
- e.target.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik01MCAxMDBMMTAwIDUwTDE1MCAxMDBMMTAwIDE1MFYxMDBIMFYxMDBaIiBmaWxsPSIjOUI5QkEwIi8+Cjwvc3ZnPgo=';
- }}
- />
-
- {/* 删除按钮 */}
- <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
- <Button
- variant="destructive"
- size="sm"
- onClick={(e) => {
- e.stopPropagation();
- onDelete(material.id);
- }}
- >
- <Trash2 className="w-4 h-4" />
- </Button>
- </div>
- {/* 素材类型标签 */}
- {typeConfig && (
- <div className="absolute top-2 right-2">
- <Badge
- className={`text-xs font-medium px-2 py-1 ${typeConfig.className}`}
- >
- {typeConfig.label}
- </Badge>
- </div>
- )}
- </div>
- <CardContent className="p-3">
- {isEditing ? (
- <div className="flex gap-2">
- <Input
- value={editName}
- onChange={(e) => setEditName(e.target.value)}
- className="flex-1"
- autoFocus
- onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveName();
- if (e.key === 'Escape') handleCancelEdit();
- }}
- />
- <Button size="sm" onClick={handleSaveName}>
- 保存
- </Button>
- <Button size="sm" variant="outline" onClick={handleCancelEdit}>
- <X className="w-4 h-4" />
- </Button>
- </div>
- ) : (
- <div className="flex items-center justify-between">
- <span className="text-sm font-medium truncate flex-1">
- {material.name || material.original_filename}
- </span>
- <Button
- size="sm"
- variant="ghost"
- onClick={() => setIsEditing(true)}
- className="ml-2"
- >
- <Edit className="w-4 h-4" />
- </Button>
- </div>
- )}
- </CardContent>
- </Card>
- );
- }
|