Skip to content

Commit 8f991c9

Browse files
feat: 支持粘贴/拖入音视频、.pptx/.pptist文件
1 parent 9a2f35f commit 8f991c9

File tree

7 files changed

+123
-79
lines changed

7 files changed

+123
-79
lines changed

src/configs/mime.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export const MIME_MAP: { [key: string]: string } = {
2+
3+
// 音频类型
4+
'audio/aac': 'aac',
5+
'audio/mpeg': 'mp3',
6+
'audio/ogg': 'oga',
7+
'audio/wav': 'wav',
8+
'audio/webm': 'weba',
9+
'audio/flac': 'flac',
10+
'audio/mp4': 'm4a',
11+
'audio/x-aiff': 'aif',
12+
'audio/x-ms-wma': 'wma',
13+
'audio/midi': 'mid',
14+
15+
// 视频类型
16+
'video/mp4': 'mp4',
17+
'video/mpeg': 'mpeg',
18+
'video/ogg': 'ogv',
19+
'video/webm': 'webm',
20+
'video/x-msvideo': 'avi',
21+
'video/quicktime': 'mov',
22+
'video/x-ms-wmv': 'wmv',
23+
'video/x-flv': 'flv',
24+
'video/3gpp': '3gp',
25+
'video/3gpp2': '3g2'
26+
}

src/hooks/useImport.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default () => {
4242
const exporting = ref(false)
4343

4444
// 导入JSON文件
45-
const importJSON = (files: FileList, cover = false) => {
45+
const importJSON = (files: FileList | File[], cover = false) => {
4646
const file = files[0]
4747

4848
const reader = new FileReader()
@@ -68,7 +68,7 @@ export default () => {
6868
}
6969

7070
// 导入pptist文件
71-
const importSpecificFile = (files: FileList, cover = false) => {
71+
const importSpecificFile = (files: FileList | File[], cover = false) => {
7272
const file = files[0]
7373

7474
const reader = new FileReader()
@@ -246,7 +246,7 @@ export default () => {
246246
}
247247

248248
// 导入PPTX文件
249-
const importPPTXFile = (files: FileList, options?: { cover?: boolean; fixedViewport?: boolean }) => {
249+
const importPPTXFile = (files: FileList | File[], options?: { cover?: boolean; fixedViewport?: boolean }) => {
250250
const defaultOptions = {
251251
cover: false,
252252
fixedViewport: false,

src/hooks/usePasteDataTransfer.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { MIME_MAP } from '@/configs/mime'
2+
import { getImageDataURL } from '@/utils/image'
3+
import useCreateElement from './useCreateElement'
4+
import useImport from './useImport'
5+
6+
export default () => {
7+
const { createImageElement, createVideoElement, createAudioElement } = useCreateElement()
8+
const { importSpecificFile, importPPTXFile } = useImport()
9+
10+
const pasteDataTransfer = (dataTransfer: DataTransfer) => {
11+
const dataItems = dataTransfer.items
12+
const dataTransferFirstItem = dataItems[0]
13+
14+
// 检查事件对象中是否存在有效文件,存在则插入对应数据,否则可继续检查是否存在文字
15+
let isFile = false
16+
17+
for (const item of dataItems) {
18+
if (item.kind === 'file') {
19+
if (item.type.indexOf('image') !== -1) {
20+
const imageFile = item.getAsFile()
21+
if (imageFile) {
22+
getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
23+
isFile = true
24+
}
25+
}
26+
else if (item.type.indexOf('video') !== -1) {
27+
const videoFile = item.getAsFile()
28+
if (videoFile) {
29+
const videoURL = URL.createObjectURL(videoFile)
30+
const ext = MIME_MAP[videoFile.type] || ''
31+
createVideoElement(videoURL, ext)
32+
isFile = true
33+
}
34+
}
35+
else if (item.type.indexOf('audio') !== -1) {
36+
const audioFile = item.getAsFile()
37+
if (audioFile) {
38+
const audioURL = URL.createObjectURL(audioFile)
39+
const ext = MIME_MAP[audioFile.type] || ''
40+
createAudioElement(audioURL, ext)
41+
isFile = true
42+
}
43+
}
44+
}
45+
}
46+
47+
if (!isFile && dataTransferFirstItem && dataTransferFirstItem.kind === 'file') {
48+
if (!isFile && dataTransferFirstItem.type === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
49+
const pptxFile = dataTransferFirstItem.getAsFile()
50+
if (pptxFile) {
51+
importPPTXFile([pptxFile])
52+
isFile = true
53+
}
54+
}
55+
else if (!isFile) {
56+
const unknownFile = dataTransferFirstItem.getAsFile()
57+
58+
if (unknownFile && unknownFile.name) {
59+
const ext = unknownFile.name.split('.').pop() || ''
60+
61+
if (ext === 'pptist') {
62+
importSpecificFile([unknownFile])
63+
isFile = true
64+
}
65+
}
66+
}
67+
}
68+
69+
return { isFile, dataTransferFirstItem }
70+
}
71+
72+
return {
73+
pasteDataTransfer,
74+
}
75+
}

src/hooks/usePasteEvent.ts

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
import { onMounted, onUnmounted } from 'vue'
22
import { storeToRefs } from 'pinia'
33
import { useMainStore } from '@/store'
4-
import { getImageDataURL } from '@/utils/image'
54
import usePasteTextClipboardData from './usePasteTextClipboardData'
6-
import useCreateElement from './useCreateElement'
5+
import usePasteDataTransfer from './usePasteDataTransfer'
76

87
export default () => {
98
const { editorAreaFocus, thumbnailsFocus, disableHotkeys } = storeToRefs(useMainStore())
109

1110
const { pasteTextClipboardData } = usePasteTextClipboardData()
12-
const { createImageElement } = useCreateElement()
13-
14-
// 粘贴图片到幻灯片元素
15-
const pasteImageFile = (imageFile: File) => {
16-
getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
17-
}
11+
const { pasteDataTransfer } = usePasteDataTransfer()
1812

1913
/**
2014
* 粘贴事件监听
@@ -26,26 +20,12 @@ export default () => {
2620

2721
if (!e.clipboardData) return
2822

29-
const clipboardDataItems = e.clipboardData.items
30-
const clipboardDataFirstItem = clipboardDataItems[0]
31-
32-
if (!clipboardDataFirstItem) return
33-
34-
// 如果剪贴板内有图片,优先尝试读取图片
35-
let isImage = false
36-
for (const item of clipboardDataItems) {
37-
if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
38-
const imageFile = item.getAsFile()
39-
if (imageFile) pasteImageFile(imageFile)
40-
isImage = true
41-
}
42-
}
43-
44-
if (isImage) return
23+
const { isFile, dataTransferFirstItem } = pasteDataTransfer(e.clipboardData)
24+
if (isFile) return
4525

46-
// 如果剪贴板内没有图片,但有文字内容,尝试解析文字内容
47-
if (clipboardDataFirstItem.kind === 'string' && clipboardDataFirstItem.type === 'text/plain') {
48-
clipboardDataFirstItem.getAsString(text => pasteTextClipboardData(text))
26+
// 如果剪贴板内不存在有效文件,但有文字内容,尝试解析文字内容
27+
if (dataTransferFirstItem && dataTransferFirstItem.kind === 'string' && dataTransferFirstItem.type === 'text/plain') {
28+
dataTransferFirstItem.getAsString(text => pasteTextClipboardData(text))
4929
}
5030
}
5131

src/views/Editor/Canvas/hooks/useDropImageOrText.ts renamed to src/views/Editor/Canvas/hooks/useDrop.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,26 @@
11
import { onMounted, onUnmounted, type ShallowRef } from 'vue'
22
import { storeToRefs } from 'pinia'
33
import { useMainStore } from '@/store'
4-
import { getImageDataURL } from '@/utils/image'
54
import { parseText2Paragraphs } from '@/utils/textParser'
65
import useCreateElement from '@/hooks/useCreateElement'
6+
import usePasteDataTransfer from '@/hooks/usePasteDataTransfer'
7+
78

89
export default (elementRef: ShallowRef<HTMLElement | null>) => {
910
const { disableHotkeys } = storeToRefs(useMainStore())
1011

11-
const { createImageElement, createTextElement } = useCreateElement()
12+
const { createTextElement } = useCreateElement()
13+
14+
const { pasteDataTransfer } = usePasteDataTransfer()
1215

13-
// 拖拽元素到画布中
16+
// 拖拽元素/页面到画布中
1417
const handleDrop = (e: DragEvent) => {
1518
if (!e.dataTransfer || e.dataTransfer.items.length === 0) return
1619

17-
const dataItems = e.dataTransfer.items
18-
const dataTransferFirstItem = dataItems[0]
19-
20-
// 检查事件对象中是否存在图片,存在则插入图片,否则继续检查是否存在文字,存在则插入文字
21-
let isImage = false
22-
for (const item of dataItems) {
23-
if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
24-
const imageFile = item.getAsFile()
25-
if (imageFile) {
26-
getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
27-
}
28-
isImage = true
29-
}
30-
}
31-
32-
if (isImage) return
20+
const { isFile, dataTransferFirstItem } = pasteDataTransfer(e.dataTransfer)
21+
if (isFile) return
3322

34-
if (dataTransferFirstItem.kind === 'string' && dataTransferFirstItem.type === 'text/plain') {
23+
if (dataTransferFirstItem && dataTransferFirstItem.kind === 'string' && dataTransferFirstItem.type === 'text/plain') {
3524
dataTransferFirstItem.getAsString(text => {
3625
if (disableHotkeys.value) return
3726
const string = parseText2Paragraphs(text)

src/views/Editor/Canvas/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ import { KEYS } from '@/configs/hotkey'
110110
111111
import useViewportSize from './hooks/useViewportSize'
112112
import useMouseSelection from './hooks/useMouseSelection'
113-
import useDropImageOrText from './hooks/useDropImageOrText'
113+
import useDrop from './hooks/useDrop'
114114
import useRotateElement from './hooks/useRotateElement'
115115
import useScaleElement from './hooks/useScaleElement'
116116
import useSelectAndMoveElement from './hooks/useSelectElement'
@@ -175,7 +175,7 @@ watchEffect(setLocalElementList)
175175
const canvasRef = useTemplateRef<HTMLElement>('canvasRef')
176176
const { dragViewport, viewportStyles } = useViewportSize(canvasRef)
177177
178-
useDropImageOrText(canvasRef)
178+
useDrop(canvasRef)
179179
180180
const { mouseSelection, mouseSelectionVisible, mouseSelectionQuadrant, updateMouseSelection } = useMouseSelection(elementList, viewportRef)
181181

src/views/Editor/CanvasTool/MediaInput.vue

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,38 +37,12 @@
3737
<script lang="ts" setup>
3838
import { ref } from 'vue'
3939
import message from '@/utils/message'
40+
import { MIME_MAP } from '@/configs/mime'
4041
import Tabs from '@/components/Tabs.vue'
4142
import Input from '@/components/Input.vue'
4243
import Button from '@/components/Button.vue'
4344
import FileInput from '@/components/FileInput.vue'
4445
45-
const MIME_MAP: { [key: string]: string } = {
46-
47-
// 音频类型
48-
'audio/aac': 'aac',
49-
'audio/mpeg': 'mp3',
50-
'audio/ogg': 'oga',
51-
'audio/wav': 'wav',
52-
'audio/webm': 'weba',
53-
'audio/flac': 'flac',
54-
'audio/mp4': 'm4a',
55-
'audio/x-aiff': 'aif',
56-
'audio/x-ms-wma': 'wma',
57-
'audio/midi': 'mid',
58-
59-
// 视频类型
60-
'video/mp4': 'mp4',
61-
'video/mpeg': 'mpeg',
62-
'video/ogg': 'ogv',
63-
'video/webm': 'webm',
64-
'video/x-msvideo': 'avi',
65-
'video/quicktime': 'mov',
66-
'video/x-ms-wmv': 'wmv',
67-
'video/x-flv': 'flv',
68-
'video/3gpp': '3gp',
69-
'video/3gpp2': '3g2'
70-
}
71-
7246
type TypeKey = 'video' | 'audio'
7347
interface TabItem {
7448
key: TypeKey

0 commit comments

Comments
 (0)