主体查询新增信用档案功能

This commit is contained in:
zhouxy 2026-06-06 16:34:04 +08:00
parent 939ce1783f
commit 04873ddf4f
3 changed files with 361 additions and 2 deletions

View File

@ -217,6 +217,20 @@ export function exportqzzxList(data) {
})
}
/**
* 获取主体信用档案图谱数据
* @param {Object} params
* @param {string} params.pripid 主体身份代码
* @param {number} [params.reportYear] 年报年度可选不传则取最近一年影响"对外投资"分支
*/
export function getCreditArchive(params) {
return request({
url: '/eBaseinfo/getCreditArchive',
method: 'get',
params
})
}
/**
* 信用修复预警统计
* @param data

View File

@ -0,0 +1,331 @@
<template>
<div v-loading="loading" class="credit-archive">
<div class="toolbar">
<span class="toolbar-label">年报年度</span>
<el-select
v-model="reportYear"
size="small"
clearable
placeholder="默认最近一年"
style="width: 160px;"
@change="loadData"
>
<el-option
v-for="y in yearOptions"
:key="y"
:label="`${y} 年`"
:value="y"
/>
</el-select>
<el-button
type="text"
icon="el-icon-refresh"
style="margin-left: 12px;"
@click="resetGraph"
>重置图谱</el-button>
<span class="toolbar-tip">提示点击一级分支可展开/收起明细单分支最多展示 {{ maxNodes }} 个节点</span>
</div>
<div ref="graph" class="graph-container" />
<!-- 节点详情弹窗 -->
<el-dialog
:visible.sync="detailVisible"
:title="detailTitle"
width="560px"
append-to-body
>
<el-descriptions v-if="detailData" :column="1" border size="small">
<el-descriptions-item
v-for="(value, key) in detailData"
:key="key"
:label="fieldLabel(key)"
>{{ value || '—' }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script>
import { getCreditArchive } from '@/api/comprehensive'
const MAX_NODES = 20
const CURRENT_YEAR = new Date().getFullYear()
const FIELD_LABEL_MAP = {
name: '姓名',
entName: '企业名称',
uniscid: '统一社会信用代码',
certType: '证件类型',
subConAm: '认缴出资额',
subConProp: '认缴出资比例(%',
respForm: '责任形式',
position: '职务',
lerepsign: '法定代表人标志',
tel: '联系电话',
email: '电子邮箱',
invid: '股东ID',
outinvid: '对外投资ID',
lmid: '联络员ID'
}
const BRANCH_DEFS = [
{ key: 'shareholders', label: '股东信息', color: '#5470c6' },
{ key: 'legalPersons', label: '法定代表人', color: '#91cc75' },
{ key: 'investments', label: '对外投资', color: '#fac858' },
{ key: 'contacts', label: '联络人', color: '#ee6666' }
]
export default {
name: 'CreditArchive',
props: {
pripid: {
type: String,
required: true
},
entName: {
type: String,
default: ''
}
},
data() {
return {
loading: false,
chart: null,
reportYear: null,
yearOptions: this.buildYearOptions(),
maxNodes: MAX_NODES,
archiveData: {
shareholders: [],
legalPersons: [],
investments: [],
contacts: []
},
truncated: {
shareholders: false,
investments: false,
contacts: false
},
branches: BRANCH_DEFS,
detailVisible: false,
detailTitle: '',
detailData: null
}
},
mounted() {
this.loadData()
window.addEventListener('resize', this.resize)
},
beforeDestroy() {
window.removeEventListener('resize', this.resize)
if (this.chart) {
this.chart.dispose()
this.chart = null
}
},
methods: {
buildYearOptions() {
const list = []
for (let y = CURRENT_YEAR; y >= CURRENT_YEAR - 9; y--) {
list.push(y)
}
return list
},
fieldLabel(key) {
return FIELD_LABEL_MAP[key] || key
},
async loadData() {
if (!this.pripid) return
this.loading = true
try {
const res = await getCreditArchive({
pripid: this.pripid,
reportYear: this.reportYear || undefined
})
const data = (res && res.data) || res || {}
this.archiveData = {
shareholders: data.shareholders || [],
legalPersons: data.legalPersons || [],
investments: data.investments || [],
contacts: data.contacts || []
}
this.truncated = data.truncated || { shareholders: false, investments: false, contacts: false }
// Q5 reportYear
if (!this.reportYear && data.reportYear) {
this.reportYear = data.reportYear
}
this.$nextTick(() => this.renderGraph())
} catch (e) {
this.$message.error('信用档案数据加载失败')
} finally {
this.loading = false
}
},
renderGraph() {
if (!this.$refs.graph) return
if (!this.chart) {
this.chart = this.$echarts.init(this.$refs.graph)
this.chart.on('click', params => this.handleNodeClick(params))
}
const centerName = this.entName || this.archiveData.entName || '主体'
const centerNode = {
id: 'center',
name: centerName,
symbolSize: 80,
category: 0,
fixed: true,
x: 0,
y: 0,
itemStyle: { color: '#409EFF' },
label: { fontWeight: 'bold' }
}
const branchNodes = this.branches.map((b, i) => ({
id: b.key,
name: `${b.label} (${(this.archiveData[b.key] || []).length})`,
symbolSize: 50,
category: i + 1,
itemStyle: { color: b.color }
}))
const branchLinks = this.branches.map(b => ({
source: 'center',
target: b.key,
lineStyle: { color: '#aaa', width: 2 }
}))
this.chart.setOption({
tooltip: {
trigger: 'item',
formatter: params => {
if (params.dataType === 'edge') return ''
return params.name
}
},
legend: [{
data: this.branches.map(b => b.label),
bottom: 0
}],
series: [{
type: 'graph',
layout: 'force',
roam: true,
draggable: true,
label: { show: true, position: 'right', formatter: '{b}' },
force: { repulsion: 400, edgeLength: 120 },
categories: [
{ name: '主体' },
...this.branches.map(b => ({ name: b.label }))
],
data: [centerNode, ...branchNodes],
links: branchLinks,
lineStyle: { color: 'source', curveness: 0.1 }
}]
}, true)
},
handleNodeClick(params) {
// /
if (!params.data || params.dataType === 'edge') return
const branch = this.branches.find(b => b.key === params.data.id)
if (branch) {
this.toggleBranch(branch)
return
}
//
if (params.data.rawData) {
const parentKey = String(params.data.id).split('-')[0]
const parentBranch = this.branches.find(b => b.key === parentKey)
this.detailTitle = parentBranch ? `${parentBranch.label}详情` : '详情'
this.detailData = params.data.rawData
this.detailVisible = true
}
},
toggleBranch(branch) {
const list = this.archiveData[branch.key] || []
if (!list.length) {
this.$message.info(`暂无${branch.label}`)
return
}
const option = this.chart.getOption()
const series = option.series[0]
const prefix = branch.key + '-'
const hasChild = series.data.some(n => String(n.id).startsWith(prefix))
if (hasChild) {
//
series.data = series.data.filter(n => !String(n.id).startsWith(prefix))
series.links = series.links.filter(l => !String(l.target).startsWith(prefix))
} else {
// Q11 MAX_NODES "..."
const displayList = list.slice(0, MAX_NODES)
const categoryIndex = this.branches.indexOf(branch) + 1
displayList.forEach((item, i) => {
const nodeId = `${branch.key}-${i}`
series.data.push({
id: nodeId,
name: this.getNodeName(branch.key, item),
symbolSize: 30,
category: categoryIndex,
itemStyle: { color: branch.color, opacity: 0.7 },
rawData: item
})
series.links.push({ source: branch.key, target: nodeId })
})
//
const isTruncated = this.truncated[branch.key] || list.length > MAX_NODES
if (isTruncated) {
const moreId = `${branch.key}-more`
series.data.push({
id: moreId,
name: `更多... (共 ${list.length}${list.length > MAX_NODES ? '+' : ''} 项)`,
symbolSize: 26,
category: categoryIndex,
itemStyle: { color: branch.color, opacity: 0.4 }
})
series.links.push({ source: branch.key, target: moreId })
}
}
this.chart.setOption(option, true)
},
getNodeName(key, item) {
if (key === 'investments') return item.entName || '—'
if (key === 'shareholders') return item.name || '—'
return item.name || '—'
},
resetGraph() {
this.renderGraph()
},
resize() {
if (this.chart) this.chart.resize()
}
}
}
</script>
<style lang="scss" scoped>
.credit-archive {
.toolbar {
display: flex;
align-items: center;
padding: 10px 12px;
background: #fafafa;
border: 1px solid #ebeef5;
border-bottom: none;
.toolbar-label {
font-size: 13px;
color: #606266;
margin-right: 6px;
}
.toolbar-tip {
margin-left: auto;
font-size: 12px;
color: #909399;
}
}
.graph-container {
width: 100%;
height: 600px;
border: 1px solid #ebeef5;
background: #fff;
}
}
</style>

View File

@ -149,6 +149,14 @@
</table>
</div>
</el-tab-pane>
<!-- 信用档案 TabQ9放在"基本信息"之后 -->
<el-tab-pane label="信用档案" name="creditArchive">
<credit-archive
v-if="activeName === 'creditArchive' && isReadCreditArchive"
:pripid="pripid"
:ent-name="entInfo && entInfo.entname"
/>
</el-tab-pane>
<!-- <el-tab-pane label="信用信息" name="second" /> -->
<el-tab-pane label="检查信息" name="third">
<div>
@ -962,11 +970,13 @@ import { getAssignLogList } from '@/api/marketAssign'
import EasyForm from '@/components/EasyForm'
import dynamicSupervisionBase from '@/views/market/dynamicsupervision/index'
import dailySupervisionBase from '@/views/market/dailysupervision/index'
import CreditArchive from './components/CreditArchive'
import { oauth } from '@/api/user'
export default {
components: {
EasyForm
EasyForm,
CreditArchive
},
mixins: [dynamicSupervisionBase, dailySupervisionBase],
data() {
@ -975,6 +985,7 @@ export default {
isRead2: false,
isRead3: false,
isRead4: false,
isReadCreditArchive: false,
isRead5: false,
isRead6: false,
isRead7: false,
@ -1222,7 +1233,10 @@ export default {
handleClick(tab, event) {
console.log(this.activeName)
this.loading = true
if (this.activeName === 'fourth') {
if (this.activeName === 'creditArchive' && !this.isReadCreditArchive) {
this.isReadCreditArchive = true
this.loading = false
} else if (this.activeName === 'fourth') {
oauth().then((res) => {
const { data } = res
const path = `/annualReportService/unifiedMGT/annualReportProgress/baseInfo/detail?pripid=${this.$route.query.pripid}&yearReportMode=1`