src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
@@ -1,23 +1,46 @@
<template>
  <div>
    <PanelHeader title="不合格预警" />
    <div class="warn-panel">
      <div class="warn-header">
        <div class="warn-header-left">
          <div class="warn-badge"></div>
          <span class="warn-title">不合格预警</span>
        </div>
        <div class="warn-range" @click="handleRangeClick">近7天</div>
      </div>
      <div class="warn-body">
        <div class="warn-list" role="list">
          <div v-for="item in warnings" :key="item.id" class="warn-item" role="listitem" @click="openWarning(item)">
            <div class="warn-tag" :class="tagClass(item.type)">{{ item.parentProductTitle }}-{{ item.productTitle }}
          <!-- loading:展示骨架架子 -->
          <template v-if="loading">
            <div v-for="n in 6" :key="`skeleton-${n}`" class="warn-item skeleton" role="listitem">
              <div class="warn-tag skeleton-block"></div>
              <div class="warn-text skeleton-block"></div>
              <div class="warn-action skeleton-block"></div>
              <div class="warn-date skeleton-block"></div>
            </div>
            <div class="warn-text" :title="item.title">{{ item.title }}</div>
            <div class="warn-action" @click.stop="openWarning(item)">查看</div>
            <div class="warn-date">{{ item.date }}</div>
          </div>
          </template>
          <!-- 空数据:先展示架子 + 空状态 -->
          <template v-else-if="warnings.length === 0">
            <div v-for="n in 4" :key="`empty-row-${n}`" class="warn-item is-empty" role="listitem">
              <div class="warn-tag tag-empty">—</div>
              <div class="warn-text empty-text">暂无预警信息</div>
              <div class="warn-action empty-action">查看</div>
              <div class="warn-date empty-date">--</div>
            </div>
          </template>
          <!-- 有数据:正常渲染 -->
          <template v-else>
            <div
              v-for="item in warnings"
              :key="item.id"
              class="warn-item"
              role="listitem"
              @click="openWarning(item)"
            >
              <div class="warn-tag" :class="tagClass(item.type)">
                {{ item.parentProductTitle }}-{{ item.productTitle }}
              </div>
              <div class="warn-text" :title="item.title">{{ item.title }}</div>
              <div class="warn-action" @click.stop="openWarning(item)">查看</div>
              <div class="warn-date">{{ item.date }}</div>
            </div>
          </template>
        </div>
      </div>
    </div>
@@ -25,12 +48,13 @@
</template>
<script setup>
import { computed, getCurrentInstance, ref, onMounted } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import { getCurrentInstance, ref, onMounted } from 'vue'
import { nonComplianceWarning } from '@/api/viewIndex.js'
import PanelHeader from "@/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue";
const { proxy } = getCurrentInstance() || {}
const loading = ref(false)
const warnings = ref([])
// 占比数据
@@ -39,12 +63,6 @@
  semiFinishedProductRatio: 0,
  finishedProductRatio: 0,
})
const TAG_COLORS = {
  raw: '#7C4DFF',
  final: '#F5A000',
  semi: '#FF66CC',
}
const tagClass = (type) => {
  if (type === 'raw') return 'tag-raw'
@@ -60,60 +78,8 @@
  return 'raw' // 默认值
}
const pieChartStyle = { width: '100%', height: '100%' }
const pieOptions = {
  backgroundColor: 'transparent',
  textStyle: { color: '#B8C8E0' },
}
const pieTooltip = {
  trigger: 'item',
  formatter: (p) => `${p.name}:${p.value}%`,
}
const pieData = computed(() => {
  return [
    { name: '原材料', value: ratios.value.rawMaterialRatio, itemStyle: { color: TAG_COLORS.raw } },
    { name: '半成品', value: ratios.value.semiFinishedProductRatio, itemStyle: { color: TAG_COLORS.semi } },
    { name: '成品', value: ratios.value.finishedProductRatio, itemStyle: { color: TAG_COLORS.final } },
  ]
})
const pieSeries = computed(() => {
  return [
    {
      type: 'pie',
      radius: ['0%', '68%'],
      center: ['50%', '50%'],
      startAngle: 90,
      clockwise: true,
      avoidLabelOverlap: true,
      label: { show: false },
      labelLine: { show: false },
      itemStyle: {
        borderColor: '#071a3a',
        borderWidth: 4,
        shadowBlur: 14,
        shadowColor: 'rgba(0, 0, 0, 0.35)',
      },
      data: pieData.value,
    },
    {
      // 内圈暗环,增强层次
      type: 'pie',
      radius: ['70%', '74%'],
      center: ['50%', '50%'],
      silent: true,
      label: { show: false },
      labelLine: { show: false },
      itemStyle: { color: 'rgba(78, 228, 255, 0.12)' },
      data: [1],
    },
  ]
})
const fetchWarnings = async () => {
  loading.value = true
  try {
    const res = await nonComplianceWarning()
    if (res?.code === 200 && res?.data) {
@@ -140,10 +106,14 @@
          date,
        }
      })
    } else {
      warnings.value = []
    }
  } catch (e) {
    // 接口失败则保持空数据
    warnings.value = []
    console.error('获取不合格预警失败:', e)
  } finally {
    loading.value = false
  }
}
@@ -153,12 +123,7 @@
    proxy.$modal.alert(title)
    return
  }
  // 兜底:没有全局 modal 时用 console
  console.log('warning:', { ...item })
}
const handleRangeClick = () => {
  // 先按截图做静态“近7天”,后续有真实筛选需求再接入
}
onMounted(() => {
@@ -173,7 +138,7 @@
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  height: 90%;
  box-sizing: border-box;
}
@@ -352,4 +317,53 @@
  opacity: 0.35;
  pointer-events: none;
}
.warn-empty {
  padding: 10px 0 0;
}
.warn-item.is-empty {
  opacity: 0.9;
  cursor: default;
}
.tag-empty {
  background: rgba(184, 200, 224, 0.22);
  color: rgba(184, 200, 224, 0.85);
  font-weight: 700;
}
.empty-text {
  color: rgba(232, 241, 255, 0.75);
}
.empty-action {
  color: rgba(255, 77, 79, 0.55);
}
.empty-date {
  color: rgba(184, 200, 224, 0.45);
}
/* skeleton */
.warn-item.skeleton {
  pointer-events: none;
}
.skeleton-block {
  height: 18px;
  border-radius: 4px;
  background: linear-gradient(90deg, rgba(184, 200, 224, 0.08) 25%, rgba(184, 200, 224, 0.18) 37%, rgba(184, 200, 224, 0.08) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.2s ease-in-out infinite;
}
.warn-item.skeleton .warn-tag.skeleton-block {
  height: 28px;
}
@keyframes shimmer {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}
</style>