Files
video-gen/video-gen-admin/src/pages/homeMaterials/WatermarkLibraryModal.tsx
T
2026-06-30 17:10:51 +08:00

208 lines
7.5 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.
import React, { useEffect, useState } from 'react';
import { Button, Form, Image, Input, Modal, Popconfirm, Space, Switch, Table, Upload, message } from 'antd';
import { DeleteOutlined, EditOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
import { deleteHomeMaterialWatermark, getHomeMaterialWatermarks, updateHomeMaterialWatermark, uploadHomeMaterialWatermark } from '../../api';
import type { HomeMaterialWatermark } from '../../types';
import { apiUrl } from '../../utils/resourceUrl';
interface Props {
open?: boolean;
onClose?: () => void;
embedded?: boolean;
onChanged?: () => void;
}
const WatermarkLibraryModal: React.FC<Props> = ({ open = true, onClose, embedded = false, onChanged }) => {
const [items, setItems] = useState<HomeMaterialWatermark[]>([]);
const [loading, setLoading] = useState(false);
const [uploadOpen, setUploadOpen] = useState(false);
const [editing, setEditing] = useState<HomeMaterialWatermark | null>(null);
const [file, setFile] = useState<File | null>(null);
const [fileList, setFileList] = useState<any[]>([]);
const [previewUrl, setPreviewUrl] = useState<string>('');
const [submitting, setSubmitting] = useState(false);
const [form] = Form.useForm();
const load = async () => {
setLoading(true);
try {
const res = await getHomeMaterialWatermarks({ page: 1, pageSize: 200 });
setItems(res.items);
} finally {
setLoading(false);
}
};
useEffect(() => { if (open) load(); }, [open]);
useEffect(() => {
return () => {
if (previewUrl.startsWith('blob:')) URL.revokeObjectURL(previewUrl);
};
}, [previewUrl]);
const resetUploadState = () => {
setFile(null);
setFileList([]);
setPreviewUrl('');
setSubmitting(false);
};
const openUpload = () => {
setEditing(null);
resetUploadState();
form.setFieldsValue({ name: '', is_default: false, is_active: true });
setUploadOpen(true);
};
const openEdit = (row: HomeMaterialWatermark) => {
setEditing(row);
resetUploadState();
setPreviewUrl(apiUrl(row.fileUrl));
form.setFieldsValue({ name: row.name, is_default: row.isDefault, is_active: row.isActive });
setUploadOpen(true);
};
const closeUploadModal = () => {
if (submitting) return;
setUploadOpen(false);
resetUploadState();
};
const handleBeforeUpload = (selectedFile: File) => {
if (!selectedFile.type.startsWith('image/')) {
message.warning('请选择图片文件');
return Upload.LIST_IGNORE;
}
if (previewUrl.startsWith('blob:')) URL.revokeObjectURL(previewUrl);
const nextPreviewUrl = URL.createObjectURL(selectedFile);
setFile(selectedFile);
setPreviewUrl(nextPreviewUrl);
setFileList([{ uid: selectedFile.name, name: selectedFile.name, status: 'done', originFileObj: selectedFile }]);
return false;
};
const handleRemoveFile = () => {
setFile(null);
setFileList([]);
if (previewUrl.startsWith('blob:')) URL.revokeObjectURL(previewUrl);
setPreviewUrl('');
};
const submit = async () => {
if (submitting) return;
const values = await form.validateFields();
if (!editing && !file) {
message.warning('请选择水印图片');
return;
}
setSubmitting(true);
try {
if (editing) {
await updateHomeMaterialWatermark(editing.id, { name: values.name, is_default: values.is_default, is_active: values.is_active });
} else if (file) {
await uploadHomeMaterialWatermark(file, values.name, values.is_default);
}
message.success('保存成功');
setUploadOpen(false);
resetUploadState();
await load();
onChanged?.();
} finally {
setSubmitting(false);
}
};
const table = (
<>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<div />
<Button type="primary" icon={<PlusOutlined />} onClick={openUpload}>上传水印</Button>
</div>
<Table
rowKey="id"
loading={loading}
dataSource={items}
pagination={{ pageSize: 20 }}
columns={[
{ title: '预览', dataIndex: 'fileUrl', render: (url: string) => <Image width={80} src={apiUrl(url)} /> },
{ title: '名称', dataIndex: 'name' },
{ title: '尺寸', render: (_: unknown, r: HomeMaterialWatermark) => r.width && r.height ? `${r.width}×${r.height}` : '-' },
{ title: '默认', dataIndex: 'isDefault', render: (v: boolean) => v ? '是' : '否' },
{ title: '启用', dataIndex: 'isActive', render: (v: boolean) => v ? '启用' : '禁用' },
{
title: '操作',
render: (_: unknown, row: HomeMaterialWatermark) => (
<Space>
<Button size="small" icon={<EditOutlined />} onClick={() => openEdit(row)}>编辑</Button>
<Popconfirm title="确认删除该水印?" onConfirm={async () => { await deleteHomeMaterialWatermark(row.id); message.success('删除成功'); await load(); onChanged?.(); }}>
<Button size="small" danger icon={<DeleteOutlined />}>删除</Button>
</Popconfirm>
</Space>
),
},
]}
/>
<Modal
title={editing ? '编辑水印' : '上传水印'}
open={uploadOpen}
onOk={submit}
onCancel={closeUploadModal}
confirmLoading={submitting}
okButtonProps={{ disabled: submitting || (!editing && !file) }}
cancelButtonProps={{ disabled: submitting }}
destroyOnHidden
>
<Form form={form} layout="vertical">
{!editing && (
<Form.Item label="水印图片" required>
<Upload
beforeUpload={(f) => handleBeforeUpload(f as File)}
onRemove={handleRemoveFile}
fileList={fileList}
maxCount={1}
accept="image/png,image/jpeg,image/webp"
disabled={submitting}
>
<Button icon={<UploadOutlined />} loading={submitting} disabled={submitting}>选择图片</Button>
</Upload>
{previewUrl && (
<div style={{ marginTop: 12 }}>
<Image
src={previewUrl}
alt="水印预览"
width={180}
style={{ maxHeight: 120, objectFit: 'contain', background: '#0f172a', borderRadius: 8, padding: 8 }}
/>
</div>
)}
</Form.Item>
)}
{editing && previewUrl && (
<Form.Item label="当前水印">
<Image
src={previewUrl}
alt="当前水印"
width={180}
style={{ maxHeight: 120, objectFit: 'contain', background: '#0f172a', borderRadius: 8, padding: 8 }}
/>
</Form.Item>
)}
<Form.Item name="name" label="水印名称" rules={[{ required: true, message: '请输入水印名称' }]}><Input disabled={submitting} /></Form.Item>
<Form.Item name="is_default" label="默认水印" valuePropName="checked"><Switch disabled={submitting} /></Form.Item>
<Form.Item name="is_active" label="启用" valuePropName="checked"><Switch disabled={submitting} /></Form.Item>
</Form>
</Modal>
</>
);
if (embedded) return table;
return <Modal title="水印库" open={open} onCancel={onClose} footer={null} width={900} destroyOnHidden>{table}</Modal>;
};
export default WatermarkLibraryModal;