123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- import {
- NModal,
- NSpin,
- NUpload,
- NUploadDragger,
- UploadFileInfo,
- useMessage
- } from 'naive-ui';
- import { defineComponent, watch, PropType, reactive, ref } from 'vue';
- import { policy } from '@/components/upload-file/api';
- import Copper from '@/components/upload-file/copper';
- import axios from 'axios';
- import styles from './index.module.less';
- import iconUploadAdd from '../../../images/icon-upload-add.png';
- import { NaturalTypeEnum, PageEnum } from '@/enums/pageEnum';
- import { formatUrlType } from '.';
- import { modalClickMask } from '/src/state';
- /**
- * 1. 图片上传可以进行裁剪
- * 2. 视频上传可以选择某一帧做为封面
- * 3. 音频只用限制某一种格式
- * 4. 只支持单个上传,因为多个上传没有办法去处理,即有视频,图片等
- */
- export default defineComponent({
- name: 'upload-file',
- 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: ''
- },
- cropper: {
- // 是否裁切, 只有图片才支持 - 失效(不支持)
- type: Boolean as PropType<boolean>,
- default: false
- },
- options: {
- type: Object,
- default: () => {
- return {
- viewMode: 0,
- autoCrop: true, //是否默认生成截图框
- enlarge: 1, // 图片放大倍数
- autoCropWidth: 200, //默认生成截图框宽度
- autoCropHeight: 200, //默认生成截图框高度
- fixedBox: false, //是否固定截图框大小 不允许改变
- previewsCircle: true, //预览图是否是原图形
- title: '上传图片'
- };
- }
- }
- },
- emits: [
- 'update:fileList',
- 'close',
- 'readFileInputEventAsArrayBuffer',
- 'remove',
- 'finished'
- ],
- setup(props, { emit, expose, slots }) {
- 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([
- // {
- // policy: '',
- // signature: '',
- // key: '',
- // KSSAccessKeyId: '',
- // acl: 'public-read',
- // name: ''
- // }
- ]) as any;
- const fileListRef = ref<UploadFileInfo[]>([]);
- const initFileList = () => {
- if (props.fileList) {
- const splitName = props.fileList.split('/');
- fileListRef.value = [
- {
- id: new Date().getTime().toString(),
- name: splitName[splitName.length - 1],
- status: 'finished',
- url: props.fileList
- }
- ];
- } else {
- fileListRef.value = [];
- }
- };
- initFileList();
- watch(
- () => props.imageList,
- () => {
- initFileList();
- }
- );
- watch(
- () => props.fileList,
- () => {
- initFileList();
- }
- );
- const handleClearFile = () => {
- uploadRef.value?.clear();
- };
- expose({
- handleClearFile
- });
- const CropperModal = ref();
- 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
- : NaturalTypeEnum.VIDEO;
- const size = type === 'IMG' ? 2 : type === 'SONG' ? 20 : 500;
- if (size) {
- isLt2M = file.file.size / 1024 / 1024 < size;
- if (!isLt2M) {
- message.error(`文件大小不能超过${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}${
- props.fileName || Date.now() + 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
- });
- 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 () => {
- emit('update:fileList', '');
- emit('remove');
- btnLoading.value = false;
- };
- // 裁切失败
- // const cropperNo = () => {}
- // 裁切成功
- const cropperOk = async (blob: any) => {
- try {
- const fileName = `${props.path}${
- props.fileName || new Date().getTime() + '.png'
- }`;
- const obj = {
- filename: fileName,
- bucketName: props.bucketName,
- postData: {
- filename: fileName,
- acl: 'public-read',
- key: fileName,
- unknowValueField: []
- }
- };
- const { data } = await policy(obj);
- state.policy = data.policy;
- state.signature = data.signature;
- state.key = fileName;
- state.KSSAccessKeyId = data.kssAccessKeyId;
- state.name = fileName;
- const formData = new FormData();
- for (const key in state) {
- formData.append(key, state[key]);
- }
- formData.append('file', blob);
- await axios.post(ossUploadUrl, formData).then(() => {
- const url = ossUploadUrl + state.key;
- const splitName = url.split('/');
- fileListRef.value = [
- {
- id: new Date().getTime().toString(),
- name: splitName[splitName.length - 1],
- status: 'finished',
- url: url
- }
- ];
- emit('update:fileList', url);
- emit('finished', {
- coverImg: url,
- content: url
- });
- visiable.value = false;
- });
- } catch {
- return false;
- }
- };
- return () => (
- <div class={styles.uploadFile}>
- <NSpin show={btnLoading.value} description="上传中...">
- <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={props.accept}
- multiple={props.multiple}
- max={props.max}
- disabled={props.disabled}
- directoryDnd={props.directoryDnd}
- showFileList={props.showFileList}
- showPreviewButton
- onBeforeUpload={(options: any) => onBeforeUpload(options)}
- onFinish={(options: any) => {
- onFinish(options);
- }}
- onChange={(options: any) => {
- // console.log(options, 'change');
- }}
- onRemove={() => onRemove()}>
- <NUploadDragger>
- {props.showType === 'default' && (
- <div class={styles.uploadBtn}>
- <img src={iconUploadAdd} class={styles.iconUploadAdd} />
- <p>上传</p>
- </div>
- )}
- {props.showType === 'custom' && slots.custom && slots.custom()}
- </NUploadDragger>
- </NUpload>
- </NSpin>
- <NModal
- maskClosable={modalClickMask}
- v-model:show={visiable.value}
- preset="dialog"
- showIcon={false}
- class={['modalTitle background']}
- title="上传图片"
- style={{ width: '800px' }}>
- {/* @cropper-no="error" @cropper-ok="success" */}
- <Copper
- ref={CropperModal}
- onClose={() => (visiable.value = false)}
- onCropperOk={cropperOk}
- />
- </NModal>
- </div>
- );
- }
- });
|