From a2ab973ce71777bc4b360f56ed542a691be9290c Mon Sep 17 00:00:00 2001 From: chenjw28 <792430652@qq.com> Date: Mon, 18 May 2026 11:23:30 +0800 Subject: [PATCH] x --- static/style.css | 162 +++++++++++++++------------- static/vue-app.js | 261 ++++++++++++++++++++++++++-------------------- 2 files changed, 239 insertions(+), 184 deletions(-) diff --git a/static/style.css b/static/style.css index d2e7d7d..3a017ba 100644 --- a/static/style.css +++ b/static/style.css @@ -1364,72 +1364,108 @@ optgroup { gap: var(--space-4); } -.mechanism-visual-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); - gap: var(--space-4); - margin-top: var(--space-4); -} - -.mechanism-visual-card { - display: grid; - grid-template-columns: 220px minmax(0, 1fr); - gap: 0; - border: 1px solid var(--border-light); - border-radius: var(--radius-xl); - background: var(--bg-primary); - overflow: hidden; -} - -.mechanism-visual-svg { - background: linear-gradient(180deg, #eff6ff 0%, #f8fafc 100%); - border-right: 1px solid var(--border-light); - display: flex; - align-items: center; - justify-content: center; - min-height: 180px; -} - -.mechanism-svg { - width: 100%; - height: 100%; -} - -.mechanism-visual-body { - padding: var(--space-4); +.side-action-list { display: flex; flex-direction: column; - gap: var(--space-4); -} - -.mechanism-kpis { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); } -.mechanism-kpi { - padding: var(--space-3); - border-radius: var(--radius-lg); - background: var(--bg-secondary); +.side-action-row { + width: 100%; + text-align: left; border: 1px solid var(--border-light); + border-radius: var(--radius-xl); + padding: var(--space-4); + background: var(--bg-primary); + cursor: pointer; + display: flex; + flex-direction: column; + gap: var(--space-3); + transition: all var(--duration-fast) var(--ease-default); } -.mechanism-kpi-label { - display: block; - font-size: var(--text-xs); - color: var(--text-tertiary); - text-transform: uppercase; - letter-spacing: 0.04em; - margin-bottom: var(--space-1); +.side-action-row:hover { + border-color: var(--primary-200); + box-shadow: var(--shadow-sm); + transform: translateY(-1px); } -.mechanism-kpi-value { - display: block; - font-size: var(--text-sm); +.side-action-row-main { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.side-action-row-title { + font-size: var(--text-base); font-weight: var(--font-semibold); color: var(--text-primary); - word-break: break-word; +} + +.chip-row { + display: flex; + flex-wrap: wrap; + gap: var(--space-2); +} + +.chip { + display: inline-flex; + align-items: center; + padding: var(--space-1) var(--space-3); + border-radius: var(--radius-full); + background: var(--bg-tertiary); + border: 1px solid var(--border-light); + font-size: var(--text-xs); + font-weight: var(--font-medium); + color: var(--text-secondary); +} + +.chip-risk { + background: #fff7ed; + border-color: #fde68a; + color: #9a3412; +} + +.drawer-overlay { + position: fixed; + inset: 0; + background: rgba(15, 23, 42, 0.35); + z-index: 2000; +} + +.drawer-panel { + position: fixed; + top: 0; + right: 0; + height: 100%; + width: min(520px, 92vw); + background: var(--bg-primary); + border-left: 1px solid var(--border-light); + box-shadow: var(--shadow-lg); + z-index: 2001; + display: flex; + flex-direction: column; +} + +.drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-5); + border-bottom: 1px solid var(--border-light); +} + +.drawer-header h3 { + margin: 0; + font-size: var(--text-lg); +} + +.drawer-body { + padding: var(--space-5); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: var(--space-4); } .mechanism-card { @@ -1512,10 +1548,6 @@ optgroup { color: var(--primary-700); } -.mechanism-modal { - max-width: 860px; -} - .single-scheme-grid { grid-template-columns: 1fr !important; } @@ -1548,18 +1580,8 @@ optgroup { .info-list { grid-template-columns: 1fr; } - - .mechanism-visual-card { - grid-template-columns: 1fr; - } - - .mechanism-visual-svg { - border-right: 0; - border-bottom: 1px solid var(--border-light); - } - - .mechanism-kpis { - grid-template-columns: 1fr; + .drawer-panel { + width: 92vw; } } diff --git a/static/vue-app.js b/static/vue-app.js index 991a37f..d66f174 100644 --- a/static/vue-app.js +++ b/static/vue-app.js @@ -1520,9 +1520,97 @@ const ResultView = { const sliderMechanisms = computed(() => selectedSideActions.value?.slider_mechanisms || []); const lifterMechanisms = computed(() => selectedSideActions.value?.lifter_mechanisms || []); const sideActionRecommendations = computed(() => selectedSideActions.value?.recommendations || []); - const allMechanisms = computed(() => { - const sliders = sliderMechanisms.value.map((item, index) => ({ type: 'slider', index, item })); - const lifters = lifterMechanisms.value.map((item, index) => ({ type: 'lifter', index, item })); + const inferSliderDirectionKey = (vector) => { + if (!Array.isArray(vector) || vector.length < 3) return null; + const values = vector.map(v => Number(v) || 0); + const abs = values.map(v => Math.abs(v)); + const max = Math.max(...abs); + if (!max) return null; + const idx = abs.findIndex(v => v === max); + const axis = idx === 0 ? 'X' : idx === 1 ? 'Y' : 'Z'; + const sign = values[idx] >= 0 ? '+' : '-'; + return `${axis}${sign}`; + }; + const demoldingDigest = computed(() => { + const sliders = sliderMechanisms.value.length; + const lifters = lifterMechanisms.value.length; + const total = sliders + lifters; + const hasHydraulic = Boolean(sideActionSummary.value?.has_hydraulic); + const complexity = sideActionSummary.value?.complexity || (total >= 6 ? 'very_complex' : total >= 4 ? 'complex' : total >= 2 ? 'moderate' : total === 1 ? 'simple' : 'none'); + const directionKeys = new Set( + sliderMechanisms.value + .map(item => inferSliderDirectionKey(item.slide_direction)) + .filter(Boolean) + ); + const directionLabel = directionKeys.size <= 1 ? '单向' : '多向'; + const riskLevel = hasHydraulic || total >= 5 ? '高' : total >= 3 ? '中' : total >= 1 ? '低' : '无'; + const actions = (sideActionRecommendations.value || []).slice(0, 2); + return { + sliders, + lifters, + total, + hasHydraulic, + complexity, + directionLabel, + riskLevel, + actions + }; + }); + const normalizeActuationLabel = (value) => { + const text = String(value || '').toLowerCase(); + if (!text) return 'N/A'; + if (text.includes('hydraulic')) return '液压'; + if (text.includes('mechanical')) return '机械'; + if (text.includes('spring')) return '弹簧'; + return value; + }; + const buildMechanismTags = (type, item = {}, totalCount = 0, hasHydraulic = false) => { + const tags = []; + const risks = []; + if (type === 'slider') { + if (item.slide_stroke != null) tags.push(`行程 ${Number(item.slide_stroke).toFixed(1)}mm`); + if (item.slide_angle != null) tags.push(`角度 ${Number(item.slide_angle).toFixed(1)}°`); + if (item.actuation) tags.push(`驱动 ${normalizeActuationLabel(item.actuation)}`); + if (item.slide_stroke != null && Number(item.slide_stroke) > 60) risks.push('行程偏大'); + if (item.slide_angle != null && Number(item.slide_angle) > 25) risks.push('角度偏大'); + } else if (type === 'lifter') { + if (item.lifter_angle != null) tags.push(`角度 ${Number(item.lifter_angle).toFixed(1)}°`); + if (item.lifter_stroke != null) tags.push(`行程 ${Number(item.lifter_stroke).toFixed(1)}mm`); + if (item.return_mechanism) tags.push(`回位 ${item.return_mechanism}`); + if (item.lifter_stroke != null && Number(item.lifter_stroke) > 60) risks.push('行程偏大'); + if (item.lifter_angle != null && Number(item.lifter_angle) > 20) risks.push('角度偏大'); + } + if (totalCount >= 5) risks.push('数量偏多'); + if (hasHydraulic) risks.push('需液压评审'); + return { tags, risks: risks.slice(0, 3) }; + }; + const mechanismRows = computed(() => { + const totalCount = demoldingDigest.value.total; + const hasHydraulic = demoldingDigest.value.hasHydraulic; + const sliders = sliderMechanisms.value.map((item, index) => { + const { tags, risks } = buildMechanismTags('slider', item, totalCount, hasHydraulic); + return { + id: `slider-${index}`, + type: 'slider', + index, + item, + title: `滑块 #${index + 1}`, + tags, + risks + }; + }); + const lifters = lifterMechanisms.value.map((item, index) => { + const { tags, risks } = buildMechanismTags('lifter', item, totalCount, hasHydraulic); + return { + id: `lifter-${index}`, + type: 'lifter', + index, + item, + title: `斜顶 #${index + 1}`, + tags, + risks + }; + }); return [...sliders, ...lifters]; }); const dfmLevelSummary = computed(() => { @@ -1568,7 +1656,7 @@ const ResultView = { { id: 'candidate-schemes', label: '方案' }, { id: 'preview-3d', label: '预览' }, { id: 'engineering-summary', label: '工程摘要' }, - { id: 'side-actions', label: '抽芯建议' }, + { id: 'demolding-risk', label: '脱模风险' }, { id: 'dfm-check', label: 'DFM' }, { id: 'export-cam', label: '导出/CAM' }, { id: 'llm-report', label: '设计报告' } @@ -1640,53 +1728,10 @@ const ResultView = { return keys.map(key => `${key}:${Number(size[key]).toFixed(1)} mm`).join(' / '); }; const formatMechanismType = (type) => { - if (type === 'slider') return '滑块抽芯'; - if (type === 'lifter') return '斜顶抽芯'; + if (type === 'slider') return '滑块'; + if (type === 'lifter') return '斜顶'; return type || '机构'; }; - const formatMechanismTypeShort = (type) => type === 'slider' ? '滑块' : type === 'lifter' ? '斜顶' : '机构'; - const normalizeVector = (vector, fallback = [0.8, -0.6, 0.5]) => { - if (!Array.isArray(vector) || vector.length < 3) return fallback; - const values = vector.map(v => Number(v) || 0); - const length = Math.sqrt(values.reduce((acc, val) => acc + val * val, 0)); - if (!length) return fallback; - return values.map(v => v / length); - }; - const mechanismSchematicSvg = (type, item = {}) => { - const direction = normalizeVector(type === 'slider' ? item.slide_direction : item.location); - const location = normalizeVector(item.location, [0.3, 0.2, 0.5]); - const startX = 120 + (location[0] * 18); - const startY = 90 - (location[1] * 16); - const endX = startX + (direction[0] * 42); - const endY = startY - (direction[2] * 28) + (direction[1] * 12); - const mechanismColor = type === 'slider' ? '#2563eb' : '#7c3aed'; - const label = type === 'slider' ? '滑块抽芯' : '斜顶抽芯'; - return ` - - `; - }; const escapeHtml = (value) => String(value || '') .replace(/&/g, '&') .replace(/ -