Files
quanxiyun-app/src/pages-sub/device/credit-detail.vue
T
2026-09-16 15:04:08 +08:00

186 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
* 信用分 对齐 C端.html #sub-credit-detail (~line 3055)
* 当前信用分仪表盘 + 五维度构成 + 等级说明
-->
<template>
<view class="sub-page">
<nav-bar title="信用分" :show-back="true" @back="closeSub()" />
<scroll-view scroll-y class="page-content">
<text class="sub-hint" v-if="deviceId">📡 单设备 · 由中控平台计算并推送</text>
<text class="sub-hint" v-else>📊 全部设备信用分均值 · 点击设备详情查看单设备明细</text>
<!-- 当前信用分仪表盘 -->
<view class="score-card">
<view class="sc-glow"></view>
<view class="sc-inner">
<text class="sc-label">当前信用分</text>
<view class="sc-ring-wrap">
<svg viewBox="0 0 120 120" class="sc-svg">
<circle fill="none" stroke="#E6F4FF" stroke-width="10" cx="60" cy="60" r="52" />
<circle stroke-dashoffset="33" cx="60" cy="60" stroke-width="10" stroke-dasharray="326.7"
r="52" fill="none" stroke="url(#creditGrad)" stroke-linecap="round" />
<defs>
<linearGradient id="creditGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1677FF" />
<stop offset="100%" stop-color="#00C4A7" />
</linearGradient>
</defs>
</svg>
<view class="sc-ring-center">
<text class="src-score">{{ creditScore }}</text>
<text class="src-total">/ 100</text>
</view>
</view>
<text class="sc-badge">{{ scoreLabel }}</text>
</view>
</view>
<!-- 信用分构成五维度)— 仅单设备模式展示 -->
<view class="card" v-if="deviceId">
<text class="card-title">信用分构成五维度</text>
<view class="info-row" v-for="d in dimensions" :key="d.name">
<text class="ir-label">{{ d.name }} (权重{{ d.weight }}%)</text>
<text class="ir-val">{{ d.raw }} {{ d.score }}</text>
</view>
</view>
<view class="card" v-else>
<text class="card-title">信用分构成五维度</text>
<view class="info-row" style="justify-content:center;padding:24rpx 0;">
<text class="ir-label" style="color:#8B91A1;">查看单设备信用分明细请从设备详情页进入</text>
</view>
</view>
<!-- 信用分等级说明 -->
<view class="card">
<text class="card-title">信用分等级说明</text>
<view class="info-row" v-for="g in grades" :key="g.label">
<text class="ir-label">{{ g.label }} ({{ g.range }})</text>
<text class="ir-val" :style="{ color: g.color }">{{ g.bonus }}</text>
</view>
</view>
<view style="height: 40rpx;"></view>
</scroll-view>
</view>
</template>
<script setup>
import NavBar from '@/components/NavBar.vue'
import request from '@/utils/request'
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { closeSub, parseSubParams } from '@/composables/useNavigation'
const creditScore = ref(96)
const dimensions = ref([])
const scoreLabel = ref('良好 · 质量良好')
const deviceId = ref('')
onLoad(async (options) => {
const params = parseSubParams(options)
deviceId.value = params.id || ''
if (deviceId.value) {
// 单设备详情模式:从设备API获取
await loadDeviceCredit(deviceId.value)
} else {
// 用户总览模式(从我的/首页进入,无 device_id):取全部设备均值
await loadUserCredit()
}
})
const loadDeviceCredit = async (deviceId) => {
try {
const res = await request.get('/c/devices/' + deviceId, { showLoading: false })
const d = res.data
creditScore.value = d.credit_score ?? 96
dimensions.value = d.credit_dimensions || []
const cs = creditScore.value
scoreLabel.value = cs >= 95 ? '优秀 · 质量优秀' : cs >= 80 ? '良好 · 质量良好' : cs >= 60 ? '一般 · 质量一般' : '极差 · 待优化'
} catch (e) { /* 设备不存在 */ }
}
const loadUserCredit = async () => {
try {
const res = await request.get('/c/profile', { showLoading: false })
const cs = res.data?.avg_credit_score
if (cs !== undefined && cs !== null) {
creditScore.value = cs
}
dimensions.value = [
{ name: '带宽达标率', weight: 30, raw: '--', score: '--' },
{ name: '丢包率', weight: 25, raw: '--', score: '--' },
{ name: '延迟', weight: 20, raw: '--', score: '--' },
{ name: '上游回传', weight: 15, raw: '--', score: '--' },
{ name: 'NAT稳定性', weight: 10, raw: '--', score: '--' },
]
const csVal = creditScore.value
scoreLabel.value = csVal >= 95 ? '优秀 · 质量优秀' : csVal >= 80 ? '良好 · 质量良好' : csVal >= 60 ? '一般 · 质量一般' : '极差 · 待优化'
} catch (e) { /* 忽略 */ }
}
const goBack = () => closeSub()
const grades = ref([
{ label: '优秀', range: '≥95', bonus: '收益加成 1.2x', color: '#00C4A7' },
{ label: '良好', range: '80-94', bonus: '收益加成 1.0x', color: '#1677FF' },
{ label: '一般', range: '60-79', bonus: '收益加成 0.8x', color: '#FF8800' },
{ label: '极差', range: '<40', bonus: '暂停结算', color: '#FF4D4F' }
])
</script>
<style lang="scss" scoped>
.sub-page { height: 100vh; display: flex; flex-direction: column; overflow: hidden; background: #F2F4F8; }
.page-content { flex: 1; height: 0; padding: 0 24rpx 40rpx; }
.sub-hint { font-size: 20rpx; color: #BCC2CE; display: block; padding: 16rpx 0 12rpx; }
/* 仪表盘卡片 */
.score-card {
background: linear-gradient(135deg, #F0F7FF, #F0F7FF);
border-radius: 16rpx;
text-align: center;
margin-bottom: 16rpx;
position: relative;
overflow: hidden;
}
.sc-glow {
position: absolute; top: -60rpx; right: -60rpx; width: 200rpx; height: 200rpx;
border-radius: 50%; background: radial-gradient(circle, rgba(22,119,255,.12), transparent 70%);
}
.sc-inner { position: relative; z-index: 1; padding: 24rpx 0 28rpx; }
.sc-label { font-size: 24rpx; color: #8B91A1; display: block; margin-bottom: 16rpx; }
.sc-ring-wrap {
width: 240rpx; height: 240rpx; margin: 0 auto 20rpx; position: relative;
display: flex; align-items: center; justify-content: center;
}
.sc-svg {
width: 100%; height: 100%; position: absolute; top: 0; left: 0;
transform: rotate(-90deg);
}
.sc-ring-center { display: flex; flex-direction: column; align-items: center; justify-content: center; }
.src-score { font-size: 72rpx; font-weight: 800; color: #1677FF; line-height: 1; font-family: 'DIN Alternate', -apple-system, sans-serif; }
.src-total { font-size: 20rpx; color: #8B91A1; margin-top: 4rpx; }
.sc-badge {
display: inline-block; font-size: 26rpx; font-weight: 600; padding: 8rpx 24rpx;
border-radius: 32rpx; background: #E6FAF5; color: #00C4A7;
}
/* 卡片 */
.card { background: #fff; border-radius: 16rpx; padding: 28rpx 24rpx; margin-bottom: 16rpx; box-shadow: 0 1px 3px rgba(15,18,38,.04); }
.card-title { font-size: 28rpx; font-weight: 600; color: #0F1226; display: block; margin-bottom: 20rpx; }
.info-row {
display: flex; justify-content: space-between; align-items: center;
padding: 14rpx 0; border-bottom: 0.5px solid #F3F5F9;
&:last-child { border-bottom: none; }
}
.ir-label { font-size: 24rpx; color: #5A6072; }
.ir-val { font-size: 24rpx; color: #00C4A7; font-weight: 500; }
</style>