feat: introduce UniversalConverter组件,支持STP转GLTF和IFC转STP文件转换功能。
This commit is contained in:
parent
3cdb7298e6
commit
ce85d70758
@ -69,14 +69,12 @@
|
||||
</div>
|
||||
|
||||
<div class="converter-panel">
|
||||
<div class="upload-section">
|
||||
<div class="upload-section" v-if="currentMode === 'stp-to-gltf'">
|
||||
<div class="upload-area" id="file-upload-area">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
<h4 v-if="currentMode === 'stp-to-gltf'">拖拽STP文件到此处</h4>
|
||||
<h4 v-else>拖拽IFC文件到此处</h4>
|
||||
<h4>拖拽STP文件到此处</h4>
|
||||
<p>或点击选择文件</p>
|
||||
<input v-if="currentMode === 'stp-to-gltf'" type="file" id="stp-file-input" accept=".stp,.step" class="hidden-input">
|
||||
<input v-else type="file" id="ifc-file-input" accept=".ifc" class="hidden-input">
|
||||
<input type="file" id="stp-file-input" accept=".stp,.step" class="hidden-input">
|
||||
<button class="upload-btn" @click="selectFile">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
选择文件
|
||||
@ -84,6 +82,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="path-input-section" v-else>
|
||||
<div class="input-group">
|
||||
<label><i class="fas fa-file-import"></i> 输入IFC绝对路径:</label>
|
||||
<input type="text" v-model="ifcPath" placeholder="例如:D:/CAD/input.ifc" class="path-input">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label><i class="fas fa-file-export"></i> 输出STP绝对路径:</label>
|
||||
<input type="text" v-model="stpPath" placeholder="例如:D:/CAD/output.stp" class="path-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="conversion-options">
|
||||
<h4>转换选项</h4>
|
||||
<!-- STP to GLTF Options -->
|
||||
@ -148,27 +157,29 @@
|
||||
</div>
|
||||
|
||||
<div class="conversion-actions">
|
||||
<button class="action-btn primary" id="start-conversion" disabled>
|
||||
<i class="fas fa-play"></i>
|
||||
开始转换
|
||||
<button class="action-btn primary" :disabled="!canConvert || isConverting" @click="startConversion">
|
||||
<i :class="isConverting ? 'fas fa-spinner fa-spin' : 'fas fa-play'"></i>
|
||||
{{ isConverting ? '转换中...' : '开始转换' }}
|
||||
</button>
|
||||
<button class="action-btn secondary">
|
||||
<button class="action-btn secondary" @click="resetForm" :disabled="isConverting">
|
||||
<i class="fas fa-undo"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="conversion-progress" id="conversion-progress" v-show="false">
|
||||
<div class="conversion-progress" v-show="isConverting || convertResult">
|
||||
<div class="progress-content">
|
||||
<div class="progress-icon">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<div class="progress-icon" :class="{ 'success-icon': convertResult === 'success', 'error-icon': convertResult === 'error' }">
|
||||
<i v-if="isConverting" class="fas fa-spinner fa-spin"></i>
|
||||
<i v-else-if="convertResult === 'success'" class="fas fa-check-circle"></i>
|
||||
<i v-else-if="convertResult === 'error'" class="fas fa-times-circle"></i>
|
||||
</div>
|
||||
<h4>正在转换...</h4>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="conversion-progress-fill"></div>
|
||||
<h4>{{ progressTitle }}</h4>
|
||||
<div class="progress-bar" v-if="isConverting">
|
||||
<div class="progress-fill" style="width: 100%; animation: pulse 1.5s infinite;"></div>
|
||||
</div>
|
||||
<p id="conversion-status">准备转换...</p>
|
||||
<p>{{ progressStatus }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -176,14 +187,80 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import websocketService from '@/services/websocketService'
|
||||
|
||||
// 格式转换页面 - 只实现UI,不实现功能
|
||||
const currentMode = ref('stp-to-gltf')
|
||||
|
||||
// IFC to STP 转换相关状态
|
||||
const ifcPath = ref('')
|
||||
const stpPath = ref('')
|
||||
const isConverting = ref(false)
|
||||
const convertResult = ref('') // 'success', 'error', ''
|
||||
const progressTitle = ref('准备转换...')
|
||||
const progressStatus = ref('')
|
||||
|
||||
const canConvert = computed(() => {
|
||||
if (currentMode.value === 'ifc-to-stp') {
|
||||
return ifcPath.value.trim() !== '' && stpPath.value.trim() !== ''
|
||||
}
|
||||
return false // STP to GLTF 目前尚未实现功能
|
||||
})
|
||||
|
||||
const selectFile = () => {
|
||||
// 空函数,只为了避免Vue警告
|
||||
// 空函数,保存 UI 免警告
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
ifcPath.value = ''
|
||||
stpPath.value = ''
|
||||
convertResult.value = ''
|
||||
progressTitle.value = '准备转换...'
|
||||
progressStatus.value = ''
|
||||
}
|
||||
|
||||
const startConversion = () => {
|
||||
if (currentMode.value === 'ifc-to-stp') {
|
||||
isConverting.value = true
|
||||
convertResult.value = ''
|
||||
progressTitle.value = '正在转换...'
|
||||
progressStatus.value = '已发送转换请求,后台处理中...'
|
||||
|
||||
websocketService.send({
|
||||
type: "convert_ifc_to_stp",
|
||||
ifc_path: ifcPath.value.trim(),
|
||||
stp_path: stpPath.value.trim()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleWsMessage = (message) => {
|
||||
if (!isConverting.value) return
|
||||
|
||||
// 成功看 msg.type === "info" 且 msg.data.stp_path
|
||||
if (message.type === "info" && message.data?.stp_path) {
|
||||
isConverting.value = false
|
||||
convertResult.value = 'success'
|
||||
progressTitle.value = '转换成功!'
|
||||
progressStatus.value = `文件已保存至: ${message.data.stp_path} (耗时: ${message.data.duration_ms}ms)`
|
||||
}
|
||||
// 失败看 msg.type === "error"
|
||||
else if (message.type === "error") {
|
||||
// 假设错误跟转换请求相关,这里做一个简单匹配或是只要是在转换期间遇到error就认为是失败
|
||||
isConverting.value = false
|
||||
convertResult.value = 'error'
|
||||
progressTitle.value = '转换失败'
|
||||
progressStatus.value = message.message || '未知错误'
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
websocketService.on('onMessage', handleWsMessage)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
websocketService.off('onMessage', handleWsMessage)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -556,5 +633,66 @@ const selectFile = () => {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 路径输入样式 */
|
||||
.path-input-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
background: var(--color-bg-tertiary);
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--color-border-primary);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
color: var(--color-text-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-group label i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.path-input {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: 8px;
|
||||
background: var(--color-bg-input);
|
||||
color: var(--color-text-primary);
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.path-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px var(--color-primary-rgb-2);
|
||||
}
|
||||
|
||||
.progress-icon.success-icon i {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.progress-icon.error-icon i {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 0.6; }
|
||||
50% { opacity: 1; }
|
||||
100% { opacity: 0.6; }
|
||||
}
|
||||
|
||||
/* 使用全局CSS变量 - 已在theme.css中统一定义 */
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user