deotalandAi/apps/FrontendDesigner/src/views/admin/AdminDisassemblyDetail/AdminDisassemblyDetail.vue

1235 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="disassembly-detail">
<div class="order-info-container">
<div class="order-info-header">
<div class="header-left">
<el-button circle @click="goBack" class="back-button">
<el-icon><ArrowLeft /></el-icon>
</el-button>
<h2 class="order-title">拆件详情</h2>
</div>
<el-tag :type="getStatusType(orderDetail.status)" class="order-status">
{{ getStatusText(orderDetail.status) }}
</el-tag>
</div>
<div class="order-info-content">
<div class="info-item">
<span class="info-label">订单号</span>
<span class="info-value">{{ orderDetail.orderNumber }}</span>
</div>
<div class="info-item">
<span class="info-label">创作者</span>
<span class="info-value">{{ orderDetail.customerName }}</span>
</div>
<div class="info-item">
<span class="info-label">模型名称</span>
<span class="info-value">{{ orderDetail.modelName }}</span>
</div>
<div class="info-item">
<span class="info-label">创建时间</span>
<span class="info-value">{{ orderDetail.createTime }}</span>
</div>
</div>
</div>
<div class="timeline-section">
<div class="timeline-header">
<div class="timeline-title-container">
<h2 class="timeline-title">处理流程</h2>
<div class="timeline-progress">
<span class="progress-text">进度</span>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: `${(currentStep / 4) * 100}%` }"></div>
</div>
<span class="progress-step">{{ currentStep }}/4</span>
</div>
</div>
</div>
<div class="timeline-container">
<el-timeline>
<!-- 第一步预览图和模型展示 -->
<el-timeline-item
:color="currentStep >= 1 ? '#6B46C1' : '#e4e7ed'"
size="large"
>
<div class="timeline-content">
<h3>{{ $t('admin.disassemblyOrders.detail.step1') }}</h3>
<div v-if="currentStep >= 1" class="step-content">
<div class="preview-container-horizontal">
<div class="preview-images-horizontal">
<div class="image-item-horizontal" @click="previewImage(thumbnailUrl)">
<img :src="thumbnailUrl" alt="缩略图" />
<div class="image-label">{{ $t('admin.disassemblyOrders.detail.preview') }}</div>
<div class="image-actions">
<el-button
type="primary"
size="small"
@click.stop="changePreviewImage"
:disabled="currentStep > 1"
>
{{ $t('admin.disassemblyOrders.detail.changePreview') }}
</el-button>
</div>
</div>
<div class="image-item-horizontal" @click="previewModel(modelUrl)">
<div class="model-preview-horizontal">
<el-icon size="48"><View /></el-icon>
<span>{{ $t('admin.disassemblyOrders.detail.previewDialog') }}</span>
</div>
<div class="image-label">{{ $t('admin.disassemblyOrders.detail.previewDialog') }}</div>
</div>
</div>
<div class="disassembly-button-container">
<el-button
type="primary"
size="large"
@click="startDisassembly"
:loading="disassemblyLoading"
>
{{ $t('admin.disassemblyOrders.detail.disassembly') }}
</el-button>
</div>
</div>
</div>
</div>
</el-timeline-item>
<!-- 第二步:展示已拆件图片 -->
<el-timeline-item
:color="currentStep >= 2 ? '#6B46C1' : '#e4e7ed'"
size="large"
>
<div class="timeline-content">
<h3>{{ $t('admin.disassemblyOrders.detail.step2') }}</h3>
<div v-if="currentStep >= 2" class="step-content">
<div class="disassembled-images">
<div
v-for="(image, index) in disassembledImages"
:key="index"
class="image-item"
@click="previewImage(image)"
@mouseenter="showImageActions(index)"
@mouseleave="hideImageActions"
>
<div class="image-wrapper">
<img :src="image" :alt="`拆件图 ${index + 1}`" />
<el-button
v-if="hoveredImageIndex === index"
class="delete-button"
type="danger"
size="small"
circle
@click.stop="deleteImage(index)"
>
<el-icon><Close /></el-icon>
</el-button>
<div v-if="hoveredImageIndex === index" class="generate-model-button-container">
<el-button
class="generate-model-button"
type="success"
size="small"
@click.stop="generateModelFromImage(image)"
>
生成模型
</el-button>
</div>
</div>
<div class="image-label">{{ $t('admin.disassemblyOrders.detail.preview') }} {{ index + 1 }}</div>
</div>
</div>
</div>
</div>
</el-timeline-item>
<!-- 第三步:展示生成的模型 -->
<el-timeline-item
:color="currentStep >= 3 ? '#6B46C1' : '#e4e7ed'"
size="large"
>
<div class="timeline-content">
<h3>{{ $t('admin.disassemblyOrders.detail.step3') }}</h3>
<div v-if="currentStep >= 3" class="step-content">
<div class="generated-models">
<div
v-for="(imgurl, index) in generatedModels"
:key="index"
class="model-item"
>
<ModelCom
@preview="previewModel"
:img-url="imgurl"
width="100%"
height="150px"
:show-delete="true"
@delete="deleteModel(index)"
/>
<div class="image-label">模型 {{ index + 1 }}</div>
</div>
</div>
<div class="step-actions">
<el-button
type="primary"
@click="handleProcessingComplete"
:loading="processingCompleteLoading"
>
加工完成
</el-button>
</div>
</div>
</div>
</el-timeline-item>
<!-- 第四步:工期信息 -->
<el-timeline-item
:color="currentStep >= 4 ? '#6B46C1' : '#e4e7ed'"
size="large"
>
<div class="timeline-content">
<h3>工期信息</h3>
<div v-if="currentStep >= 4" class="step-content">
<div class="work-period-info">
<div class="info-card">
<div class="card-content">
<div class="info-icon">
<el-icon><Calendar /></el-icon>
</div>
<div class="info-content">
<div class="info-label">开始工期</div>
<div class="info-value">{{ formatDate(orderDetail.createTime) }}</div>
</div>
</div>
</div>
<div class="info-card">
<div class="card-content">
<div class="info-icon">
<el-icon><Check /></el-icon>
</div>
<div class="info-content">
<div class="info-label">完成工期</div>
<div class="info-value">{{ formatDate(orderDetail.processingCompleteTime) }}</div>
</div>
</div>
</div>
<div class="info-card total-days-card">
<div class="card-content">
<div class="info-icon">
<el-icon><Timer /></el-icon>
</div>
<div class="info-content">
<div class="info-label">共计天数</div>
<div class="info-value">{{ calculateTotalDays() }} 天</div>
</div>
</div>
</div>
</div>
<div class="step-actions">
<el-button @click="goBack">返回列表</el-button>
</div>
</div>
</div>
</el-timeline-item>
</el-timeline>
</div>
</div>
<!-- 图片预览对话框 -->
<el-dialog v-model="imagePreviewVisible" :title="$t('admin.disassemblyOrders.detail.preview')" width="50%" >
<img :src="previewImageUrl" style="width:100%;object-fit: contain;" />
</el-dialog>
<!-- 模型预览对话框 -->
<el-dialog v-model="modelPreviewVisible" :title="$t('admin.disassemblyOrders.detail.previewDialog')" width="70%">
<ModelViewer
:model-url="previewModelUrl"
:width="'100%'"
/>
<template #footer>
<el-button @click="modelPreviewVisible = false">{{ $t('admin.disassemblyOrders.detail.close') }}</el-button>
</template>
</el-dialog>
<!-- 更换预览图对话框 -->
<el-dialog v-model="showImageUpload" :title="$t('admin.disassemblyOrders.detail.changePreview')" width="30%">
<div class="upload-container">
<el-upload
class="image-uploader"
action="#"
:show-file-list="false"
:before-upload="handleImageUpload"
accept="image/*"
:disabled="uploadLoading"
>
<div class="upload-trigger">
<el-icon size="32" v-if="!uploadLoading"><Plus /></el-icon>
<el-icon size="32" v-else><Loading /></el-icon>
<div class="upload-text">
{{ uploadLoading ? $t('common.loading') : $t('admin.disassemblyOrders.detail.uploadImage') }}
</div>
</div>
</el-upload>
</div>
<template #footer>
<el-button @click="cancelImageUpload">{{ $t('common.cancel') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
ArrowLeft,
View,
Close,
Calendar,
Check,
Timer,
Plus,
Loading
} from '@element-plus/icons-vue'
import ModelViewer from '@/components/common/ModelViewer.vue'
import ModelCom from '@/components/modelCom/index.vue'
import { AdminDisassemblyDetail } from './AdminDisassemblyDetail.js';
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const Plug = new AdminDisassemblyDetail();
// 订单详情
const orderDetail = ref({
orderNumber: '',
customerName: '',
modelName: '',
createTime: '',
status: 'pending',
processingCompleteTime: null
})
// 当前步骤
const currentStep = ref(1)
// 加载状态
const disassemblyLoading = ref(false)
const generateStep2Loading = ref(false)
const processingCompleteLoading = ref(false)
const submitShippingLoading = ref(false)
// 预览相关
const imagePreviewVisible = ref(false)
const modelPreviewVisible = ref(false)
const previewImageUrl = ref('')
const previewModelUrl = ref('')
// 图片删除相关
const hoveredImageIndex = ref(-1)
// 更换预览图相关
const showImageUpload = ref(false)
const uploadLoading = ref(false)
// 模型加载状态管理
const modelLoadingStates = ref([])
const modelLoadingProgress = ref([])
const modelInitializationStates = ref([])
const modelInitializationProgress = ref([])
// 资源路径
const thumbnailUrl = ref('/src/assets/demo/suoluetu.png')
const modelUrl = ref('/src/assets/demo/model.glb')
const generatedModelUrl = ref('/src/assets/demo/model.glb')
// 存储多个生成的模型
const generatedModels = ref([])
// 拆件后的图片(模拟)
const disassembledImages = ref([
])
// 发货表单
const shippingForm = reactive({
logisticsCompany: '',
trackingNumber: '',
shippingAddress: '',
contactPhone: '',
notes: ''
})
// 预览模型
const previewModel = (url) => {
previewModelUrl.value = url
modelPreviewVisible.value = true
}
// 获取状态标签类型
const getStatusType = (status) => {
const statusMap = {
pending: 'warning',
processing: 'primary',
'awaiting-shipment': 'success',
completed: 'success',
failed: 'danger'
}
return statusMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status) => {
switch (status) {
case 'pending':
return '待处理'
case 'processing':
return '处理中'
case 'awaiting-shipment':
return '待发货'
case 'completed':
return '已完成'
case 'failed':
return '失败'
default:
return '未知'
}
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return '未设置'
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
// 计算总天数
const calculateTotalDays = () => {
if (!orderDetail.value.createTime || !orderDetail.value.processingCompleteTime) {
return 0
}
const startDate = new Date(orderDetail.value.createTime)
const endDate = new Date(orderDetail.value.processingCompleteTime)
const diffTime = Math.abs(endDate - startDate)
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
return diffDays
}
// 返回上一页
const goBack = () => {
router.push('/admin/disassembly-orders')
}
// 预览图片
const previewImage = (url) => {
previewImageUrl.value = url
imagePreviewVisible.value = true
}
// 更换预览图
const changePreviewImage = () => {
showImageUpload.value = true
}
// 处理图片上传
const handleImageUpload = (file) => {
uploadLoading.value = true
// 模拟文件上传处理
const reader = new FileReader()
reader.onload = (e) => {
// 更新预览图URL
thumbnailUrl.value = e.target.result
uploadLoading.value = false
showImageUpload.value = false
ElMessage.success(t('admin.disassemblyOrders.detail.messages.uploadSuccess') || '图片上传成功')
}
reader.onerror = () => {
uploadLoading.value = false
ElMessage.error(t('admin.disassemblyOrders.detail.messages.uploadFailed') || '图片上传失败')
}
reader.readAsDataURL(file.raw || file)
}
// 取消图片上传
const cancelImageUpload = () => {
showImageUpload.value = false
}
const startDisassembly = () => {
for(let i = 0;i<4;i++){
handleDisassembly();
}
}
// 执行拆件
const handleDisassembly = () => {
disassemblyLoading.value = true
Plug.disassemble(thumbnailUrl.value, (result) => {
// 更新拆件图片
disassembledImages.value.push(result)
currentStep.value = 2
disassemblyLoading.value = false
})
}
const deleteModel = (index) => {
ElMessageBox.confirm(
'确定要删除这个模型吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
generatedModels.value.splice(index, 1)
// 清理对应的加载状态
modelLoadingStates.value.splice(index, 1)
modelLoadingProgress.value.splice(index, 1)
modelInitializationStates.value.splice(index, 1)
modelInitializationProgress.value.splice(index, 1)
ElMessage.success('模型已删除')
}).catch(() => {
// 用户取消删除
})
}
// 加工完成方法
const handleProcessingComplete = () => {
ElMessageBox.confirm(
'订单完成后将进入待发货状态,是否确认完成订单加工?',
'加工完成确认',
{
confirmButtonText: '确认完成',
cancelButtonText: '取消',
type: 'info',
}
).then(() => {
processingCompleteLoading.value = true
// 模拟API调用
setTimeout(() => {
processingCompleteLoading.value = false
currentStep.value = 4
orderDetail.value.status = 'awaiting-shipment'
// 记录加工完成时间
orderDetail.value.processingCompleteTime = new Date().toISOString()
ElMessage.success('订单加工已完成,进入待发货状态')
// TODO: 这里是加工完成确认后的具体功能事件占位符
// 用户可以在此处添加自定义功能
console.log('加工完成确认事件触发,可在此处添加具体功能')
}, 1500)
}).catch(() => {
// 用户取消
})
}
// 导出模型
const handleExport = (format) => {
ElMessage.success(`${t('admin.disassemblyOrders.detail.messages.exportStart')} ${format}`)
// 这里应该调用实际的导出API
}
// 处理发货
const handleShipping = () => {
currentStep.value = 4
}
// 提交发货信息
const submitShipping = () => {
// 验证表单
if (!shippingForm.logisticsCompany || !shippingForm.trackingNumber ||
!shippingForm.shippingAddress || !shippingForm.contactPhone) {
ElMessage.warning(t('admin.disassemblyOrders.detail.messages.requiredFields'))
return
}
submitShippingLoading.value = true
// 模拟API调用
setTimeout(() => {
// 这里应该调用实际的提交API
currentStep.value = 4
submitShippingLoading.value = false
ElMessage.success(t('admin.disassemblyOrders.detail.messages.shippingSuccess'))
// 返回订单列表
setTimeout(() => {
router.push('/admin/disassembly-orders')
}, 1500)
}, 1500)
}
// 组件挂载时获取订单信息
onMounted(() => {
const orderId = route.params.id
// 这里应该根据订单ID获取订单详情
console.log('Order ID:', orderId)
// 模拟获取订单详情
// 在实际应用中这里应该调用API获取订单详情
orderDetail.value = {
orderNumber: `ORD20231100${orderId}`,
customerName: '张三',
modelName: '测试模型',
createTime: '2023-11-20 10:30:00',
status: 'pending',
processingCompleteTime: null
}
// 模拟已有部分步骤完成的情况
// 可以根据订单状态设置currentStep
// currentStep.value = 2 // 如果已完成拆件
// currentStep.value = 3 // 如果已生成模型
})
// 组件卸载时清理资源
onUnmounted(() => {
// 清理预览状态
imagePreviewVisible.value = false
modelPreviewVisible.value = false
})
// 删除图片
const deleteImage = (index) => {
ElMessageBox.confirm(t('admin.disassemblyOrders.detail.messages.confirmDelete'), t('admin.disassemblyOrders.detail.messages.confirmTitle'), {
confirmButtonText: t('admin.disassemblyOrders.detail.messages.confirm'),
cancelButtonText: t('admin.disassemblyOrders.detail.messages.cancel'),
type: 'warning'
}).then(() => {
disassembledImages.value.splice(index, 1)
ElMessage.success(t('admin.disassemblyOrders.detail.messages.deleteSuccess'))
}).catch(() => {
// 用户取消删除
})
}
// 生成第二步内容(添加新图片)
const handleGenerateStep2 = () => {
generateStep2Loading.value = true
// 模拟API调用
disassembledImages.value.push(newImage)
generateStep2Loading.value = false
}
// 显示图片操作按钮
const showImageActions = (index) => {
hoveredImageIndex.value = index
}
// 隐藏图片操作按钮
const hideImageActions = () => {
hoveredImageIndex.value = -1
}
// 从单张图片生成模型并进入第三步
const generateModelFromImage = async (image) => {
generatedModels.value.push(image);
// 如果是第一次生成模型,进入第三步
if (currentStep.value < 3) {
currentStep.value = 3
}
}
</script>
<style scoped>
.page-header {
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #e5e7eb;
}
/* 订单信息容器 */
.order-info-container {
background-color: #ffffff;
border-radius: 8px;
padding: 20px;
margin-bottom: 24px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e5e7eb;
}
.order-info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #f3f4f6;
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
}
.back-button {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9fafb;
border: 1px solid #e5e7eb;
color: #6b7280;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
}
.back-button:hover {
background-color: #f3f4f6;
border-color: #d1d5db;
color: #374151;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
.order-title {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #1f2937;
}
.order-status {
font-weight: 500;
}
.order-info-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
}
.info-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.info-label {
font-size: 14px;
color: #6b7280;
font-weight: 500;
}
.info-value {
font-size: 15px;
color: #1f2937;
font-weight: 400;
}
.page-title {
font-size: 28px;
font-weight: 600;
color: #1f2937;
margin: 0 0 8px 0;
}
.page-subtitle {
color: #6b7280;
margin: 0;
font-size: 14px;
}
.timeline-section {
margin-top: 24px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e5e7eb;
overflow: hidden;
}
.timeline-header {
padding: 20px;
border-bottom: 1px solid #f3f4f6;
background: linear-gradient(135deg, #6B46C1 0%, #8B5CF6 100%);
}
.timeline-title-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.timeline-title {
margin: 0;
font-size: 22px;
font-weight: 600;
color: #ffffff;
}
.timeline-progress {
display: flex;
align-items: center;
gap: 12px;
}
.progress-text {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
.progress-bar {
width: 120px;
height: 6px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #ffffff;
border-radius: 3px;
transition: width 0.3s ease;
}
.progress-step {
font-size: 14px;
color: #ffffff;
font-weight: 600;
}
.timeline-container {
margin-top: 0;
padding: 20px;
}
.timeline-content {
padding-left: 16px;
}
.timeline-content h3 {
font-size: 18px;
font-weight: 600;
color: #1f2937;
margin: 0 0 16px 0;
display: flex;
align-items: center;
position: relative;
top: -1px;
}
/* 调整Element Plus时间轴组件的节点样式 */
:deep(.el-timeline-item__wrapper) {
padding-left: 28px;
position: relative;
}
:deep(.el-timeline-item__tail) {
left: 13px;
border-width: 2px;
}
:deep(.el-timeline-item__node) {
left: 7px;
width: 12px;
height: 12px;
background-color: #ffffff;
border: 2px solid #e4e7ed;
border-radius: 50%;
}
:deep(.el-timeline-item__node--primary) {
border-color: #6B46C1;
background-color: #6B46C1;
}
:deep(.el-timeline-item__timestamp) {
padding-left: 28px;
}
/* 调整时间轴内容的位置 */
:deep(.el-timeline-item__content) {
padding-left: 0;
position: relative;
top: -2px;
}
.step-content {
background-color: #f9fafb;
border-radius: 8px;
padding: 16px;
margin-top: 12px;
}
/* 横板预览容器样式 */
.preview-container-horizontal {
display: flex;
flex-direction: column;
position: relative;
border-radius: 8px;
overflow: hidden;
}
.preview-images-horizontal {
display: flex;
gap: 16px;
margin-bottom: 16px;
}
.image-item-horizontal {
flex: 1;
cursor: pointer;
transition: transform 0.2s;
max-width: 300px;
}
.image-item-horizontal:hover {
transform: scale(1.02);
}
.image-item-horizontal img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 8px;
border: 1px solid #e5e7eb;
}
.model-preview-horizontal {
width: 100%;
height: 180px;
background-color: #f3f4f6;
border-radius: 8px;
border: 1px solid #e5e7eb;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #6b7280;
}
.disassembly-button-container {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
/* 原有样式保持不变 */
.preview-container,
.disassembled-images,
.generated-models {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 16px;
}
.image-item,
.model-item {
position: relative;
width: 200px;
cursor: pointer;
transition: transform 0.2s;
display: flex;
flex-direction: column;
align-items: center;
border: 1px dashed #d9d9d9;
border-radius: 8px;
padding: 8px;
}
.image-item:hover,
.model-item:hover {
transform: scale(1.02);
}
.model-wrapper {
position: relative;
width: 100%;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
}
.delete-model-button {
position: absolute;
top: 4px;
right: 4px;
z-index: 10;
}
.model-item:hover {
border-color: #6B46C1;
box-shadow: 0 4px 12px rgba(107, 70, 193, 0.1);
}
.model-actions {
position: absolute;
bottom: 4px;
left: 0;
right: 0;
display: flex;
justify-content: center;
z-index: 10;
}
.image-item img {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 8px;
border: 1px solid #e5e7eb;
}
.model-preview {
width: 100%;
height: 150px;
background-color: #f3f4f6;
border-radius: 8px;
border: 1px solid #e5e7eb;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #6b7280;
}
.image-label {
text-align: center;
margin-top: 8px;
font-size: 14px;
color: #6b7280;
}
.image-actions {
display: flex;
justify-content: center;
margin-top: 8px;
gap: 8px;
}
.step-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
}
.shipping-form {
max-width: 600px;
}
.work-period-info {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-bottom: 24px;
}
.info-card {
flex: 1;
min-width: 280px;
background-color: #fff;
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.info-card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.card-content {
display: flex;
align-items: center;
gap: 16px;
}
.total-days-card {
background: linear-gradient(135deg, #6B46C1 0%, #9333EA 100%);
color: white;
border: none;
}
.info-icon {
width: 48px;
height: 48px;
border-radius: 12px;
background-color: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
color: #6B46C1;
font-size: 20px;
flex-shrink: 0;
}
.total-days-card .info-icon {
background-color: rgba(255, 255, 255, 0.2);
color: white;
}
.info-content {
flex: 1;
}
.info-label {
font-size: 14px;
color: #6b7280;
margin-bottom: 4px;
font-weight: 500;
}
.total-days-card .info-label {
color: rgba(255, 255, 255, 0.8);
}
.info-value {
font-size: 16px;
color: #1f2937;
font-weight: 600;
}
.total-days-card .info-value {
color: white;
font-size: 24px;
font-weight: 700;
}
.model-preview-dialog {
display: flex;
justify-content: center;
align-items: center;
}
/* 图片包装器和删除按钮样式 */
.image-wrapper {
position: relative;
display: block;
width: 100%;
}
.delete-button {
position: absolute;
top: 8px;
right: 8px;
z-index: 10;
opacity: 0.9;
transition: all 0.2s ease;
}
.delete-button:hover {
opacity: 1;
transform: scale(1.1);
}
.generate-model-button-container {
position: absolute;
bottom: 8px;
left: 0;
right: 0;
display: flex;
justify-content: center;
z-index: 10;
}
.generate-model-button {
opacity: 0.9;
transition: all 0.2s ease;
}
.generate-model-button:hover {
opacity: 1;
}
/* 上传容器样式 */
.upload-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.image-uploader {
width: 100%;
}
.upload-trigger {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 150px;
border: 2px dashed #d9d9d9;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
margin: 0 auto;
}
.upload-trigger:hover {
border-color: #6B46C1;
background-color: #f9fafb;
}
.upload-text {
margin-top: 8px;
font-size: 14px;
color: #6b7280;
}
@media (max-width: 768px) {
.disassembly-detail {
padding: 12px;
}
.page-title {
font-size: 24px;
}
.preview-container,
.disassembled-images,
.generated-model {
flex-direction: column;
}
.preview-images-horizontal {
flex-direction: column;
}
.image-item,
.model-item,
.image-item-horizontal {
width: 100%;
max-width: none;
}
.step-actions {
flex-direction: column;
}
.disassembly-button-container {
justify-content: center;
}
.shipping-form {
max-width: 100%;
}
.work-period-info {
flex-direction: column;
gap: 16px;
}
.info-card {
min-width: auto;
}
}
</style>