调整生产订单
调整生产工单页面逻辑
调整主页ui排版
修改质量模块大屏ui排版和没有数据的状态
| | |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | align-items: flex-start; |
| | | justify-content: space-evenly; |
| | | align-items: stretch; |
| | | justify-content: flex-start; |
| | | flex-wrap: nowrap; |
| | | } |
| | | |
| | | .top-left { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | height: 180px; |
| | | flex: 0 0 320px; |
| | | min-width: 280px; |
| | | } |
| | | |
| | | .data-cards { |
| | | flex: 1 1 auto; |
| | | min-width: 0; |
| | | display: flex; |
| | | gap: 16px; |
| | | justify-content: flex-start; |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .todo-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | height: 180px; |
| | | flex: 0 0 360px; |
| | | min-width: 320px; |
| | | } |
| | | |
| | | .welcome-banner { |
| | | padding: 12px 14px; |
| | | background: linear-gradient(135deg, rgba(229, 240, 255, 0.95), rgba(214, 232, 255, 0.75), rgba(207, 236, 255, 0.95)); |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: space-between; |
| | | gap: 4px; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .welcome-title { |
| | | flex: 1 1 auto; |
| | | min-width: 0; |
| | | font-size: 12px; |
| | | font-weight: bolder; |
| | | color: #222; |
| | | line-height: 1.3; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .welcome-time { |
| | | flex: 0 0 auto; |
| | | margin-top: 0; |
| | | font-size: 8px; |
| | | font-weight: bold; |
| | | color: rgba(0, 0, 0, 0.55); |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .company-info { |
| | | padding: 0; |
| | | overflow: hidden; |
| | | border-radius: 12px; |
| | | background: #fff; |
| | | height: 100%; |
| | | } |
| | | |
| | | .welcome-banner { |
| | | padding: 10px 10px; |
| | | background: linear-gradient(135deg, rgba(229, 240, 255, 0.9), rgba(214, 232, 255, 0.7), rgba(207, 236, 255, 0.9)); |
| | | } |
| | | |
| | | .welcome-title { |
| | | font-size: 18px; |
| | | font-weight: 700; |
| | | color: #222; |
| | | line-height: 1.3; |
| | | } |
| | | |
| | | .welcome-user { |
| | | margin-right: 6px; |
| | | } |
| | | |
| | | .welcome-time { |
| | | margin-top: 10px; |
| | | font-size: 16px; |
| | | color: rgba(0, 0, 0, 0.55); |
| | | } |
| | | |
| | | .user-card { |
| | |
| | | flex: 0 0 auto; |
| | | } |
| | | .data-cards { |
| | | width: 50%; |
| | | display: flex; |
| | | gap: 16px; |
| | | justify-content: flex-start; |
| | |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | height: 180px; |
| | | width: 20%; |
| | | flex: 0 0 320px; |
| | | min-width: 280px; |
| | | } |
| | | .todo-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | height: 180px; |
| | | width: 30%; |
| | | flex: 0 0 360px; |
| | | min-width: 320px; |
| | | } |
| | | .todo-list { |
| | | height: 100px; |
| | |
| | | min-width: 220px; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 1280px) { |
| | | .dashboard-top { |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .top-left { |
| | | flex: 1 1 320px; |
| | | } |
| | | |
| | | .data-cards { |
| | | flex: 1 1 560px; |
| | | } |
| | | |
| | | .todo-panel { |
| | | flex: 1 1 320px; |
| | | min-width: 280px; |
| | | height: auto; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .dashboard { |
| | | padding: 12px; |
| | | } |
| | | |
| | | .dashboard-top { |
| | | flex-direction: column; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | .top-left, |
| | | .data-cards, |
| | | .todo-panel { |
| | | width: 100%; |
| | | flex: 1 1 auto; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .top-left { |
| | | height: auto; |
| | | } |
| | | |
| | | .data-cards { |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .data-card { |
| | | width: calc(50% - 8px); |
| | | min-width: 160px; |
| | | } |
| | | |
| | | .welcome-banner { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | |
| | | } |
| | | |
| | | .welcome-title { |
| | | white-space: normal; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | > |
| | | <el-date-picker |
| | | v-model="formState.productionDate" |
| | | type="date" |
| | | type="datetime" |
| | | placeholder="请选择生产日期" |
| | | :size="size" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | |
| | | productModelName: "", |
| | | unit: "", |
| | | quantity: 0, |
| | | productionDate: new Date().toISOString().substr(0, 10), |
| | | // 默认有年月日时分秒 |
| | | productionDate: new Date().toISOString().substr(0, 10) + ' 00:00:00', |
| | | }); |
| | | |
| | | const isShow = computed({ |
| | |
| | | productName: "", |
| | | productModelName: "", |
| | | quantity: '', |
| | | productionDate: new Date().toISOString().substr(0, 10), |
| | | productionDate: new Date().toISOString().substr(0, 10) + ' 00:00:00', |
| | | }; |
| | | isShow.value = false; |
| | | }; |
| | |
| | | // prop: "salesContractNo", |
| | | // width: '150px', |
| | | // }, |
| | | { |
| | | label: "客户名称", |
| | | prop: "customerName", |
| | | width: '200px', |
| | | }, |
| | | // { |
| | | // label: "客户名称", |
| | | // prop: "customerName", |
| | | // width: '200px', |
| | | // }, |
| | | { |
| | | label: "产品名称", |
| | | prop: "productCategory", |
| | |
| | | width: 180, |
| | | }, |
| | | { |
| | | label: "开始日期", |
| | | prop: "startTime", |
| | | label: "生产日期", |
| | | prop: "productionDate", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "结束日期", |
| | | prop: "endTime", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "交付日期", |
| | | prop: "deliveryDate", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: 120, |
| | | }, |
| | | // { |
| | | // label: "结束日期", |
| | | // prop: "endTime", |
| | | // formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | // width: 120, |
| | | // }, |
| | | // { |
| | | // label: "交付日期", |
| | | // prop: "deliveryDate", |
| | | // formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | // width: 120, |
| | | // }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | |
| | | handleEdit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "流转卡", |
| | | clickFun: row => { |
| | | downloadAndPrintWorkOrder(row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "流转卡", |
| | | // clickFun: row => { |
| | | // downloadAndPrintWorkOrder(row); |
| | | // }, |
| | | // }, |
| | | { |
| | | name: "附件", |
| | | clickFun: row => { |
| | |
| | | <template> |
| | | <div> |
| | | <div class="chart-header"> |
| | | <PanelHeader title="完成检验数" /> |
| | | <div class="warn-range" @click="handleRangeClick">近7天</div> |
| | | </div> |
| | | <div class="main-panel panel-item-customers"> |
| | | <Echarts |
| | | ref="chart" |
| | |
| | | const yAxis1 = [ |
| | | { |
| | | type: 'value', |
| | | name: '单位: 件', |
| | | nameLocation: 'start', |
| | | nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 10] }, |
| | | axisLabel: { color: '#B8C8E0', fontSize: 12 }, |
| | |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | height: 450px; |
| | | position: relative; |
| | | background: radial-gradient(circle at 50% 50%, rgba(78, 228, 255, 0.05) 0%, rgba(0, 0, 0, 0) 70%); |
| | | } |
| | |
| | | <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> |
| | |
| | | </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([]) |
| | | |
| | | // 占比数据 |
| | |
| | | semiFinishedProductRatio: 0, |
| | | finishedProductRatio: 0, |
| | | }) |
| | | |
| | | const TAG_COLORS = { |
| | | raw: '#7C4DFF', |
| | | final: '#F5A000', |
| | | semi: '#FF66CC', |
| | | } |
| | | |
| | | const tagClass = (type) => { |
| | | if (type === 'raw') return 'tag-raw' |
| | |
| | | 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) { |
| | |
| | | date, |
| | | } |
| | | }) |
| | | } else { |
| | | warnings.value = [] |
| | | } |
| | | } catch (e) { |
| | | // 接口失败则保持空数据 |
| | | warnings.value = [] |
| | | console.error('获取不合格预警失败:', e) |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | |
| | | proxy.$modal.alert(title) |
| | | return |
| | | } |
| | | // 兜底:没有全局 modal 时用 console |
| | | console.log('warning:', { ...item }) |
| | | } |
| | | |
| | | const handleRangeClick = () => { |
| | | // 先按截图做静态“近7天”,后续有真实筛选需求再接入 |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | height: 100%; |
| | | height: 90%; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | |
| | | 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> |
| | |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | height: 948px; |
| | | } |
| | | |
| | | .chart-wrapper { |
| | |
| | | border: 1px solid #1a58b0; |
| | | padding: 14px 18px; |
| | | width: 100%; |
| | | height: 960px; |
| | | height: 950px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 420px; |
| | | height: 447px; |
| | | } |
| | | |
| | | .pie-chart-wrapper { |
| | |
| | | <PanelHeader title="不合格产品排名" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <div class="main-panel-container"> |
| | | <div style="color: white" class="main-panel-box" v-for="(item, index) in panelList" :key="index"> |
| | | <!-- <div style="flex: 1" class="main-panel-box-left">{{ item.rank }}</div> --> |
| | | <div style="flex: 1" class="main-panel-box-left">{{ item.productName }}</div> |
| | | <div style="flex: 3" class="main-panel-box-right"> |
| | | <!-- <div class="main-panel-box-right-title">{{ item.productName }}</div> --> |
| | | <div class="main-panel-box-right-text"> |
| | | <span>总数量:{{ item.total }}</span> |
| | | <span>已完成:{{ item.finished }}</span> |
| | | <span>合格率:{{ item.qualifiedRate }}%</span> |
| | | </div> |
| | | <div class="main-panel-box-right-progress"> |
| | | <el-progress :percentage="item.percentage" :format="format" /> |
| | | <!-- loading:骨架架子 --> |
| | | <template v-if="loading"> |
| | | <div class="main-panel-box skeleton" v-for="n in 6" :key="`sk-${n}`"> |
| | | <div class="main-panel-box-left skeleton-block"></div> |
| | | <div class="main-panel-box-right"> |
| | | <div class="main-panel-box-right-text"> |
| | | <span class="skeleton-block"></span> |
| | | <span class="skeleton-block"></span> |
| | | <span class="skeleton-block"></span> |
| | | </div> |
| | | <div class="main-panel-box-right-progress"> |
| | | <el-progress :percentage="0" :show-text="false" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- empty:先给架子,再给空状态 --> |
| | | <template v-else-if="panelList.length === 0"> |
| | | <div style="color: white" class="main-panel-box is-empty" v-for="n in 4" :key="`empty-${n}`"> |
| | | <div style="flex: 1" class="main-panel-box-left">—</div> |
| | | <div style="flex: 3" class="main-panel-box-right"> |
| | | <div class="main-panel-box-right-text"> |
| | | <span>总数量:--</span> |
| | | <span>已完成:--</span> |
| | | <span>合格率:--</span> |
| | | </div> |
| | | <div class="main-panel-box-right-progress"> |
| | | <el-progress :percentage="0" :format="format" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- data --> |
| | | <template v-else> |
| | | <div style="color: white" class="main-panel-box" v-for="(item, index) in panelList" :key="index"> |
| | | <div style="flex: 1" class="main-panel-box-left">{{ item.productName }}</div> |
| | | <div style="flex: 3" class="main-panel-box-right"> |
| | | <div class="main-panel-box-right-text"> |
| | | <span>总数量:{{ item.total }}</span> |
| | | <span>已完成:{{ item.finished }}</span> |
| | | <span>合格率:{{ item.qualifiedRate }}%</span> |
| | | </div> |
| | | <div class="main-panel-box-right-progress"> |
| | | <el-progress :percentage="item.percentage" :format="format" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | import { unqualifiedProductRanking } from '@/api/viewIndex.js' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | |
| | | const loading = ref(false) |
| | | const panelList = ref([]) |
| | | |
| | | const format = (percentage) => { |
| | | return `${percentage}%` |
| | | } |
| | | |
| | | const fetchData = () => { |
| | | unqualifiedProductRanking() |
| | | .then((res) => { |
| | | if (res?.code === 200 && Array.isArray(res?.data)) { |
| | | const data = res.data |
| | | panelList.value = data.map((item, index) => { |
| | | const total = Number(item.totalCount) || 0 |
| | | const finished = Number(item.completedCount) || 0 |
| | | const passRate = Number(item.passRate) || 0 |
| | | |
| | | return { |
| | | rank: `Top${index + 1}`, |
| | | productName: item.productName || `产品${index + 1}`, |
| | | total: total.toFixed(2), |
| | | finished: finished.toFixed(2), |
| | | qualifiedRate: passRate.toFixed(2), |
| | | percentage: Math.min(100, Math.max(0, passRate)), // 确保百分比在0-100之间 |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | console.error('获取工单执行效率分析数据失败:', err) |
| | | }) |
| | | const fetchData = async () => { |
| | | loading.value = true |
| | | try { |
| | | const res = await unqualifiedProductRanking() |
| | | if (res?.code === 200 && Array.isArray(res?.data)) { |
| | | const data = res.data |
| | | panelList.value = data.map((item, index) => { |
| | | const total = Number(item.totalCount) || 0 |
| | | const finished = Number(item.completedCount) || 0 |
| | | const passRate = Number(item.passRate) || 0 |
| | | |
| | | return { |
| | | rank: `Top${index + 1}`, |
| | | productName: item.productName || `产品${index + 1}`, |
| | | total: total.toFixed(2), |
| | | finished: finished.toFixed(2), |
| | | qualifiedRate: passRate.toFixed(2), |
| | | percentage: Math.min(100, Math.max(0, passRate)), |
| | | } |
| | | }) |
| | | } else { |
| | | panelList.value = [] |
| | | } |
| | | } catch (err) { |
| | | panelList.value = [] |
| | | console.error('获取不合格产品排名数据失败:', err) |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | height: 449px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .panel-empty { |
| | | padding-top: 6px; |
| | | } |
| | | |
| | | .main-panel-box.is-empty { |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .main-panel-box.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; |
| | | } |
| | | |
| | | .main-panel-box-left.skeleton-block { |
| | | height: 28px; |
| | | margin: 0 20px; |
| | | } |
| | | |
| | | .main-panel-box.skeleton .main-panel-box-right-text span.skeleton-block { |
| | | display: inline-block; |
| | | width: 30%; |
| | | } |
| | | |
| | | @keyframes shimmer { |
| | | 0% { |
| | | background-position: 100% 0; |
| | | } |
| | | 100% { |
| | | background-position: -100% 0; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24px; |
| | | gap: 16px; |
| | | width: 520px; |
| | | } |
| | | |