first commit

This commit is contained in:
2026-09-16 15:04:08 +08:00
commit 43d095ea0f
117 changed files with 31982 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
<!--
* 特征码对接详情 方案B/A双轨验证流程对比表
-->
<template>
<view class="sub-page">
<nav-bar title="特征码对接" :show-back="true" @back="closeSub()" />
<scroll-view scroll-y class="page-content">
<!-- 状态卡 -->
<view class="status-banner" :class="verified ? 'success' : ''">
<text class="sb-icon">{{ verified ? '✓' : '!' }}</text>
<view class="sb-texts">
<text class="sb-title">{{ verified ? '已验证(方案B' : '待验证' }}</text>
<text class="sb-desc">Agent本地生成特征码基于硬件指纹绑定</text>
</view>
</view>
<!-- 硬件指纹信息 -->
<view class="info-card">
<text class="info-title">硬件指纹信息</text>
<view class="info-rows">
<view class="ir-item">
<text class="ir-label">设备名称</text>
<text class="ir-value">{{ deviceName }}</text>
</view>
<view class="ir-item">
<text class="ir-label">对接方式</text>
<text class="ir-value">{{ dockMethod }}</text>
</view>
<view class="ir-item">
<text class="ir-label">特征码</text>
<text class="ir-value mono">{{ featureCode }}</text>
</view>
<view class="ir-item">
<text class="ir-label">生成方式</text>
<text class="ir-value">本地Agent生成</text>
</view>
<view class="ir-item">
<text class="ir-label">绑定时间</text>
<text class="ir-value">{{ bindTime }}</text>
</view>
<view class="ir-item">
<text class="ir-label">验证状态</text>
<text class="ir-value" :class="verified ? 'status-success' : ''">{{ verified ? '已验证通过' : '未验证' }}</text>
</view>
</view>
</view>
<!-- 双轨策略 -->
<view class="section-block">
<text class="section-title">双轨策略</text>
<!-- 方案B -->
<view class="strategy-card active">
<view class="sc-badge">当前使用</view>
<view class="sc-header">
<text class="sc-name">方案B</text>
<text class="sc-secure">安全等级 </text>
</view>
<view class="sc-steps">
<view v-for="(s, i) in planBSteps" :key="i" class="scs-item">
<text class="scs-num">{{ i + 1 }}</text>
<text class="scs-text">{{ s }}</text>
</view>
</view>
</view>
<!-- 方案A -->
<view class="strategy-card disabled">
<view class="sc-badge" style="background:#f5f7fa;color:#BCC2CE;">二期上线</view>
<view class="sc-header">
<text class="sc-name" style="color:#BCC2CE;">方案A</text>
<text class="sc-secure" style="color:#d9d9d9;">安全等级 </text>
</view>
<view class="sc-steps">
<view v-for="(s, i) in planASteps" :key="i" class="scs-item" style="opacity:.5">
<text class="scs-num">{{ i + 1 }}</text>
<text class="scs-text">{{ s }}</text>
</view>
</view>
</view>
</view>
<!-- 通用验证流程 -->
<view class="section-block">
<text class="section-title">对接验证流程</text>
<view class="flow-list">
<view v-for="(f, i) in flowSteps" :key="i" class="fl-item">
<view class="fl-num">{{ f.step }}</view>
<view class="fl-body">
<text class="fl-title">{{ f.title }}</text>
<text class="fl-desc">{{ f.desc }}</text>
</view>
</view>
</view>
</view>
<!-- 对比表 -->
<view class="section-block">
<text class="section-title">方案对比</text>
<view class="compare-table">
<view class="ct-row ct-header">
<text class="ct-cell ct-col-item">对比项</text>
<text class="ct-cell ct-col-b">方案B</text>
<text class="ct-cell ct-col-a">方案A</text>
</view>
<view v-for="(row, i) in compareRows" :key="i" class="ct-row">
<text class="ct-cell ct-col-item">{{ row.item }}</text>
<text class="ct-cell ct-col-b highlight">{{ row.planB }}</text>
<text class="ct-cell ct-col-a dim">{{ row.planA }}</text>
</view>
</view>
</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 deviceName = ref('--')
const featureCode = ref('--')
const dockMethod = ref('方案BAgent生成)')
const bindTime = ref('--')
const verified = ref(false)
onLoad(async (options) => {
const params = parseSubParams(options)
if (params.id) {
try {
const res = await request.get('/c/devices/' + params.id, { showLoading: false })
const d = res.data
deviceName.value = d.name || d.device_id || '--'
dockMethod.value = d.source === 'own' ? '方案BAgent生成)' : '平台设备'
bindTime.value = d.created_at || '--'
verified.value = d.status === 'running'
// 特征码用 device_id 脱敏展示
if (d.device_id) {
featureCode.value = d.device_id.substring(0, 4) + '****'
}
} catch (e) { /* 忽略 */ }
}
})
const goBack = () => closeSub()
const planBSteps = ref([
'Agent安装后本地生成特征码',
'特征码与硬件指纹绑定',
'加密传输至服务器',
'服务器端验证并激活',
'绑定成功,进入运行态'
])
const planASteps = ref([
'手动录入硬件信息',
'服务器生成特征码',
'人工审核并下发',
'设备端确认绑定',
'激活上线'
])
const flowSteps = ref([
{ step: '01', title: '特征码生成', desc: '系统根据设备硬件信息生成唯一特征码' },
{ step: '02', title: '本地上传', desc: 'Agent将特征码加密上传至服务器' },
{ step: '03', title: '服务器验证', desc: '服务器对比硬件指纹与特征码一致性' },
{ step: '04', title: '绑定确认', desc: '验证通过后,特征码与设备完成绑定' },
{ step: '05', title: '激活上线', desc: '绑定成功,设备进入运行中状态' }
])
const compareRows = ref([
{ item: '特征码生成', planB: 'Agent端本地生成', planA: '服务器端生成' },
{ item: '硬件绑定方式', planB: '硬件指纹自动绑定', planA: '手动录入信息' },
{ item: '部署复杂度', planB: '低(自动完成)', planA: '高(需人工参与)' },
{ item: '安全性', planB: '高(端到端加密)', planA: '中(人工审核)' },
{ item: '上线速度', planB: '分钟级', planA: '小时级' },
{ item: '适用场景', planB: '自有设备/新部署', planA: '存量设备迁移' },
{ item: '二期支持', planB: '-', planA: '二期上线后支持' }
])
</script>
<style lang="scss" scoped>
.sub-page { min-height: 100vh; background: #F2F4F8; }
.page-content { height: calc(100vh - 88rpx); padding: 24rpx 24rpx 60rpx; }
// Status banner
.status-banner {
display: flex;
align-items: center;
gap: 16rpx;
padding: 24rpx;
border-radius: 16rpx;
margin-bottom: 20rpx;
&.success { background: linear-gradient(135deg, #e6faf5, #d4f5ed); }
}
.sb-icon {
width: 56rpx; height: 56rpx; border-radius: 50%; background: #00C4A7;
display: flex; align-items: center; justify-content: center;
font-size: 28rpx; color: #fff; flex-shrink: 0;
}
.sb-title { font-size: 28rpx; font-weight: 700; color: #0F1226; display: block; }
.sb-desc { font-size: 22rpx; color: #5A6072; display: block; margin-top: 4rpx; }
// Info card
.info-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(15,18,38,.04);
}
.info-title { font-size: 28rpx; font-weight: 700; color: #0F1226; display: block; margin-bottom: 20rpx; }
.ir-item { display: flex; padding: 16rpx 0; border-bottom: 1rpx solid #f5f7fa; &:last-child { border: none; } }
.ir-label { font-size: 24rpx; color: #8B91A1; width: 160rpx; flex-shrink: 0; }
.ir-value { font-size: 24rpx; color: #0F1226; flex: 1;
&.mono { font-family: 'Courier New', monospace; }
&.status-success { color: #00C4A7; font-weight: 600; }
}
// Section
.section-block { margin-bottom: 24rpx; }
.section-title {
font-size: 28rpx; font-weight: 700; color: #0F1226; margin-bottom: 16rpx;
display: flex; align-items: center; gap: 10rpx;
&::before { content: ''; width: 6rpx; height: 26rpx; background: linear-gradient(180deg, #1677FF, #4096FF); border-radius: 3rpx; }
}
// Strategy card
.strategy-card {
background: #fff;
border-radius: 14rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(15,18,38,.04);
position: relative;
&.active { border-left: 6rpx solid #00C4A7; }
&.disabled { border-left: 6rpx solid #e0e0e0; opacity: .7; }
}
.sc-badge { position: absolute; top: 16rpx; right: 16rpx; font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 6rpx; background: #e6faf5; color: #00C4A7; }
.sc-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
.sc-name { font-size: 30rpx; font-weight: 700; color: #0F1226; }
.sc-secure { font-size: 20rpx; color: #FF8800; }
.sc-steps { display: flex; flex-direction: column; gap: 8rpx; }
.scs-item { display: flex; align-items: center; gap: 12rpx; }
.scs-num { width: 32rpx; height: 32rpx; border-radius: 50%; background: #e6faf5; color: #00C4A7; font-size: 20rpx; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.scs-text { font-size: 24rpx; color: #5A6072; }
// Flow list
.flow-list { display: flex; flex-direction: column; gap: 12rpx; }
.fl-item { display: flex; gap: 16rpx; background: #fff; border-radius: 14rpx; padding: 20rpx; box-shadow: 0 2rpx 8rpx rgba(15,18,38,.04); }
.fl-num { width: 48rpx; height: 48rpx; background: linear-gradient(135deg, #1677FF, #4096FF); border-radius: 10rpx; display: flex; align-items: center; justify-content: center; font-size: 22rpx; color: #fff; font-weight: 700; flex-shrink: 0; }
.fl-body { flex: 1; }
.fl-title { font-size: 26rpx; font-weight: 600; color: #0F1226; display: block; }
.fl-desc { font-size: 22rpx; color: #8B91A1; display: block; margin-top: 4rpx; }
// Compare table
.compare-table {
background: #fff;
border-radius: 14rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(15,18,38,.04);
}
.ct-row { display: flex; padding: 18rpx 16rpx; border-bottom: 1rpx solid #f5f7fa; &:last-child { border: none; } }
.ct-header { background: #fafafa; }
.ct-cell { font-size: 22rpx; color: #5A6072; text-align: center;
.ct-header & { font-weight: 600; color: #0F1226; }
}
.ct-col-item { flex: 1.2; text-align: left; }
.ct-col-b { flex: 1; }
.ct-col-a { flex: 1; }
.highlight { color: #00C4A7; font-weight: 500; }
.dim { color: #BCC2CE; }
</style>