This commit is contained in:
ditiqi 2025-02-25 08:46:22 +08:00
parent 2db4063368
commit 3d02643de3
4 changed files with 55 additions and 14 deletions

View File

@ -24,7 +24,8 @@ export default function BaseSettingPage() {
const [isFormChanged, setIsFormChanged] = useState(false); const [isFormChanged, setIsFormChanged] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { user, hasSomePermissions } = useAuth(); const { user, hasSomePermissions } = useAuth();
const { pageWidth } = useContext?.(MainLayoutContext); const context = useContext(MainLayoutContext);
const pageWidth = context?.pageWidth;
function handleFieldsChange() { function handleFieldsChange() {
setIsFormChanged(true); setIsFormChanged(true);
} }
@ -43,7 +44,6 @@ export default function BaseSettingPage() {
} }
async function onSubmit(values: BaseSetting) { async function onSubmit(values: BaseSetting) {
setLoading(true); setLoading(true);
try { try {
await update.mutateAsync({ await update.mutateAsync({
where: { where: {

View File

@ -11,6 +11,7 @@ export interface TusUploaderProps {
value?: string[]; value?: string[];
onChange?: (value: string[]) => void; onChange?: (value: string[]) => void;
multiple?: boolean; multiple?: boolean;
allowTypes?: string[];
} }
interface UploadingFile { interface UploadingFile {
@ -25,8 +26,8 @@ export const TusUploader = ({
value = [], value = [],
onChange, onChange,
multiple = true, multiple = true,
allowTypes = undefined,
}: TusUploaderProps) => { }: TusUploaderProps) => {
const { handleFileUpload, uploadProgress } = useTusUpload(); const { handleFileUpload, uploadProgress } = useTusUpload();
const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([]); const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([]);
const [completedFiles, setCompletedFiles] = useState<UploadingFile[]>( const [completedFiles, setCompletedFiles] = useState<UploadingFile[]>(
@ -61,7 +62,10 @@ export const TusUploader = ({
const handleBeforeUpload = useCallback( const handleBeforeUpload = useCallback(
(file: File) => { (file: File) => {
if (allowTypes && !allowTypes.includes(file.type)) {
toast.error(`文件类型 ${file.type} 不在允许范围内`);
return Upload.LIST_IGNORE; // 使用 antd 的官方阻止方式
}
const fileKey = `${file.name}-${Date.now()}`; const fileKey = `${file.name}-${Date.now()}`;
setUploadingFiles((prev) => [ setUploadingFiles((prev) => [
@ -136,10 +140,10 @@ export const TusUploader = ({
return ( return (
<div className="space-y-1"> <div className="space-y-1">
<Upload.Dragger <Upload.Dragger
accept={allowTypes?.join(",")}
name="files" name="files"
multiple={multiple} multiple={multiple}
showUploadList={false} showUploadList={false}
beforeUpload={handleBeforeUpload}> beforeUpload={handleBeforeUpload}>
<p className="ant-upload-drag-icon"> <p className="ant-upload-drag-icon">
<UploadOutlined /> <UploadOutlined />
@ -149,6 +153,11 @@ export const TusUploader = ({
</p> </p>
<p className="ant-upload-hint"> <p className="ant-upload-hint">
{multiple ? "支持单个或批量上传文件" : "仅支持上传单个文件"} {multiple ? "支持单个或批量上传文件" : "仅支持上传单个文件"}
{allowTypes && (
<span className="block text-xs text-gray-500">
: {allowTypes.join(", ")}
</span>
)}
</p> </p>
<div className="px-2 py-0 rounded mt-1"> <div className="px-2 py-0 rounded mt-1">
@ -165,10 +174,10 @@ export const TusUploader = ({
file.status === "done" file.status === "done"
? 100 ? 100
: Math.round( : Math.round(
uploadProgress?.[ uploadProgress?.[
file.fileKey! file.fileKey!
] || 0 ] || 0
) )
} }
status={ status={
file.status === "error" file.status === "error"

View File

@ -10,7 +10,13 @@ import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor"; import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor";
import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
import { Lecture, LectureType, LessonTypeLabel, PostType } from "@nice/common"; import {
Lecture,
LectureType,
LessonTypeLabel,
PostType,
videoMimeTypes,
} from "@nice/common";
import { usePost } from "@nice/client"; import { usePost } from "@nice/client";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
@ -134,7 +140,9 @@ export const SortableLecture: React.FC<SortableLectureProps> = ({
name="title" name="title"
initialValue={field?.title} initialValue={field?.title}
className="mb-0 flex-1" className="mb-0 flex-1"
rules={[{ required: true }]}> rules={[
{ required: true, message: "请输入课时标题" },
]}>
<Input placeholder="课时标题" /> <Input placeholder="课时标题" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@ -158,14 +166,24 @@ export const SortableLecture: React.FC<SortableLectureProps> = ({
<Form.Item <Form.Item
name={["meta", "videoIds"]} name={["meta", "videoIds"]}
className="mb-0 flex-1" className="mb-0 flex-1"
rules={[{ required: true }]}> rules={[
<TusUploader multiple={false} /> {
required: true,
message: "请输入课时标题",
},
]}>
<TusUploader
allowTypes={videoMimeTypes}
multiple={false}
/>
</Form.Item> </Form.Item>
) : ( ) : (
<Form.Item <Form.Item
name="content" name="content"
className="mb-0 flex-1" className="mb-0 flex-1"
rules={[{ required: true }]}> rules={[
{ required: true, message: "请输入内容" },
]}>
<QuillEditor /> <QuillEditor />
</Form.Item> </Form.Item>
)} )}

View File

@ -81,3 +81,17 @@ export const InitAppConfigs: Prisma.AppConfigCreateInput[] = [
description: "", description: "",
}, },
]; ];
export const videoMimeTypes = [
"video/*", // 通配符 (部分浏览器可能不支持)
"video/mp4", // .mp4
"video/quicktime", // .mov
"video/x-msvideo", // .avi
"video/x-matroska", // .mkv
"video/webm", // .webm
"video/ogg", // .ogv
"video/mpeg", // .mpeg
"video/3gpp", // .3gp
"video/3gpp2", // .3g2
"video/x-flv", // .flv
"video/x-ms-wmv", // .wmv
];