1235 lines
29 KiB
Vue
1235 lines
29 KiB
Vue
<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> |