add flxg7e8f

This commit is contained in:
2025-12-29 19:45:55 +08:00
parent c0fc989c8f
commit ae247daf44
4 changed files with 1449 additions and 0 deletions

View File

@@ -0,0 +1,404 @@
<template>
<div class="px-4 pb-4">
<div class="grid grid-cols-[max-content_1fr] gap-x-2 gap-y-3">
<!-- 经办法院/执行法院 -->
<span class="text-base text-[#666666]">{{ isSpecialCase ? '执行法院' : '经办法院' }}</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_jbfy || caseData.executiveCourt || "—" }}</span>
<!-- 所属地域 -->
<span class="text-base text-[#666666]">所属地域</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_ssdy || caseData.province || "—" }}</span>
<!-- 案件类型 -->
<template v-if="caseData.n_ajlx">
<span class="text-base text-[#666666]">案件类型</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_ajlx || "—" }}</span>
</template>
<!-- 案号 -->
<template v-if="caseData.c_ah">
<span class="text-base text-[#666666]">案号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_ah || "—" }}</span>
</template>
<!-- 原审案号 -->
<template v-if="caseData.c_ah_ys">
<span class="text-base text-[#666666]">原审案号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_ah_ys || "—" }}</span>
</template>
<!-- 后续案号 -->
<template v-if="caseData.c_ah_hx">
<span class="text-base text-[#666666]">后续案号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_ah_hx || "—" }}</span>
</template>
<!-- 立案时间 -->
<template v-if="caseData.d_larq || caseData.fileDate || caseData.larq">
<span class="text-base text-[#666666]">立案时间</span>
<span class="text-base font-medium text-[#333333]">{{ formatDate(caseData.d_larq || caseData.fileDate || caseData.larq) }}</span>
</template>
<!-- 立案案由 -->
<template v-if="caseData.n_laay_tree || caseData.n_laay">
<span class="text-base text-[#666666]">立案案由</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_laay_tree || caseData.n_laay || "暂无" }}</span>
</template>
<!-- 立案案由标签 -->
<template v-if="caseData.n_laay_tag">
<span class="text-base text-[#666666]">立案案由标签</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_laay_tag || "—" }}</span>
</template>
<!-- 当事人信息 -->
<template v-if="caseData.c_dsrxx && caseData.c_dsrxx.length > 0">
<span class="text-base text-[#666666]">当事人信息</span>
<span class="text-base font-medium text-[#333333]">
<span v-for="(party, partyIndex) in caseData.c_dsrxx" :key="partyIndex">
{{ party.n_ssdw || "其他" }}: {{ party.c_mc }}<span v-if="partyIndex < caseData.c_dsrxx.length - 1">; </span>
</span>
</span>
</template>
<!-- 失信被执行人特有字段 -->
<template v-if="caseType === 'breachCase'">
<!-- 案号 -->
<template v-if="caseData.caseNumber">
<span class="text-base text-[#666666]">案号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.caseNumber || "—" }}</span>
</template>
<!-- 发布日期 -->
<template v-if="caseData.issueDate">
<span class="text-base text-[#666666]">发布日期</span>
<span class="text-base font-medium text-[#333333]">{{ formatDate(caseData.issueDate) }}</span>
</template>
<!-- 性别 -->
<template v-if="caseData.sex">
<span class="text-base text-[#666666]">性别</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.sex || "—" }}</span>
</template>
<!-- 履行情况 -->
<template v-if="caseData.fulfillStatus">
<span class="text-base text-[#666666]">履行情况</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.fulfillStatus || "—" }}</span>
</template>
<!-- 判决金额估计 -->
<template v-if="caseData.estimatedJudgementAmount">
<span class="text-base text-[#666666]">判决金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.estimatedJudgementAmount) || "—" }}</span>
</template>
<!-- 失信被执行人行为具体情形 -->
<template v-if="caseData.concreteDetails">
<span class="text-base text-[#666666]">行为具体情形</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.concreteDetails || "—" }}</span>
</template>
<!-- 生效法律文书确定的义务 -->
<template v-if="caseData.obligation">
<span class="text-base text-[#666666]">生效法律文书确定的义务</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.obligation || "—" }}</span>
</template>
<!-- 执行依据单位 -->
<template v-if="caseData.enforcementBasisOrganization">
<span class="text-base text-[#666666]">执行依据单位</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.enforcementBasisOrganization || "—" }}</span>
</template>
<!-- 执行依据文号 -->
<template v-if="caseData.enforcementBasisNumber">
<span class="text-base text-[#666666]">执行依据文号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.enforcementBasisNumber || "—" }}</span>
</template>
</template>
<!-- 限制消费被执行人特有字段 -->
<template v-if="caseType === 'consumptionRestriction'">
<!-- 案件编号 -->
<template v-if="caseData.caseNumber">
<span class="text-base text-[#666666]">案件编号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.caseNumber || "—" }}</span>
</template>
<!-- 发布日期 -->
<template v-if="caseData.issueDate">
<span class="text-base text-[#666666]">发布日期</span>
<span class="text-base font-medium text-[#333333]">{{ formatDate(caseData.issueDate) }}</span>
</template>
<!-- 立案时间 -->
<template v-if="caseData.fileDate">
<span class="text-base text-[#666666]">立案时间</span>
<span class="text-base font-medium text-[#333333]">{{ formatDate(caseData.fileDate) }}</span>
</template>
</template>
<!-- 刑事案件特有字段 -->
<template v-if="caseType === 'criminal'">
<!-- 定罪罪名 -->
<template v-if="caseData.n_dzzm">
<span class="text-base text-[#666666]">定罪罪名</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_dzzm || "—" }}</span>
</template>
<!-- 定罪罪名详细 -->
<template v-if="caseData.n_dzzm_tree">
<span class="text-base text-[#666666]">定罪罪名详细</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_dzzm_tree || "—" }}</span>
</template>
<!-- 判处结果 -->
<template v-if="caseData.n_pcjg">
<span class="text-base text-[#666666]">判处结果</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_pcjg || "—" }}</span>
</template>
<!-- 犯罪金额 -->
<template v-if="caseData.n_fzje">
<span class="text-base text-[#666666]">犯罪金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_fzje) || "—" }}</span>
</template>
<!-- 被请求赔偿金额 -->
<template v-if="caseData.n_bqqpcje">
<span class="text-base text-[#666666]">被请求赔偿金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_bqqpcje) || "—" }}</span>
</template>
<!-- 财产刑执行金额 -->
<template v-if="caseData.n_ccxzxje">
<span class="text-base text-[#666666]">财产刑执行金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_ccxzxje) || "—" }}</span>
</template>
<!-- 财产刑执行金额估计 -->
<template v-if="caseData.n_ccxzxje_gj">
<span class="text-base text-[#666666]">财产刑执行金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_ccxzxje_gj) || "—" }}</span>
</template>
<!-- 判处赔偿金额 -->
<template v-if="caseData.n_pcpcje">
<span class="text-base text-[#666666]">判处赔偿金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_pcpcje) || "—" }}</span>
</template>
<!-- 判处赔偿金额估计 -->
<template v-if="caseData.n_pcpcje_gj">
<span class="text-base text-[#666666]">判处赔偿金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_pcpcje_gj) || "—" }}</span>
</template>
<!-- 被请求赔偿金额估计 -->
<template v-if="caseData.n_bqqpcje_gj">
<span class="text-base text-[#666666]">被请求赔偿金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_bqqpcje_gj) || "—" }}</span>
</template>
</template>
<!-- 执行案件特有字段 -->
<template v-if="caseType === 'implement'">
<!-- 申请执行标的金额 -->
<template v-if="caseData.n_sqzxbdje">
<span class="text-base text-[#666666]">申请执行标的金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_sqzxbdje) || "—" }}</span>
</template>
<!-- 实际到位金额 -->
<template v-if="caseData.n_sjdwje !== undefined">
<span class="text-base text-[#666666]">实际到位金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_sjdwje) || "—" }}</span>
</template>
<!-- 未执行金额 -->
<template v-if="caseData.n_wzxje !== undefined">
<span class="text-base text-[#666666]">未执行金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_wzxje) || "—" }}</span>
</template>
<!-- 相关案件号 -->
<template v-if="caseData.c_gkws_glah">
<span class="text-base text-[#666666]">相关案件号</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_gkws_glah || "—" }}</span>
</template>
</template>
<!-- 非诉保全审查案件特有字段 -->
<template v-if="caseType === 'preservation'">
<!-- 申请保全数额 -->
<template v-if="caseData.n_sqbqse">
<span class="text-base text-[#666666]">申请保全数额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_sqbqse) || "—" }}</span>
</template>
<!-- 申请保全标的物 -->
<template v-if="caseData.c_sqbqbdw">
<span class="text-base text-[#666666]">申请保全标的物</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_sqbqbdw || "—" }}</span>
</template>
</template>
<!-- 案件通用字段 -->
<!-- 诉讼地位 -->
<template v-if="caseData.n_ssdw">
<span class="text-base text-[#666666]">诉讼地位</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_ssdw || "—" }}</span>
</template>
<!-- 一审诉讼地位 -->
<template v-if="caseData.n_ssdw_ys">
<span class="text-base text-[#666666]">一审诉讼地位</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_ssdw_ys || "—" }}</span>
</template>
<!-- 案件进展阶段 -->
<template v-if="caseData.n_ajjzjd">
<span class="text-base text-[#666666]">案件进展阶段</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_ajjzjd || "—" }}</span>
</template>
<!-- 审理程序 -->
<template v-if="caseData.n_slcx">
<span class="text-base text-[#666666]">审理程序</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_slcx || "—" }}</span>
</template>
<!-- 法院所属层级 -->
<template v-if="caseData.n_jbfy_cj">
<span class="text-base text-[#666666]">法院所属层级</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_jbfy_cj || "—" }}</span>
</template>
<!-- 起诉标的金额 -->
<template v-if="caseData.n_qsbdje">
<span class="text-base text-[#666666]">起诉标的金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_qsbdje) || "—" }}</span>
</template>
<!-- 起诉标的金额估计 -->
<template v-if="caseData.n_qsbdje_gj">
<span class="text-base text-[#666666]">起诉标的金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_qsbdje_gj) || "—" }}</span>
</template>
<!-- 结案标的金额 -->
<template v-if="caseData.n_jabdje">
<span class="text-base text-[#666666]">结案标的金额</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_jabdje) || "—" }}</span>
</template>
<!-- 结案标的金额估计 -->
<template v-if="caseData.n_jabdje_gj">
<span class="text-base text-[#666666]">结案标的金额估计</span>
<span class="text-base font-medium text-[#333333]">{{ formatLawsuitMoney(caseData.n_jabdje_gj) || "—" }}</span>
</template>
<!-- 结案案由 -->
<template v-if="caseData.n_jaay_tree || caseData.n_jaay">
<span class="text-base text-[#666666]">结案案由</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_jaay_tree || caseData.n_jaay || "—" }}</span>
</template>
<!-- 结案案由标签 -->
<template v-if="caseData.n_jaay_tag">
<span class="text-base text-[#666666]">结案案由标签</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_jaay_tag || "—" }}</span>
</template>
<!-- 结案方式 -->
<template v-if="caseData.n_jafs">
<span class="text-base text-[#666666]">结案方式</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_jafs || "—" }}</span>
</template>
<!-- 结案时间 -->
<template v-if="caseData.d_jarq">
<span class="text-base text-[#666666]">结案时间</span>
<span class="text-base font-medium text-[#333333]">{{ formatDate(caseData.d_jarq) }}</span>
</template>
<!-- 胜诉估计 -->
<template v-if="caseData.n_pj_victory">
<span class="text-base text-[#666666]">胜诉估计</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.n_pj_victory || "—" }}</span>
</template>
<!-- 公开文书ID -->
<template v-if="caseData.c_gkws_id">
<span class="text-base text-[#666666]">公开文书ID</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_gkws_id || "—" }}</span>
</template>
<!-- 相关当事人 -->
<template v-if="caseData.c_gkws_dsr">
<span class="text-base text-[#666666]">相关当事人</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_gkws_dsr || "—" }}</span>
</template>
<!-- 判决结果 -->
<template v-if="caseData.c_gkws_pjjg">
<span class="text-base text-[#666666]">判决结果</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_gkws_pjjg || "—" }}</span>
</template>
<!-- 审理方式信息 -->
<template v-if="caseData.c_slfsxx">
<span class="text-base text-[#666666]">审理方式信息</span>
<span class="text-base font-medium text-[#333333]">{{ caseData.c_slfsxx || "—" }}</span>
</template>
<!-- 后续案件信息 -->
<template v-if="caseData.next">
<span class="text-base text-[#666666]">后续案件</span>
<span class="text-base font-medium text-[#333333]">
{{ caseData.next.c_ah }}
<span v-if="caseData.next.stage_type" class="ml-2 text-sm px-2 py-0.5 rounded bg-[#EB3C3C1A] text-[#EB3C3C]">
{{
caseData.next.stage_type === 2
? "二审"
: caseData.next.stage_type === 3
? "再审"
: caseData.next.stage_type === 4
? "申请再审"
: caseData.next.stage_type === 5
? "执行"
: "其他"
}}
</span>
</span>
</template>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { formatDate, formatLawsuitMoney } from '../utils/lawsuitUtils.js'
const props = defineProps({
caseData: {
type: Object,
required: true
},
typeColor: {
type: String,
default: 'text-blue-600 bg-blue-50'
},
caseType: {
type: String,
required: true
}
})
// 判断是否为特殊案件类型(失信被执行人、限高被执行人)
const isSpecialCase = computed(() => {
return props.caseType === 'breachCase' || props.caseType === 'consumptionRestriction'
})
</script>

View File

@@ -0,0 +1,285 @@
<template>
<div class="">
<!-- 概览标题 -->
<div class="p-4">
<!-- 添加风险概览总结 -->
<div class="p-4 rounded-lg" :class="getRiskOverviewClass()">
<div class="flex items-center">
<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">
{{ totalCases }}
起涉诉案件中
<span v-if="stats.highRiskItems > 0" class="text-orange-600 font-medium">
{{ stats.highRiskItems }}
</span>
<span v-else class="text-green-600 font-medium">0</span>
起高风险案件
<span v-if="stats.caseTypes.length > 0" class="ml-1">
涉及 {{ stats.caseTypes.length }} 种案件类型
</span>
</div>
</div>
</div>
</div>
<!-- 主要风险指标 -->
<div class="grid grid-cols-2 gap-3 p-4">
<!-- 风险事项卡片 -->
<div class="p-4 bg-[#EB3C3C1A] border border-[#EB3C3C4D] rounded-xl text-center">
<div class="text-2xl font-bold text-[#EB3C3C] mb-1">{{ stats.totalRiskItems || 0 }}</div>
<div class="text-sm font-medium text-gray-800 mb-1">风险事项</div>
<div class="text-sm text-gray-500">
平均{{ stats.totalRiskItems && totalCases > 0 ?
(stats.totalRiskItems / totalCases).toFixed(1) :
'0.0'
}}/案件
</div>
</div>
<!-- 高风险案件卡片 -->
<div class="p-4 bg-[#EB3C3C1A] border border-[#EB3C3C4D] rounded-xl text-center">
<div class="text-2xl font-bold text-[#EB3C3C] mb-1">{{ stats.highRiskItems || 0 }}</div>
<div class="text-sm font-medium text-gray-800 mb-1">高风险案件</div>
<div class="text-sm text-gray-500 mb-1">
占比{{ totalCases > 0 && stats ?
((stats.highRiskItems /
totalCases) * 100).toFixed(1) : '0.0' }}%
</div>
<div class="text-sm text-orange-600">
<span class="mr-3">失信{{ stats.breachCaseCount || 0 }}</span>
<span style="color: #D6943E;">限高{{ stats.consumptionRestrictionCount || 0 }}</span>
</div>
</div>
<!-- 已结案件卡片 -->
<div class="p-4 bg-[#2B79EE1A] border border-[#2B79EE4D] rounded-xl text-center">
<div class="text-2xl font-bold text-[#2B79EE] mb-1">{{ stats.closedCases || 0 }}</div>
<div class="text-sm font-medium text-gray-800 mb-1">已结案件</div>
<div class="text-sm text-gray-500">
占比{{ totalCases > 0 && stats ?
Math.round((stats.closedCases / totalCases) * 100) :
0
}}%
</div>
</div>
<!-- 案件类型卡片 -->
<div class="p-4 bg-[#2B79EE1A] border border-[#2B79EE4D] rounded-xl text-center">
<div class="text-2xl font-bold text-[#2B79EE] mb-1">{{ stats.caseTypes.length || 0 }}</div>
<div class="text-sm font-medium text-gray-800 mb-1">案件类型</div>
<div class="text-sm text-gray-500">
涉及多种类型
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<!-- 案件类型分布 -->
<LTitle title="案件类型分布" />
<div class="h-[300px] px-4">
<v-chart :option="caseTypeChartOption" autoresize />
</div>
<!-- 风险等级分布 -->
<LTitle title="风险等级分布" />
<div class="bg-[#F9ECEC] border border-[#F0CACA] rounded-xl mx-4 p-4">
<div class="grid grid-cols-3 gap-4 text-center">
<!-- 高风险案件 -->
<div>
<div class="text-sm text-gray-600 mb-1">高风险案件</div>
<div class="text-xl font-bold text-[#EB3C3C]">{{ stats.highRiskItems || 0 }}</div>
</div>
<!-- 中风险案件 -->
<div>
<div class="text-sm text-gray-600 mb-1">中风险案件</div>
<div class="text-xl font-bold text-[#EB3C3C]">{{ stats.mediumRiskItems || 0 }}</div>
</div>
<!-- 低风险案件 -->
<div>
<div class="text-sm text-gray-600 mb-1">低风险案件</div>
<div class="text-xl font-bold text-[#D6943E]">{{ stats.lowRiskItems || 0 }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent, TitleComponent, LegendComponent } from 'echarts/components'
import VChart from 'vue-echarts'
import LTitle from '@/components/LTitle.vue'
import { lawsuitTypeMap, getCaseTypeText, getCaseTypeDarkColor } from '../utils/lawsuitUtils.js'
// 注册必须的组件
use([CanvasRenderer, BarChart, GridComponent, TooltipComponent, TitleComponent, LegendComponent])
const props = defineProps({
stats: {
type: Object,
required: true,
},
totalCases: {
type: Number,
required: true,
},
})
// 案件类型分布横向柱状图配置
const caseTypeChartOption = computed(() => {
// 获取所有可能的案件类型,确保即使没有数据的类型也会显示
const allCaseTypes = Object.keys(lawsuitTypeMap).map(key => ({
type: key,
name: lawsuitTypeMap[key].text,
color: lawsuitTypeMap[key].color,
darkColor: lawsuitTypeMap[key].darkColor,
count: 0, // 默认为0
}))
// 如果有统计数据,更新数量
if (props.stats && props.stats.caseTypes && props.stats.caseTypes.length > 0) {
// 用实际数据更新默认值
props.stats.caseTypes.forEach(item => {
const existingType = allCaseTypes.find(type => type.type === item.type)
if (existingType) {
existingType.count = item.count
}
})
} else {
// 如果没有任何数据,显示暂无数据的信息
return {
title: {
text: '暂无数据',
left: 'center',
top: 'center',
textStyle: {
fontSize: 14,
fontWeight: 'normal',
color: '#aaa',
},
},
}
}
// 准备横向柱状图数据 - 不过滤,显示所有类型
const categories = allCaseTypes.map(item => item.name)
const values = allCaseTypes.map(item => item.count)
return {
tooltip: {
trigger: 'axis',
formatter: function (params) {
const dataIndex = params[0].dataIndex
return `${categories[dataIndex]}: ${values[dataIndex]}`
},
},
grid: {
left: '0%',
right: '5%',
bottom: '5%',
top: '5%',
containLabel: true,
},
xAxis: {
type: 'value',
min: 0,
max: function (value) {
// 如果最大值是0设置一个最小值让柱子能显示
return Math.max(value.max, 1)
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#f0f0f0',
},
},
axisLabel: {
fontSize: 12,
color: '#666',
},
axisLine: {
lineStyle: {
color: '#ddd',
},
},
},
yAxis: {
type: 'category',
data: categories,
axisLabel: {
fontSize: 12,
color: '#666',
},
axisLine: {
lineStyle: {
color: '#ddd',
},
},
axisTick: {
show: false,
},
},
series: [
{
name: '案件数量',
type: 'bar',
barWidth: '30%',
data: values.map((value) => {
return {
value: value === 0 ? 0.1 : value, // 0值显示为0.1,让柱子能显示一个小尖尖
itemStyle: {
color: '#5d7eeb',
borderRadius: [0, 4, 4, 0],
},
}
}),
label: {
show: true,
position: 'right',
fontSize: 12,
color: '#666',
formatter: function (params) {
// 如果是0.1实际为0显示为0
return params.value === 0.1 ? '0' : params.value
},
},
},
],
}
})
// 获取风险概览样式
const getRiskOverviewClass = () => {
// 有高风险案件 - 红色警告
if (props.stats.highRiskItems > 0) {
return 'bg-[#F9ECEC] border border-[#F0CACA]'
}
// 有案件但无高风险 - 黄色警示
if (props.totalCases > 0) {
return 'bg-[#FFF8E1] border border-[#FFE082]'
}
// 无案件 - 绿色正常
return 'bg-[#ECF9EF] border border-[#CAECD3]'
}
// 获取风险图标
const getRiskIcon = () => {
// 有高风险案件 - 高风险图标
if (props.stats.highRiskItems > 0) {
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
}
// 有案件但无高风险 - 中风险图标
if (props.totalCases > 0) {
return new URL('@/assets/images/report/zfx.png', import.meta.url).href
}
// 无案件 - 正常图标
return new URL('@/assets/images/report/zq.png', import.meta.url).href
}
</script>

View File

@@ -0,0 +1,457 @@
<template>
<div class="card shadow-sm rounded-xl overflow-hidden p-4">
<div class="border border-[#EEEEEE] rounded-xl">
<!-- 标题 -->
<div class="flex items-center mb-3 p-4">
<div class="w-8 h-8 flex items-center justify-center mr-2">
<img src="@/assets/images/report/ssfxfx.png" alt="个人涉诉风险" class="w-8 h-8 object-contain" />
</div>
<span class="font-bold text-gray-800">个人涉诉风险分析</span>
</div>
<LTitle title="涉诉风险整体概览" />
<!-- 全局风险概览面板 -->
<StatisticsOverview class="" v-if="totalCases > 0 && lawsuitStats" :stats="lawsuitStats"
:total-cases="totalCases" />
<!-- 案件类型筛选tab -->
<div v-if="totalCases > 0" class="p-4">
<van-tabs v-model:active="activeCaseTypeFilter" line-width="30px" swipeable class="lawsuit-tabs">
<!-- 全部风险 -->
<van-tab name="all">
<template #title>
<div class="flex items-center gap-1">
<span>全部风险</span>
<span>({{ caseTypeCounts.all }})</span>
</div>
</template>
</van-tab>
<!-- 各类型案件 - 使用v-for渲染 -->
<van-tab v-for="(typeInfo, type) in lawsuitTypeMap" :key="type" :name="type">
<template #title>
<div class="flex items-center gap-1">
<span>{{ typeInfo.text }}({{ caseTypeCounts[type] || 0 }})</span>
</div>
</template>
<div v-if="filteredCases.length === 0" class="p-8 text-center text-gray-500">
<div class="flex flex-col items-center justify-center">
<van-empty :description="`暂无${typeInfo.text}记录`" />
</div>
</div>
</van-tab>
</van-tabs>
</div>
<!-- 案件列表 -->
<div v-if="filteredCases.length > 0" class="space-y-3 px-4 mb-4">
<div v-for="(caseItem, index) in filteredCases" :key="index" class="case-wrapper">
<!-- 案件卡片 - 可点击展开 -->
<div class="bg-white rounded-xl overflow-hidden border px-4 pt-3 border-[#DDDDDD]">
<div class="cursor-pointer relative" @click="toggleCaseExpand(caseItem.id || index, 'case', index)">
<!-- 顶部区域案件标题和案件类型 -->
<div class=" flex items-center">
<!-- 案件标题 -->
<div class="font-bold text-base text-[#333333] mr-2">{{ caseItem.c_ah || caseItem.caseNumber || '暂无案号' }}</div>
<!-- 案件类型标签 -->
<span class="px-2 py-1 text-sm rounded-md font-medium bg-[#F9ECEC] text-[#EB3C3C]">
{{ getCaseTypeText(caseItem.type) }}
</span>
</div>
<!-- 中间区域立案时间 -->
<div class="pb-2">
<span class="text-sm text-[#666666]">立案</span>
<span class="text-sm text-[#333333]">{{ formatDate(caseItem.d_larq || caseItem.fileDate) }}</span>
</div>
<!-- 底部区域风险等级和案件状态 -->
<div class="flex items-center gap-2">
<!-- 风险等级标签 -->
<span class="px-2 py-1 text-sm rounded-md font-medium"
:class="getCaseTypeRiskLevel(caseItem.type).color">
{{ getCaseTypeRiskLevel(caseItem.type).text }}
</span>
<!-- 案件状态标签 -->
<span v-if="caseItem.n_ajjzjd" class="px-2 py-1 text-sm rounded-md font-medium"
:class="getCaseStatusClass(caseItem.n_ajjzjd)">
{{ caseItem.n_ajjzjd }}
</span>
</div>
<!-- 展开指示器 -->
<div class="absolute right-4 bottom-3 flex items-center text-sm text-gray-500">
<img src="@/assets/images/report/zk.png" alt="展开" class="w-4 h-4 container"
:class="{ 'rotate-180': isCaseExpanded(caseItem.id || index, 'case', index) }" />
</div>
</div>
<!-- 案件详情抽屉 -->
<div class="mt-4 overflow-hidden transition-all duration-300 ease-in-out" :class="{
'max-h-0 opacity-0': !isCaseExpanded(caseItem.id || index, 'case', index),
'max-h-none opacity-100': isCaseExpanded(caseItem.id || index, 'case', index),
}">
<div class="mt-1 transform transition-all duration-300">
<div class="relative">
<CaseDetail :case-data="caseItem" :type-color="getCaseTypeColor(caseItem.type)"
:case-type="caseItem.type" />
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 无涉诉风险时的空状态展示 -->
<div v-else class="text-gray-500 py-10 text-center bg-gray-50 rounded-lg mx-4 mb-4">
<div class="text-gray-300 text-3xl mb-2"></div>
暂无涉诉风险记录
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import LTitle from '@/components/LTitle.vue'
import LRemark from '@/components/LRemark.vue'
import StatisticsOverview from './components/StatisticsOverview.vue'
import CaseDetail from './components/CaseDetail.vue'
import { useRiskNotifier } from '@/composables/useRiskNotifier'
import {
lawsuitTypeMap,
getCaseTypeText,
getCaseTypeColor,
getRiskLevel,
getCaseStatusClass,
formatDate,
getLawsuitStats,
getCaseTypeRiskLevel,
} from './utils/lawsuitUtils.js'
const props = defineProps({
data: {
type: Object,
required: true,
},
apiId: {
type: String,
default: '',
},
index: {
type: Number,
default: 0,
},
notifyRiskStatus: {
type: Function,
default: () => { },
},
})
// 获取 judicial_data 数据
const judicialData = computed(() => {
return props.data?.data?.judicial_data || props.data?.judicial_data || {}
})
// 获取 lawsuitStat 数据
const lawsuitStat = computed(() => {
return judicialData.value.lawsuitStat || {
administrative: {},
bankrupt: {},
cases_tree: { criminal: [], civil: [] },
civil: {},
count: {},
criminal: { cases: [], count: {} },
implement: {},
preservation: {},
}
})
// 获取失信列表
const breachCaseList = computed(() => {
return judicialData.value.breachCaseList || []
})
// 获取限制消费列表
const consumptionRestrictionList = computed(() => {
return judicialData.value.consumptionRestrictionList || []
})
// 用于跟踪展开的案件卡片
const expandedCases = ref({})
// 切换展开/收起案件详情
const toggleCaseExpand = (caseId, listType, index) => {
const uniqueKey = `${caseId}_${listType}_${index}`
expandedCases.value[uniqueKey] = !expandedCases.value[uniqueKey]
}
// 检查案件是否展开
const isCaseExpanded = (caseId, listType, index) => {
const uniqueKey = `${caseId}_${listType}_${index}`
return !!expandedCases.value[uniqueKey]
}
// 当前选中的案件类型筛选
const activeCaseTypeFilter = ref('all')
// 计算所有案件数据
const allCases = computed(() => {
const cases = []
// 添加失信被执行人案件
breachCaseList.value.forEach((item, index) => {
cases.push({
...item,
type: 'breachCase',
id: `breachCase_${index}`,
// 映射字段以保持兼容性
c_ah: item.caseNumber,
d_larq: item.fileDate,
n_ajjzjd: item.fulfillStatus === '全部未履行' ? '未结案' : '已结案',
})
})
// 添加限高被执行人案件
consumptionRestrictionList.value.forEach((item, index) => {
cases.push({
...item,
type: 'consumptionRestriction',
id: `consumptionRestriction_${index}`,
// 映射字段以保持兼容性
c_ah: item.caseNumber,
d_larq: item.fileDate,
n_ajjzjd: '未结案', // 限高案件通常为未结案
})
})
// 添加其他类型案件
if (lawsuitStat.value) {
// 处理民事案件
if (lawsuitStat.value.civil && lawsuitStat.value.civil.cases) {
lawsuitStat.value.civil.cases.forEach((item, index) => {
cases.push({
...item,
type: 'civil',
id: `civil_${index}`,
})
})
}
// 处理刑事案件
if (lawsuitStat.value.criminal && lawsuitStat.value.criminal.cases) {
lawsuitStat.value.criminal.cases.forEach((item, index) => {
cases.push({
...item,
type: 'criminal',
id: `criminal_${index}`,
})
})
}
// 处理执行案件
if (lawsuitStat.value.implement && lawsuitStat.value.implement.cases) {
lawsuitStat.value.implement.cases.forEach((item, index) => {
cases.push({
...item,
type: 'implement',
id: `implement_${index}`,
})
})
}
// 处理行政案件
if (lawsuitStat.value.administrative && lawsuitStat.value.administrative.cases) {
lawsuitStat.value.administrative.cases.forEach((item, index) => {
cases.push({
...item,
type: 'administrative',
id: `administrative_${index}`,
})
})
}
// 处理破产案件
if (lawsuitStat.value.bankrupt && lawsuitStat.value.bankrupt.cases) {
lawsuitStat.value.bankrupt.cases.forEach((item, index) => {
cases.push({
...item,
type: 'bankrupt',
id: `bankrupt_${index}`,
})
})
}
// 处理保全案件
if (lawsuitStat.value.preservation && lawsuitStat.value.preservation.cases) {
lawsuitStat.value.preservation.cases.forEach((item, index) => {
cases.push({
...item,
type: 'preservation',
id: `preservation_${index}`,
})
})
}
}
return cases
})
// 计算总案件数
const totalCases = computed(() => allCases.value.length)
// 计算涉诉风险统计
const lawsuitStats = computed(() => {
if (totalCases.value === 0) return null
const stats = {
totalRiskItems: totalCases.value,
highRiskItems: 0,
mediumRiskItems: 0,
lowRiskItems: 0,
breachCaseCount: breachCaseList.value.length,
consumptionRestrictionCount: consumptionRestrictionList.value.length,
closedCases: 0,
caseTypes: [],
}
// 统计各类型案件数量
const typeCounts = {}
Object.keys(lawsuitTypeMap).forEach(type => {
typeCounts[type] = 0
})
allCases.value.forEach(caseItem => {
// 根据案件类型统计风险等级
const riskLevel = getCaseTypeRiskLevel(caseItem.type).level
if (riskLevel === 'high') {
stats.highRiskItems++
} else if (riskLevel === 'medium') {
stats.mediumRiskItems++
} else {
stats.lowRiskItems++
}
// 统计已结案件
if (caseItem.n_ajjzjd && caseItem.n_ajjzjd.includes('已结')) {
stats.closedCases++
}
// 统计案件类型
if (caseItem.type) {
typeCounts[caseItem.type] = (typeCounts[caseItem.type] || 0) + 1
}
})
// 转换为数组格式
stats.caseTypes = Object.keys(typeCounts)
.filter(type => typeCounts[type] > 0)
.map(type => ({
type,
count: typeCounts[type],
name: getCaseTypeText(type),
color: getCaseTypeColor(type),
}))
.sort((a, b) => b.count - a.count)
return stats
})
// 按案件类型筛选案件
const filteredCases = computed(() => {
if (activeCaseTypeFilter.value === 'all') {
return allCases.value
}
return allCases.value.filter(caseItem => caseItem.type === activeCaseTypeFilter.value)
})
// 获取每种案件类型的数量
const caseTypeCounts = computed(() => {
const counts = {
all: totalCases.value,
}
// 初始化所有案件类型的计数
Object.keys(lawsuitTypeMap).forEach(type => {
counts[type] = 0
})
// 计算每种类型的案件数量
allCases.value.forEach(caseItem => {
if (caseItem.type) {
counts[caseItem.type]++
}
})
return counts
})
// 设置当前筛选类型
const setCaseTypeFilter = type => {
activeCaseTypeFilter.value = type
}
// 计算风险评分0-100分分数越高越安全
const riskScore = computed(() => {
const cases = totalCases.value;
// 根据涉诉案件数量计算评分
// 0件100分最安全
// 1-2件70分中等风险
// 3-5件50分较高风险
// 6-10件30分高风险
// 10件以上10分极高风险
if (cases === 0) return 100;
if (cases <= 2) return 70;
if (cases <= 5) return 50;
if (cases <= 10) return 30;
return 10;
});
// 使用 composable 通知父组件风险评分
useRiskNotifier(props, riskScore);
// 暴露给父组件
defineExpose({
riskScore
});
</script>
<style lang="scss" scoped>
.case-wrapper {
@apply relative;
}
.lawsuit-tabs :deep(.van-tabs__wrap) {
height: 32px !important;
background-color: transparent !important;
padding: 0 !important;
border-bottom: 1px solid #DDDDDD !important;
}
.lawsuit-tabs :deep(.van-tabs__nav) {
background-color: transparent !important;
gap: 0;
height: 32px !important;
}
.lawsuit-tabs :deep(.van-tab) {
color: #999999 !important;
font-size: 14px !important;
font-weight: 400 !important;
}
.lawsuit-tabs :deep(.van-tab--active) {
color: var(--van-theme-primary) !important;
background-color: unset !important;
}
.lawsuit-tabs :deep(.van-tabs__line) {
height: 3px !important;
border-radius: 1px !important;
}
</style>

View File

@@ -0,0 +1,303 @@
// 案件类型映射表
export const lawsuitTypeMap = {
breachCase: {
text: '失信被执行',
color: 'text-red-600 bg-red-50',
darkColor: 'bg-red-500',
riskLevel: 'high', // 高风险
},
consumptionRestriction: {
text: '限高被执行',
color: 'text-orange-600 bg-orange-50',
darkColor: 'bg-orange-500',
riskLevel: 'high', // 高风险
},
criminal: {
text: '刑事案件',
color: 'text-red-600 bg-red-50',
darkColor: 'bg-red-500',
riskLevel: 'high', // 高风险
},
civil: {
text: '民事案件',
color: 'text-blue-600 bg-blue-50',
darkColor: 'bg-blue-500',
riskLevel: 'medium', // 中风险
},
administrative: {
text: '行政案件',
color: 'text-purple-600 bg-purple-50',
darkColor: 'bg-purple-500',
riskLevel: 'medium', // 中风险
},
implement: {
text: '执行案件',
color: 'text-orange-600 bg-orange-50',
darkColor: 'bg-orange-500',
riskLevel: 'medium', // 中风险
},
bankrupt: {
text: '强制清算与破产案件',
color: 'text-rose-600 bg-rose-50',
darkColor: 'bg-rose-500',
riskLevel: 'high', // 高风险
},
preservation: {
text: '非诉保全审查',
color: 'text-amber-600 bg-amber-50',
darkColor: 'bg-amber-500',
riskLevel: 'low', // 低风险
},
}
// 案件类型文本
export const getCaseTypeText = type => {
return lawsuitTypeMap[type]?.text || '其他案件'
}
// 案件类型颜色
export const getCaseTypeColor = type => {
return lawsuitTypeMap[type]?.color || 'text-gray-600 bg-gray-50'
}
// 案件类型深色
export const getCaseTypeDarkColor = type => {
return lawsuitTypeMap[type]?.darkColor || 'bg-gray-500'
}
// 格式化日期显示
export const formatDate = dateStr => {
if (!dateStr) return '—'
// 转换YYYY-MM-DD为年月日格式
if (dateStr.includes('-')) {
const parts = dateStr.split('-')
if (parts.length === 3) {
return `${parts[0]}${parts[1]}${parts[2]}`
}
}
return dateStr // 如果不是标准格式则返回原始字符串
}
// 格式化金额显示(单位:万元)
export const formatLawsuitMoney = money => {
if (!money) return '—'
const value = parseFloat(money)
if (isNaN(value)) return '—'
// 超过1亿显示亿元
if (value >= 10000) {
return (
(value / 10000).toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}) + ' 亿元'
)
}
// 否则显示万元
return (
value.toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}) + ' 万元'
)
}
// 获取案件状态样式
export const getCaseStatusClass = status => {
if (!status) return 'bg-gray-100 text-gray-500'
if (status.includes('已结') || status.includes('已办结')) {
return 'bg-green-50 text-green-600'
} else if (status.includes('执行中') || status.includes('审理中')) {
return 'bg-blue-50 text-blue-600'
} else if (status.includes('未执行')) {
return 'bg-amber-50 text-amber-600'
} else {
return 'bg-gray-100 text-gray-500'
}
}
// 获取企业状态对应的样式
export const getStatusClass = status => {
if (!status) return 'bg-gray-100 text-gray-500'
if (status.includes('注销') || status.includes('吊销')) {
return 'bg-red-50 text-red-600'
} else if (status.includes('存续') || status.includes('在营')) {
return 'bg-green-50 text-green-600'
} else if (status.includes('筹建') || status.includes('新设')) {
return 'bg-blue-50 text-blue-600'
} else {
return 'bg-yellow-50 text-yellow-600'
}
}
// 格式化资本金额显示
export const formatCapital = (capital, currency) => {
if (!capital) return '—'
// 检查是否包含"万"字或需要显示为万元
let unit = ''
let value = parseFloat(capital)
// 处理原始数据中可能带有的单位
if (typeof capital === 'string' && capital.includes('万')) {
unit = '万'
// 提取数字部分
const numMatch = capital.match(/[\d.]+/)
value = numMatch ? parseFloat(numMatch[0]) : 0
} else if (value >= 10000) {
// 大额数字转换为万元显示
value = value / 10000
unit = '万'
}
// 格式化数字,保留两位小数(如果有小数部分)
const formattedValue = value.toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})
return `${formattedValue}${unit} ${currency || '人民币'}`
}
// 获取涉诉风险等级
export const getRiskLevel = lawsuitInfo => {
if (!lawsuitInfo) {
return {
level: 'low',
text: '低风险',
color: 'text-green-600 bg-green-50',
}
}
// 失信被执行人是最高风险
if (lawsuitInfo.breachCaseList && lawsuitInfo.breachCaseList.length > 0) {
return {
level: 'high',
text: '高风险',
color: 'text-red-600 bg-red-50',
}
}
// 限高被执行人是最高风险
if (lawsuitInfo.consumptionRestrictionList && lawsuitInfo.consumptionRestrictionList.length > 0) {
return {
level: 'high',
text: '高风险',
color: 'text-red-600 bg-red-50',
}
}
// 有涉诉数据的风险级别
if (lawsuitInfo.lawsuitStat && Object.keys(lawsuitInfo.lawsuitStat).length > 0) {
// 检查是否有未结案的案件
const data = lawsuitInfo.lawsuitStat
if (data.count && data.count.count_wei_total && data.count.count_wei_total > 0) {
return {
level: 'medium',
text: '中风险',
color: 'text-amber-600 bg-amber-50',
}
}
// 只有已结案的为低中风险
return {
level: 'low-medium',
text: '低中风险',
color: 'text-yellow-600 bg-yellow-50',
}
}
return {
level: 'low',
text: '低风险',
color: 'text-green-600 bg-green-50',
}
}
// 获取涉诉案件统计
export const getLawsuitStats = lawsuitInfo => {
if (!lawsuitInfo) return null
const stats = {
total: 0,
types: [],
}
// 统计各类型案件数量
Object.keys(lawsuitTypeMap).forEach(type => {
let count = 0
if (type === 'breachCase') {
count = lawsuitInfo.breachCaseList && lawsuitInfo.breachCaseList.length > 0 ? lawsuitInfo.breachCaseList.length : 0
} else if (type === 'consumptionRestriction') {
count = lawsuitInfo.consumptionRestrictionList && lawsuitInfo.consumptionRestrictionList.length > 0 ? lawsuitInfo.consumptionRestrictionList.length : 0
} else if (lawsuitInfo.lawsuitStat && lawsuitInfo.lawsuitStat[type] && Object.keys(lawsuitInfo.lawsuitStat[type]).length > 0) {
const typeData = lawsuitInfo.lawsuitStat[type]
count = typeData.cases && typeData.cases.length ? typeData.cases.length : 0
}
if (count > 0) {
stats.total += count
stats.types.push({
type,
count,
name: getCaseTypeText(type),
color: getCaseTypeColor(type),
darkColor: getCaseTypeDarkColor(type),
})
}
})
return stats
}
// 获取案件类型优先级顺序
export const getCaseTypePriority = () => {
return [
'breachCase', // 失信被执行人(最高风险)
'consumptionRestriction', // 限高被执行人
'criminal', // 刑事案件
'civil', // 民事案件
'administrative', // 行政案件
'implement', // 执行案件
'bankrupt', // 强制清算与破产案件
'preservation', // 非诉保全审查
]
}
// 根据案件类型获取风险等级
export const getCaseTypeRiskLevel = caseType => {
const typeInfo = lawsuitTypeMap[caseType]
if (!typeInfo) {
return {
level: 'low',
text: '低风险',
color: 'text-green-600 bg-green-50',
}
}
const riskLevelMap = {
high: {
text: '高风险',
color: 'text-red-600 bg-red-50',
},
medium: {
text: '中风险',
color: 'text-amber-600 bg-amber-50',
},
low: {
text: '低风险',
color: 'text-green-600 bg-green-50',
},
}
return {
level: typeInfo.riskLevel,
...riskLevelMap[typeInfo.riskLevel],
}
}