Files
vue-model-review/src/views/ModelDetail.vue
2026-04-27 09:57:00 +08:00

326 lines
7.0 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="model-detail" :class="{ 'mobile': isMobile }">
<!-- 顶部标题栏 -->
<div class="header">
<el-button
icon="el-icon-arrow-left"
circle
size="small"
@click="goBack"
class="back-btn"
></el-button>
<h2 class="title">{{ modelData.modelName }}</h2>
</div>
<!-- 主内容区 -->
<div class="content">
<!-- PC端布局左右分栏 -->
<template v-if="!isMobile">
<div class="viewer-section">
<three-viewer
v-if="modelPath"
:model-path="modelPath"
:annotations="modelData.componentCheck"
@annotation-click="handleAnnotationClick"
@model-loaded="handleModelLoaded"
@model-error="handleModelError"
/>
<!-- <three-viewer-debug
:model-path="modelPath"
:annotations="modelData.componentCheck"
:debug-mode="true"
@annotation-click="handleAnnotationClick"
@model-loaded="handleModelLoaded"
/> -->
<div v-if="loading" class="loading-overlay">
<el-progress
type="circle"
:percentage="loadingProgress"
:width="80"
></el-progress>
<p>加载模型中...</p>
</div>
</div>
<div class="table-section">
<check-table
:appearance-check="modelData.appearanceCheck"
:main-data-check="modelData.mainDataCheck"
:component-check="modelData.componentCheck"
:highlighted-index="selectedComponentIndex"
/>
</div>
</template>
<!-- 移动端布局上中下 -->
<template v-else>
<div class="viewer-section-mobile">
<three-viewer
v-if="modelPath"
:model-path="modelPath"
:annotations="modelData.componentCheck"
@annotation-click="handleAnnotationClick"
@model-loaded="handleModelLoaded"
@model-error="handleModelError"
/>
<div v-if="loading" class="loading-overlay">
<el-progress
type="circle"
:percentage="loadingProgress"
:width="60"
></el-progress>
<p>加载中...</p>
</div>
</div>
<div class="table-section-mobile">
<check-table
:appearance-check="modelData.appearanceCheck"
:main-data-check="modelData.mainDataCheck"
:component-check="modelData.componentCheck"
:highlighted-index="selectedComponentIndex"
/>
</div>
</template>
</div>
<!-- 移动端点击标注后的详情抽屉 -->
<el-drawer
:visible.sync="drawerVisible"
direction="btt"
size="60%"
:with-header="false"
v-if="isMobile && selectedComponent"
>
<div class="drawer-content">
<h3>{{ selectedComponent.name }}</h3>
<div class="requirement-title">检查要求</div>
<ol>
<li v-for="(content, idx) in selectedComponent.content" :key="idx">
{{ content }}
</li>
</ol>
</div>
</el-drawer>
</div>
</template>
<script>
import ThreeViewer from '../components/ThreeViewer.vue'
import ThreeViewerDebug from '../components/ThreeViewerDebug.vue'
import CheckTable from '../components/CheckTable.vue'
import modelData from '../data/data.json'
export default {
name: 'ModelDetail',
components: {
ThreeViewer,
CheckTable,
ThreeViewerDebug
},
data() {
return {
modelData: {},
modelPath: '',
loading: true,
loadingProgress: 0,
selectedComponentIndex: -1,
selectedComponent: null,
drawerVisible: false,
isMobile: false
}
},
created() {
this.checkDevice()
this.loadModelData()
window.addEventListener('resize', this.checkDevice)
},
beforeDestroy() {
window.removeEventListener('resize', this.checkDevice)
},
methods: {
checkDevice() {
this.isMobile = window.innerWidth <= 768
},
loadModelData() {
const id = parseInt(this.$route.params.id)
if (id >= 0 && id < modelData.length) {
this.modelData = modelData[id]
// 构建模型路径
this.modelPath = `/models/${this.modelData.modelPath}.glb`
} else {
this.$message.error('模型不存在')
this.goBack()
}
},
handleAnnotationClick(annotation, index) {
this.selectedComponentIndex = index
this.selectedComponent = annotation
if (this.isMobile) {
this.drawerVisible = true
}
},
handleModelLoaded() {
this.loading = false
this.$message.success('模型加载完成')
},
handleModelError(error) {
this.loading = false
this.$message.error('模型加载失败,请检查模型文件是否存在')
console.error('模型加载错误:', error)
},
goBack() {
this.$router.push({ name: 'ModelList' })
}
}
}
</script>
<style scoped>
.model-detail {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #fff;
}
.header {
height: 60px;
background: #409eff;
color: white;
display: flex;
align-items: center;
padding: 0 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
}
.back-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
margin-right: 15px;
}
.back-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.content {
flex: 1;
display: flex;
overflow: hidden;
}
/* PC端布局 */
.viewer-section {
flex: 1;
position: relative;
border-right: 1px solid #e4e7ed;
}
.table-section {
width: 450px;
overflow: hidden;
}
/* 移动端布局 */
.mobile .content {
flex-direction: column;
}
.viewer-section-mobile {
height: 40%;
position: relative;
border-bottom: 1px solid #e4e7ed;
}
.table-section-mobile {
flex: 1;
overflow: hidden;
}
/* 加载状态 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
.loading-overlay p {
margin-top: 15px;
color: #606266;
font-size: 14px;
}
/* 抽屉内容 */
.drawer-content {
padding: 20px;
}
.drawer-content h3 {
margin: 0 0 15px 0;
color: #303133;
font-size: 18px;
padding-bottom: 10px;
border-bottom: 2px solid #409eff;
}
.drawer-content .requirement-title {
font-weight: 500;
color: #409eff;
margin: 15px 0 10px 0;
font-size: 15px;
}
.drawer-content ol {
margin: 0;
padding-left: 20px;
}
.drawer-content li {
margin: 10px 0;
color: #606266;
line-height: 1.8;
font-size: 14px;
}
@media (max-width: 768px) {
.header {
height: 50px;
padding: 0 15px;
}
.title {
font-size: 16px;
}
.back-btn {
margin-right: 10px;
}
}
</style>