| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <PanelHeader title="ä¸åæ ¼äº§åæå"> |
| | | <template #extra> |
| | | <span class="range-tip">(è¿ 30 天)</span> |
| | | </template> |
| | | </PanelHeader> |
| | | <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" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { unqualifiedProductRanking } from '@/api/viewIndex.js' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | |
| | | 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) |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fetchData() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .main-panel-box { |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | height: 40px; |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | animation: rankItemFadeIn 0.5s ease-out both; |
| | | } |
| | | |
| | | @keyframes rankItemFadeIn { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateX(20px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateX(0); |
| | | } |
| | | } |
| | | |
| | | .main-panel-box:hover { |
| | | transform: translateX(5px); |
| | | } |
| | | |
| | | /* æå项åºé¨å
线 */ |
| | | .main-panel-box::after { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | height: 1px; |
| | | background: linear-gradient(90deg, transparent, rgba(255, 77, 79, 0.3), transparent); |
| | | opacity: 0; |
| | | transition: opacity 0.3s; |
| | | } |
| | | |
| | | .main-panel-box:hover::after { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .main-panel-box-left { |
| | | background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%); |
| | | border-radius: 20px; |
| | | text-align: center; |
| | | line-height: 32px; |
| | | margin: 0 20px; |
| | | padding: 0 12px; |
| | | font-weight: 600; |
| | | font-size: 12px; |
| | | color: white; |
| | | min-width: 60px; |
| | | transition: all 0.3s ease; |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* æåæ ç¾åå
*/ |
| | | .main-panel-box-left::before { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 0; |
| | | left: -100%; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); |
| | | animation: rankShine 2s ease-in-out infinite; |
| | | } |
| | | |
| | | @keyframes rankShine { |
| | | 0% { left: -100%; } |
| | | 50%, 100% { left: 100%; } |
| | | } |
| | | |
| | | .main-panel-box:hover .main-panel-box-left { |
| | | box-shadow: 0 0 15px rgba(255, 77, 79, 0.5); |
| | | } |
| | | |
| | | .main-panel-box-right { |
| | | display: flex; |
| | | flex-direction: column; |
| | | flex: 1; |
| | | |
| | | .main-panel-box-right-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #ffffff; |
| | | margin-bottom: 6px; |
| | | } |
| | | |
| | | .main-panel-box-right-text { |
| | | font-size: 12px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | padding-right: 60px; |
| | | margin-bottom: 4px; |
| | | color: rgba(184, 200, 224, 0.8); |
| | | transition: color 0.3s; |
| | | } |
| | | |
| | | .main-panel-box:hover .main-panel-box-right-text { |
| | | color: rgba(184, 200, 224, 1); |
| | | } |
| | | |
| | | .main-panel-box-right-progress { |
| | | :deep(.el-progress__text) { |
| | | color: white !important; |
| | | } |
| | | :deep(.el-progress-bar__outer) { |
| | | background-color: rgba(78, 228, 255, 0.15); |
| | | } |
| | | :deep(.el-progress-bar__inner) { |
| | | transition: width 0.8s ease; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .main-panel-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | height: 100%; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .main-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .panel-item-customers { |
| | | border: 1px solid #1a58b0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 449px; |
| | | overflow: hidden; |
| | | position: relative; |
| | | } |
| | | |
| | | /* 颿¿è§è½è£
饰 */ |
| | | .panel-item-customers::before, |
| | | .panel-item-customers::after { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 15px; |
| | | height: 15px; |
| | | border-color: rgba(255, 77, 79, 0.5); |
| | | border-style: solid; |
| | | pointer-events: none; |
| | | } |
| | | |
| | | .panel-item-customers::before { |
| | | top: -1px; |
| | | left: -1px; |
| | | border-width: 2px 0 0 2px; |
| | | } |
| | | |
| | | .panel-item-customers::after { |
| | | bottom: -1px; |
| | | right: -1px; |
| | | border-width: 0 2px 2px 0; |
| | | } |
| | | |
| | | /* 颿¿èæ¯èå² */ |
| | | .panel-item-customers::before { |
| | | content: ''; |
| | | position: absolute; |
| | | inset: 0; |
| | | background: radial-gradient(circle at 50% 50%, rgba(255, 77, 79, 0.03) 0%, transparent 70%); |
| | | pointer-events: none; |
| | | animation: panelPulse 5s ease-in-out infinite; |
| | | z-index: 0; |
| | | } |
| | | |
| | | @keyframes panelPulse { |
| | | 0%, 100% { opacity: 0.3; } |
| | | 50% { opacity: 0.5; } |
| | | } |
| | | |
| | | .range-tip { |
| | | font-size: 14px; |
| | | color: rgba(184, 200, 224, 0.75); |
| | | animation: tipFade 2s ease-in-out infinite; |
| | | } |
| | | |
| | | @keyframes tipFade { |
| | | 0%, 100% { opacity: 0.75; } |
| | | 50% { opacity: 1; } |
| | | } |
| | | </style> |