|
|
@@ -0,0 +1,203 @@
|
|
|
+<script setup lang="ts" name="ReviewBreakdownCard">
|
|
|
+// ORDER-FLOW-CHAIN-DETAILS-1:意见评审四方明细。
|
|
|
+// fixture 中 order_review.subSteps[0](意见评审)的 breakdown 为 4 个部门记录。
|
|
|
+import { computed } from 'vue';
|
|
|
+import type { SubStepDetail } from '/@/views/aidop/s8/monitoring/data/order-execution/types';
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ breakdown: SubStepDetail[] | undefined | null;
|
|
|
+ title?: string;
|
|
|
+}
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ title: '意见评审四方明细',
|
|
|
+});
|
|
|
+
|
|
|
+const STATUS_LABEL: Record<SubStepDetail['status'], string> = {
|
|
|
+ green: '正常',
|
|
|
+ yellow: '关注',
|
|
|
+ red: '严重',
|
|
|
+ pending: '未开始',
|
|
|
+};
|
|
|
+
|
|
|
+const rows = computed(() =>
|
|
|
+ (props.breakdown ?? []).map((dept) => {
|
|
|
+ const variance =
|
|
|
+ dept.actualHours == null ? null : Number((dept.actualHours - dept.piHours).toFixed(2));
|
|
|
+ return {
|
|
|
+ name: dept.name,
|
|
|
+ piText: `${dept.piHours.toFixed(1)} h`,
|
|
|
+ actualText: dept.actualHours == null ? '--' : `${dept.actualHours.toFixed(1)} h`,
|
|
|
+ status: dept.status,
|
|
|
+ statusLabel: STATUS_LABEL[dept.status],
|
|
|
+ varianceText:
|
|
|
+ variance == null ? '--' : `${variance > 0 ? '+' : ''}${variance.toFixed(2)} h`,
|
|
|
+ varianceTone: variance == null ? 'neutral' : variance > 0 ? 'over' : 'under',
|
|
|
+ };
|
|
|
+ }),
|
|
|
+);
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <section class="oe-breakdown">
|
|
|
+ <header class="oe-breakdown__head">
|
|
|
+ <span class="oe-breakdown__bar" />
|
|
|
+ <h2 class="oe-breakdown__title">{{ title }}</h2>
|
|
|
+ </header>
|
|
|
+ <div v-if="rows.length === 0" class="oe-breakdown__empty">暂无四方评审明细</div>
|
|
|
+ <div v-else class="oe-breakdown__grid">
|
|
|
+ <div
|
|
|
+ v-for="row in rows"
|
|
|
+ :key="row.name"
|
|
|
+ class="oe-breakdown__cell"
|
|
|
+ :class="`oe-breakdown__cell--${row.status}`"
|
|
|
+ >
|
|
|
+ <div class="oe-breakdown__name">
|
|
|
+ {{ row.name }}
|
|
|
+ <span class="oe-breakdown__chip" :class="`oe-breakdown__chip--${row.status}`">
|
|
|
+ {{ row.statusLabel }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <dl class="oe-breakdown__metrics">
|
|
|
+ <div class="oe-breakdown__metric">
|
|
|
+ <dt>PI 工时</dt>
|
|
|
+ <dd>{{ row.piText }}</dd>
|
|
|
+ </div>
|
|
|
+ <div class="oe-breakdown__metric">
|
|
|
+ <dt>实际</dt>
|
|
|
+ <dd>{{ row.actualText }}</dd>
|
|
|
+ </div>
|
|
|
+ <div class="oe-breakdown__metric">
|
|
|
+ <dt>偏差</dt>
|
|
|
+ <dd :class="`oe-breakdown__variance--${row.varianceTone}`">
|
|
|
+ {{ row.varianceText }}
|
|
|
+ </dd>
|
|
|
+ </div>
|
|
|
+ </dl>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.oe-breakdown {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__head {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__bar {
|
|
|
+ width: 5px;
|
|
|
+ height: 18px;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: var(--order-accent, #7bd0ff);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__title {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__empty {
|
|
|
+ padding: 14px 16px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: var(--order-panel, rgba(25, 28, 34, 0.72));
|
|
|
+ border: 1px dashed var(--order-border, rgba(144, 144, 151, 0.18));
|
|
|
+ color: var(--order-text-secondary, #c6c6cd);
|
|
|
+ font-size: 13px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__cell {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 12px 14px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: var(--order-panel, rgba(25, 28, 34, 0.72));
|
|
|
+ border: 1px solid var(--order-border, rgba(144, 144, 151, 0.18));
|
|
|
+ border-left-width: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__cell--green { border-left-color: #88fd54; }
|
|
|
+.oe-breakdown__cell--yellow { border-left-color: #ffc107; }
|
|
|
+.oe-breakdown__cell--red { border-left-color: #ffb4ab; }
|
|
|
+.oe-breakdown__cell--pending { border-left-color: rgba(144, 144, 151, 0.35); }
|
|
|
+
|
|
|
+.oe-breakdown__name {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--order-text-primary, #e1e2eb);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__chip {
|
|
|
+ padding: 1px 8px;
|
|
|
+ border-radius: 999px;
|
|
|
+ font-size: 11px;
|
|
|
+ line-height: 1.6;
|
|
|
+ font-weight: 400;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__chip--green { background: rgba(109, 224, 57, 0.12); color: #88fd54; }
|
|
|
+.oe-breakdown__chip--yellow { background: rgba(255, 193, 7, 0.12); color: #ffc107; }
|
|
|
+.oe-breakdown__chip--red { background: rgba(255, 180, 171, 0.12); color: #ffb4ab; }
|
|
|
+.oe-breakdown__chip--pending { background: rgba(144, 144, 151, 0.12); color: var(--order-text-muted, #909097); }
|
|
|
+
|
|
|
+.oe-breakdown__metrics {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ gap: 6px;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__metric {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2px;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__metric dt {
|
|
|
+ font-size: 11px;
|
|
|
+ color: var(--order-text-muted, #909097);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__metric dd {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--order-text-primary, #e1e2eb);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-breakdown__variance--over { color: #ffb4ab; }
|
|
|
+.oe-breakdown__variance--under { color: #88fd54; }
|
|
|
+.oe-breakdown__variance--neutral { color: var(--order-text-muted, #909097); }
|
|
|
+
|
|
|
+@media (max-width: 1080px) {
|
|
|
+ .oe-breakdown__grid {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 640px) {
|
|
|
+ .oe-breakdown__grid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|