<script lang="ts" setup> import { toast } from 'vue-sonner' import { createResource } from '~/api/course' import { uploadFile } from '~/api/file' import type { FetchError, IResource } from '~/types' const props = withDefaults( defineProps<{ modelValue: boolean accept?: string }>(), { accept: '.docx, .pptx, .pdf, .png, .jpg, .mp4, .mp3', }, ) const emit = defineEmits<{ 'update:modelValue': [isOpen: boolean] 'on-create': [resource: IResource] }>() const isDialogOpen = computed({ get: () => props.modelValue, set: (value: boolean) => emit('update:modelValue', value), }) const loginState = useLoginState() // const isDialogOpen = ref(false); const loading = ref(false) const selectedFile = ref<File | null>(null) const onUpload = async () => { if (!selectedFile.value) { toast.error('请先选择文件', { id: 'file-upload-error-no-file-selected' }) return } loading.value = true toast.promise( new Promise((resolve, reject) => { uploadFile(selectedFile.value!, 'resource') .then((url) => { createResource({ resourceName: selectedFile.value!.name, resourceSize: selectedFile.value!.size, resourceType: 'resource', resourceUrl: url, allowDownload: true, isRepo: false, ownerId: loginState.user.userId, }) .then((result) => { if (result.code !== 200) { reject(new Error(result.msg || '文件上传失败')) } else { emit('on-create', { id: result.resourceId, resourceName: selectedFile.value!.name, resourceSize: selectedFile.value!.size, resourceType: 'resource', resourceUrl: url, allowDownload: true, isRepo: false, ownerId: loginState.user.userId, }) resolve('文件上传成功') } }) .catch((error) => { reject(error) }) }) .catch((error) => { reject(error) }) }), { loading: '正在上传文件...', success: () => { isDialogOpen.value = false return '文件上传成功' }, error: (error: FetchError) => { return error.message || '文件上传失败,请稍后重试' }, finally: () => { loading.value = false }, }, ) } </script> <template> <Dialog v-model:open="isDialogOpen"> <DialogTrigger as-child> <slot name="trigger" /> </DialogTrigger> <DialogContent class="sm:max-w-[520px]"> <DialogHeader> <DialogTitle>资源上传</DialogTitle> <DialogDescription> <p>上传资源文件并将其添加到课程中。</p> <p> 支持的格式:<span class="font-medium">{{ accept }}</span> </p> </DialogDescription> </DialogHeader> <div class="flex flex-col gap-4"> <FormField name="file"> <FormItem> <FormLabel>选择文件</FormLabel> <FormControl> <Input type="file" :accept="accept" @change="(e: any) => { const files = e.target.files; if (files && files.length > 0) { selectedFile = files[0]; } else { selectedFile = null; } }" /> </FormControl> <FormMessage /> </FormItem> </FormField> <input type="hidden" name="courseId" /> <div class="text-xs text-muted-foreground space-y-2"> <p> 根据国家《出版管理条例》《网络出版服务管理规定》及教育部《职业教育专业教学资源库建设工作手册》等相关规定,上传的资源必须符合以下要求: </p> <ul class="list-disc list-inside space-y-1 text-[11px] text-muted-foreground/80 text-justify" > <li> 没有法律、法规禁止出版的内容,没有政治性、道德性问题和科学性错误,不泄露国家秘密。 </li> <li> 不含有侵犯他人著作权、肖像权、名誉权等权益的内容,资源具有原创性,引用需指明作者姓名、作品名称,使用他人作品应取得许可。 </li> <li> 采用法定计量单位,名词、术语、符号等符合国家统一规定,尚无统一规定的,可采用习惯用法并保持一致。 </li> <li> 地图具有严肃的政治性、严密的科学性和严格的法定性,使用的地图应根据《地图管理条例》的要求已送相关部门审核并标注审图号。 </li> <li>不含有商业广告、商业性宣传内容。</li> <li>不含有色情、赌博、迷信、暴力等内容。</li> </ul> </div> </div> <DialogFooter> <Button :disabled="loading" @click="onUpload" > {{ loading ? "上传中..." : "上传" }} </Button> </DialogFooter> </DialogContent> </Dialog> </template> <style scoped></style>