MianyVue/src/components/pages/GeometryComplexityResult.vue
sladro 653917eab3 feat: 实现Creo几何复杂度分析结果页面UI
- 完全照抄参考项目的几何复杂度分析UI设计
- 实现分析摘要、零件列表表格、选择删除功能界面
- 添加几何复杂度专用CSS变量到theme.css统一管理
- 使用模拟数据展示6个零件的复杂度分析结果
- 支持全选/清空、复杂度等级颜色标识功能
- 完全复用现有系统CSS变量,保持主题一致性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-17 13:37:54 +08:00

638 lines
16 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="geometry-complexity-result-page">
<div class="page-header">
<h2>
<i class="fas fa-chart-line"></i>
几何复杂度分析结果
</h2>
<p>Creo几何复杂度分析结果展示</p>
</div>
<div class="page-content">
<!-- 分析统计摘要 -->
<div class="complexity-analysis-summary">
<div class="summary-header">
<h4><i class="fas fa-chart-pie"></i> 分析摘要</h4>
<div class="analysis-time">
<i class="fas fa-clock"></i>
完成时间: {{ analysisTime }}
</div>
</div>
<div class="summary-stats">
<div class="stat-card">
<div class="stat-value">{{ mockData.length }}</div>
<div class="stat-label">总零件数</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ averageComplexity }}</div>
<div class="stat-label">平均复杂度</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ selectedParts.size }}</div>
<div class="stat-label">已选择零件</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ highComplexityCount }}</div>
<div class="stat-label">高复杂度零件</div>
</div>
</div>
</div>
<!-- 零件列表区域 -->
<div class="complexity-parts-list">
<div class="complexity-parts-header">
<h4>
<i class="fas fa-list"></i>
几何复杂度分析结果 ({{ mockData.length }} 个零件)
</h4>
<div class="complexity-controls">
<button class="complexity-select-all" @click="selectAll">
<i class="fas fa-check-square"></i>
全选
</button>
<button class="complexity-clear-all" @click="clearAll">
<i class="far fa-square"></i>
清空
</button>
</div>
</div>
<div class="table-container">
<table class="complexity-parts-table">
<thead>
<tr>
<th width="50">选择</th>
<th width="60">ID</th>
<th width="200">零件名称</th>
<th width="80">表面数</th>
<th width="80">特征数</th>
<th width="80">曲面数</th>
<th width="60">孔数</th>
<th width="80">圆角数</th>
<th width="100">复杂度得分</th>
<th width="100">体积 (mm³)</th>
</tr>
</thead>
<tbody id="complexity-parts-tbody">
<tr
v-for="(part, index) in mockData"
:key="index"
class="complexity-part-row"
:class="{ selected: selectedParts.has(index + 1) }"
:data-part-id="index + 1"
>
<td>
<input
type="checkbox"
class="complexity-checkbox"
:data-part-id="index + 1"
:checked="selectedParts.has(index + 1)"
@change="updateSelection"
/>
</td>
<td>{{ index + 1 }}</td>
<td>
<div class="part-info">
<span class="complexity-part-name">{{ part.part_name }}</span>
<div class="complexity-part-path">{{ part.part_path }}</div>
<div class="complexity-file-size">{{ part.file_size }}</div>
</div>
</td>
<td>{{ part.surface_count }}</td>
<td>{{ part.feature_count }}</td>
<td>{{ part.curved_surface_count }}</td>
<td>{{ part.hole_count }}</td>
<td>{{ part.fillet_count }}</td>
<td>
<span
class="complexity-score"
:class="getComplexityClass(part.complexity_score)"
>
{{ part.complexity_score.toFixed(2) }}
</span>
</td>
<td>{{ formatVolume(part.volume) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 删除操作区域 -->
<div class="complexity-delete-section">
<div class="complexity-delete-header">
<h4>
<i class="fas fa-trash"></i>
删除操作
</h4>
<div class="complexity-selected-count" id="complexity-selected-count">
已选择: {{ selectedParts.size }} 个零件
</div>
</div>
<div class="complexity-delete-summary">
<p>选中的零件将从模型中删除。请仔细检查选择的零件,删除操作无法撤销。</p>
</div>
<button
id="complexity-delete-btn"
class="complexity-delete-btn"
:disabled="selectedParts.size === 0"
@click="deleteSelected"
>
<i class="fas fa-trash"></i>
删除选中零件
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
// 响应式数据
const selectedParts = ref(new Set())
const analysisTime = ref('')
// 模拟数据
const mockData = ref([
{
part_name: 'HOUSING_ASSEMBLY_001',
part_path: '/PROJECT/ASSEMBLY/HOUSING_ASSEMBLY_001.PRT',
file_size: '2.4 MB',
surface_count: 142,
feature_count: 23,
curved_surface_count: 18,
hole_count: 8,
fillet_count: 15,
complexity_score: 8.5,
complexity_level: 'Very High',
volume: 125400.50
},
{
part_name: 'BRACKET_SUPPORT_V2',
part_path: '/PROJECT/ASSEMBLY/BRACKET_SUPPORT_V2.PRT',
file_size: '1.8 MB',
surface_count: 98,
feature_count: 18,
curved_surface_count: 12,
hole_count: 6,
fillet_count: 11,
complexity_score: 7.2,
complexity_level: 'High',
volume: 89230.25
},
{
part_name: 'CONNECTOR_PLATE_A',
part_path: '/PROJECT/ASSEMBLY/CONNECTOR_PLATE_A.PRT',
file_size: '0.9 MB',
surface_count: 54,
feature_count: 12,
curved_surface_count: 6,
hole_count: 4,
fillet_count: 8,
complexity_score: 5.8,
complexity_level: 'Medium',
volume: 42150.75
},
{
part_name: 'BASE_FRAME_MAIN',
part_path: '/PROJECT/ASSEMBLY/BASE_FRAME_MAIN.PRT',
file_size: '3.2 MB',
surface_count: 186,
feature_count: 31,
curved_surface_count: 24,
hole_count: 12,
fillet_count: 19,
complexity_score: 9.1,
complexity_level: 'Very High',
volume: 198750.00
},
{
part_name: 'GUIDE_RAIL_SET',
part_path: '/PROJECT/ASSEMBLY/GUIDE_RAIL_SET.PRT',
file_size: '1.2 MB',
surface_count: 76,
feature_count: 15,
curved_surface_count: 8,
hole_count: 5,
fillet_count: 9,
complexity_score: 6.4,
complexity_level: 'Medium',
volume: 67890.30
},
{
part_name: 'SIMPLE_WASHER_001',
part_path: '/PROJECT/ASSEMBLY/SIMPLE_WASHER_001.PRT',
file_size: '0.1 MB',
surface_count: 8,
feature_count: 3,
curved_surface_count: 2,
hole_count: 1,
fillet_count: 2,
complexity_score: 2.1,
complexity_level: 'Low',
volume: 1250.45
}
])
// 计算属性
const averageComplexity = computed(() => {
const total = mockData.value.reduce((sum, part) => sum + part.complexity_score, 0)
return (total / mockData.value.length).toFixed(2)
})
const highComplexityCount = computed(() => {
return mockData.value.filter(part => part.complexity_score >= 7.0).length
})
// 方法
const getComplexityClass = (score) => {
if (score >= 8.5) return 'very-high'
if (score >= 7.0) return 'high'
if (score >= 5.0) return 'medium'
if (score >= 3.0) return 'low'
return 'very-low'
}
const formatVolume = (volume) => {
return volume.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
}
const selectAll = () => {
selectedParts.value.clear()
for (let i = 1; i <= mockData.value.length; i++) {
selectedParts.value.add(i)
}
}
const clearAll = () => {
selectedParts.value.clear()
}
const updateSelection = (event) => {
const partId = parseInt(event.target.getAttribute('data-part-id'))
if (event.target.checked) {
selectedParts.value.add(partId)
} else {
selectedParts.value.delete(partId)
}
}
const deleteSelected = () => {
if (selectedParts.value.size === 0) {
alert('请先选择要删除的零件')
return
}
const confirmed = confirm(`确定要删除选中的 ${selectedParts.value.size} 个零件吗?`)
if (confirmed) {
alert('删除功能仅为UI演示未实现实际删除逻辑')
}
}
// 生命周期
onMounted(() => {
analysisTime.value = new Date().toLocaleString('zh-CN')
})
</script>
<style scoped>
/* 页面基础样式 */
.geometry-complexity-result-page {
padding: var(--spacing-2xl);
height: 100%;
}
.page-header {
margin-bottom: var(--spacing-2xl);
}
.page-header h2 {
display: flex;
align-items: center;
gap: var(--spacing-md);
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
margin: 0 0 var(--spacing-sm) 0;
}
.page-header h2 i {
color: var(--color-complexity);
}
.page-header p {
color: var(--color-text-secondary);
margin: 0;
}
.page-content {
display: flex;
flex-direction: column;
gap: var(--spacing-xl);
}
/* 分析统计摘要样式 */
.complexity-analysis-summary {
background: var(--color-bg-secondary);
border: 1px solid var(--color-complexity-border-1);
border-radius: var(--size-border-radius-card);
padding: var(--spacing-xl);
}
.summary-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-complexity-border-2);
}
.summary-header h4 {
margin: 0;
color: var(--color-complexity);
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.analysis-time {
color: var(--color-text-muted);
font-size: var(--font-size-sm);
display: flex;
align-items: center;
gap: var(--spacing-xs);
}
.summary-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: var(--spacing-md);
}
.stat-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--color-complexity-border-15);
border-radius: var(--size-border-radius);
padding: var(--spacing-md);
text-align: center;
transition: all 0.2s ease;
}
.stat-card:hover {
background: var(--color-complexity-bg-2);
transform: translateY(-2px);
}
.stat-value {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--color-complexity);
line-height: 1.2;
margin-bottom: var(--spacing-xs);
}
.stat-label {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
font-weight: var(--font-weight-medium);
}
/* 零件列表样式 */
.complexity-parts-list {
background: var(--color-bg-secondary);
border-radius: var(--size-border-radius-card);
overflow: hidden;
}
.complexity-parts-header {
background: var(--color-complexity-border-15);
padding: var(--spacing-lg);
border-bottom: 1px solid var(--color-complexity-border-2);
display: flex;
justify-content: space-between;
align-items: center;
}
.complexity-parts-header h4 {
margin: 0;
color: var(--color-complexity);
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.complexity-controls {
display: flex;
gap: var(--spacing-sm);
align-items: center;
}
.complexity-select-all,
.complexity-clear-all {
background: var(--color-complexity-border-15);
border: 1px solid var(--color-complexity-border-3);
color: var(--color-complexity);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
cursor: pointer;
transition: all 0.2s ease;
font-size: var(--font-size-sm);
display: flex;
align-items: center;
gap: var(--spacing-xs);
}
.complexity-select-all:hover,
.complexity-clear-all:hover {
background: var(--color-complexity-border-2);
}
.table-container {
max-height: 400px;
overflow-y: auto;
}
.complexity-parts-table {
width: 100%;
border-collapse: collapse;
}
.complexity-parts-table th {
background: var(--color-complexity-bg-2);
color: var(--color-complexity);
padding: var(--spacing-sm) var(--spacing-md);
text-align: left;
font-weight: var(--font-weight-semibold);
border-bottom: 1px solid var(--color-complexity-border-2);
position: sticky;
top: 0;
z-index: 1;
}
.complexity-parts-table td {
padding: var(--spacing-sm) var(--spacing-md);
border-bottom: 1px solid var(--color-complexity-border-15);
color: var(--color-text-secondary);
}
.complexity-parts-table tr:hover {
background: var(--color-complexity-bg-1);
}
.complexity-parts-table tr.selected {
background: var(--color-complexity-bg-2);
border-left: 3px solid var(--color-complexity);
}
.complexity-checkbox {
width: 18px;
height: 18px;
accent-color: var(--color-complexity);
cursor: pointer;
}
.part-info {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.complexity-part-name {
color: var(--color-primary);
font-weight: var(--font-weight-medium);
font-size: var(--font-size-sm);
}
.complexity-part-path {
color: #28a745;
font-size: var(--font-size-xs);
font-family: 'Courier New', monospace;
opacity: 0.8;
}
.complexity-file-size {
color: var(--color-text-muted);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
}
.complexity-score {
display: inline-block;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-sm);
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-sm);
}
.complexity-score.very-high {
background: rgba(139, 0, 0, 0.1);
color: #8B0000;
font-weight: var(--font-weight-bold);
}
.complexity-score.high {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
font-weight: var(--font-weight-semibold);
}
.complexity-score.medium {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
}
.complexity-score.low {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
}
.complexity-score.very-low {
background: rgba(34, 139, 34, 0.1);
color: #228B22;
}
/* 删除操作区域 */
.complexity-delete-section {
background: var(--color-complexity-bg-1);
border: 1px solid var(--color-complexity-border-2);
border-radius: var(--size-border-radius-card);
padding: var(--spacing-xl);
}
.complexity-delete-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
}
.complexity-delete-header h4 {
margin: 0;
color: var(--color-complexity);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.complexity-selected-count {
background: var(--color-complexity-border-15);
color: var(--color-complexity);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-full);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
}
.complexity-delete-summary {
margin-bottom: var(--spacing-lg);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-complexity-bg-2);
border-radius: var(--size-border-radius);
border-left: 4px solid var(--color-complexity);
color: var(--color-text-secondary);
font-size: var(--font-size-sm);
}
.complexity-delete-btn {
background: linear-gradient(135deg, #dc3545, #c82333);
border: none;
color: white;
padding: var(--spacing-sm) var(--spacing-lg);
border-radius: var(--size-border-radius);
cursor: pointer;
font-size: var(--font-size-base);
font-weight: var(--font-weight-semibold);
display: flex;
align-items: center;
gap: var(--spacing-sm);
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3);
}
.complexity-delete-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #c82333, #a71e2a);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4);
}
.complexity-delete-btn:disabled {
background: rgba(108, 117, 125, 0.5);
cursor: not-allowed;
box-shadow: none;
transform: none;
}
</style>