| | |
| | | |
| | | function openStepSettings() { stepDraft.value = stepSettings.value.map(item => ({ ...item })); selectedPlanSteps.value = []; stepEditing.value = true } |
| | | function togglePlanStep(position: number, checked: string | number | boolean) { const next = new Set(selectedPlanSteps.value); checked ? next.add(position) : next.delete(position); selectedPlanSteps.value = [...next].sort((a, b) => a - b) } |
| | | async function addStepMapping() { const positions = [...selectedPlanSteps.value].sort((a, b) => a - b); if (positions.length < 2) { ElMessage.warning('请先勾选至少两个连续工步'); return }; const contiguous = positions.every((value, index) => index === 0 || value === positions[index - 1] + 1); if (!contiguous) { ElMessage.warning('只能合并连续工步'); return }; if (stepDraft.value.some(item => positions[0] <= item.endStepNo && positions[positions.length - 1] >= item.startStepNo)) { ElMessage.warning('选中的工步已在其他合并组中'); return }; try { const result = await ElMessageBox.prompt('请输入逻辑工步名称', '合并工步', { inputPlaceholder: '例如:充电阶段', inputValidator: value => value.trim() ? true : '逻辑工步名称不能为空' }); const next = stepDraft.value.length + 1; stepDraft.value.push({ logicalNo: next, logicalName: result.value.trim(), startStepNo: positions[0], endStepNo: positions[positions.length - 1] }); selectedPlanSteps.value = [] } catch { /* 用户取消命名 */ } } |
| | | async function persistStepMappings(mappings: TestLogicStepMapping[]) { |
| | | const detail = await saveLogicStepMappings(testId.value, mappings) |
| | | stepSettings.value = detail.logicStepMappings || [] |
| | | backendDetail.value = detail |
| | | stepDraft.value = stepSettings.value.map(item => ({ ...item })) |
| | | } |
| | | async function addStepMapping() { |
| | | const positions = [...selectedPlanSteps.value].sort((a, b) => a - b) |
| | | if (positions.length < 2) { ElMessage.warning('请先勾选至少两个连续工步'); return } |
| | | const contiguous = positions.every((value, index) => index === 0 || value === positions[index - 1] + 1) |
| | | if (!contiguous) { ElMessage.warning('只能合并连续工步'); return } |
| | | if (stepDraft.value.some(item => positions[0] <= item.endStepNo && positions[positions.length - 1] >= item.startStepNo)) { |
| | | ElMessage.warning('选中的工步已在其他合并组中'); return |
| | | } |
| | | try { |
| | | const result = await ElMessageBox.prompt('请输入逻辑工步名称', '合并工步', { |
| | | inputPlaceholder: '例如:充电阶段', inputValidator: value => value.trim() ? true : '逻辑工步名称不能为空', |
| | | }) |
| | | const previous = stepDraft.value.map(item => ({ ...item })) |
| | | const next = stepDraft.value.length + 1 |
| | | stepDraft.value.push({ logicalNo: next, logicalName: result.value.trim(), startStepNo: positions[0], endStepNo: positions[positions.length - 1] }) |
| | | normalizeStepDraft() |
| | | try { |
| | | await persistStepMappings(stepDraft.value) |
| | | selectedPlanSteps.value = [] |
| | | ElMessage.success('工步合并规则已保存') |
| | | } catch (error) { |
| | | stepDraft.value = previous |
| | | ElMessage.error(error instanceof Error ? error.message : '工步合并保存失败') |
| | | } |
| | | } catch { /* 用户取消命名或确认 */ } |
| | | } |
| | | function normalizeStepDraft() { stepDraft.value.sort((a, b) => a.startStepNo - b.startStepNo); stepDraft.value.forEach((item, i) => { item.logicalNo = i + 1 }) } |
| | | function removeStepMapping(index: number) { stepDraft.value.splice(index, 1); normalizeStepDraft() } |
| | | async function saveSteps() { |
| | | try { normalizeStepDraft(); const detail = await saveLogicStepMappings(testId.value, stepDraft.value); stepSettings.value = detail.logicStepMappings || []; backendDetail.value = detail; stepEditing.value = false; selectedPlanSteps.value = []; ElMessage.success('工步合并规则已保存') } |
| | | catch (error) { ElMessage.error(error instanceof Error ? error.message : '工步设置保存失败') } |
| | | async function removeStepMapping(index: number) { |
| | | try { |
| | | await ElMessageBox.confirm('解除后将立即保存该变更,确定解除合并吗?', '解除合并', { |
| | | confirmButtonText: '解除合并', cancelButtonText: '取消', type: 'warning', |
| | | }) |
| | | const previous = stepDraft.value.map(item => ({ ...item })) |
| | | stepDraft.value.splice(index, 1) |
| | | normalizeStepDraft() |
| | | try { |
| | | await persistStepMappings(stepDraft.value) |
| | | ElMessage.success('合并规则已解除') |
| | | } catch (error) { |
| | | stepDraft.value = previous |
| | | ElMessage.error(error instanceof Error ? error.message : '解除合并保存失败') |
| | | } |
| | | } catch { /* 用户取消解除 */ } |
| | | } |
| | | function cancelStepSettings() { stepEditing.value = false; selectedPlanSteps.value = [] } |
| | | |
| | |
| | | |
| | | <div class="console-layout"> |
| | | <main> |
| | | <section class="panel step-panel"><header class="panel-heading"><div><i></i><strong>项目参数</strong><span>STEP PARAMETERS</span></div><div class="step-actions"><template v-if="stepEditing"><button class="primary-button compact-button" type="button" @click="addStepMapping"><el-icon><Plus /></el-icon>合并选中</button><button class="primary-button compact-button" type="button" @click="saveSteps"><el-icon><Check /></el-icon>保存</button><button class="secondary-button compact-button" type="button" @click="cancelStepSettings">取消</button></template><button v-else class="primary-button compact-button" type="button" @click="openStepSettings"><el-icon><Setting /></el-icon>工步设置</button></div></header><div class="project-meta"><span>测试项目 <strong>{{ profile.purpose }}</strong></span><span>当前循环 <strong>{{ currentCycleNo || '-' }} / 计划 {{ plannedCycleCount || '-' }}</strong></span><span>当前工步 <strong>{{ currentStepNo || '-' }}</strong></span><span>执行状态 <strong>{{ testStatusLabel }}</strong></span></div><div class="step-table-wrap"><table><thead><tr><th v-if="stepEditing">选择</th><th>工步号</th><th>工步名称</th><th>时间(h:min:s.ms)</th><th>电压(V)</th><th>执行状态</th></tr></thead><tbody><template v-if="stepEditing"><tr v-for="step in actualPlanRows" :key="`edit-${step.cyclePosition}`" :class="{ active: step.executionStatus === '进行中' }"><td><el-checkbox :model-value="selectedPlanSteps.includes(step.cyclePosition)" @change="(checked: string | number | boolean) => togglePlanStep(step.cyclePosition, checked)" /></td><td>{{ step.stepNo }}</td><td><strong>{{ step.stepName }}</strong></td><td>{{ formatDuration(step.durationSeconds) }}</td><td>{{ step.voltageV == null ? '-' : `${Number(step.voltageV).toFixed(4)} V` }}</td><td><span class="step-state" :class="step.executionStatus">{{ step.executionStatus }}</span></td></tr></template><template v-else><tr v-for="(step, index) in planRows" :key="`${index}-${step.no}-${step.name}`" :class="{ active: step.state === '进行中' }"><td>{{ step.no }}</td><td><strong>{{ step.name }}</strong></td><td>{{ step.time }}</td><td>{{ step.voltage }}</td><td><span class="step-state" :class="step.state">{{ step.state }}</span></td></tr></template><tr v-if="!planRows.length && !actualPlanRows.length"><td :colspan="stepEditing ? 6 : 5" class="empty-cell">暂无后端检测柜工步方案</td></tr></tbody></table></div><div v-if="stepEditing && stepDraft.length" class="mapping-list"><div v-for="(item, index) in stepDraft" :key="index" class="mapping-row"><strong>逻辑工步 {{ item.logicalNo }}</strong><span>第 {{ item.startStepNo }} 至 {{ item.endStepNo }} 个工步</span><button class="secondary-button compact-button" type="button" aria-label="解除合并" @click="removeStepMapping(index)"><el-icon><Delete /></el-icon>解除合并</button></div></div></section> |
| | | <section class="panel step-panel"><header class="panel-heading"><div><i></i><strong>项目参数</strong><span>STEP PARAMETERS</span></div><div class="step-actions"><template v-if="stepEditing"><button class="primary-button compact-button" type="button" @click="addStepMapping"><el-icon><Plus /></el-icon>合并选中</button><button class="secondary-button compact-button" type="button" @click="cancelStepSettings">取消</button></template><button v-else class="primary-button compact-button" type="button" @click="openStepSettings"><el-icon><Setting /></el-icon>工步设置</button></div></header><div class="project-meta"><span>测试项目 <strong>{{ profile.purpose }}</strong></span><span>当前循环 <strong>{{ currentCycleNo || '-' }} / 计划 {{ plannedCycleCount || '-' }}</strong></span><span>当前工步 <strong>{{ currentStepNo || '-' }}</strong></span><span>执行状态 <strong>{{ testStatusLabel }}</strong></span></div><div class="step-table-wrap"><table><thead><tr><th v-if="stepEditing">选择</th><th>工步号</th><th>工步名称</th><th>时间(h:min:s.ms)</th><th>电压(V)</th><th>执行状态</th></tr></thead><tbody><template v-if="stepEditing"><tr v-for="step in actualPlanRows" :key="`edit-${step.cyclePosition}`" :class="{ active: step.executionStatus === '进行中' }"><td><el-checkbox :model-value="selectedPlanSteps.includes(step.cyclePosition)" @change="(checked: string | number | boolean) => togglePlanStep(step.cyclePosition, checked)" /></td><td>{{ step.stepNo }}</td><td><strong>{{ step.stepName }}</strong></td><td>{{ formatDuration(step.durationSeconds) }}</td><td>{{ step.voltageV == null ? '-' : `${Number(step.voltageV).toFixed(4)} V` }}</td><td><span class="step-state" :class="step.executionStatus">{{ step.executionStatus }}</span></td></tr></template><template v-else><tr v-for="(step, index) in planRows" :key="`${index}-${step.no}-${step.name}`" :class="{ active: step.state === '进行中' }"><td>{{ step.no }}</td><td><strong>{{ step.name }}</strong></td><td>{{ step.time }}</td><td>{{ step.voltage }}</td><td><span class="step-state" :class="step.state">{{ step.state }}</span></td></tr></template><tr v-if="!planRows.length && !actualPlanRows.length"><td :colspan="stepEditing ? 6 : 5" class="empty-cell">暂无后端检测柜工步方案</td></tr></tbody></table></div><div v-if="stepEditing && stepDraft.length" class="mapping-list"><div v-for="(item, index) in stepDraft" :key="index" class="mapping-row"><strong>逻辑工步 {{ item.logicalNo }}</strong><span>第 {{ item.startStepNo }} 至 {{ item.endStepNo }} 个工步</span><button class="secondary-button compact-button" type="button" aria-label="解除合并" @click="removeStepMapping(index)"><el-icon><Delete /></el-icon>解除合并</button></div></div></section> |
| | | <section class="panel alarm-panel"><header class="panel-heading"><div><i></i><strong>异常警告</strong><span>ALARM</span></div></header><div v-if="currentExceptions.length" class="alarm-list"><div v-for="(item, index) in currentExceptions" :key="`${item.sourceId || item.sourceType}-${item.channelCode || index}-${index}`"><el-icon><WarningFilled /></el-icon><span><strong>{{ item.sourceType === 'COLLECTOR' ? `采集器${item.collectorName || item.collectorCode || `#${item.sourceId}`}${item.status === 'OPEN' ? '离线' : '异常已处理'} · ${item.channelCode || '来源'}` : (item.message || '数据来源当前不可观测') }}</strong><small>{{ item.measurementName ? `${item.measurementName}受影响 · ` : '' }}{{ item.message || (item.status === 'OPEN' ? '当前异常' : '异常已处理') }}</small></span></div></div><div v-else class="alarm-empty"><el-icon><CircleCheckFilled /></el-icon>暂无异常</div></section> |
| | | <section class="panel channel-panel"><header class="panel-heading"><div><i></i><strong>通道</strong><span>CHANNEL STATUS</span></div></header><div class="channel-table-wrap"><table :style="{ minWidth: `${1120 + collectorColumns.length * 120}px` }"><thead><tr><th>电池号</th><th>测试柜</th><th>柜状态</th><th>检测通道</th><th>采集器</th><th>采集器状态</th><th v-for="column in collectorColumns" :key="column.key">{{ column.label }}</th></tr></thead><tbody><tr v-for="item in channels" :key="item.no"><td><strong>{{ item.no }}</strong></td><td>{{ item.cabinet }}</td><td><span class="device-state" :class="item.cabinetState">{{ item.cabinetState }}</span></td><td>{{ item.channel }}</td><td>{{ item.collector }}</td><td><span class="device-state" :class="{ 在线: item.collectorState === '在线', 异常: item.collectorState !== '在线' }">{{ item.collectorState }}</span></td><td v-for="column in collectorColumns" :key="column.key">{{ item.measurements[column.measurementCode] || '-' }}</td></tr><tr v-if="!channels.length"><td :colspan="6 + collectorColumns.length" class="empty-cell">暂无通道配置</td></tr></tbody></table></div></section> |
| | | </main> |