Merge branch 'main' of http://1.117.67.95:3000/team/tydata-webview-v2
This commit is contained in:
@@ -259,6 +259,11 @@ const featureMap = {
|
||||
import("@/ui/CJRZQ8203.vue")
|
||||
),
|
||||
},
|
||||
IVYZ0S0D: {
|
||||
name: "劳动仲裁信息",
|
||||
component: defineAsyncComponent(() => import("@/ui/IVYZ0S0D.vue")),
|
||||
remark: '劳动仲裁信息展示被查询人在失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程及通知函触达等方面的风险信息。',
|
||||
},
|
||||
FLXG3D56: {
|
||||
name: "违约失信",
|
||||
component: defineAsyncComponent(() => import("@/ui/CFLXG3D56.vue")),
|
||||
@@ -504,6 +509,7 @@ const featureMap = {
|
||||
name: "个人消费等级",
|
||||
component: defineAsyncComponent(() => import("@/ui/JRZQ8B3C/index.vue")),
|
||||
},
|
||||
IVYZ6M8P: { name: "职业资格证书查询", component: defineAsyncComponent(() => import("@/ui/CIVYZ6M8P.vue")) },
|
||||
};
|
||||
|
||||
const maskValue = computed(() => {
|
||||
@@ -590,6 +596,8 @@ const featureRiskLevels = {
|
||||
'QYGL3F8E': 5, // 人企关系加强版
|
||||
'QCXG7A2B': 5, // 名下车辆
|
||||
'JRZQ09J8': 5, // 收入评估
|
||||
'IVYZ0S0D': 10, // 劳动仲裁信息
|
||||
'IVYZ6M8P': 5,
|
||||
|
||||
// 🔵 低风险类 - 权重 3
|
||||
'IVYZ5733': 3, // 婚姻状态
|
||||
@@ -612,7 +620,7 @@ const featureRiskLevels = {
|
||||
'DWBG8B4D_LeasingRisk': 6,
|
||||
'DWBG8B4D_RiskSupervision': 8,
|
||||
'DWBG8B4D_RiskWarningTab': 9,
|
||||
'DWBG8B4D_CourtInfo':9,
|
||||
'DWBG8B4D_CourtInfo': 9,
|
||||
|
||||
// 司南报告子模块
|
||||
'DWBG6A2C_StandLiveInfo': 4,
|
||||
@@ -627,7 +635,7 @@ const featureRiskLevels = {
|
||||
'DWBG6A2C_CreditDetail': 5,
|
||||
'DWBG6A2C_RentalBehavior': 5,
|
||||
'DWBG6A2C_RiskSupervision': 8,
|
||||
'DWBG6A2C_CourtRiskInfo':14,
|
||||
'DWBG6A2C_CourtRiskInfo': 14,
|
||||
|
||||
// 贷款风险评估子模块
|
||||
'CJRZQ5E9F_RiskOverview': 8,
|
||||
@@ -869,6 +877,7 @@ watch([reportData, componentRiskScores], () => {
|
||||
@apply p-3;
|
||||
box-shadow: 0px 0px 24px 0px #3F3F3F0F;
|
||||
}
|
||||
|
||||
/* 梯形背景图片样式 */
|
||||
.trapezoid-bg-image {
|
||||
background-size: contain;
|
||||
|
||||
BIN
src/ui.zip
BIN
src/ui.zip
Binary file not shown.
104
src/ui/CIVYZ6M8P.vue
Normal file
104
src/ui/CIVYZ6M8P.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="header-box">
|
||||
<h3 class="header-title">职业资格证书查询</h3>
|
||||
<p class="header-desc">展示查询到的职业资格考试及等级信息</p>
|
||||
</div>
|
||||
|
||||
<div v-if="hasData" class="result-section">
|
||||
<div class="info-row">
|
||||
<span class="info-label">考试名称</span>
|
||||
<span class="info-value">{{ examName || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">级别</span>
|
||||
<span class="info-value">{{ level || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">专业</span>
|
||||
<span class="info-value">{{ major || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">通过日期</span>
|
||||
<span class="info-value">{{ passDate || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="hasData && !examName && !major && !level && !passDate" class="empty-tip">
|
||||
已返回结果,但格式不在预期范围内,请联系技术支持查看原始数据。
|
||||
</div>
|
||||
|
||||
<div v-if="!hasData" class="empty-tip">暂无查询结果</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
params: { type: Object, default: () => ({}) },
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => { } },
|
||||
});
|
||||
|
||||
const hasData = computed(() => props.data && Object.keys(props.data).length > 0);
|
||||
|
||||
// ED0001: 考试名称, ED0002: 级别, ED0003: 专业, ED0004: 通过年月
|
||||
const examName = computed(() => props.data?.ED0001 || '');
|
||||
const level = computed(() => props.data?.ED0002 || '');
|
||||
const major = computed(() => props.data?.ED0003 || '');
|
||||
const passDate = computed(() => props.data?.ED0004 || '');
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.header-box {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
background: #f9fafb;
|
||||
padding: 0.75rem 0.875rem;
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.875rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #111827;
|
||||
margin-left: 1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
color: #9ca3af;
|
||||
font-size: 0.875rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
524
src/ui/IVYZ0S0D.vue
Normal file
524
src/ui/IVYZ0S0D.vue
Normal file
@@ -0,0 +1,524 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier';
|
||||
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
params: { type: Object, default: () => ({}) },
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => { } },
|
||||
});
|
||||
|
||||
const periodTab = ref('threeYears'); // 近三年 | 近五年
|
||||
|
||||
// 支持 data 或 data.result(接口返回的 result 对象)
|
||||
const result = computed(() => props.data?.result ?? props.data ?? {});
|
||||
|
||||
const getStatusText = (value) => {
|
||||
if (value === 1) return '未命中';
|
||||
if (value === 2) return '命中';
|
||||
return '—';
|
||||
};
|
||||
|
||||
// 获取通知函期间描述文本(支持数字或字符串如 "2")
|
||||
const getNoticeLetterPeriodText = (period) => {
|
||||
const p = Number(period);
|
||||
const periodMap = { 0: '没有被发送通知函', 1: '近2年内', 2: '2-4年', 3: '5年以上' };
|
||||
return periodMap[p] ?? '—';
|
||||
};
|
||||
|
||||
// 检查是否至少有一个数据类别有内容
|
||||
const hasAnyData = computed(() => {
|
||||
const r = result.value;
|
||||
return Object.keys(r).length > 0;
|
||||
});
|
||||
|
||||
// 汇总数据 - 按分类分组 { key, title, rows }
|
||||
const summaryGroups = computed(() => {
|
||||
const groups = [];
|
||||
const basic = result.value.basic_info;
|
||||
if (basic?.risk_flag !== undefined) {
|
||||
groups.push({ key: 'basic', title: '基础风险', rows: [{ label: '该人员是否有风险', value: basic.risk_flag }] });
|
||||
}
|
||||
const dishonesty = result.value.dishonesty?.dishonesty;
|
||||
const highConsumption = result.value.high_consumption?.high_consumption;
|
||||
if (dishonesty !== undefined || highConsumption !== undefined) {
|
||||
const rows = [];
|
||||
if (dishonesty !== undefined) rows.push({ label: '失信人员风险', value: dishonesty });
|
||||
if (highConsumption !== undefined) rows.push({ label: '限制高消费人员风险', value: highConsumption });
|
||||
groups.push({ key: 'credit', title: '失信限高', rows });
|
||||
}
|
||||
const labor = result.value.labor_disputes;
|
||||
if (labor) {
|
||||
const items = [['劳动争议', labor.labor_disputes], ['劳动合同纠纷', labor.labor_contract], ['劳动关系纠纷', labor.labor_relation], ['追索劳动报酬纠纷', labor.wage_claim], ['经济补偿金纠纷', labor.compensation], ['集体合同纠纷', labor.collective_contract], ['劳务派遣合同纠纷', labor.dispatch_contract], ['非全日制用工纠纷', labor.part_time], ['竞业限制纠纷', labor.non_compete]];
|
||||
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
|
||||
if (rows.length) groups.push({ key: 'labor', title: '劳动争议', rows });
|
||||
}
|
||||
const social = result.value.social_insurance;
|
||||
if (social) {
|
||||
const items = [['社会保险纠纷', social.social_insurance], ['养老保险待遇纠纷', social.pension], ['工伤保险待遇纠纷', social.injury_insurance], ['医疗保险待遇纠纷', social.medical_insurance], ['生育保险待遇纠纷', social.maternity_insurance], ['商业保险待遇纠纷', social.commercial_insurance]];
|
||||
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
|
||||
if (rows.length) groups.push({ key: 'social', title: '社会保险', rows });
|
||||
}
|
||||
if (result.value.welfare_disputes?.welfare !== undefined) {
|
||||
groups.push({ key: 'welfare', title: '福利待遇', rows: [{ label: '福利待遇纠纷', value: result.value.welfare_disputes.welfare }] });
|
||||
}
|
||||
const personnel = result.value.personnel_disputes;
|
||||
if (personnel) {
|
||||
const items = [['人事争议类纠纷', personnel.personnel_dispute], ['辞职争议纠纷', personnel.resignation_dispute], ['辞退争议纠纷', personnel.dismissal_dispute], ['聘用合同争议纠纷', personnel.employment_contract]];
|
||||
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
|
||||
if (rows.length) groups.push({ key: 'personnel', title: '人事争议', rows });
|
||||
}
|
||||
const arb = result.value.arbitration;
|
||||
if (arb && (arb.arbitration_confirmation !== undefined || arb.arbitration_revocation !== undefined)) {
|
||||
const rows = [];
|
||||
if (arb.arbitration_confirmation !== undefined) rows.push({ label: '申请仲裁确认', value: arb.arbitration_confirmation });
|
||||
if (arb.arbitration_revocation !== undefined) rows.push({ label: '撤销仲裁裁决', value: arb.arbitration_revocation });
|
||||
groups.push({ key: 'arbitration', title: '仲裁流程', rows });
|
||||
}
|
||||
const notice = result.value.notice_letter;
|
||||
if (notice?.notice_letter !== undefined) {
|
||||
const rows = [{ label: '通知函触达', value: notice.notice_letter }];
|
||||
if (notice.notice_letter_period !== undefined && notice.notice_letter === 2) rows.push({ label: '通知函发送时间', value: null, period: notice.notice_letter_period });
|
||||
groups.push({ key: 'notice', title: '通知函触达', rows });
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
|
||||
const summaryRows = computed(() => summaryGroups.value.flatMap((g) => g.rows));
|
||||
|
||||
// 真正的风险项(文档:失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程、通知函触达)
|
||||
// 排除 basic_info.risk_flag(汇总结论)和 notice_letter_period(非风险项)
|
||||
const riskItemRows = computed(() =>
|
||||
summaryGroups.value
|
||||
.filter((g) => g.key !== 'basic')
|
||||
.flatMap((g) => g.rows)
|
||||
.filter((r) => r.value === 1 || r.value === 2)
|
||||
);
|
||||
|
||||
// 近三年/近五年 - 按分类分组
|
||||
const periodGroups = computed(() => {
|
||||
const suffix = periodTab.value === 'threeYears' ? '_3y' : '_5y';
|
||||
const groups = [];
|
||||
const labor = result.value.labor_disputes;
|
||||
if (labor) {
|
||||
const keys = ['labor_disputes', 'labor_relation', 'wage_claim', 'compensation', 'collective_contract', 'dispatch_contract', 'part_time', 'non_compete'];
|
||||
const labels = ['劳动争议', '劳动关系', '追索劳动报酬', '经济补偿金', '集体合同', '劳务派遣', '非全日制用工', '竞业限制'];
|
||||
const rows = keys.map((k, i) => ({ label: labels[i], value: labor[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
|
||||
if (rows.length) groups.push({ key: 'labor', title: '劳动争议', rows });
|
||||
}
|
||||
const social = result.value.social_insurance;
|
||||
if (social) {
|
||||
const keys = ['pension', 'injury_insurance', 'medical_insurance', 'maternity_insurance', 'commercial_insurance'];
|
||||
const labels = ['养老保险', '工伤保险', '医疗保险', '生育保险', '商业保险'];
|
||||
const rows = keys.map((k, i) => ({ label: labels[i], value: social[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
|
||||
if (rows.length) groups.push({ key: 'social', title: '社会保险', rows });
|
||||
}
|
||||
const personnel = result.value.personnel_disputes;
|
||||
if (personnel) {
|
||||
const keys = ['resignation_dispute', 'dismissal_dispute', 'employment_contract'];
|
||||
const labels = ['辞职争议', '辞退争议', '聘用合同'];
|
||||
const rows = keys.map((k, i) => ({ label: labels[i], value: personnel[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
|
||||
if (rows.length) groups.push({ key: 'personnel', title: '人事争议', rows });
|
||||
}
|
||||
const arb = result.value.arbitration;
|
||||
if (arb) {
|
||||
const rows = [];
|
||||
if (arb[`arbitration_confirmation${suffix}`] === 2) rows.push({ label: '申请仲裁确认', value: 2 });
|
||||
if (arb[`arbitration_revocation${suffix}`] === 2) rows.push({ label: '撤销仲裁裁决', value: 2 });
|
||||
if (rows.length) groups.push({ key: 'arbitration', title: '仲裁流程', rows });
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
|
||||
const periodRows = computed(() => periodGroups.value.flatMap((g) => g.rows));
|
||||
|
||||
// 用于 riskScore:需要近三年+近五年全部数据
|
||||
const recentThreeYearsRows = computed(() => {
|
||||
const r = result.value;
|
||||
const rows = [];
|
||||
const labor = r.labor_disputes;
|
||||
if (labor) {
|
||||
[['labor_disputes_3y'], ['labor_relation_3y'], ['wage_claim_3y'], ['compensation_3y'], ['collective_contract_3y'], ['dispatch_contract_3y'], ['part_time_3y'], ['non_compete_3y']].forEach(([k]) => { if (labor[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const social = r.social_insurance;
|
||||
if (social) {
|
||||
['pension_3y', 'injury_insurance_3y', 'medical_insurance_3y', 'maternity_insurance_3y', 'commercial_insurance_3y'].forEach((k) => { if (social[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const personnel = r.personnel_disputes;
|
||||
if (personnel) {
|
||||
['resignation_dispute_3y', 'dismissal_dispute_3y', 'employment_contract_3y'].forEach((k) => { if (personnel[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const arb = r.arbitration;
|
||||
if (arb) {
|
||||
if (arb.arbitration_confirmation_3y === 2) rows.push({ value: 2 });
|
||||
if (arb.arbitration_revocation_3y === 2) rows.push({ value: 2 });
|
||||
}
|
||||
return rows;
|
||||
});
|
||||
const recentFiveYearsRows = computed(() => {
|
||||
const r = result.value;
|
||||
const rows = [];
|
||||
const labor = r.labor_disputes;
|
||||
if (labor) {
|
||||
[['labor_disputes_5y'], ['labor_relation_5y'], ['wage_claim_5y'], ['compensation_5y'], ['collective_contract_5y'], ['dispatch_contract_5y'], ['part_time_5y'], ['non_compete_5y']].forEach(([k]) => { if (labor[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const social = r.social_insurance;
|
||||
if (social) {
|
||||
['pension_5y', 'injury_insurance_5y', 'medical_insurance_5y', 'maternity_insurance_5y', 'commercial_insurance_5y'].forEach((k) => { if (social[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const personnel = r.personnel_disputes;
|
||||
if (personnel) {
|
||||
['resignation_dispute_5y', 'dismissal_dispute_5y', 'employment_contract_5y'].forEach((k) => { if (personnel[k] === 2) rows.push({ value: 2 }); });
|
||||
}
|
||||
const arb = r.arbitration;
|
||||
if (arb) {
|
||||
if (arb.arbitration_confirmation_5y === 2) rows.push({ value: 2 });
|
||||
if (arb.arbitration_revocation_5y === 2) rows.push({ value: 2 });
|
||||
}
|
||||
return rows;
|
||||
});
|
||||
|
||||
// 头部风险总结:风险类型、建议、命中统计(仅真正的风险项)
|
||||
const riskSummary = computed(() => {
|
||||
const basic = result.value.basic_info;
|
||||
const hasRisk = basic?.risk_flag === 2;
|
||||
|
||||
// 真正的风险项:总项数、命中数(文档:失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程、通知函触达)
|
||||
const totalItems = riskItemRows.value.length;
|
||||
const hitItems = riskItemRows.value.filter((r) => r.value === 2).length;
|
||||
|
||||
// 命中的风险分类(汇总中 value=2 的 group)
|
||||
const riskCategories = summaryGroups.value
|
||||
.filter((g) => g.key !== 'basic' && g.rows.some((r) => r.value === 2))
|
||||
.map((g) => g.title);
|
||||
|
||||
// 精简建议(取主要类型合并为一句)
|
||||
const suggestionMap = {
|
||||
'失信限高': '征信修复与限高事项',
|
||||
'劳动争议': '劳动纠纷与薪酬离职',
|
||||
'社会保险': '社保缴纳与补缴',
|
||||
'福利待遇': '福利待遇合规',
|
||||
'人事争议': '辞职辞退与聘用合同',
|
||||
'仲裁流程': '仲裁案件进展',
|
||||
'通知函触达': '仲裁调解涉诉通知',
|
||||
};
|
||||
const suggestionParts = riskCategories.map((c) => suggestionMap[c]).filter(Boolean);
|
||||
const suggestion = suggestionParts.length ? `建议关注${suggestionParts.slice(0, 3).join('、')}。` : '';
|
||||
|
||||
return {
|
||||
hasRisk,
|
||||
label: hasRisk ? '有风险' : '无风险',
|
||||
riskCategories,
|
||||
suggestion,
|
||||
totalItems,
|
||||
hitItems,
|
||||
};
|
||||
});
|
||||
|
||||
// 风险评分 0-100,越高越安全(供 BaseReport 分析指数)
|
||||
// 基于真正的风险项(riskItemRows),与展示逻辑一致,通过 useRiskNotifier 递交 BaseReport
|
||||
const riskScore = computed(() => {
|
||||
const basic = result.value.basic_info;
|
||||
if (!basic || basic.risk_flag === 1) return 100;
|
||||
const hitItems = riskItemRows.value.filter((r) => r.value === 2).length;
|
||||
const score = 100 - hitItems * 2; // 每命中 1 项扣 2 分
|
||||
return Math.max(0, Math.min(100, score));
|
||||
});
|
||||
useRiskNotifier(props, riskScore);
|
||||
defineExpose({ riskScore });
|
||||
|
||||
// 借鉴司法涉诉概览:风险图标与背景样式
|
||||
const getRiskIcon = () => {
|
||||
if (riskSummary.value.hasRisk) return new URL('@/assets/images/report/gfx.png', import.meta.url).href;
|
||||
return new URL('@/assets/images/report/zq.png', import.meta.url).href;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="report-wrap">
|
||||
<!-- 头部风险总结(底色固定白色,命中项用内嵌 card 展现) -->
|
||||
<div v-if="hasAnyData" class="risk-summary card">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-12 h-12 mr-3 flex-shrink-0">
|
||||
<img :src="getRiskIcon()" alt="风险" class="w-12 h-12 object-contain" />
|
||||
</div>
|
||||
<div class="text-gray-700 text-[15px] leading-relaxed">
|
||||
<template v-if="riskSummary.hasRisk">
|
||||
<span v-if="riskSummary.riskCategories.length">涉及{{ riskSummary.riskCategories.join('、')
|
||||
}};</span>
|
||||
<span v-if="riskSummary.suggestion">{{ riskSummary.suggestion }}</span>
|
||||
</template>
|
||||
<template v-else>未检测到相关风险</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 命中项:仅显示总项数 / 命中数(真正的风险项:失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程、通知函触达) -->
|
||||
<div v-if="riskSummary.totalItems > 0" class="inner-card p-4 rounded-xl text-center"
|
||||
:class="riskSummary.hitItems > 0 ? 'inner-card-risk' : 'inner-card-safe'">
|
||||
<div class="text-2xl font-bold mb-1"
|
||||
:class="riskSummary.hitItems > 0 ? 'text-[#EB3C3C]' : 'text-[#10b981]'">
|
||||
{{ riskSummary.hitItems }}/{{ riskSummary.totalItems }}
|
||||
</div>
|
||||
<div class="text-sm font-medium text-gray-800">命中项</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 1: 汇总 -->
|
||||
<section class="card">
|
||||
<header class="card-header">
|
||||
<span class="card-title">风险概览</span>
|
||||
<span class="card-subtitle">综合评估结果</span>
|
||||
</header>
|
||||
<div v-if="hasAnyData" class="group-list">
|
||||
<div v-for="(group, gi) in summaryGroups" :key="group.key" class="group-box">
|
||||
<div class="group-header">
|
||||
<span class="group-title">{{ group.title }}</span>
|
||||
</div>
|
||||
<div class="group-body">
|
||||
<div v-for="(row, ri) in group.rows" :key="ri" class="data-row">
|
||||
<span class="data-label">{{ row.label }}</span>
|
||||
<span v-if="row.period !== undefined" class="data-value data-value-text">{{
|
||||
getNoticeLetterPeriodText(row.period) }}</span>
|
||||
<span v-else
|
||||
:class="['data-value', 'data-badge', row.value === 2 ? 'badge-risk' : 'badge-safe']">
|
||||
<span class="badge-dot" :class="row.value === 2 ? 'dot-risk' : 'dot-safe'" />
|
||||
{{ getStatusText(row.value) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-state">
|
||||
<span class="empty-icon">—</span>
|
||||
<span class="empty-text">暂无相关风险数据</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Card 2: 近三年 / 近五年 -->
|
||||
<section class="card">
|
||||
<header class="card-header">
|
||||
<span class="card-title">时间维度</span>
|
||||
<span class="card-subtitle">按周期查看命中情况</span>
|
||||
</header>
|
||||
<div class="period-tabs">
|
||||
<button type="button" :class="['period-tab', periodTab === 'threeYears' && 'active']"
|
||||
@click="periodTab = 'threeYears'">近三年</button>
|
||||
<button type="button" :class="['period-tab', periodTab === 'fiveYears' && 'active']"
|
||||
@click="periodTab = 'fiveYears'">近五年</button>
|
||||
</div>
|
||||
<div v-if="periodGroups.length" class="group-list">
|
||||
<div v-for="(group, gi) in periodGroups" :key="group.key" class="group-box">
|
||||
<div class="group-header">
|
||||
<span class="group-title">{{ group.title }}</span>
|
||||
</div>
|
||||
<div class="group-body">
|
||||
<div v-for="(row, ri) in group.rows" :key="ri" class="data-row">
|
||||
<span class="data-label">{{ row.label }}</span>
|
||||
<span class="data-value data-badge badge-risk">
|
||||
<span class="badge-dot dot-risk" />
|
||||
{{ getStatusText(row.value) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-state">
|
||||
<span class="empty-icon">—</span>
|
||||
<span class="empty-text">暂无{{ periodTab === 'threeYears' ? '近三年' : '近五年' }}命中数据</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.report-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.inner-card {
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.inner-card-risk {
|
||||
background: rgba(235, 60, 60, 0.1);
|
||||
border-color: rgba(235, 60, 60, 0.3);
|
||||
}
|
||||
|
||||
.inner-card-safe {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.period-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
padding: 4px;
|
||||
background: #f8fafc;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.period-tab {
|
||||
flex: 1;
|
||||
padding: 10px 16px;
|
||||
font-size: 15px;
|
||||
color: #64748b;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.period-tab.active {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.group-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.group-box {
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 12px 16px;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.group-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.data-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.data-row:not(:last-child) {
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
color: #475569;
|
||||
flex: 1;
|
||||
margin-right: 12px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.data-value-text {
|
||||
color: #64748b;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.data-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.badge-risk {
|
||||
background: #fff1f2;
|
||||
color: #be123c;
|
||||
}
|
||||
|
||||
.dot-risk {
|
||||
background: #e11d48;
|
||||
}
|
||||
|
||||
.badge-safe {
|
||||
background: #f0fdf4;
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.dot-safe {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 28px 16px;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 20px;
|
||||
color: #cbd5e1;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 15px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user