x
This commit is contained in:
+283
-5
@@ -1221,9 +1221,36 @@ const ResultView = {
|
||||
task: null,
|
||||
loading: true,
|
||||
error: '',
|
||||
selectedSchemeId: null
|
||||
selectedSchemeId: null,
|
||||
previewStatus: 'idle',
|
||||
camLoading: false,
|
||||
camError: '',
|
||||
camPlan: null,
|
||||
camForm: {
|
||||
mold_steel: 'P20',
|
||||
surface_quality: 'standard',
|
||||
controller: 'fanuc',
|
||||
include_gcode: false
|
||||
}
|
||||
});
|
||||
|
||||
const camSteelOptions = [
|
||||
{ value: 'P20', label: 'P20' },
|
||||
{ value: '718H', label: '718H' },
|
||||
{ value: 'NAK80', label: 'NAK80' },
|
||||
{ value: 'S136', label: 'S136' },
|
||||
{ value: 'H13', label: 'H13' }
|
||||
];
|
||||
const camSurfaceOptions = [
|
||||
{ value: 'standard', label: '标准' },
|
||||
{ value: 'fine', label: '精细' },
|
||||
{ value: 'mirror', label: '镜面' }
|
||||
];
|
||||
const camControllerOptions = [
|
||||
{ value: 'fanuc', label: 'Fanuc' },
|
||||
{ value: 'siemens', label: 'Siemens' }
|
||||
];
|
||||
|
||||
const loadTask = async () => {
|
||||
console.log('[ResultView] loadTask 开始, taskId:', route.params.taskId);
|
||||
try {
|
||||
@@ -1238,6 +1265,12 @@ const ResultView = {
|
||||
} else if (state.task?.candidate_schemes?.length) {
|
||||
state.selectedSchemeId = state.task.candidate_schemes[0].scheme_id;
|
||||
}
|
||||
const prefs = state.task?.cam_preferences || {};
|
||||
state.camForm.mold_steel = prefs.mold_steel || 'P20';
|
||||
state.camForm.surface_quality = prefs.surface_quality || 'standard';
|
||||
state.camForm.controller = prefs.controller || 'fanuc';
|
||||
state.camForm.include_gcode = Boolean(prefs.include_gcode || false);
|
||||
state.previewStatus = 'loading';
|
||||
} catch (e) {
|
||||
state.error = handleApiError(e, '加载任务详情');
|
||||
} finally {
|
||||
@@ -1268,6 +1301,7 @@ const ResultView = {
|
||||
};
|
||||
|
||||
const candidateSchemes = computed(() => state.task?.candidate_schemes || []);
|
||||
const hasSingleScheme = computed(() => candidateSchemes.value.length === 1);
|
||||
const selectedScheme = computed(() => {
|
||||
if (!candidateSchemes.value.length) return null;
|
||||
return candidateSchemes.value.find(s => s.scheme_id === state.selectedSchemeId) || candidateSchemes.value[0];
|
||||
@@ -1275,9 +1309,21 @@ const ResultView = {
|
||||
const selectedCavityData = computed(() => selectedScheme.value?.cavity_data || state.task?.cavity_data || null);
|
||||
const selectedKeyInfo = computed(() => selectedScheme.value?.key_info || state.task?.key_info || null);
|
||||
const selectedHtmlFile = computed(() => selectedScheme.value?.html_file || state.task?.html_file || '');
|
||||
const selectedDfmViolations = computed(() => selectedScheme.value?.dfm_violations || []);
|
||||
|
||||
const selectScheme = (schemeId) => {
|
||||
state.selectedSchemeId = schemeId;
|
||||
state.previewStatus = 'loading';
|
||||
state.camPlan = null;
|
||||
state.camError = '';
|
||||
};
|
||||
|
||||
const onPreviewLoad = () => {
|
||||
state.previewStatus = 'loaded';
|
||||
};
|
||||
|
||||
const onPreviewError = () => {
|
||||
state.previewStatus = 'error';
|
||||
};
|
||||
|
||||
const formatSchemeDirection = (scheme) => {
|
||||
@@ -1319,21 +1365,64 @@ const ResultView = {
|
||||
addNotification(`导出失败: ${e.message}`, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const generateCamPlan = async () => {
|
||||
try {
|
||||
state.camLoading = true;
|
||||
state.camError = '';
|
||||
const taskId = route.params.taskId;
|
||||
const payload = {
|
||||
task_id: taskId,
|
||||
scheme_id: selectedScheme.value?.scheme_id,
|
||||
mold_steel: state.camForm.mold_steel,
|
||||
surface_quality: state.camForm.surface_quality,
|
||||
controller: state.camForm.controller,
|
||||
include_gcode: state.camForm.include_gcode
|
||||
};
|
||||
const result = await apiRequest('/api/cam/plan', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
state.camPlan = result?.data || null;
|
||||
if (result?.cam_preferences) {
|
||||
state.camForm.mold_steel = result.cam_preferences.mold_steel || state.camForm.mold_steel;
|
||||
state.camForm.surface_quality = result.cam_preferences.surface_quality || state.camForm.surface_quality;
|
||||
state.camForm.controller = result.cam_preferences.controller || state.camForm.controller;
|
||||
state.camForm.include_gcode = Boolean(result.cam_preferences.include_gcode);
|
||||
}
|
||||
addNotification('CAM 计划生成完成', 'success');
|
||||
} catch (e) {
|
||||
state.camPlan = null;
|
||||
state.camError = e.message || 'CAM 计划生成失败';
|
||||
addNotification(`CAM 计划生成失败: ${state.camError}`, 'error');
|
||||
} finally {
|
||||
state.camLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
candidateSchemes,
|
||||
hasSingleScheme,
|
||||
selectedScheme,
|
||||
selectedCavityData,
|
||||
selectedKeyInfo,
|
||||
selectedHtmlFile,
|
||||
selectedDfmViolations,
|
||||
formatFileSize,
|
||||
formatDateTime,
|
||||
formatNumber,
|
||||
getPriorityText,
|
||||
isFoamMaterial,
|
||||
exportCAD,
|
||||
generateCamPlan,
|
||||
camSteelOptions,
|
||||
camSurfaceOptions,
|
||||
camControllerOptions,
|
||||
selectScheme,
|
||||
onPreviewLoad,
|
||||
onPreviewError,
|
||||
formatSchemeDirection
|
||||
};
|
||||
},
|
||||
@@ -1359,6 +1448,9 @@ const ResultView = {
|
||||
{{ state.task.status }}
|
||||
</span>
|
||||
<div v-if="state.task.status === 'completed'" class="export-buttons">
|
||||
<button class="btn-sm btn-primary" :disabled="state.camLoading" @click="generateCamPlan()" title="基于当前选中方案生成CAM工艺计划">
|
||||
{{ state.camLoading ? '生成中...' : '生成 CAM 计划' }}
|
||||
</button>
|
||||
<button class="btn-sm btn-primary" @click="exportCAD('step')" title="导出STEP格式(UG/FreeCAD/SolidWorks通用)">
|
||||
导出 STEP
|
||||
</button>
|
||||
@@ -1422,11 +1514,11 @@ const ResultView = {
|
||||
|
||||
<div v-if="candidateSchemes.length" class="viewer-section">
|
||||
<h3>候选分模方案</h3>
|
||||
<div class="result-grid">
|
||||
<div :class="['result-grid', hasSingleScheme ? 'single-scheme-grid' : '']">
|
||||
<div
|
||||
v-for="scheme in candidateSchemes"
|
||||
:key="scheme.scheme_id"
|
||||
class="result-card"
|
||||
:class="['result-card', hasSingleScheme ? 'single-scheme-card' : '']"
|
||||
:style="state.selectedSchemeId === scheme.scheme_id ? 'border: 2px solid var(--primary-color);' : ''"
|
||||
>
|
||||
<div class="summary-header">
|
||||
@@ -1438,6 +1530,10 @@ const ResultView = {
|
||||
<span class="info-label">分型方向</span>
|
||||
<span class="info-value">{{ formatSchemeDirection(scheme) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">模具结构</span>
|
||||
<span class="info-value">{{ scheme.mold_structure_type === 'two_half_cavity' ? '两板半腔(无独立模芯)' : '型腔 + 模芯' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">分型面位置</span>
|
||||
<span class="info-value">{{ scheme.offset_label || '中面' }}</span>
|
||||
@@ -1470,8 +1566,25 @@ const ResultView = {
|
||||
<span class="info-label">风险得分</span>
|
||||
<span class="info-value">{{ formatNumber(scheme.score_breakdown?.risk || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">DFM 违规项</span>
|
||||
<span class="info-value">{{ scheme.dfm_violation_count || 0 }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">方案可信度</span>
|
||||
<span class="info-value">{{ formatNumber(scheme.confidence_score || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">回退标记</span>
|
||||
<span class="info-value">{{ scheme.is_fallback ? '是' : '否' }}</span>
|
||||
</div>
|
||||
<div v-if="scheme.fallback_reason" class="info-item">
|
||||
<span class="info-label">回退原因</span>
|
||||
<span class="info-value">{{ scheme.fallback_reason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-sm btn-primary" @click="selectScheme(scheme.scheme_id)">查看此方案</button>
|
||||
<button v-if="candidateSchemes.length > 1" class="btn-sm btn-primary" @click="selectScheme(scheme.scheme_id)">查看此方案</button>
|
||||
<div v-else class="inline-note">当前仅生成 1 套可用分模方案</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1486,6 +1599,8 @@ const ResultView = {
|
||||
<th>方向</th>
|
||||
<th>位置</th>
|
||||
<th>总分</th>
|
||||
<th>可信度</th>
|
||||
<th>回退</th>
|
||||
<th>法向匹配</th>
|
||||
<th>可制造性</th>
|
||||
<th>分型质量</th>
|
||||
@@ -1499,6 +1614,8 @@ const ResultView = {
|
||||
<td>{{ formatSchemeDirection(scheme) }}</td>
|
||||
<td>{{ scheme.offset_label || '中面' }}</td>
|
||||
<td>{{ formatNumber(scheme.score) }}</td>
|
||||
<td>{{ formatNumber(scheme.confidence_score || 0) }}</td>
|
||||
<td>{{ scheme.is_fallback ? '是' : '否' }}</td>
|
||||
<td>{{ formatNumber(scheme.normal_alignment_score || 0) }}</td>
|
||||
<td>{{ formatNumber(scheme.score_breakdown?.manufacturability || 0) }}</td>
|
||||
<td>{{ formatNumber(scheme.score_breakdown?.parting_quality || 0) }}</td>
|
||||
@@ -1524,6 +1641,10 @@ const ResultView = {
|
||||
<span class="info-label">材料</span>
|
||||
<span class="info-value">{{ selectedCavityData?.metadata?.material || selectedKeyInfo?.metadata?.material || 'N/A' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">结构判定</span>
|
||||
<span class="info-value">{{ selectedScheme?.mold_structure_type === 'two_half_cavity' ? '两板半腔(无独立模芯)' : '型腔 + 模芯' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-card" v-if="selectedCavityData?.mold_cavities || selectedKeyInfo?.mold_cavities">
|
||||
@@ -1537,10 +1658,167 @@ const ResultView = {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="viewer-section">
|
||||
<h3>DFM 检查</h3>
|
||||
<div v-if="!selectedDfmViolations.length" class="inline-alert inline-alert-warning">
|
||||
<div class="inline-alert-title">未发现显著 DFM 违规项</div>
|
||||
<div class="inline-alert-message">当前选中方案未命中高风险规则,可进入下一步工艺评审。</div>
|
||||
</div>
|
||||
<div v-else class="result-card full-width">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>规则</th>
|
||||
<th>级别</th>
|
||||
<th>说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, idx) in selectedDfmViolations" :key="'dfm-' + idx">
|
||||
<td>{{ item.rule || 'N/A' }}</td>
|
||||
<td>{{ item.level || 'medium' }}</td>
|
||||
<td>{{ item.message || 'N/A' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="viewer-section">
|
||||
<h3>CAM 准备包</h3>
|
||||
<div class="result-card" style="margin-bottom: var(--space-4);">
|
||||
<h4>CAM 参数</h4>
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="info-label">模具钢</span>
|
||||
<select v-model="state.camForm.mold_steel" class="form-select">
|
||||
<option v-for="opt in camSteelOptions" :key="'steel-' + opt.value" :value="opt.value">{{ opt.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">表面质量</span>
|
||||
<select v-model="state.camForm.surface_quality" class="form-select">
|
||||
<option v-for="opt in camSurfaceOptions" :key="'surf-' + opt.value" :value="opt.value">{{ opt.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">控制器</span>
|
||||
<select v-model="state.camForm.controller" class="form-select">
|
||||
<option v-for="opt in camControllerOptions" :key="'ctrl-' + opt.value" :value="opt.value">{{ opt.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">包含G代码</span>
|
||||
<label class="checkbox-group">
|
||||
<input type="checkbox" class="checkbox-input" v-model="state.camForm.include_gcode" />
|
||||
<span class="checkbox-label">返回G代码文本</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="state.camError" class="inline-alert inline-alert-warning">
|
||||
<div class="inline-alert-title">CAM 计划生成失败</div>
|
||||
<div class="inline-alert-message">{{ state.camError }}</div>
|
||||
</div>
|
||||
<div v-else-if="!state.camPlan" class="inline-alert inline-alert-warning">
|
||||
<div class="inline-alert-title">尚未生成 CAM 计划</div>
|
||||
<div class="inline-alert-message">点击上方“生成 CAM 计划”后,将返回工序计划、刀具建议与制造风险提示。</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="result-grid">
|
||||
<div class="result-card">
|
||||
<h4>计划摘要</h4>
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="info-label">方案ID</span>
|
||||
<span class="info-value">{{ state.camPlan.scheme_id || 'N/A' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">总工序数</span>
|
||||
<span class="info-value">{{ state.camPlan.summary?.total_operations || 0 }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">预计总时长</span>
|
||||
<span class="info-value">{{ formatNumber(state.camPlan.summary?.total_estimated_time_min || 0) }} min</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">方案可信度</span>
|
||||
<span class="info-value">{{ formatNumber(state.camPlan.confidence?.score || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-card">
|
||||
<h4>刀具建议</h4>
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="info-label">粗加工刀具</span>
|
||||
<span class="info-value">{{ state.camPlan.tooling_suggestion?.roughing_tool?.tool_id || 'N/A' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">精加工刀具</span>
|
||||
<span class="info-value">{{ state.camPlan.tooling_suggestion?.finishing_tool?.tool_id || 'N/A' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-card full-width">
|
||||
<h4>工序计划</h4>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>工序</th>
|
||||
<th>刀具</th>
|
||||
<th>主轴转速</th>
|
||||
<th>进给率</th>
|
||||
<th>预计时长(min)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="op in (state.camPlan.process_plan || [])" :key="'cam-op-' + op.seq">
|
||||
<td>{{ op.seq }}</td>
|
||||
<td>{{ op.operation }}</td>
|
||||
<td>{{ op.tool_id || 'N/A' }}</td>
|
||||
<td>{{ formatNumber(op.spindle_speed_rpm || 0) }}</td>
|
||||
<td>{{ formatNumber(op.feed_rate_mm_min || 0) }}</td>
|
||||
<td>{{ formatNumber(op.estimated_time_min || 0) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="result-card full-width">
|
||||
<h4>制造风险提示</h4>
|
||||
<div class="recommendations-list">
|
||||
<div class="recommendation-item medium" v-for="(warn, idx) in (state.camPlan.manufacturing_warnings || [])" :key="'cam-warn-' + idx">
|
||||
<div class="recommendation-text">{{ warn }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedHtmlFile" class="viewer-section">
|
||||
<h3>3D 预览</h3>
|
||||
<iframe :src="selectedHtmlFile" class="viewer-frame"></iframe>
|
||||
<div v-if="state.previewStatus === 'error'" class="inline-alert inline-alert-warning" style="margin-bottom: var(--space-3);">
|
||||
<div class="inline-alert-title">预览加载失败</div>
|
||||
<div class="inline-alert-message">HTML 已生成但加载异常,请检查该链接是否可访问:{{ selectedHtmlFile }}</div>
|
||||
</div>
|
||||
<iframe
|
||||
:key="selectedHtmlFile"
|
||||
:src="selectedHtmlFile"
|
||||
class="viewer-frame"
|
||||
@load="onPreviewLoad"
|
||||
@error="onPreviewError"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<div v-else class="viewer-section">
|
||||
<h3>3D 预览</h3>
|
||||
<div class="inline-alert inline-alert-warning">
|
||||
<div class="inline-alert-title">预览未生成</div>
|
||||
<div class="inline-alert-message">当前任务没有返回 HTML 预览链接,属于未生成状态。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="state.task.analysis_result" class="viewer-section">
|
||||
|
||||
Reference in New Issue
Block a user