|
|
@@ -0,0 +1,261 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import type { UploadFile, UploadProps } from 'ant-design-vue';
|
|
|
+
|
|
|
+import type { MedicalCaseLibrarySubmitVO } from '#/api/outcome';
|
|
|
+
|
|
|
+import { ref } from 'vue';
|
|
|
+
|
|
|
+import {
|
|
|
+ InboxOutlined,
|
|
|
+ PictureOutlined,
|
|
|
+ UploadOutlined,
|
|
|
+} from '@ant-design/icons-vue';
|
|
|
+import { message, Upload, UploadDragger } from 'ant-design-vue';
|
|
|
+
|
|
|
+import { useEditShell } from '#/adapter/shell';
|
|
|
+import { invokeMethod } from '#/adapter/vxe-table/proxy/invoke-method';
|
|
|
+import { uploadFileMethod } from '#/api/common';
|
|
|
+import { useWorkroomStore } from '#/stores';
|
|
|
+
|
|
|
+import { medicalCaseLibraryForm } from '../medical-case-library.data';
|
|
|
+
|
|
|
+const workroomStore = useWorkroomStore();
|
|
|
+
|
|
|
+const pdfFileList = ref<UploadFile[]>([]);
|
|
|
+const videoFileList = ref<UploadFile[]>([]);
|
|
|
+const thumbnailFileList = ref<UploadFile[]>([]);
|
|
|
+const pdfUrl = ref<string>();
|
|
|
+const videoUrl = ref<string>();
|
|
|
+const videoThumbnailUrl = ref<string>();
|
|
|
+const pdfUploading = ref(false);
|
|
|
+const videoUploading = ref(false);
|
|
|
+const thumbnailUploading = ref(false);
|
|
|
+
|
|
|
+function createUploadFile(url: string | undefined, name: string): UploadFile[] {
|
|
|
+ if (!url) return [];
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ uid: '-1',
|
|
|
+ name,
|
|
|
+ status: 'done',
|
|
|
+ url,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
+function resetUploads() {
|
|
|
+ pdfFileList.value = [];
|
|
|
+ videoFileList.value = [];
|
|
|
+ thumbnailFileList.value = [];
|
|
|
+ pdfUrl.value = void 0;
|
|
|
+ videoUrl.value = void 0;
|
|
|
+ videoThumbnailUrl.value = void 0;
|
|
|
+ pdfUploading.value = false;
|
|
|
+ videoUploading.value = false;
|
|
|
+ thumbnailUploading.value = false;
|
|
|
+}
|
|
|
+
|
|
|
+async function uploadImmediately(
|
|
|
+ file: File,
|
|
|
+ listRef: typeof pdfFileList,
|
|
|
+ urlRef: typeof pdfUrl,
|
|
|
+ uploadingRef: typeof pdfUploading,
|
|
|
+) {
|
|
|
+ const uploadFile: UploadFile = {
|
|
|
+ uid: `${Date.now()}`,
|
|
|
+ name: file.name,
|
|
|
+ status: 'uploading',
|
|
|
+ percent: 0,
|
|
|
+ };
|
|
|
+ listRef.value = [uploadFile];
|
|
|
+ uploadingRef.value = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const url = await invokeMethod(uploadFileMethod(file), { force: true });
|
|
|
+ if (!url) {
|
|
|
+ throw new Error('upload empty url');
|
|
|
+ }
|
|
|
+ urlRef.value = url;
|
|
|
+ listRef.value = [
|
|
|
+ {
|
|
|
+ ...uploadFile,
|
|
|
+ status: 'done',
|
|
|
+ url,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ } catch {
|
|
|
+ urlRef.value = void 0;
|
|
|
+ listRef.value = [
|
|
|
+ {
|
|
|
+ ...uploadFile,
|
|
|
+ status: 'error',
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ message.error(`${file.name} 上传失败,请重试`);
|
|
|
+ } finally {
|
|
|
+ uploadingRef.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const { Form, Shell } = useEditShell<MedicalCaseLibrarySubmitVO>(
|
|
|
+ medicalCaseLibraryForm,
|
|
|
+ {
|
|
|
+ handleLoad(model) {
|
|
|
+ resetUploads();
|
|
|
+ pdfUrl.value = model.pdfUrl;
|
|
|
+ videoUrl.value = model.video?.url;
|
|
|
+ videoThumbnailUrl.value = model.video?.thumbnailUrl;
|
|
|
+ pdfFileList.value = createUploadFile(model.pdfUrl, '医案.pdf');
|
|
|
+ videoFileList.value = createUploadFile(model.video?.url, '讲解视频');
|
|
|
+ thumbnailFileList.value = createUploadFile(
|
|
|
+ model.video?.thumbnailUrl,
|
|
|
+ '视频首图',
|
|
|
+ );
|
|
|
+ return model;
|
|
|
+ },
|
|
|
+ handleSubmit(values) {
|
|
|
+ const workroomId = values.workroomId || workroomStore.workroomId;
|
|
|
+ if (!workroomId) {
|
|
|
+ message.error('请先选择工作室');
|
|
|
+ throw new Error('workroom required');
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ pdfUploading.value ||
|
|
|
+ videoUploading.value ||
|
|
|
+ thumbnailUploading.value
|
|
|
+ ) {
|
|
|
+ message.warning('文件上传中,请稍候');
|
|
|
+ throw new Error('uploading');
|
|
|
+ }
|
|
|
+ if (!pdfUrl.value) {
|
|
|
+ message.error('请上传医案PDF文件');
|
|
|
+ throw new Error('pdf required');
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...values,
|
|
|
+ workroomId,
|
|
|
+ pdfUrl: pdfUrl.value,
|
|
|
+ video: {
|
|
|
+ ...values.video,
|
|
|
+ url: videoUrl.value,
|
|
|
+ thumbnailUrl: videoThumbnailUrl.value,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ onClosed: resetUploads,
|
|
|
+ },
|
|
|
+);
|
|
|
+
|
|
|
+const beforePdfUpload: UploadProps['beforeUpload'] = (file) => {
|
|
|
+ const isPdf =
|
|
|
+ file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
|
|
|
+ if (!isPdf) {
|
|
|
+ message.error('仅支持PDF格式文件');
|
|
|
+ return Upload.LIST_IGNORE;
|
|
|
+ }
|
|
|
+ void uploadImmediately(file, pdfFileList, pdfUrl, pdfUploading);
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+const beforeVideoUpload: UploadProps['beforeUpload'] = (file) => {
|
|
|
+ const maxSize = 10 * 1024 * 1024;
|
|
|
+ if (file.size > maxSize) {
|
|
|
+ message.error('上传视频大小不超过10M');
|
|
|
+ return Upload.LIST_IGNORE;
|
|
|
+ }
|
|
|
+ void uploadImmediately(file, videoFileList, videoUrl, videoUploading);
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+const beforeThumbnailUpload: UploadProps['beforeUpload'] = (file) => {
|
|
|
+ const isImage = file.type.startsWith('image/');
|
|
|
+ if (!isImage) {
|
|
|
+ message.error('请上传图片文件');
|
|
|
+ return Upload.LIST_IGNORE;
|
|
|
+ }
|
|
|
+ void uploadImmediately(
|
|
|
+ file,
|
|
|
+ thumbnailFileList,
|
|
|
+ videoThumbnailUrl,
|
|
|
+ thumbnailUploading,
|
|
|
+ );
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+function onPdfRemove() {
|
|
|
+ pdfUrl.value = void 0;
|
|
|
+}
|
|
|
+
|
|
|
+function onVideoRemove() {
|
|
|
+ videoUrl.value = void 0;
|
|
|
+}
|
|
|
+
|
|
|
+function onThumbnailRemove() {
|
|
|
+ videoThumbnailUrl.value = void 0;
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <Shell>
|
|
|
+ <div class="mx-4 max-h-[70vh] overflow-y-auto pr-1">
|
|
|
+ <Form />
|
|
|
+
|
|
|
+ <div class="col-span-2 pb-4">
|
|
|
+ <div class="mb-2 text-sm">
|
|
|
+ <span class="text-red-500">*</span>
|
|
|
+ 医案PDF文件
|
|
|
+ </div>
|
|
|
+ <UploadDragger
|
|
|
+ v-model:file-list="pdfFileList"
|
|
|
+ :before-upload="beforePdfUpload"
|
|
|
+ :max-count="1"
|
|
|
+ accept=".pdf,application/pdf"
|
|
|
+ @remove="onPdfRemove"
|
|
|
+ >
|
|
|
+ <p class="ant-upload-drag-icon">
|
|
|
+ <InboxOutlined />
|
|
|
+ </p>
|
|
|
+ <p class="ant-upload-text">点击上传或拖拽PDF文件到此处</p>
|
|
|
+ <p class="ant-upload-hint">仅支持PDF格式文件</p>
|
|
|
+ </UploadDragger>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-span-2 pb-4">
|
|
|
+ <div class="mb-2 text-sm">讲解视频</div>
|
|
|
+ <Upload
|
|
|
+ v-model:file-list="videoFileList"
|
|
|
+ :before-upload="beforeVideoUpload"
|
|
|
+ :max-count="1"
|
|
|
+ accept="video/*"
|
|
|
+ list-type="picture"
|
|
|
+ @remove="onVideoRemove"
|
|
|
+ >
|
|
|
+ <a-button :loading="videoUploading">
|
|
|
+ <UploadOutlined />
|
|
|
+ 上传
|
|
|
+ </a-button>
|
|
|
+ <template #tip>
|
|
|
+ <div class="mt-1 text-xs text-gray-400">上传视频大小不超过10M</div>
|
|
|
+ </template>
|
|
|
+ </Upload>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-span-2 pb-2">
|
|
|
+ <div class="mb-2 text-sm">讲解视频首图</div>
|
|
|
+ <Upload
|
|
|
+ v-model:file-list="thumbnailFileList"
|
|
|
+ :before-upload="beforeThumbnailUpload"
|
|
|
+ :max-count="1"
|
|
|
+ accept="image/*"
|
|
|
+ list-type="picture-card"
|
|
|
+ @remove="onThumbnailRemove"
|
|
|
+ >
|
|
|
+ <div v-if="thumbnailFileList.length === 0" class="text-gray-400">
|
|
|
+ <PictureOutlined class="text-2xl" />
|
|
|
+ </div>
|
|
|
+ </Upload>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Shell>
|
|
|
+</template>
|