284 lines
6.8 KiB
Markdown
284 lines
6.8 KiB
Markdown
# Spec: 第二步 - 拆件结果展示功能
|
|
|
|
## ADDED Requirements
|
|
|
|
### 拆件结果展示
|
|
- **Requirement**: 实现已拆件图片内容的展示布局,支持多张图片的展示
|
|
- **Scenario**: 用户可以看到拆件后的图片结果,图片以网格或列表形式展示,支持点击预览
|
|
|
|
### 重新生成功能
|
|
- **Requirement**: 添加重新生成按钮,实现重新执行拆件逻辑并更新图片内容
|
|
- **Scenario**: 用户对当前拆件结果不满意时,点击重新生成按钮,系统重新执行拆件算法并生成新的图片结果
|
|
|
|
### 生成模型功能
|
|
- **Requirement**: 添加生成模型按钮,点击后进入第三步模型生成环节
|
|
- **Scenario**: 用户满意拆件结果后,点击生成模型按钮,系统进入模型生成流程并进入第三步
|
|
|
|
### 步骤覆盖逻辑
|
|
- **Requirement**: 实现上游操作覆盖下游内容的逻辑处理
|
|
- **Scenario**: 用户在第二步点击重新生成时,如果已有第三步或第四步内容,系统会弹出确认对话框并清空下游步骤
|
|
|
|
## 技术实现细节
|
|
|
|
### 布局结构
|
|
```html
|
|
<div class="disassembly-step-content">
|
|
<div class="disassembly-results">
|
|
<div class="results-header">
|
|
<h3>{{ t('disassembly.steps.disassembly') }}</h3>
|
|
<el-tag type="success">{{ t('disassembly.status.completed') }}</el-tag>
|
|
</div>
|
|
|
|
<div class="results-grid">
|
|
<div
|
|
v-for="(image, index) in disassemblyImages"
|
|
:key="index"
|
|
class="result-image-container"
|
|
@click="openResultImagePreview(image)"
|
|
>
|
|
<img :src="image.url" :alt="`拆件结果 ${index + 1}`" />
|
|
<div class="image-overlay">
|
|
<el-icon><ZoomIn /></el-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step-actions">
|
|
<el-button
|
|
type="warning"
|
|
@click="regenerateDisassembly"
|
|
:loading="regenerateLoading"
|
|
>
|
|
{{ t('disassembly.actions.regenerate') }}
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
@click="generateModel"
|
|
:loading="modelGenerationLoading"
|
|
>
|
|
{{ t('disassembly.actions.generateModel') }}
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### 重新生成逻辑
|
|
```javascript
|
|
const regenerateDisassembly = async () => {
|
|
// 检查是否有下游步骤需要覆盖
|
|
if (hasDownstreamContent()) {
|
|
const confirmed = await ElMessageBox.confirm(
|
|
t('disassembly.confirm.regenerateOverride'),
|
|
t('common.confirm'),
|
|
{
|
|
confirmButtonText: t('common.confirm'),
|
|
cancelButtonText: t('common.cancel'),
|
|
type: 'warning'
|
|
}
|
|
)
|
|
|
|
if (!confirmed) return
|
|
}
|
|
|
|
try {
|
|
regenerateLoading.value = true
|
|
|
|
// 重新执行拆件逻辑(用户后续实现)
|
|
// const newResults = await callDisassemblyAPI(contentId, true)
|
|
|
|
// 模拟重新生成过程
|
|
await new Promise(resolve => setTimeout(resolve, 3000))
|
|
|
|
// 更新拆件结果
|
|
disassemblyImages.value = generateMockDisassemblyResults()
|
|
|
|
// 清空下游步骤
|
|
clearDownstreamSteps(2)
|
|
|
|
ElMessage.success(t('disassembly.success.regenerateCompleted'))
|
|
|
|
} catch (error) {
|
|
ElMessage.error(t('disassembly.errors.regenerateFailed'))
|
|
} finally {
|
|
regenerateLoading.value = false
|
|
}
|
|
}
|
|
|
|
const generateModel = async () => {
|
|
try {
|
|
modelGenerationLoading.value = true
|
|
|
|
// 生成模型(用户后续实现)
|
|
// const modelData = await generateModelAPI(contentId)
|
|
|
|
// 模拟模型生成过程
|
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
|
|
// 更新步骤状态
|
|
updateStepStatus(2, 'completed')
|
|
updateStepStatus(3, 'current')
|
|
workflowState.currentStep = 3
|
|
workflowState.contentData.generatedModel = '/src/assets/demo/model.glb'
|
|
|
|
ElMessage.success(t('disassembly.success.modelGenerationStarted'))
|
|
|
|
} catch (error) {
|
|
ElMessage.error(t('disassembly.errors.modelGenerationFailed'))
|
|
} finally {
|
|
modelGenerationLoading.value = false
|
|
}
|
|
}
|
|
```
|
|
|
|
### 下游步骤覆盖逻辑
|
|
```javascript
|
|
const hasDownstreamContent = () => {
|
|
return workflowState.currentStep > 2
|
|
}
|
|
|
|
const clearDownstreamSteps = (fromStep) => {
|
|
Object.keys(workflowState.stepStatus).forEach(step => {
|
|
const stepNum = parseInt(step)
|
|
if (stepNum > fromStep) {
|
|
workflowState.stepStatus[step] = 'pending'
|
|
if (stepNum === 3) {
|
|
workflowState.contentData.generatedModel = null
|
|
}
|
|
if (stepNum === 4) {
|
|
workflowState.contentData.shippingInfo = {}
|
|
}
|
|
}
|
|
})
|
|
|
|
// 调整当前步骤
|
|
workflowState.currentStep = fromStep
|
|
}
|
|
```
|
|
|
|
### 样式实现
|
|
```css
|
|
.disassembly-results {
|
|
background: #ffffff;
|
|
border-radius: 12px;
|
|
padding: 24px;
|
|
margin-bottom: 32px;
|
|
box-shadow: 0 2px 8px rgba(107, 70, 193, 0.1);
|
|
}
|
|
|
|
.results-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.results-header h3 {
|
|
margin: 0;
|
|
color: #1f2937;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.results-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.result-image-container {
|
|
position: relative;
|
|
aspect-ratio: 1;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
transition: transform 200ms ease;
|
|
}
|
|
|
|
.result-image-container:hover {
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.result-image-container img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.image-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(107, 70, 193, 0.8);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
transition: opacity 200ms ease;
|
|
color: white;
|
|
font-size: 24px;
|
|
}
|
|
|
|
.result-image-container:hover .image-overlay {
|
|
opacity: 1;
|
|
}
|
|
|
|
.step-actions {
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: flex-end;
|
|
padding-top: 24px;
|
|
border-top: 1px solid #e5e7eb;
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 768px) {
|
|
.disassembly-results {
|
|
padding: 16px;
|
|
}
|
|
|
|
.results-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 12px;
|
|
}
|
|
|
|
.step-actions {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
```
|
|
|
|
## 数据模拟
|
|
```javascript
|
|
// 模拟拆件结果数据
|
|
const generateMockDisassemblyResults = () => {
|
|
return [
|
|
{
|
|
url: '/src/assets/demo/suoluetu.png',
|
|
title: '拆件结果 1',
|
|
description: '主体部分'
|
|
},
|
|
{
|
|
url: '/src/assets/demo/suoluetu.png',
|
|
title: '拆件结果 2',
|
|
description: '细节部分'
|
|
},
|
|
{
|
|
url: '/src/assets/demo/suoluetu.png',
|
|
title: '拆件结果 3',
|
|
description: '组件部分'
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## 验证标准
|
|
- 拆件结果图片能够正确加载和显示
|
|
- 点击图片能够弹出预览对话框
|
|
- 重新生成按钮能够触发重新拆件流程
|
|
- 生成模型按钮能够正常进入第三步
|
|
- 步骤覆盖逻辑在各种情况下正常工作
|
|
- 响应式布局在各种设备上表现良好 |