|
@@ -0,0 +1,358 @@
|
|
|
+import { PropType, computed, defineComponent, reactive, ref } from 'vue';
|
|
|
+import styles from './index.module.less';
|
|
|
+import {
|
|
|
+ NButton,
|
|
|
+ NSpace,
|
|
|
+ NUpload,
|
|
|
+ NUploadDragger,
|
|
|
+ UploadFileInfo,
|
|
|
+ useMessage
|
|
|
+} from 'naive-ui';
|
|
|
+import iconUploadAdd from '../../../images/icon-upload-add.png';
|
|
|
+import { NaturalTypeEnum, PageEnum } from '/src/enums/pageEnum';
|
|
|
+import { policy } from '/src/components/upload-file/api';
|
|
|
+import { formatUrlType } from '../upload-modal';
|
|
|
+import axios from 'axios';
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'save-modal',
|
|
|
+ props: {
|
|
|
+ fileList: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ imageList: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ accept: {
|
|
|
+ // 支持类型
|
|
|
+ type: String,
|
|
|
+ default: '.jpg,.png,.jpeg,.gif'
|
|
|
+ },
|
|
|
+ showType: {
|
|
|
+ type: String as PropType<'default' | 'custom'>,
|
|
|
+ default: 'default'
|
|
|
+ },
|
|
|
+ showFileList: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ max: {
|
|
|
+ type: Number as PropType<number>,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ multiple: {
|
|
|
+ type: Boolean as PropType<boolean>,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ disabled: {
|
|
|
+ type: Boolean as PropType<boolean>,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ bucketName: {
|
|
|
+ type: String,
|
|
|
+ default: 'gyt'
|
|
|
+ },
|
|
|
+ directoryDnd: {
|
|
|
+ type: Boolean as PropType<boolean>,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ path: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ fileName: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ emits: ['close', 'confrim'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const ossUploadUrl = `https://${props.bucketName}.ks3-cn-beijing.ksyuncs.com/`;
|
|
|
+ const message = useMessage();
|
|
|
+ const visiable = ref<boolean>(false);
|
|
|
+ const btnLoading = ref<boolean>(false);
|
|
|
+ const tempFiileBuffer = ref();
|
|
|
+ const uploadRef = ref();
|
|
|
+ const state = reactive([]) as any;
|
|
|
+
|
|
|
+ const fileListRef = ref<UploadFileInfo[]>([]);
|
|
|
+ const uploadList = ref([] as any);
|
|
|
+ const onBeforeUpload = async (options: any) => {
|
|
|
+ const file = options.file;
|
|
|
+ // 文件大小
|
|
|
+ let isLt2M = true;
|
|
|
+
|
|
|
+ const type = file.type.includes('image')
|
|
|
+ ? NaturalTypeEnum.IMG
|
|
|
+ : file.type.includes('audio')
|
|
|
+ ? NaturalTypeEnum.SONG
|
|
|
+ : file.type.includes('video')
|
|
|
+ ? NaturalTypeEnum.VIDEO
|
|
|
+ : 'other';
|
|
|
+
|
|
|
+ console.log(type, 'type');
|
|
|
+ if (type === 'other') {
|
|
|
+ message.error(`文件格式不支持`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const size = type === 'IMG' ? 2 : type === 'SONG' ? 20 : 500;
|
|
|
+ if (size) {
|
|
|
+ isLt2M = file.file.size / 1024 / 1024 < size;
|
|
|
+ if (!isLt2M) {
|
|
|
+ const typeStr =
|
|
|
+ type === 'IMG' ? '图片' : type === 'SONG' ? '音频' : '视频';
|
|
|
+ message.error(`${typeStr}大小不能超过${size}M`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isLt2M) {
|
|
|
+ return isLt2M;
|
|
|
+ }
|
|
|
+ // 是否裁切
|
|
|
+ // if (props.cropper && type === 'IMG') {
|
|
|
+ // getBase64(file.file, (imageUrl: any) => {
|
|
|
+ // const target = Object.assign({}, props.options, {
|
|
|
+ // img: imageUrl,
|
|
|
+ // name: file.file.name // 上传文件名
|
|
|
+ // });
|
|
|
+ // visiable.value = true;
|
|
|
+
|
|
|
+ // setTimeout(() => {
|
|
|
+ // CropperModal.value?.edit(target);
|
|
|
+ // }, 100);
|
|
|
+ // });
|
|
|
+ // return false;
|
|
|
+ // }
|
|
|
+
|
|
|
+ try {
|
|
|
+ btnLoading.value = true;
|
|
|
+ const name = file.file.name;
|
|
|
+ const suffix = name.slice(name.lastIndexOf('.'));
|
|
|
+ const fileName = `${props.path}${Date.now() + file.id + suffix}`;
|
|
|
+ const obj = {
|
|
|
+ filename: fileName,
|
|
|
+ bucketName: props.bucketName,
|
|
|
+ postData: {
|
|
|
+ filename: fileName,
|
|
|
+ acl: 'public-read',
|
|
|
+ key: fileName,
|
|
|
+ unknowValueField: []
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const { data } = await policy(obj);
|
|
|
+
|
|
|
+ state.push({
|
|
|
+ id: file.id,
|
|
|
+ tempFiileBuffer: file.file,
|
|
|
+ policy: data.policy,
|
|
|
+ signature: data.signature,
|
|
|
+ acl: 'public-read',
|
|
|
+ key: fileName,
|
|
|
+ KSSAccessKeyId: data.kssAccessKeyId,
|
|
|
+ name: fileName
|
|
|
+ });
|
|
|
+
|
|
|
+ // tempFiileBuffer.value = file.file;
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ // message.error('上传失败')
|
|
|
+ btnLoading.value = false;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+ const getBase64 = async (img: any, callback: any) => {
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.addEventListener('load', () => callback(reader.result));
|
|
|
+ reader.readAsDataURL(img);
|
|
|
+ };
|
|
|
+
|
|
|
+ const onFinish = (options: any) => {
|
|
|
+ // console.log(options, 'onFinish');
|
|
|
+ onFinishAfter(options);
|
|
|
+ };
|
|
|
+ const onFinishAfter = async (options: any) => {
|
|
|
+ const item = state.find((c: any) => c.id == options.file.id);
|
|
|
+ const url = ossUploadUrl + item.key;
|
|
|
+ const type = formatUrlType(url);
|
|
|
+ let coverImg = '';
|
|
|
+ if (type === 'IMG') {
|
|
|
+ coverImg = url;
|
|
|
+ } else if (type === 'SONG') {
|
|
|
+ coverImg = PageEnum.SONG_DEFAULT_COVER;
|
|
|
+ } else if (type === 'VIDEO') {
|
|
|
+ // 获取视频封面图
|
|
|
+ coverImg = await getVideoCoverImg(item.tempFiileBuffer);
|
|
|
+ }
|
|
|
+ // emit('update:fileList', url);
|
|
|
+ // emit('readFileInputEventAsArrayBuffer', item.tempFiileBuffer);
|
|
|
+ // console.log(url, 'url onFinishAfter');
|
|
|
+ // emit('finished', {
|
|
|
+ // coverImg,
|
|
|
+ // content: url
|
|
|
+ // });
|
|
|
+
|
|
|
+ uploadList.value.push({
|
|
|
+ coverImg,
|
|
|
+ content: url,
|
|
|
+ id: options.file.id,
|
|
|
+ name: options.file.name
|
|
|
+ ? options.file.name.slice(0, options.file.name.lastIndexOf('.'))
|
|
|
+ : ''
|
|
|
+ });
|
|
|
+
|
|
|
+ options.file.url = url;
|
|
|
+ visiable.value = false;
|
|
|
+ btnLoading.value = false;
|
|
|
+ };
|
|
|
+ const getVideoMsg = (file: any) => {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ // let dataURL = '';
|
|
|
+ const videoElement = document.createElement('video');
|
|
|
+ videoElement.currentTime = 1;
|
|
|
+ videoElement.src = URL.createObjectURL(file);
|
|
|
+ videoElement.addEventListener('loadeddata', function () {
|
|
|
+ const canvas: any = document.createElement('canvas'),
|
|
|
+ width = videoElement.videoWidth, //canvas的尺寸和图片一样
|
|
|
+ height = videoElement.videoHeight;
|
|
|
+ canvas.width = width;
|
|
|
+ canvas.height = height;
|
|
|
+ canvas.getContext('2d').drawImage(videoElement, 0, 0, width, height); //绘制canvas
|
|
|
+ // dataURL = canvas.toDataURL('image/jpeg'); //转换为base64
|
|
|
+ // console.log(canvas);
|
|
|
+ canvas.toBlob((blob: any) => {
|
|
|
+ // console.log(blob);
|
|
|
+ resolve(blob);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const getVideoCoverImg = async (file: any) => {
|
|
|
+ try {
|
|
|
+ btnLoading.value = true;
|
|
|
+ const imgBlob: any = await getVideoMsg(file || tempFiileBuffer.value);
|
|
|
+ const fileName = `${props.path}${Date.now() + '.png'}`;
|
|
|
+ const obj = {
|
|
|
+ filename: fileName,
|
|
|
+ bucketName: props.bucketName,
|
|
|
+ postData: {
|
|
|
+ filename: fileName,
|
|
|
+ acl: 'public-read',
|
|
|
+ key: fileName,
|
|
|
+ unknowValueField: []
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const { data } = await policy(obj);
|
|
|
+
|
|
|
+ const fileParams = {
|
|
|
+ policy: data.policy,
|
|
|
+ signature: data.signature,
|
|
|
+ key: fileName,
|
|
|
+ acl: 'public-read',
|
|
|
+ KSSAccessKeyId: data.kssAccessKeyId,
|
|
|
+ name: fileName
|
|
|
+ } as any;
|
|
|
+ const formData = new FormData();
|
|
|
+ for (const key in fileParams) {
|
|
|
+ formData.append(key, fileParams[key]);
|
|
|
+ }
|
|
|
+
|
|
|
+ formData.append('file', imgBlob);
|
|
|
+ await axios.post(ossUploadUrl, formData);
|
|
|
+
|
|
|
+ const url = ossUploadUrl + fileName;
|
|
|
+ return url;
|
|
|
+ } finally {
|
|
|
+ btnLoading.value = false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onRemove = async (file: any) => {
|
|
|
+ const index = uploadList.value.findIndex(
|
|
|
+ (update: any) => update.id === file.file.id
|
|
|
+ );
|
|
|
+ uploadList.value.splice(index, 1);
|
|
|
+ btnLoading.value = false;
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+
|
|
|
+ const uploadStatus = computed(() => {
|
|
|
+ let status = false;
|
|
|
+ fileListRef.value.forEach((file: any) => {
|
|
|
+ if (file.status !== 'finished') {
|
|
|
+ status = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return status || fileListRef.value.length <= 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ const onSubmit = async () => {
|
|
|
+ const list: any = [];
|
|
|
+ fileListRef.value.forEach((file: any) => {
|
|
|
+ const item = uploadList.value.find(
|
|
|
+ (child: any) => child.id === file.id
|
|
|
+ );
|
|
|
+ if (item) {
|
|
|
+ list.push(item);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ emit('confrim', list);
|
|
|
+ };
|
|
|
+
|
|
|
+ return () => (
|
|
|
+ <div class={styles.saveModal}>
|
|
|
+ <NUpload
|
|
|
+ ref={uploadRef}
|
|
|
+ action={ossUploadUrl}
|
|
|
+ data={(file: any) => {
|
|
|
+ const item = state.find((c: any) => {
|
|
|
+ return c.id == file.file.id;
|
|
|
+ });
|
|
|
+ const { id, tempFiileBuffer, ...more } = item;
|
|
|
+ return { ...more };
|
|
|
+ }}
|
|
|
+ v-model:fileList={fileListRef.value}
|
|
|
+ accept=".jpg,jpeg,.png,audio/mp3,video/mp4"
|
|
|
+ multiple={true}
|
|
|
+ max={10}
|
|
|
+ // disabled={props.disabled}
|
|
|
+ showFileList={true}
|
|
|
+ showPreviewButton
|
|
|
+ onBeforeUpload={(options: any) => onBeforeUpload(options)}
|
|
|
+ onFinish={(options: any) => {
|
|
|
+ onFinish(options);
|
|
|
+ }}
|
|
|
+ onRemove={(options: any) => onRemove(options)}>
|
|
|
+ <NUploadDragger>
|
|
|
+ <div class={styles.uploadBtn}>
|
|
|
+ <div class={styles.iconUploadAdd} />
|
|
|
+ <h3>点击或者拖动文件到该区域来上传</h3>
|
|
|
+ <p>
|
|
|
+ 仅支持JPG、PNG、MP3、MP4格式文件,单次最多支持
|
|
|
+ <br />
|
|
|
+ 上传10个文件
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </NUploadDragger>
|
|
|
+ </NUpload>
|
|
|
+
|
|
|
+ <NSpace class={styles.btnGroup} justify="center">
|
|
|
+ <NButton round onClick={() => emit('close')}>
|
|
|
+ 取消
|
|
|
+ </NButton>
|
|
|
+ <NButton
|
|
|
+ round
|
|
|
+ type="primary"
|
|
|
+ disabled={uploadStatus.value}
|
|
|
+ onClick={onSubmit}>
|
|
|
+ 确定
|
|
|
+ </NButton>
|
|
|
+ </NSpace>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|