4578b65965
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m30s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m37s
CI/CD Pipeline / Frontend Lint (push) Successful in 3m3s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m18s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m18s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 3m17s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m33s
CI/CD Pipeline / Integration Tests (push) Successful in 2m22s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m19s
CI/CD Pipeline / Unit Tests (push) Failing after 6m57s
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m31s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 16s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m46s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m59s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
import React from "react"
|
|
import type { MediaAsset } from "@/api/template-editor"
|
|
import { MATERIAL_TYPE_LABELS, MATERIAL_TYPE_ICONS } from "@/api/template-editor"
|
|
import { formatSize, formatDuration, getQualityLevel } from "./utils"
|
|
|
|
interface AssetCardProps {
|
|
asset: MediaAsset
|
|
isSelected: boolean
|
|
isDragging: boolean
|
|
isDragOver: boolean
|
|
showCheckbox: boolean
|
|
compact?: boolean
|
|
onToggleSelect: (asset: MediaAsset, shiftKey: boolean) => void
|
|
onCardClick: (asset: MediaAsset, e: React.MouseEvent) => void
|
|
onDragStart: (e: React.DragEvent) => void
|
|
onDragOver: (e: React.DragEvent) => void
|
|
onDrop: (e: React.DragEvent) => void
|
|
onDragEnd: () => void
|
|
onMouseEnter: (e: React.MouseEvent) => void
|
|
onMouseLeave: () => void
|
|
}
|
|
|
|
const AssetCard: React.FC<AssetCardProps> = ({
|
|
asset,
|
|
isSelected,
|
|
isDragging,
|
|
isDragOver,
|
|
showCheckbox,
|
|
onToggleSelect,
|
|
onCardClick,
|
|
onDragStart,
|
|
onDragOver,
|
|
onDrop,
|
|
onDragEnd,
|
|
onMouseEnter,
|
|
onMouseLeave,
|
|
}) => {
|
|
const qualityLevel = getQualityLevel(asset.quality_score)
|
|
|
|
return (
|
|
<div
|
|
className={[
|
|
"as-card",
|
|
isSelected ? "selected" : "",
|
|
isDragging ? "dragging" : "",
|
|
isDragOver ? "drag-over" : "",
|
|
showCheckbox ? "has-checkbox" : "",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ")}
|
|
draggable
|
|
onDragStart={onDragStart}
|
|
onDragOver={onDragOver}
|
|
onDrop={onDrop}
|
|
onDragEnd={onDragEnd}
|
|
onClick={(e) => onCardClick(asset, e)}
|
|
onMouseEnter={onMouseEnter}
|
|
onMouseLeave={onMouseLeave}
|
|
>
|
|
{/* 缩略图 */}
|
|
<div className="as-card-thumb">
|
|
{asset.thumbnail_url ? (
|
|
<img src={asset.thumbnail_url} alt={asset.name} loading="lazy" />
|
|
) : (
|
|
<span className="as-card-thumb-icon">{MATERIAL_TYPE_ICONS[asset.type]}</span>
|
|
)}
|
|
|
|
{/* Checkbox */}
|
|
{showCheckbox && (
|
|
<span
|
|
data-checkbox
|
|
className={`as-card-checkbox${isSelected ? " checked" : ""}`}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
onToggleSelect(asset, e.shiftKey)
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* 类型角标 */}
|
|
<span className="as-card-type-badge">{MATERIAL_TYPE_LABELS[asset.type]}</span>
|
|
|
|
{/* 时长角标 */}
|
|
{asset.duration != null && (
|
|
<span className="as-card-duration">{formatDuration(asset.duration)}</span>
|
|
)}
|
|
|
|
{/* 质量分角标 */}
|
|
{asset.quality_score != null && (
|
|
<span
|
|
className={`as-card-quality ${qualityLevel}`}
|
|
title={`质量分: ${asset.quality_score}`}
|
|
>
|
|
{asset.quality_score}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* 信息 */}
|
|
<div className="as-card-info">
|
|
<p className="as-card-name" title={asset.name}>
|
|
{asset.name}
|
|
</p>
|
|
<div className="as-card-meta">{formatSize(asset.size)}</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default AssetCard
|