|
|
@@ -2,7 +2,8 @@
|
|
|
<div class="collapsible-toolbar" :class="{ collapsed: isCollapsed }">
|
|
|
<div class="toolbar-content" v-show="!isCollapsed">
|
|
|
<div class="sidebar-content">
|
|
|
- <div class="sidebar-item" :class="{ active: activeSubmenu === 'cocoai' }" @click="toggleSubmenu('cocoai')">
|
|
|
+ <div class="sidebar-item feature-sidebar-item" :class="{ active: activeSubmenu === 'cocoai' }"
|
|
|
+ @click="toggleSubmenu('cocoai')">
|
|
|
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
<path d="M12 2L2 7l10 5 10-5-10-5z"></path>
|
|
|
<path d="M2 17l10 5 10-5"></path>
|
|
|
@@ -10,7 +11,18 @@
|
|
|
</svg>
|
|
|
<span class="item-label">Coco AI</span>
|
|
|
</div>
|
|
|
- <div class="sidebar-item" :class="{ active: activeSubmenu === 'page' }" @click="toggleSubmenu('page')">
|
|
|
+ <div class="sidebar-item feature-sidebar-item" :class="{ active: activeSubmenu === 'uploadFile' }"
|
|
|
+ @click="toggleSubmenu('uploadFile')">
|
|
|
+ <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M12 16V4"></path>
|
|
|
+ <path d="M7 9l5-5 5 5"></path>
|
|
|
+ <path d="M4 16.5v1.5A2 2 0 006 20h12a2 2 0 002-2v-1.5"></path>
|
|
|
+ <path d="M5 14h14"></path>
|
|
|
+ </svg>
|
|
|
+ <span class="item-label">{{ lang.ssUploadFile }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="sidebar-divider"></div>
|
|
|
+ <!-- <div class="sidebar-item" :class="{ active: activeSubmenu === 'page' }" @click="toggleSubmenu('page')">
|
|
|
<svg class="item-icon" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
<g id="Component 1">
|
|
|
<path id="Vector"
|
|
|
@@ -20,7 +32,7 @@
|
|
|
</g>
|
|
|
</svg>
|
|
|
<span class="item-label">{{ lang.ssPage }}</span>
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
<div class="sidebar-item" :class="{ active: activeSubmenu === 'interactive' }"
|
|
|
@click="toggleSubmenu('interactive')">
|
|
|
<svg class="item-icon" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
@@ -78,10 +90,12 @@
|
|
|
</svg>
|
|
|
<span class="item-label">{{ lang.ssMultimedia }}</span>
|
|
|
</div>
|
|
|
- <div class="sidebar-item" :class="{ active: activeSubmenu === 'english' }"
|
|
|
- @click="toggleSubmenu('english')">
|
|
|
+ <div class="sidebar-item" :class="{ active: activeSubmenu === 'english' }" @click="toggleSubmenu('english')">
|
|
|
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"></path><path d="M19 10v2a7 7 0 01-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>
|
|
|
+ <path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"></path>
|
|
|
+ <path d="M19 10v2a7 7 0 01-14 0v-2"></path>
|
|
|
+ <line x1="12" y1="19" x2="12" y2="23"></line>
|
|
|
+ <line x1="8" y1="23" x2="16" y2="23"></line>
|
|
|
</svg>
|
|
|
<span class="item-label">{{ lang.ssEnglish }}</span>
|
|
|
</div>
|
|
|
@@ -104,6 +118,104 @@
|
|
|
<AiChat :userid="props.userid" />
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="submenu" :class="{ visible: activeSubmenu === 'uploadFile' }">
|
|
|
+ <div class="submenu-title" style="margin-bottom: 0;">
|
|
|
+ <div class="title">{{ lang.ssUploadFile }}</div>
|
|
|
+ <div class="close-icon" @click="toggleSubmenu('uploadFile')">
|
|
|
+ <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <g id="Component 3">
|
|
|
+ <g id="Component 1">
|
|
|
+ <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
|
|
|
+ </g>
|
|
|
+ </g>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="submenu-content">
|
|
|
+ <template v-if="readingFile">
|
|
|
+ <div class="reading-file">
|
|
|
+ <div class="loading-spinner"></div>
|
|
|
+ <div class="reading-text">{{ lang.ssReadingFile }}</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="!showFileConfirmModal && !exportingDialog">
|
|
|
+ <FileInput accept=".pptx" @change="handleFileUpload">
|
|
|
+ <div class="upload-dropzone">
|
|
|
+ <div class="upload-dropzone-icon">
|
|
|
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
|
|
|
+ <path d="M12 15V4"></path>
|
|
|
+ <path d="M7 9l5-5 5 5"></path>
|
|
|
+ <path d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2"></path>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="upload-dropzone-title">{{ lang.ssDragAndDrop }}</div>
|
|
|
+ <div class="upload-dropzone-subtitle">{{ lang.ssSupportPptx }}</div>
|
|
|
+ <!-- <div class="upload-dropzone-footnote">同类型文件支持批量导入,跨类型文件请分开处理</div> -->
|
|
|
+ </div>
|
|
|
+ </FileInput>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-else-if="!exportingDialog">
|
|
|
+ <div class="file-confirm-inline">
|
|
|
+ <div class="file-info">
|
|
|
+ <div class="file-title">{{ lang.ssFileDetected }}{{ currentFileName }}</div>
|
|
|
+ <div class="file-subtitle">{{ currentFileName }}({{ lang.ssTotalPages.replace('{count}',
|
|
|
+ pageCount.toString()) }})</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="import-options">
|
|
|
+ <div class="import-option" :class="{ active: selectedImportOption === 'page' }"
|
|
|
+ @click="selectedImportOption = 'page'">
|
|
|
+ <div class="option-icon">
|
|
|
+ </div>
|
|
|
+ <div class="option-text">
|
|
|
+ <div class="option-title">{{ lang.ssImportAsSlide }}</div>
|
|
|
+ <div class="option-desc">{{ lang.ssImportAsSlideDesc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-show="false" class="import-option" :class="{ active: selectedImportOption === 'library' }"
|
|
|
+ @click="selectedImportOption = 'library'">
|
|
|
+ <div class="option-icon">
|
|
|
+ </div>
|
|
|
+ <div class="option-text">
|
|
|
+ <div class="option-title">{{ lang.ssImportAndSave }}</div>
|
|
|
+ <div class="option-desc">{{ lang.ssImportAndSaveDesc }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="modal-buttons">
|
|
|
+ <button class="cancel-btn" @click="cancelFileUpload">{{ lang.ssCancel }}</button>
|
|
|
+ <button class="confirm-btn" @click="confirmFileUpload">{{ lang.ssConfirm }}</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-if="exportingDialog">
|
|
|
+ <div class="progress-inline">
|
|
|
+ <div class="progress-header">
|
|
|
+ <!-- <span class="file-name">{{ currentFileName }}</span>
|
|
|
+ <span class="progress-percent">{{ importProgress }}%</span> -->
|
|
|
+ <div class="upload-task-main">
|
|
|
+ <div class="upload-task-name">{{ currentFileName }}</div>
|
|
|
+ <div class="upload-task-meta">课件文件 · {{ formatFileSize(currentFileSize) }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="upload-task-side">
|
|
|
+ <div class="upload-task-percent">{{ importProgress }}%</div>
|
|
|
+ <button type="button" class="upload-task-action" :class="{ 'upload-task-close': !exporting }"
|
|
|
+ @click="handleParsingClose">{{ exporting ? '取消' : '×' }}</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" :style="{ width: importProgress + '%' }"></div>
|
|
|
+ </div>
|
|
|
+ <div class="progress-loading" v-if="exporting">上传中...</div>
|
|
|
+ <!-- <button class="close-btn" @click="handleParsingClose">{{ lang.ssClose }}</button> -->
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div class="submenu" :class="{ visible: activeSubmenu === 'page' }">
|
|
|
<div class="submenu-title">
|
|
|
<div class="title">{{ lang.ssAddTemplatePage }}</div>
|
|
|
@@ -175,7 +287,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
|
- @change="handleFileUpload">
|
|
|
+ @change="handleFileUpload" v-if="false">
|
|
|
<div class="submenu-upload">
|
|
|
<div class="submenu-icon">
|
|
|
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
@@ -253,25 +365,25 @@
|
|
|
<div class="submenu-item" @click="handleToolClick('vote')" @mouseenter="hoveredTool = 'vote'"
|
|
|
@mouseleave="hoveredTool = null">
|
|
|
<svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <polyline points="9 11 12 14 22 4"></polyline>
|
|
|
- <path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"></path>
|
|
|
+ <polyline points="9 11 12 14 22 4"></polyline>
|
|
|
+ <path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"></path>
|
|
|
</svg>
|
|
|
<span class="submenu-label">{{ lang.ssVote }}</span>
|
|
|
</div>
|
|
|
<div class="submenu-item" @click="handleToolClick('photo')" @mouseenter="hoveredTool = 'photo'"
|
|
|
@mouseleave="hoveredTool = null">
|
|
|
<svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"></path>
|
|
|
- <circle cx="12" cy="13" r="4"></circle>
|
|
|
+ <path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"></path>
|
|
|
+ <circle cx="12" cy="13" r="4"></circle>
|
|
|
</svg>
|
|
|
<span class="submenu-label">{{ lang.ssPhoto }}</span>
|
|
|
</div>
|
|
|
<div class="submenu-item" @click="handleToolClick('creative')" @mouseenter="hoveredTool = null"
|
|
|
@mouseleave="hoveredTool = null">
|
|
|
<svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <circle cx="12" cy="12" r="10"/>
|
|
|
- <line x1="12" y1="8" x2="12" y2="16"/>
|
|
|
- <line x1="8" y1="12" x2="16" y2="12"/>
|
|
|
+ <circle cx="12" cy="12" r="10" />
|
|
|
+ <line x1="12" y1="8" x2="12" y2="16" />
|
|
|
+ <line x1="8" y1="12" x2="16" y2="12" />
|
|
|
</svg>
|
|
|
<span class="submenu-label">{{ lang.ssCreative }}</span>
|
|
|
</div>
|
|
|
@@ -376,7 +488,17 @@
|
|
|
</div>
|
|
|
<span class="submenu-label">{{ lang.ssUploadWebpageLink }}</span>
|
|
|
</div>
|
|
|
- <!-- <div class="submenu-item">
|
|
|
+ <div class="submenu-item" @click="handleToolClick('createWebpage')">
|
|
|
+ <div class="submenu-icon">
|
|
|
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <rect x="3" y="3" width="18" height="14" rx="2"></rect>
|
|
|
+ <path d="M12 8v6"></path>
|
|
|
+ <path d="M9 11h6"></path>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <span class="submenu-label">{{ lang.ssNewWebpage }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="submenu-item" @click="handleToolClick('uploadCode')">
|
|
|
<div class="submenu-icon">
|
|
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
<g id="Component 1">
|
|
|
@@ -389,16 +511,6 @@
|
|
|
</svg>
|
|
|
</div>
|
|
|
<span class="submenu-label">{{ lang.ssUploadWebpage }}</span>
|
|
|
- </div> -->
|
|
|
- <div class="submenu-item" @click="handleToolClick('createWebpage')">
|
|
|
- <div class="submenu-icon">
|
|
|
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <rect x="3" y="3" width="18" height="14" rx="2"></rect>
|
|
|
- <path d="M12 8v6"></path>
|
|
|
- <path d="M9 11h6"></path>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <span class="submenu-label">{{ lang.ssNewWebpage }}</span>
|
|
|
</div>
|
|
|
<!-- <div class="submenu-item">
|
|
|
<div class="submenu-icon">
|
|
|
@@ -446,6 +558,67 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="submenu" :class="{ visible: activeSubmenu === 'uploadCode' }">
|
|
|
+ <div class="submenu-title">
|
|
|
+ <div class="title">{{ lang.ssUploadWebpage }}</div>
|
|
|
+ <div class="close-icon" @click="activeSubmenu = 'h5page'">
|
|
|
+ <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
+ <g id="Component 3">
|
|
|
+ <g id="Component 1">
|
|
|
+ <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
|
|
|
+ </g>
|
|
|
+ </g>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="line_box">
|
|
|
+ <div class="upload-box">
|
|
|
+ <div class="upload-tabs">
|
|
|
+ <button class="upload-tab" :class="{ active: uploadTab === 'file' }"
|
|
|
+ @click="switchUploadTab('file')">上传文件</button>
|
|
|
+ <button class="upload-tab" :class="{ active: uploadTab === 'code' }"
|
|
|
+ @click="switchUploadTab('code')">粘贴代码</button>
|
|
|
+ </div>
|
|
|
+ <div class="uploadFilePanel" v-if="uploadTab === 'file'">
|
|
|
+ <div class="form-group">
|
|
|
+ <FileInput accept=".html,.htm" @change="handleFileCodeUpload" v-if="!uploadCodeFile">
|
|
|
+ <div class="file-upload-area">
|
|
|
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
|
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
|
|
|
+ <polyline points="17 8 12 3 7 8"></polyline>
|
|
|
+ <line x1="12" y1="3" x2="12" y2="15"></line>
|
|
|
+ </svg>
|
|
|
+ <p class="upload-text">{{ lang.ssDragAndDropHint }}</p>
|
|
|
+ <p class="upload-hint">{{ lang.ssSupportHTML }}</p>
|
|
|
+ </div>
|
|
|
+ </FileInput>
|
|
|
+ <div class="file-name-display" v-else>
|
|
|
+ <span>{{ uploadCodeFile.name }}</span>
|
|
|
+ <button type="button" class="upload-task-action"
|
|
|
+ @click="handleFileCodeUpload(null)">×</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="pasteCodePanel" v-if="uploadTab === 'code'">
|
|
|
+ <div class="form-group">
|
|
|
+ <textarea class="code-textarea" :placeholder="lang.ssPasteHTML" v-model="codeInput"></textarea>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <button class="webpage-link-button"
|
|
|
+ :class="{ 'loading': isLoading, 'disabled': !uploadCodeFile || isLoading }"
|
|
|
+ :disabled="!uploadCodeFile || isLoading" @click="handleUploadCodeFile"
|
|
|
+ v-if="uploadTab === 'file'">
|
|
|
+ {{ isLoading ? lang.ssUploading : !uploadCodeFile ? lang.ssWaitingForInput2 : lang.ssStartUpload }}
|
|
|
+ </button>
|
|
|
+ <button class="webpage-link-button"
|
|
|
+ :class="{ 'loading': isLoading, 'disabled': !codeInput || isLoading }"
|
|
|
+ :disabled="!codeInput || isLoading" @click="handleUploadCode"
|
|
|
+ v-else-if="uploadTab === 'code'">
|
|
|
+ {{ isLoading ? lang.ssUploading : !codeInput ? lang.ssWaitingForInput2 : lang.ssStartUpload }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div class="submenu" :class="{ visible: activeSubmenu === 'multimedia' }">
|
|
|
<div class="submenu-title">
|
|
|
<div class="title">{{ lang.ssAddMultimedia }}</div>
|
|
|
@@ -527,26 +700,9 @@
|
|
|
<SpeakingPanel />
|
|
|
</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 ? lang.ssParsing : lang.ssExportCompleted }}</h3>
|
|
|
- <p v-if="exporting">{{ lang.ssParsingFile }}{{ currentFileName }}</p>
|
|
|
- <p v-if="!exporting">{{ lang.ssParsingCompleted }}</p>
|
|
|
- <button class="close-btn2" @click="handleParsingClose">
|
|
|
- {{ exporting ? lang.ssClose : lang.ssComplete }}
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+
|
|
|
+
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -779,8 +935,15 @@ const uploadWebpageLink = async () => {
|
|
|
})
|
|
|
|
|
|
if (!isValid) {
|
|
|
- message.error(lang.ssCocoLinkTip)
|
|
|
+ // message.error(lang.ssCocoLinkTip)
|
|
|
+
|
|
|
+ message.warning(lang.ssCocoLinkTip2)
|
|
|
+ createSlide()
|
|
|
+ createFrameElement(webpageUrl.value, 81) // 假设15是网页工具的类型
|
|
|
isLoading.value = false
|
|
|
+ // 清空输入框和验证状态
|
|
|
+ webpageUrl.value = ''
|
|
|
+ isValidUrl.value = null
|
|
|
return
|
|
|
}
|
|
|
isLoading.value = false
|
|
|
@@ -862,7 +1025,7 @@ const handleToolClick = _.debounce((tool: string) => {
|
|
|
}
|
|
|
else if (tool === 'photo') {
|
|
|
parentWindow?.addTool?.(79)
|
|
|
- }
|
|
|
+ }
|
|
|
else if (tool === 'qa') {
|
|
|
parentWindow?.addTool?.(15)
|
|
|
}
|
|
|
@@ -892,6 +1055,9 @@ const handleToolClick = _.debounce((tool: string) => {
|
|
|
else if (tool === 'uploadWebpage') {
|
|
|
activeSubmenu.value = 'uploadWebpage'
|
|
|
}
|
|
|
+ else if (tool === 'uploadCode') {
|
|
|
+ activeSubmenu.value = 'uploadCode'
|
|
|
+ }
|
|
|
}, 300)
|
|
|
|
|
|
const loadContentList = () => {
|
|
|
@@ -973,6 +1139,7 @@ const getTypeLabel = (type?: number) => {
|
|
|
15: lang.ssQATest,
|
|
|
72: lang.ssAiApp,
|
|
|
73: lang.ssHPage,
|
|
|
+ 81: lang.ssHPage,
|
|
|
74: lang.ssVideo,
|
|
|
75: lang.lang == 'cn' ? lang.ssBiliVideo : lang.ssYouTube,
|
|
|
76: lang.ssCreative,
|
|
|
@@ -998,16 +1165,88 @@ const getTypeClass = (type?: number) => {
|
|
|
|
|
|
import useImport from '@/hooks/useImport'
|
|
|
import message from '@/utils/message'
|
|
|
-const { importPPTXFile, exporting, getFile } = useImport()
|
|
|
+const { importPPTXFile, exporting, getFile, getPPTInfo, uploadFileToS3 } = useImport()
|
|
|
const currentFileName = ref('')
|
|
|
+const currentFileSize = ref(0)
|
|
|
const parsingStatus = ref<'parsing' | 'success'>('parsing')
|
|
|
const parsingAbortController = ref<AbortController | null>(null)
|
|
|
+const showFileConfirmModal = ref(false)
|
|
|
+const pendingFile = ref<FileList | null>(null)
|
|
|
+const selectedImportOption = ref<'page' | 'library'>('page')
|
|
|
+const pageCount = ref(1)
|
|
|
+const readingFile = ref(false)
|
|
|
+const importProgress = ref(0)
|
|
|
+const progressInterval = ref<NodeJS.Timeout | null>(null)
|
|
|
+const exportingDialog = ref(false)
|
|
|
+
|
|
|
+// 格式化文件大小
|
|
|
+const formatFileSize = (bytes: number): string => {
|
|
|
+ if (bytes === 0) return '0 B'
|
|
|
+ const k = 1024
|
|
|
+ const sizes = ['B', 'KB', 'MB', 'GB']
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
|
+}
|
|
|
|
|
|
const handleFileUpload = async (files: FileList) => {
|
|
|
if (!files || files.length === 0) return
|
|
|
|
|
|
const file = files[0]
|
|
|
currentFileName.value = file.name
|
|
|
+ currentFileSize.value = file.size
|
|
|
+ pendingFile.value = files
|
|
|
+ selectedImportOption.value = 'page'
|
|
|
+ readingFile.value = true
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 读取 PPT 信息获取页码
|
|
|
+ const info = await getPPTInfo(file)
|
|
|
+ pageCount.value = info.pageCount
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ console.error('获取 PPT 信息失败:', error)
|
|
|
+ pageCount.value = 1 // 失败时默认显示 1 页
|
|
|
+ }
|
|
|
+ finally {
|
|
|
+ readingFile.value = false
|
|
|
+ }
|
|
|
+
|
|
|
+ showFileConfirmModal.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const confirmFileUpload = async () => {
|
|
|
+ if (!pendingFile.value) return
|
|
|
+
|
|
|
+ showFileConfirmModal.value = false
|
|
|
+ importProgress.value = 0
|
|
|
+ exportingDialog.value = true
|
|
|
+ const startTimer = () => {
|
|
|
+ // 启动虚拟进度条(30秒从0-99%)
|
|
|
+ if (progressInterval.value) clearInterval(progressInterval.value)
|
|
|
+ const startTime = Date.now()
|
|
|
+ const duration = 30000 // 30秒
|
|
|
+ progressInterval.value = setInterval(() => {
|
|
|
+ console.log('progressInterval.value', progressInterval.value)
|
|
|
+ const elapsed = Date.now() - startTime
|
|
|
+ const progress = Math.min(99, Math.floor((elapsed / duration) * 99))
|
|
|
+ importProgress.value = progress
|
|
|
+ if (progress >= 99) {
|
|
|
+ importProgress.value = 99
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!exporting.value) {
|
|
|
+ if (progressInterval.value) {
|
|
|
+ clearInterval(progressInterval.value)
|
|
|
+ importProgress.value = 100
|
|
|
+ progressInterval.value = null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 100)
|
|
|
+ }
|
|
|
+
|
|
|
+ const confirmOnclose = () => {
|
|
|
+ handleParsingClose()
|
|
|
+ }
|
|
|
|
|
|
try {
|
|
|
// 创建AbortController用于取消操作
|
|
|
@@ -1015,7 +1254,7 @@ const handleFileUpload = async (files: FileList) => {
|
|
|
const signal = parsingAbortController.value.signal
|
|
|
|
|
|
// 调用importPPTXFile并传入signal
|
|
|
- await importPPTXFile(files, { signal })
|
|
|
+ await importPPTXFile(pendingFile.value, { signal, startTimer, confirmOnclose })
|
|
|
}
|
|
|
catch (error) {
|
|
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
|
@@ -1026,9 +1265,26 @@ const handleFileUpload = async (files: FileList) => {
|
|
|
message.error(lang.ssFileParseFailedRetry)
|
|
|
}
|
|
|
}
|
|
|
+ finally {
|
|
|
+ // if (progressInterval.value) {
|
|
|
+ // clearInterval(progressInterval.value)
|
|
|
+ // progressInterval.value = null
|
|
|
+ // importProgress.value = 100
|
|
|
+ // }
|
|
|
+ pendingFile.value = null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const cancelFileUpload = () => {
|
|
|
+ showFileConfirmModal.value = false
|
|
|
+ pendingFile.value = null
|
|
|
}
|
|
|
|
|
|
const handleParsingClose = () => {
|
|
|
+ if (progressInterval.value) {
|
|
|
+ clearInterval(progressInterval.value)
|
|
|
+ progressInterval.value = null
|
|
|
+ }
|
|
|
if (exporting.value && parsingAbortController.value) {
|
|
|
parsingAbortController.value.abort()
|
|
|
exporting.value = false
|
|
|
@@ -1038,6 +1294,50 @@ const handleParsingClose = () => {
|
|
|
else if (!exporting.value) {
|
|
|
emit('close')
|
|
|
}
|
|
|
+
|
|
|
+ exportingDialog.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const uploadTab = ref('file')
|
|
|
+const switchUploadTab = (tab: 'file' | 'code') => {
|
|
|
+ uploadTab.value = tab
|
|
|
+}
|
|
|
+
|
|
|
+const codeInput = ref('')
|
|
|
+const uploadCodeFile = ref<File | null>()
|
|
|
+const handleFileCodeUpload = (files: FileList | null) => {
|
|
|
+
|
|
|
+ if (!files || files.length === 0) {
|
|
|
+ uploadCodeFile.value = null
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const file = files[0]
|
|
|
+ uploadCodeFile.value = file
|
|
|
+}
|
|
|
+
|
|
|
+const handleUploadCodeFile = async () => {
|
|
|
+ if (!uploadCodeFile.value) return
|
|
|
+ const fileName = uploadCodeFile.value.name.toLowerCase()
|
|
|
+ if (fileName.endsWith('.html') || fileName.endsWith('.htm')) {
|
|
|
+ isLoading.value = true
|
|
|
+ const url = await uploadFileToS3(uploadCodeFile.value)
|
|
|
+ createSlide()
|
|
|
+ createFrameElement(url, 73)
|
|
|
+ uploadCodeFile.value = null
|
|
|
+ isLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleUploadCode = async () => {
|
|
|
+ if (!codeInput.value) return
|
|
|
+ const file = new File([codeInput.value], 'index.html', { type: 'text/html' })
|
|
|
+ isLoading.value = true
|
|
|
+ const url = await uploadFileToS3(file)
|
|
|
+ createSlide()
|
|
|
+ createFrameElement(url, 73)
|
|
|
+ codeInput.value = ''
|
|
|
+ isLoading.value = false
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
@@ -1065,6 +1365,44 @@ const handleParsingClose = () => {
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
+.sidebar-divider {
|
|
|
+ position: relative;
|
|
|
+ margin: 4px 2px 2px;
|
|
|
+ height: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-divider::before {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 50%;
|
|
|
+ height: 1px;
|
|
|
+ background: #e8ddd0;
|
|
|
+}
|
|
|
+
|
|
|
+.feature-sidebar-item {
|
|
|
+ min-height: 82px;
|
|
|
+ padding-top: 12px;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ gap: 7px;
|
|
|
+ border: 1px solid #f1e2cc;
|
|
|
+ background: #fffdfa;
|
|
|
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.05);
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #fff2df !important;
|
|
|
+ border-color: rgba(247, 139, 34, 0.4) !important;
|
|
|
+ box-shadow: 0 14px 28px rgba(247, 139, 34, 0.18) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #fff2df !important;
|
|
|
+ border-color: rgba(247, 139, 34, 0.4) !important;
|
|
|
+ box-shadow: 0 14px 28px rgba(247, 139, 34, 0.18) !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.sidebar-item {
|
|
|
width: 84px;
|
|
|
padding: 12px 8px;
|
|
|
@@ -1467,112 +1805,129 @@ const handleParsingClose = () => {
|
|
|
}
|
|
|
|
|
|
|
|
|
-.parsing-modal {
|
|
|
- position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- background: rgba(0, 0, 0, 0.5);
|
|
|
+
|
|
|
+
|
|
|
+.progress-inline {
|
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- z-index: 1000;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 14px 14px 12px;
|
|
|
+ border-radius: 16px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #f0ebe3;
|
|
|
+ box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04);
|
|
|
+ width: calc(100% - 30px);
|
|
|
+ margin: 15px auto;
|
|
|
|
|
|
- .parsing-content {
|
|
|
- background: white;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 24px;
|
|
|
- width: 400px;
|
|
|
- text-align: center;
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
-
|
|
|
- .loading-spinner {
|
|
|
- width: 48px;
|
|
|
- height: 48px;
|
|
|
- border: 4px solid #f0f0f0;
|
|
|
- border-top: 4px solid #FF9300;
|
|
|
- border-radius: 50%;
|
|
|
- margin: 0 auto 20px;
|
|
|
- animation: spin 1s linear infinite;
|
|
|
+ .progress-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ // .file-name {
|
|
|
+ // font-size: 14px;
|
|
|
+ // font-weight: 600;
|
|
|
+ // color: #333;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .progress-percent {
|
|
|
+ // font-size: 14px;
|
|
|
+ // font-weight: 600;
|
|
|
+ // color: #FF9300;
|
|
|
+ // }
|
|
|
+
|
|
|
+ .upload-task-main {
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .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;
|
|
|
+ .upload-task-name {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #111827;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
- h3 {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- color: #333;
|
|
|
- margin: 0 0 12px;
|
|
|
+ .upload-task-meta {
|
|
|
+ margin-top: 2px;
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8b7356;
|
|
|
}
|
|
|
|
|
|
- p {
|
|
|
- font-size: 14px;
|
|
|
- color: #666;
|
|
|
- margin: 0 0 24px;
|
|
|
+ .upload-task-side {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
- .close-btn2 {
|
|
|
- background: #FF9300;
|
|
|
- color: white;
|
|
|
- border: none;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 12px 24px;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
+ .upload-task-percent {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #c76a0c;
|
|
|
+ min-width: 40px;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-task-action {
|
|
|
+ border: 0;
|
|
|
+ background: transparent;
|
|
|
+ color: #9a8a77;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
cursor: pointer;
|
|
|
- width: 100%;
|
|
|
- transition: all 0.3s;
|
|
|
+ }
|
|
|
|
|
|
- &:hover {
|
|
|
- background: #e68a00;
|
|
|
- }
|
|
|
+ .upload-task-close {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 999px;
|
|
|
+ color: #8b7356;
|
|
|
+ font-size: 16px;
|
|
|
+ line-height: 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @keyframes spin {
|
|
|
- 0% {
|
|
|
- transform: rotate(0deg);
|
|
|
- }
|
|
|
+ .progress-bar {
|
|
|
+ width: 100%;
|
|
|
+ height: 4px;
|
|
|
+ background: #f0f0f0;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
|
|
|
- 100% {
|
|
|
- transform: rotate(360deg);
|
|
|
+ .progress-fill {
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, #f9b24e 0%, #f07815 100%);
|
|
|
+ transition: width 0.24s ease;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: width 0.1s ease;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .progress-loading {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #111827;
|
|
|
+ }
|
|
|
+
|
|
|
.close-btn {
|
|
|
- width: 32px;
|
|
|
- height: 32px;
|
|
|
+ background: #FF9300;
|
|
|
+ color: white;
|
|
|
border: none;
|
|
|
- background: none;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 10px 20px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
cursor: pointer;
|
|
|
- color: #999;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- border-radius: 4px;
|
|
|
- transition: all 0.2s;
|
|
|
+ width: 100%;
|
|
|
+ transition: all 0.3s;
|
|
|
|
|
|
&:hover {
|
|
|
- background: #f0f0f0;
|
|
|
- color: #666;
|
|
|
- }
|
|
|
-
|
|
|
- svg {
|
|
|
- width: 16px;
|
|
|
- height: 16px;
|
|
|
+ background: #e68a00;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1645,4 +2000,413 @@ const handleParsingClose = () => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+.upload-dropzone {
|
|
|
+ position: relative;
|
|
|
+ border: 1.5px dashed rgba(247, 139, 34, 0.38);
|
|
|
+ border-radius: 20px;
|
|
|
+ background: #fffaf4;
|
|
|
+ min-height: 156px;
|
|
|
+ padding: 22px 20px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ text-align: center;
|
|
|
+ gap: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.22s ease;
|
|
|
+ overflow: hidden;
|
|
|
+ width: calc(100% - 30px);
|
|
|
+ margin: 15px auto;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-style: solid;
|
|
|
+ border-color: #f78b22;
|
|
|
+ background: #fff3e2;
|
|
|
+ box-shadow: 0 18px 34px rgba(247, 139, 34, 0.16);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-dropzone-icon {
|
|
|
+ width: 56px;
|
|
|
+ height: 56px;
|
|
|
+ border-radius: 18px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: #f78b22;
|
|
|
+ background: rgba(247, 139, 34, 0.10);
|
|
|
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
|
+
|
|
|
+ svg {
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-dropzone-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #111827;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-dropzone-subtitle {
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #7c6d5d;
|
|
|
+ max-width: 320px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-dropzone-footnote {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #9a8a77;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.reading-file {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 40px 20px;
|
|
|
+ gap: 16px;
|
|
|
+
|
|
|
+ .loading-spinner {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border: 4px solid #f0f0f0;
|
|
|
+ border-top: 4px solid #FF9300;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ .reading-text {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes spin {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.file-confirm-inline {
|
|
|
+ width: calc(100% - 30px);
|
|
|
+ margin: 15px auto;
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin-top: 18px;
|
|
|
+ padding: 18px;
|
|
|
+ border-radius: 18px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #f3e4cf;
|
|
|
+ box-shadow: 0 12px 28px rgba(15, 23, 42, 0.06);
|
|
|
+
|
|
|
+ .file-info {
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .file-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #111827;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-subtitle {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #7c6d5d;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .import-options {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .import-option {
|
|
|
+ border: 1px solid #efe6d8;
|
|
|
+ border-radius: 16px;
|
|
|
+ padding: 14px;
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.22s ease;
|
|
|
+ background: #fffdfa;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: rgba(247, 139, 34, 0.4);
|
|
|
+ background: #fffdfa;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border-color: #ff9300;
|
|
|
+ background: #fff5e5;
|
|
|
+
|
|
|
+ .option-icon {
|
|
|
+ border-color: #f78b22;
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ inset: 3px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #f78b22;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .option-icon {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 1.5px solid #d6c0a1;
|
|
|
+ margin-top: 2px;
|
|
|
+ position: relative;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .option-text {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .option-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #111827;
|
|
|
+ margin-bottom: 3px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .option-desc {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #6b7280;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-buttons {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ justify-content: flex-end;
|
|
|
+
|
|
|
+ .cancel-btn,
|
|
|
+ .confirm-btn {
|
|
|
+ min-width: 84px;
|
|
|
+ height: 34px;
|
|
|
+ padding: 0 14px;
|
|
|
+ border-radius: 10px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cancel-btn {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e5e7eb;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
+
|
|
|
+ .confirm-btn {
|
|
|
+ background: linear-gradient(180deg, #f89a34 0%, #f07815 100%);
|
|
|
+ border: 0;
|
|
|
+ color: #fff;
|
|
|
+ box-shadow: 0 10px 20px rgba(240, 120, 21, 0.18);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.line_box {
|
|
|
+ height: calc(100% - 75px);
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-box {
|
|
|
+ padding: 20px;
|
|
|
+
|
|
|
+ .upload-tabs {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ border-bottom: 1px solid rgb(229, 231, 235);
|
|
|
+
|
|
|
+ .upload-tab {
|
|
|
+ border-top-width: initial;
|
|
|
+ border-right-width: initial;
|
|
|
+ border-left-width: initial;
|
|
|
+ border-top-color: initial;
|
|
|
+ border-right-color: initial;
|
|
|
+ border-left-color: initial;
|
|
|
+ background-color: transparent;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgb(107, 114, 128);
|
|
|
+ margin-bottom: -1px;
|
|
|
+ padding: 10px 16px;
|
|
|
+ border-style: none none solid;
|
|
|
+ border-image: initial;
|
|
|
+ border-bottom: 2px solid transparent;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ color: rgb(247, 139, 34);
|
|
|
+ border-bottom-color: rgb(247, 139, 34);
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .uploadFilePanel {
|
|
|
+
|
|
|
+ .form-group {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ .file-upload-area {
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ min-height: 190px;
|
|
|
+ border-width: 2px;
|
|
|
+ border-style: dashed;
|
|
|
+ border-color: rgb(229, 231, 235);
|
|
|
+ border-image: initial;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: rgb(250, 251, 252);
|
|
|
+ padding: 40px 20px;
|
|
|
+ transition: 0.2s;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ svg {
|
|
|
+ color: rgb(156, 163, 175);
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-text {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: rgb(55, 65, 81);
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-hint {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgb(107, 114, 128);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: rgb(247, 139, 34);
|
|
|
+ background: rgb(255, 248, 240);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-name-display {
|
|
|
+ margin-top: 12px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: rgb(75, 85, 99);
|
|
|
+ background-color: rgb(249, 250, 251);
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border-width: 1px;
|
|
|
+ border-style: solid;
|
|
|
+ border-color: rgb(229, 231, 235);
|
|
|
+ border-image: initial;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ span {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-task-action {
|
|
|
+ border: 0;
|
|
|
+ background: transparent;
|
|
|
+ color: #9a8a77;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .pasteCodePanel {
|
|
|
+ .form-group {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+
|
|
|
+ .code-textarea {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 190px;
|
|
|
+ font-family: monospace;
|
|
|
+ font-size: 13px;
|
|
|
+ resize: vertical;
|
|
|
+ border-width: 1px;
|
|
|
+ border-style: solid;
|
|
|
+ border-color: rgb(209, 213, 219);
|
|
|
+ border-image: initial;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 12px;
|
|
|
+ transition: border-color 0.2s;
|
|
|
+ box-sizing: border-box;
|
|
|
+ resize: none;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ outline: none;
|
|
|
+ border-color: rgb(247, 139, 34);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .webpage-link-button {
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px 24px;
|
|
|
+ border: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ background-color: #FF9300;
|
|
|
+ color: white;
|
|
|
+ margin: 0 auto;
|
|
|
+ display: block;
|
|
|
+ margin-top: 15px;
|
|
|
+
|
|
|
+ &:hover:not(:disabled) {
|
|
|
+ background-color: #e68a00;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:disabled {
|
|
|
+ opacity: 0.5;
|
|
|
+ cursor: not-allowed;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.loading {
|
|
|
+ cursor: not-allowed;
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.error {
|
|
|
+ background-color: #ff4d4f;
|
|
|
+ color: white;
|
|
|
+
|
|
|
+ &:hover:not(:disabled) {
|
|
|
+ background-color: #ff7875;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|