feat(files): 新增前端上传与订单证据图片

This commit is contained in:
yml2213
2026-10-30 14:08:11 +08:00
parent 8499dac163
commit 4fc4e9236b
5 changed files with 324 additions and 14 deletions
+132
View File
@@ -0,0 +1,132 @@
import { useState } from 'react'
import { App, Button, Image, Input, Space, Typography, Upload } from 'antd'
import { DeleteOutlined, LinkOutlined, UploadOutlined } from '@ant-design/icons'
import { isImageFileURL, uploadFile } from '@/shared/api/files'
interface EvidenceUploadProps {
scene: string
value?: string[]
onChange?: (value: string[]) => void
max?: number
disabled?: boolean
}
export function EvidenceUpload({
scene,
value = [],
onChange,
max = 9,
disabled = false,
}: EvidenceUploadProps) {
const { message } = App.useApp()
const [uploading, setUploading] = useState(false)
const [link, setLink] = useState('')
const add = (url: string) => {
const trimmed = url.trim()
if (!trimmed || value.includes(trimmed)) return
onChange?.([...value, trimmed])
}
const remove = (url: string) => {
onChange?.(value.filter(item => item !== url))
}
const handleFile = async (file: File) => {
if (value.length >= max) {
message.warning(`最多上传 ${max} 张图片`)
return
}
if (!file.type.startsWith('image/')) {
message.error('证据图片仅支持 JPG、PNG、WebP 格式')
return
}
if (file.size > 10 * 1024 * 1024) {
message.error('单张图片不能超过 10MB')
return
}
setUploading(true)
try {
const uploaded = await uploadFile(file, scene)
add(uploaded.url)
message.success('图片已上传')
} catch (error) {
message.error(error instanceof Error ? error.message : '图片上传失败')
} finally {
setUploading(false)
}
}
const addLink = () => {
const trimmed = link.trim()
if (!trimmed) return
if (!/^https?:\/\//i.test(trimmed)) {
message.warning('请填写以 http:// 或 https:// 开头的链接')
return
}
add(trimmed)
setLink('')
}
return (
<div className="evidence-upload">
<Space wrap size={10} className="evidence-upload__grid">
{value.map(url => (
<div className="evidence-upload__item" key={url}>
{isImageFileURL(url) ? (
<Image src={url} alt="证据图片" width={92} height={92} className="evidence-upload__image" />
) : (
<a href={url} target="_blank" rel="noreferrer" className="evidence-upload__link-card">
<LinkOutlined />
<span>外部链接</span>
</a>
)}
<Button
type="text"
size="small"
danger
icon={<DeleteOutlined />}
className="evidence-upload__remove"
aria-label="删除证据"
onClick={() => remove(url)}
/>
</div>
))}
<Upload
accept="image/jpeg,image/png,image/webp"
multiple
showUploadList={false}
disabled={disabled || uploading}
beforeUpload={file => {
void handleFile(file)
return false
}}
>
<button type="button" className="evidence-upload__add" disabled={disabled || uploading}>
<UploadOutlined />
<span>{uploading ? '上传中…' : '上传图片'}</span>
</button>
</Upload>
</Space>
<Space.Compact style={{ width: '100%', marginTop: 10 }}>
<Input
value={link}
placeholder="也可以粘贴外部证据链接"
prefix={<LinkOutlined />}
disabled={disabled}
onChange={event => setLink(event.target.value)}
onPressEnter={addLink}
/>
<Button onClick={addLink} disabled={disabled || !link.trim()}>
添加
</Button>
</Space.Compact>
<Typography.Text type="secondary" className="evidence-upload__hint">
最多 {max} 条,支持图片或外部链接。
</Typography.Text>
</div>
)
}