lty/qy-lty-admin/components/food/food-media-upload.tsx
pmc bd95ba470c feat: update admin panel, API modules, and add migrations
- Update food, outfits, props, home-decor pages and components
- Add permissions page and sidebar updates
- Update API client and all API modules (auth, food, dances, etc.)
- Add card model migrations for optional fields
- Update Django views, serializers, and authentication
- Add affinity level migrations and user app updates
- Add project documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 13:06:50 +08:00

251 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import React, { useState } from 'react'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { FileUpload } from '@/components/ui/file-upload'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Image, Play, Music, Trash2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
interface FoodMediaUploadProps {
/** 当前图片URL */
imageUrl?: string
/** 当前动画URL */
animationUrl?: string
/** 当前音频URL */
audioUrl?: string
/** 图片上传成功回调 */
onImageUpload?: (url: string) => void
/** 动画上传成功回调 */
onAnimationUpload?: (url: string) => void
/** 音频上传成功回调 */
onAudioUpload?: (url: string) => void
/** 文件移除回调 */
onRemove?: (type: 'image' | 'animation' | 'audio') => void
/** 是否禁用 */
disabled?: boolean
}
export function FoodMediaUpload({
imageUrl,
animationUrl,
audioUrl,
onImageUpload,
onAnimationUpload,
onAudioUpload,
onRemove,
disabled = false,
}: FoodMediaUploadProps) {
const [activeTab, setActiveTab] = useState('image')
return (
<div className="space-y-4">
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="image" className="flex items-center gap-2">
<Image className="h-4 w-4" />
</TabsTrigger>
<TabsTrigger value="animation" className="flex items-center gap-2">
<Play className="h-4 w-4" />
</TabsTrigger>
<TabsTrigger value="audio" className="flex items-center gap-2">
<Music className="h-4 w-4" />
</TabsTrigger>
</TabsList>
{/* 图片上传 */}
<TabsContent value="image" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Image className="h-5 w-5" />
</CardTitle>
<CardDescription>
JPEGPNGGIF 300x300px
</CardDescription>
</CardHeader>
<CardContent>
{imageUrl ? (
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border">
<div className="flex items-center space-x-3">
<img
src={imageUrl}
alt="食物图片预览"
className="h-16 w-16 object-cover rounded border"
/>
<span className="text-sm text-gray-700"></span>
</div>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => onRemove?.('image')}
className="text-red-500 hover:text-red-700 hover:bg-red-50"
disabled={disabled}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
) : (
<FileUpload
imageOnly={true}
multiple={false}
maxFiles={1}
maxSize={5 * 1024 * 1024}
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.gif', '.webp'] }}
placeholder="选择或拖拽食物图片"
disabled={disabled}
onUploadSuccess={(files) => {
if (files.length > 0) {
onImageUpload?.(files[0].url)
}
}}
onRemove={() => onRemove?.('image')}
/>
)}
</CardContent>
</Card>
</TabsContent>
{/* 动画上传 */}
<TabsContent value="animation" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Play className="h-5 w-5" />
</CardTitle>
<CardDescription>
MP4GIFLottie 50MB
</CardDescription>
</CardHeader>
<CardContent>
{animationUrl ? (
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border">
<div className="flex items-center space-x-3">
{animationUrl.endsWith('.gif') ? (
<img
src={animationUrl}
alt="动画预览"
className="h-16 w-16 object-cover rounded border"
/>
) : animationUrl.match(/\.(mp4|avi|mov|wmv|flv)$/i) ? (
<video
src={animationUrl}
className="h-16 w-16 object-cover rounded border"
muted
loop
autoPlay
/>
) : (
<div className="h-16 w-16 bg-gray-200 rounded border flex items-center justify-center">
<Play className="h-6 w-6 text-gray-500" />
</div>
)}
<span className="text-sm text-gray-700"></span>
</div>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => onRemove?.('animation')}
className="text-red-500 hover:text-red-700 hover:bg-red-50"
disabled={disabled}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
) : (
<FileUpload
imageOnly={false}
multiple={false}
maxFiles={1}
maxSize={50 * 1024 * 1024}
accept={{
'video/*': ['.mp4', '.avi', '.mov', '.wmv', '.flv'],
'image/gif': ['.gif'],
'application/json': ['.json'],
'application/x-lottie': ['.lottie']
}}
placeholder="选择或拖拽动画文件"
disabled={disabled}
onUploadSuccess={(files) => {
if (files.length > 0) {
onAnimationUpload?.(files[0].url)
}
}}
onRemove={() => onRemove?.('animation')}
/>
)}
</CardContent>
</Card>
</TabsContent>
{/* 音频上传 */}
<TabsContent value="audio" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Music className="h-5 w-5" />
</CardTitle>
<CardDescription>
MP3WAVOGG 20MB
</CardDescription>
</CardHeader>
<CardContent>
{audioUrl ? (
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border">
<div className="flex items-center space-x-3">
<div className="h-16 w-16 bg-blue-100 rounded border flex items-center justify-center">
<Music className="h-6 w-6 text-blue-500" />
</div>
<div className="flex-1">
<span className="text-sm text-gray-700 block"></span>
<audio controls className="mt-1 w-full max-w-xs">
<source src={audioUrl} />
</audio>
</div>
</div>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => onRemove?.('audio')}
className="text-red-500 hover:text-red-700 hover:bg-red-50"
disabled={disabled}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
) : (
<FileUpload
imageOnly={false}
multiple={false}
maxFiles={1}
maxSize={20 * 1024 * 1024}
accept={{
'audio/*': ['.mp3', '.wav', '.ogg', '.aac', '.flac', '.wma', '.m4a']
}}
placeholder="选择或拖拽音频文件"
disabled={disabled}
onUploadSuccess={(files) => {
if (files.length > 0) {
onAudioUpload?.(files[0].url)
}
}}
onRemove={() => onRemove?.('audio')}
/>
)}
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
)
}