|
|
@@ -1,10 +1,10 @@
|
|
|
<template>
|
|
|
<div class="create-course-dialog">
|
|
|
<div class="dialog-header">
|
|
|
- <button class="close-btn" @click="$emit('close')">
|
|
|
+ <button class="close-btn" @click="handleClose">
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <line x1="18" y1="6" x2="6" y2="18"/>
|
|
|
- <line x1="6" y1="6" x2="18" y2="18"/>
|
|
|
+ <line x1="18" y1="6" x2="6" y2="18" />
|
|
|
+ <line x1="6" y1="6" x2="18" y2="18" />
|
|
|
</svg>
|
|
|
</button>
|
|
|
</div>
|
|
|
@@ -24,10 +24,8 @@
|
|
|
<p>AI自动生成完整教学内容</p>
|
|
|
<div class="coming-soon">待上线</div>
|
|
|
</div>
|
|
|
- <FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation" @change="files => {
|
|
|
- importPPTXFile(files)
|
|
|
- $emit('close')
|
|
|
- }">
|
|
|
+ <FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
|
+ @change="handleFileUpload">
|
|
|
<div class="option-card">
|
|
|
<div class="option-icon">
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
@@ -71,24 +69,96 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 解析状态弹窗 -->
|
|
|
+ <div v-if="exporting" class="parsing-modal">
|
|
|
+ <div class="parsing-content">
|
|
|
+ <div class="loading-spinner" v-if="exporting"></div>
|
|
|
+ <div class="success-icon" v-if="!exporting">
|
|
|
+ <svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <g id="Component 1">
|
|
|
+ <path id="Vector" d="M5.41675 14.084L9.75008 18.4173L20.5834 7.58398" stroke="#FF9300"
|
|
|
+ stroke-width="2.16667" stroke-linecap="round" stroke-linejoin="round" />
|
|
|
+ </g>
|
|
|
+ </svg>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <h3>{{ exporting ? '解析中...' : '导出完成' }}</h3>
|
|
|
+ <p v-if="exporting">正在解析 {{ currentFileName }}</p>
|
|
|
+ <p v-if="!exporting">解析完成,已生成课件</p>
|
|
|
+ <button class="close-btn2" @click="handleParsingClose">
|
|
|
+ {{ exporting ? '关闭' : '完成' }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
+import { ref } from 'vue'
|
|
|
import useImport from '@/hooks/useImport'
|
|
|
import FileInput from '@/components/FileInput.vue'
|
|
|
+import message from '@/utils/message'
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
(e: 'close'): void
|
|
|
(e: 'select', option: string): void
|
|
|
}>()
|
|
|
|
|
|
-const { importPPTXFile } = useImport()
|
|
|
+const { importPPTXFile, exporting } = useImport()
|
|
|
+const currentFileName = ref('')
|
|
|
+const parsingStatus = ref<'parsing' | 'success'>('parsing')
|
|
|
+const parsingAbortController = ref<AbortController | null>(null)
|
|
|
|
|
|
const handleOptionClick = (option: string) => {
|
|
|
emit('select', option)
|
|
|
emit('close')
|
|
|
}
|
|
|
+
|
|
|
+const handleClose = () => {
|
|
|
+ interface ParentWindowWithToolList extends Window {
|
|
|
+ goBack?: () => void;
|
|
|
+ }
|
|
|
+ const parentWindow = window.parent as ParentWindowWithToolList
|
|
|
+ parentWindow?.goBack?.()
|
|
|
+}
|
|
|
+
|
|
|
+const handleFileUpload = async (files: FileList) => {
|
|
|
+ if (!files || files.length === 0) return
|
|
|
+
|
|
|
+ const file = files[0]
|
|
|
+ currentFileName.value = file.name
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 创建AbortController用于取消操作
|
|
|
+ parsingAbortController.value = new AbortController()
|
|
|
+ const signal = parsingAbortController.value.signal
|
|
|
+
|
|
|
+ // 调用importPPTXFile并传入signal
|
|
|
+ await importPPTXFile(files, { signal, onclose: () => emit('close') })
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ if (error instanceof DOMException && error.name === 'AbortError') {
|
|
|
+ console.log('文件解析已取消')
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.error('文件解析失败:', error)
|
|
|
+ message.error('文件解析失败,请重试')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleParsingClose = () => {
|
|
|
+ if (exporting.value && parsingAbortController.value) {
|
|
|
+ parsingAbortController.value.abort()
|
|
|
+ exporting.value = false
|
|
|
+ parsingAbortController.value = null
|
|
|
+ // message.info('解析已取消')
|
|
|
+ }
|
|
|
+ else if (!exporting.value) {
|
|
|
+ emit('close')
|
|
|
+ }
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@@ -150,104 +220,205 @@ const handleOptionClick = (option: string) => {
|
|
|
gap: 20px;
|
|
|
|
|
|
.option-card {
|
|
|
- background: #fafbfc;
|
|
|
- border: 1px solid #E5E7EB;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 24px;
|
|
|
- text-align: center;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s;
|
|
|
- position: relative;
|
|
|
+ background: #fafbfc;
|
|
|
+ border: 1px solid #E5E7EB;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 24px;
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ position: relative;
|
|
|
|
|
|
- &:hover {
|
|
|
- border-color: #FF9300;
|
|
|
- // box-shadow: 0 4px 12px rgba(255, 147, 0, 0.15);
|
|
|
- background: #FFFAF0;
|
|
|
+ &:hover {
|
|
|
+ border-color: #FF9300;
|
|
|
+ background: #FFFAF0;
|
|
|
|
|
|
- .option-icon {
|
|
|
- color: #FF9300;
|
|
|
- }
|
|
|
+ .option-icon {
|
|
|
+ color: #FF9300;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #FFFAF0;
|
|
|
+ border-color: #FF9300;
|
|
|
+ }
|
|
|
|
|
|
- &.active {
|
|
|
- background: #FFFAF0;
|
|
|
- border-color: #FF9300;
|
|
|
+ &.disabled {
|
|
|
+ background: #f8f8f9;
|
|
|
+ border-color: #eff0f3;
|
|
|
+ cursor: not-allowed;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ color: #7c7f86;
|
|
|
}
|
|
|
|
|
|
- &.disabled {
|
|
|
- background: #f8f8f9;
|
|
|
- border-color: #eff0f3;
|
|
|
- cursor: not-allowed;
|
|
|
-
|
|
|
- h3 {
|
|
|
- color: #7c7f86;
|
|
|
- }
|
|
|
-
|
|
|
- p {
|
|
|
- color: #b5b9bf;
|
|
|
- }
|
|
|
-
|
|
|
- .option-icon {
|
|
|
- color: #a9aeb5;
|
|
|
- background: #fff;
|
|
|
- }
|
|
|
-
|
|
|
- // &:hover {
|
|
|
- // border-color: #E5E7EB;
|
|
|
- // box-shadow: none;
|
|
|
- // background: #F3F4F6;
|
|
|
-
|
|
|
- // .option-icon {
|
|
|
- // color: #D1D5DB;
|
|
|
- // }
|
|
|
- // }
|
|
|
+ p {
|
|
|
+ color: #b5b9bf;
|
|
|
}
|
|
|
|
|
|
.option-icon {
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- background: #eef3ff;
|
|
|
- border-radius: 12px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- margin: 0 auto 16px;
|
|
|
- color: #6b7280;
|
|
|
- transition: all 0.3s;
|
|
|
-
|
|
|
- svg {
|
|
|
- width: 24px;
|
|
|
- height: 24px;
|
|
|
- }
|
|
|
+ color: #a9aeb5;
|
|
|
+ background: #fff;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- h3 {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 600;
|
|
|
- color: #333;
|
|
|
- margin: 0 0 8px;
|
|
|
- }
|
|
|
+ .option-icon {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ background: #eef3ff;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin: 0 auto 16px;
|
|
|
+ color: #6b7280;
|
|
|
+ transition: all 0.3s;
|
|
|
|
|
|
- p {
|
|
|
- font-size: 14px;
|
|
|
- color: #999;
|
|
|
- margin: 0 0 16px;
|
|
|
+ svg {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- .coming-soon {
|
|
|
- position: absolute;
|
|
|
- top: 12px;
|
|
|
- right: 12px;
|
|
|
- background: #c5c9d0;
|
|
|
- color: #fff;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- padding: 4px 8px;
|
|
|
- border-radius: 15px;
|
|
|
- text-transform: uppercase;
|
|
|
- }
|
|
|
+ h3 {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ margin: 0 0 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ p {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #999;
|
|
|
+ margin: 0 0 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .coming-soon {
|
|
|
+ position: absolute;
|
|
|
+ top: 12px;
|
|
|
+ right: 12px;
|
|
|
+ background: #c5c9d0;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ padding: 4px 8px;
|
|
|
+ border-radius: 15px;
|
|
|
+ text-transform: uppercase;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .parsing-modal {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ z-index: 1000;
|
|
|
+
|
|
|
+ .parsing-content {
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 40px;
|
|
|
+ text-align: center;
|
|
|
+ max-width: 400px;
|
|
|
+ width: 90%;
|
|
|
+
|
|
|
+ .loading-spinner {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ border: 4px solid #f3f3f3;
|
|
|
+ border-top: 4px solid #FF9300;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+ margin: 0 auto 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .success-icon {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ margin: 0 auto 20px;
|
|
|
+ background: #FFFAF0;
|
|
|
+ border-radius: 5px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: white;
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ margin: 0 0 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ p {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ margin: 0 0 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-btn2 {
|
|
|
+ background: #FF9300;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px 24px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ cursor: pointer;
|
|
|
+ width: 100%;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #e68a00;
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes spin {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-btn {
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border: none;
|
|
|
+ background: none;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #999;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: all 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #f0f0f0;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ svg {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
</style>
|