Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
9f41b96e
提交
9f41b96e
authored
4月 09, 2026
作者:
闫鹏
浏览文件
操作
浏览文件
下载
差异文件
合并分支 'yp-dev' 到 'pre'
Yp dev 查看合并请求
!327
上级
79410949
5c38f268
流水线
#397
已通过 于阶段
in 6 分 8 秒
变更
14
流水线
1
显示空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
900 行增加
和
954 行删除
+900
-954
exportControl.js
src/api/exportControl.js
+3
-2
index.js
src/api/finance/index.js
+25
-0
index.vue
src/views/exportControl/index.vue
+4
-13
index.vue
...omponents/sanctionsOverview/components/listPage/index.vue
+13
-11
index.vue
...omponents/sanctionsOverview/components/listPage/index.vue
+3
-8
empty.png
...finance/entityList/components/deepMining/assets/empty.png
+0
-0
icon-mark.png
...nce/entityList/components/deepMining/assets/icon-mark.png
+0
-0
back.vue
...ance/entityList/components/deepMining/components/back.vue
+429
-0
constrainedAssociation.vue
...mponents/deepMining/components/constrainedAssociation.vue
+332
-902
mock.json
...nce/entityList/components/deepMining/components/mock.json
+74
-0
index.vue
src/views/finance/entityList/components/deepMining/index.vue
+8
-5
index.vue
...s/sanctionsOverview/components/introductionPage/index.vue
+0
-5
index.vue
...omponents/sanctionsOverview/components/listPage/index.vue
+6
-5
index.vue
src/views/finance/index.vue
+3
-3
没有找到文件。
src/api/exportControl.js
浏览文件 @
9f41b96e
...
...
@@ -498,14 +498,15 @@ export function getEntitiesUpdateCount(sanTypeId = 1) {
* @param {string} rule - 规则
* @param {string} type - 类型
*/
export
function
getSanDomainCount
(
rule
,
type
)
{
export
function
getSanDomainCount
(
rule
,
sanTypeIds
,
type
)
{
return
request200
(
request
({
method
:
"GET"
,
url
:
"/api/entitiesDataCount/getSanDomainCount"
,
params
:
{
rule
,
type
sanTypeIds
// type
}
})
);
...
...
src/api/finance/index.js
浏览文件 @
9f41b96e
...
...
@@ -136,3 +136,27 @@ export function getRelateNews(sanRecordId) {
export
function
getReasonAndSan
(
sanRecordId
)
{
return
http
.
get
(
`/api/sanctionList/invFin/getReasonAndSan?sanRecordId=
${
sanRecordId
}
`
);
}
/**
* 制裁历程
* url:/entitiesDataCount/getSanRecord
*/
export
function
getSanRecord
(
params
)
{
return
http
.
get
(
"/api/entitiesDataCount/getSanRecord"
,
params
);
}
/**
* 限制关系分析-限制举措关系图
* url:/sanctionList/invFin/recordRelation
*/
export
function
getRecordRelation
(
sanRecordIds
)
{
return
http
.
get
(
`/api/sanctionList/invFin/recordRelation?sanRecordIds=
${
sanRecordIds
}
`
);
}
/**
* 查询投融资限制关联-图谱-节点详情
* url:/sanctionList/invFin/getVertexInfo
*/
export
function
getVertexInfo
(
sanRecordId
)
{
return
http
.
get
(
`/api/sanctionList/invFin/getVertexInfo?sanRecordId=
${
sanRecordId
}
`
);
}
\ No newline at end of file
src/views/exportControl/index.vue
浏览文件 @
9f41b96e
...
...
@@ -1141,7 +1141,7 @@ const radarOption = ref({
// 获取雷达图数据
const
fetchRadarData
=
async
checked
=>
{
try
{
const
data
=
await
getSanDomainCount
(
checked
,
"export"
);
const
data
=
await
getSanDomainCount
(
checked
,
allSanTypeIds
.
value
.
join
(
","
)
);
if
(
data
&&
Array
.
isArray
(
data
)
&&
data
.
length
>
0
)
{
// 收集所有可能的领域名称
const
allDomains
=
new
Set
();
...
...
@@ -1785,15 +1785,6 @@ const handleMediaClick = item => {
</
script
>
<
style
lang=
"scss"
scoped
>
// * {
// margin: 0;
// padding: 0;
// }
:deep
(
.el-input__wrapper
)
{
// box-shadow: none;
}
.list-page
{
padding-top
:
0
;
}
...
...
@@ -3247,10 +3238,10 @@ const handleMediaClick = item => {
}
.text
{
font-size
:
20
px
;
font-size
:
16
px
;
font-weight
:
700
;
font-family
:
"
Microsoft YaHei
"
;
line-height
:
2
6
px
;
font-family
:
"
Source Han Sans CN
"
;
line-height
:
2
4
px
;
color
:
rgb
(
5
,
95
,
194
);
}
}
...
...
src/views/exportControl/v2.0CommercialControlList/components/sanctionsOverview/components/listPage/index.vue
浏览文件 @
9f41b96e
...
...
@@ -2,14 +2,14 @@
<div
class=
"list-page"
>
<div
class=
"search-box"
>
<div
style=
"display: flex; justify-content: center"
>
<el-select
v-model=
"currentCCLVersion"
style=
"width: 3
88
px; height: 32px; margin-right: 14px"
>
<el-select
v-model=
"currentCCLVersion"
style=
"width: 3
60
px; height: 32px; margin-right: 14px"
>
<el-option
v-for=
"item in cclVersionList"
:key=
"item.key"
:label=
"item.value"
:value=
"item.key"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
class=
"search-input"
placeholder=
"搜索物项或ECCN编码"
:suffix-icon=
"Search"
/>
</div>
<div
class=
"filters"
>
<el-checkbox
v-model=
"viewNew"
label=
"查看最近更新内容"
/>
<el-select
placeholder=
"全部类别"
v-model=
"currentCCLType"
style=
"width: 3
88
px; height: 32px; margin-right: 14px"
>
<el-select
placeholder=
"全部类别"
v-model=
"currentCCLType"
style=
"width: 3
60
px; height: 32px; margin-right: 14px"
>
<el-option
v-for=
"item in CCLTypeList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</div>
...
...
@@ -381,14 +381,15 @@ onMounted(async () => {
align-items
:
center
;
.search-input
{
width
:
3
88
px
;
width
:
3
60
px
;
height
:
32px
;
:deep
(
.el-input__wrapper
)
{
padding
:
0
11px
;
border
:
1
.5px
solid
#dcdfe6
;
//
border: 1.5px solid #dcdfe6;
background-color
:
#fff
;
border-radius
:
4px
;
box-shadow
:
0
0
0
1px
var
(
--
el-input-border-color
,
var
(
--
el-border-color
))
inset
;
}
:deep
(
.el-input__inner
)
{
...
...
@@ -439,25 +440,26 @@ onMounted(async () => {
.left
{
padding-bottom
:
20px
;
width
:
3
88
px
;
width
:
3
60
px
;
height
:
auto
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background-color
:
#fff
;
.checkbox-group
{
display
:
flex
;
flex-wrap
:
wrap
;
padding
:
0
0
0
2
4px
;
display
:
grid
;
grid-template-columns
:
repeat
(
2
,
160px
)
;
gap
:
8px
4px
;
padding-left
:
24px
;
.el-checkbox
{
width
:
50%
;
margin-right
:
0
;
margin-bottom
:
4px
;
//
margin-bottom: 4px;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
}
...
...
@@ -504,7 +506,7 @@ onMounted(async () => {
}
.right
{
width
:
1
196
px
;
width
:
1
223
px
;
height
:
auto
;
.title
{
width
:
100%
;
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/listPage/index.vue
浏览文件 @
9f41b96e
...
...
@@ -514,11 +514,6 @@ watch(customDateRange, () => {
</
script
>
<
style
scoped
lang=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
}
.list-page
{
width
:
1601px
;
padding-bottom
:
50px
;
...
...
@@ -532,7 +527,7 @@ watch(customDateRange, () => {
align-items
:
center
;
.search-input
{
width
:
3
88
px
;
width
:
3
60
px
;
height
:
32px
;
:deep
(
.el-input__wrapper
)
{
...
...
@@ -583,7 +578,7 @@ watch(customDateRange, () => {
.left
{
padding-bottom
:
20px
;
width
:
3
88
px
;
width
:
3
60
px
;
height
:
auto
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
...
...
@@ -654,7 +649,7 @@ watch(customDateRange, () => {
}
.right
{
width
:
1
196
px
;
width
:
1
223
px
;
height
:
auto
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
...
...
src/views/finance/entityList/components/deepMining/assets/empty.png
0 → 100644
浏览文件 @
9f41b96e
7.2 KB
src/views/finance/entityList/components/deepMining/assets/icon-mark.png
0 → 100644
浏览文件 @
9f41b96e
2.8 KB
src/views/finance/entityList/components/deepMining/components/back.vue
0 → 100644
浏览文件 @
9f41b96e
<
template
>
<div
class=
"main main-association"
>
<div
class=
"left"
>
<!-- ... 左侧代码保持不变 ... -->
<AnalysisBox
title=
"制裁历程"
>
<div
class=
"left-main"
>
<div
class=
"date-picker-box"
>
<el-date-picker
v-model=
"dateRange"
type=
"daterange"
range-separator=
"--"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
value-format=
"YYYY-MM-DD"
style=
"width: 100%"
:clearable=
"false"
@
change=
"handleDateChange"
/>
</div>
<div
class=
"list-header"
>
<el-checkbox
v-model=
"isAllSelected"
:indeterminate=
"indeterminate"
@
change=
"handleCheckAllChange"
>
全选
</el-checkbox>
<div
class=
"count"
>
共
{{
sanctionList
.
length
}}
次制裁
</div>
</div>
<div
class=
"list-content"
v-loading=
"loading"
>
<div
class=
"list-item"
v-for=
"item in sanctionList"
:key=
"item.id"
@
click=
"handleSanctionSelect(item.id)"
>
<el-checkbox
v-model=
"item.checked"
@
change=
"val => handleCheckOneChange(val, item)"
@
click
.
stop
>
<div
class=
"item-label"
>
<div
class=
"item-left"
>
{{
item
.
date
}}
-
{{
"SDN清单更新"
}}
</div>
<div
class=
"item-right"
>
{{
item
.
count
}}{{
item
.
unit
}}
</div>
</div>
</el-checkbox>
</div>
</div>
<el-button
type=
"primary"
@
click=
"handleAssociationClick"
style=
"height: 36px"
>
多投融资限制举措关联分析
<el-icon><Right
/></el-icon>
</el-button>
</div>
</AnalysisBox>
</div>
<div
class=
"right"
>
<AnalysisBox
title=
"投融资限制举措关系图"
>
<div
class=
"right-main"
>
<div
class=
"relation-empty"
v-if=
"selectedSanctionIds.length == 0"
>
<el-empty
:image=
"emptyImg"
:image-size=
"200"
>
<template
#
description
>
<div
class=
"empty"
>
请在左侧勾选多次投融资限制制裁后点击“开始分析”查看结果
</div>
</
template
>
</el-empty>
</div>
<div
class=
"relation-content"
v-else
>
<!-- 修改点:绑定转换后的 graphNodes 和 graphLinks -->
<GraphChart
:nodes=
"graphNodes"
:links=
"graphLinks"
/>
</div>
</div>
</AnalysisBox>
</div>
</div>
</template>
<
script
setup
>
import
{
ref
,
onMounted
,
nextTick
,
onUnmounted
,
computed
}
from
"vue"
;
import
defaultTitle
from
"../../../assets/default-icon2.png"
;
import
GraphChart
from
"@/components/base/GraphChart/index.vue"
;
import
emptyImg
from
"../assets/empty.png"
;
import
markIcon
from
"../assets/icon-mark.png"
;
import
{
getSanRecord
,
getRecordRelation
}
from
"@/api/finance"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
const
colors
=
{
// 相似
similarity
:
{
fontColor
:
"rgba(5, 95, 194)"
,
color
:
"rgb(231, 243, 255)"
},
// 继承
inheritance
:
{
fontColor
:
"rgba(19, 168, 168)"
,
color
:
"rgba(230, 255, 251, 1)"
},
// 冲突
conflict
:
{
fontColor
:
"rgb(206, 79, 81)"
,
color
:
"rgba(5, 95, 194)"
}
};
// ... 其他原有变量保持不变 ...
const
selectedSanctionIds
=
ref
([]);
const
isAllSelected
=
computed
({
get
()
{
return
sanctionList
.
value
.
length
>
0
&&
sanctionList
.
value
.
every
(
item
=>
item
.
checked
);
},
set
(
val
)
{}
});
const
handleCheckAllChange
=
val
=>
{
sanctionList
.
value
.
forEach
(
item
=>
{
item
.
checked
=
val
;
});
updateSelectedIds
();
};
const
handleCheckOneChange
=
(
val
,
item
)
=>
{
item
.
checked
=
val
;
updateSelectedIds
();
};
const
updateSelectedIds
=
()
=>
{
selectedSanctionIds
.
value
=
sanctionList
.
value
.
filter
(
item
=>
item
.
checked
).
map
(
item
=>
item
.
id
);
if
(
selectedSanctionIds
.
value
.
length
===
1
)
{
currentSanctionId
.
value
=
selectedSanctionIds
.
value
[
0
];
}
};
const
indeterminate
=
computed
(()
=>
{
const
checkedCount
=
sanctionList
.
value
.
filter
(
item
=>
item
.
checked
).
length
;
return
checkedCount
>
0
&&
checkedCount
<
sanctionList
.
value
.
length
;
});
const
recordRelation
=
ref
({
noRelationVertices
:
[],
relationVoList
:
[]
});
// 【新增】计算属性:处理图表数据
const
graphNodes
=
computed
(()
=>
{
const
nodesMap
=
new
Map
();
// 1. 处理无关联节点 (noRelationVertices)
if
(
recordRelation
.
value
.
noRelationVertices
)
{
recordRelation
.
value
.
noRelationVertices
.
forEach
(
node
=>
{
if
(
!
nodesMap
.
has
(
node
.
id
))
{
nodesMap
.
set
(
node
.
id
,
{
id
:
node
.
id
,
name
:
node
.
name
,
// 可以根据需要添加 category 或其他样式属性
itemStyle
:
{
color
:
"#91cc75"
// 例如:无关联节点用绿色区分
}
});
}
});
}
// 2. 处理关联节点 (relationVoList 中的 fromVertex 和 toVertex)
if
(
recordRelation
.
value
.
relationVoList
)
{
recordRelation
.
value
.
relationVoList
.
forEach
(
rel
=>
{
const
from
=
rel
.
fromVertex
;
const
to
=
rel
.
toVertex
;
if
(
from
&&
!
nodesMap
.
has
(
from
.
id
))
{
nodesMap
.
set
(
from
.
id
,
{
id
:
from
.
id
,
name
:
from
.
name
,
itemStyle
:
{
color
:
"#5470c6"
// 例如:有关联节点用蓝色
}
});
}
if
(
to
&&
!
nodesMap
.
has
(
to
.
id
))
{
nodesMap
.
set
(
to
.
id
,
{
id
:
to
.
id
,
name
:
to
.
name
,
itemStyle
:
{
color
:
"#5470c6"
}
});
}
});
}
return
Array
.
from
(
nodesMap
.
values
());
});
const
graphLinks
=
computed
(()
=>
{
if
(
!
recordRelation
.
value
.
relationVoList
)
return
[];
return
recordRelation
.
value
.
relationVoList
.
map
(
rel
=>
{
return
{
source
:
rel
.
fromVertex
.
id
,
target
:
rel
.
toVertex
.
id
,
// 将 edgeInfo 挂载到 data 上,以便在 formatter 中访问
label
:
{
formatter
:
rel
.
edgeInfo
?
rel
.
edgeInfo
.
value
:
""
}
};
});
});
const
handleAssociationClick
=
()
=>
{
console
.
log
(
"handleAssociationClick"
,
selectedSanctionIds
.
value
);
fetchRecordRelation
();
};
const
fetchRecordRelation
=
async
()
=>
{
if
(
selectedSanctionIds
.
value
.
length
===
0
)
{
return
;
}
try
{
const
res
=
await
getRecordRelation
(
selectedSanctionIds
.
value
);
console
.
log
(
"getRecordRelation"
,
res
);
if
(
!!
res
)
{
recordRelation
.
value
=
res
;
}
else
{
recordRelation
.
value
=
{
noRelationVertices
:
[],
relationVoList
:
[]
};
}
}
catch
(
error
)
{
console
.
error
(
"获取制裁记录关联信息失败:"
,
error
);
recordRelation
.
value
=
{
noRelationVertices
:
[],
relationVoList
:
[]
};
}
};
const
loading
=
ref
(
false
);
const
currentPage
=
ref
(
1
);
const
fetchSanRecord
=
async
()
=>
{
loading
.
value
=
true
;
const
params
=
{
startDate
:
dateRange
.
value
&&
dateRange
.
value
[
0
]
?
dateRange
.
value
[
0
]
:
""
,
endDate
:
dateRange
.
value
&&
dateRange
.
value
[
1
]
?
dateRange
.
value
[
1
]
:
""
,
sanTypeId
:
sanTypeId
.
value
||
1
};
try
{
const
res
=
await
getSanRecord
(
params
);
if
(
res
&&
res
.
length
>
0
)
{
sanctionList
.
value
=
res
.
map
(
item
=>
({
id
:
item
.
sanRecordId
,
date
:
item
.
sanRecordDate
,
title
:
item
.
sanRecordName
,
count
:
item
.
cnEntitiesNum
,
unit
:
"家中国实体"
}))
.
reverse
();
if
(
sanctionList
.
value
.
length
>
0
)
{
currentSanctionId
.
value
=
sanctionList
.
value
[
0
].
id
;
}
}
else
{
sanctionList
.
value
=
[];
}
}
catch
(
error
)
{
console
.
error
(
"获取选择制裁数据失败:"
,
error
);
sanctionList
.
value
=
[];
}
finally
{
loading
.
value
=
false
;
}
};
const
handleDateChange
=
()
=>
{
fetchSanRecord
();
};
const
handleSanctionSelect
=
id
=>
{
currentSanctionId
.
value
=
id
;
};
const
dateRange
=
ref
([
"2025-01-01"
,
"2025-12-31"
]);
const
sanctionList
=
ref
([]);
const
currentSanctionId
=
ref
(
5
);
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
""
;
fetchSanRecord
();
});
</
script
>
<
style
lang=
"scss"
scoped
>
/* 样式保持不变 */
.main
{
width
:
100%
;
padding-top
:
12px
;
padding-bottom
:
50px
;
display
:
flex
;
justify-content
:
space-between
;
.left
{
width
:
480px
;
height
:
828px
;
.left-main
{
margin-top
:
11px
;
padding
:
0
22px
0
23px
;
display
:
flex
;
flex-direction
:
column
;
height
:
calc
(
100%
-
25px
);
.date-picker-box
{
margin-bottom
:
16px
;
}
.list-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
margin-bottom
:
16px
;
font-size
:
14px
;
color
:
#666
;
.count
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
95
,
101
,
108
);
}
}
.list-content
{
flex
:
1
;
overflow-y
:
auto
;
padding-bottom
:
20px
;
&
:
:-
webkit-scrollbar
{
width
:
6px
;
}
&
:
:-
webkit-scrollbar-thumb
{
background
:
#ccc
;
border-radius
:
3px
;
}
.list-item
{
border
:
1px
solid
rgb
(
234
,
236
,
238
);
border-radius
:
4px
;
margin-bottom
:
8px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
15px
16px
;
cursor
:
pointer
;
transition
:
all
0
.3s
;
position
:
relative
;
background
:
#fff
;
.item-label
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
width
:
100%
;
}
.item-left
{
width
:
260px
;
font-weight
:
700
;
color
:
rgb
(
59
,
65
,
75
);
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
margin-right
:
10px
;
}
.item-right
{
color
:
rgb
(
132
,
136
,
142
);
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
flex-shrink
:
0
;
}
&
:hover
{
border-color
:
#055fc2
;
}
&
.active
{
border-color
:
rgb
(
5
,
95
,
194
);
background-color
:
rgba
(
246
,
250
,
255
,
1
);
.item-left
,
.item-right
{
color
:
rgb
(
5
,
95
,
194
);
}
&
:
:
after
{
content
:
""
;
position
:
absolute
;
right
:
0
;
top
:
10px
;
bottom
:
10px
;
width
:
4px
;
background-color
:
rgb
(
5
,
95
,
194
);
}
}
}
}
}
}
.right
{
width
:
1105px
;
height
:
828px
;
.right-main
{
margin-top
:
11px
;
height
:
calc
(
100%
-
10px
);
padding
:
0
16px
16px
16px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
.relation-empty
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
height
:
300px
;
width
:
300px
;
.empty
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
Source
Han
Sans
CN
;
color
:
rgb
(
132
,
136
,
142
);
line-height
:
30px
;
}
}
.relation-content
{
width
:
100%
;
height
:
100%
;
}
}
}
}
.main-association
{
justify-content
:
flex-start
!
important
;
gap
:
16px
;
}
</
style
>
src/views/finance/entityList/components/deepMining/components/constrainedAssociation.vue
浏览文件 @
9f41b96e
<
template
>
<div
class=
"main main-association"
>
<div
class=
"left"
>
<!-- ... 左侧代码保持不变 ... -->
<AnalysisBox
title=
"制裁历程"
>
<div
class=
"left-main"
>
<div
class=
"date-picker-box"
>
...
...
@@ -21,250 +22,154 @@
全选
</el-checkbox>
<div
class=
"count"
>
共
{{
sanctionList
.
length
}}
次制裁
</div>
<!-- 暂时隐藏,说这里可能是轮播图的效果 -->
<!--
<div
class=
"pagination"
>
<div
class=
"page-btn prev"
@
click=
"handlePrevClick"
>
<el-icon><ArrowLeft
/></el-icon>
</div>
<div
class=
"page-btn next"
@
click=
"handleNextClick"
>
<el-icon><ArrowRight
/></el-icon>
</div>
</div>
-->
</div>
<div
class=
"list-content"
v-loading=
"loading"
>
<div
class=
"list-item"
v-for=
"item in sanctionList"
:key=
"item.id"
:class=
"
{ active: currentSanctionId === item.id }"
@click="handleSanctionSelect(item.id)"
>
<div
class=
"list-item"
v-for=
"item in sanctionList"
:key=
"item.id"
@
click=
"handleSanctionSelect(item.id)"
>
<el-checkbox
v-model=
"item.checked"
@
change=
"val => handleCheckOneChange(val, item)"
@
click
.
stop
>
<span
class=
"item-label"
>
<span
class=
"item-left"
>
{{
item
.
date
}}
-
{{
item
.
title
}}
</span>
<span
class=
"item-right"
>
{{
item
.
count
}}{{
item
.
unit
}}
</span>
</span>
<div
class=
"item-label"
>
<div
class=
"item-left"
>
{{
dayjs
(
item
.
date
).
format
(
"YYYY年MM月DD日"
)
}}
-
{{
"SDN清单更新"
}}
</div>
<div
class=
"item-right"
>
{{
item
.
count
}}{{
item
.
unit
}}
</div>
</div>
</el-checkbox>
<!--
<div
class=
"item-left"
>
{{
item
.
date
}}
-
{{
item
.
title
}}
</div>
<div
class=
"item-right"
>
{{
item
.
count
}}{{
item
.
unit
}}
</div>
-->
</div>
</div>
<el-button
type=
"primary"
@
click=
"handleAssociationClick"
style=
"height: 36px"
>
多投融资限制举措关联分析
<el-icon><Right
/></el-icon>
</el-button>
</div>
</AnalysisBox>
</div>
<div
class=
"right"
>
<AnalysisBox
title=
"制裁产业链时序图"
>
<template
#
header-btn
>
<el-select
v-model=
"selectedIndustryId"
placeholder=
"请选择"
class=
"industry-select"
@
change=
"
() =>
{
getFishboneData();
getCnEntityOnChainData();
}
"
>
<el-option
v-for=
"item in industryList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</
template
>
<AnalysisBox
title=
"投融资限制举措关系图"
>
<div
class=
"right-main"
>
<div
class=
"right-main-content"
>
<div
class=
"hintWrap"
>
<div
class=
"icon1"
></div>
<div
class=
"title"
>
2025年实体清单制裁范围扩大至芯片制造环节,为中国的芯片制造能力划定“技术天花板”,阻止其向更先进水平发展。制裁范围向上游设备和材料、下游先进封装以及关键工具(如EDA软件)延伸,意图瓦解中国构建自主可控产业链的努力。
</div>
<div
class=
"icon2Wrap"
>
<div
class=
"icon2"
></div>
</div>
</div>
<div
class=
"right-main-content-main"
>
<div
class=
"fishbone-wrapper"
>
<div
class=
"fishbone-scroll-container"
ref=
"scrollContainerRef"
>
<div
class=
"fishbone"
ref=
"fishboneRef"
v-if=
"fishboneDataList.length > 0"
>
<div
class=
"main-line"
:style=
"{ width: fishboneDataList.length * 200 + 300 + 'px' }"
>
<!-- 主轴上的标签 -->
<div
class=
"main-line-text"
v-for=
"(item, index) in mainLineLabels"
:key=
"'label-' + index"
:class=
"{
'blue-theme': index < 2,
'green-theme': index >= 2 && index < 4,
'purple-theme': index >= 4
}"
:style=
"{ left: index * 200 + 220 + 'px' }"
>
{{ item }}
</div>
</div>
<!-- 奇数索引的数据组放在上方 -->
<div
v-for=
"(causeGroup, groupIndex) in getOddGroups(fishboneDataList)"
:key=
"'top-' + groupIndex"
:class=
"getTopBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 420 + 'px' }"
>
<div
class=
"left-bone"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-' + index"
>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
<div
class=
"line"
></div>
</div>
</div>
<div
class=
"right-bone"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-' + index"
>
<div
class=
"line"
></div>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
</div>
</div>
</div>
<!-- 偶数索引的数据组放在下方 -->
<div
v-for=
"(causeGroup, groupIndex) in getEvenGroups(fishboneDataList)"
:key=
"'bottom-' + groupIndex"
:class=
"getBottomBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 220 + 'px' }"
>
<div
class=
"left-bone"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-bottom-' + index"
>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
<div
class=
"line"
></div>
</div>
</div>
<div
class=
"right-bone"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-bottom-' + index"
>
<div
class=
"line"
></div>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
</div>
</div>
</div>
</div>
<div
v-else
style=
"
display: flex;
justify-content: center;
align-items: center;
height: 200px;
width: 100%;
"
>
<el-empty
description=
"暂无相关数据"
/>
</div>
</div>
</div>
<div
class=
"relation-empty"
v-if=
"selectedSanctionIds.length == 0"
>
<el-empty
:image=
"emptyImg"
:image-size=
"200"
>
<template
#
description
>
<div
class=
"empty"
>
请在左侧勾选多次投融资限制制裁后点击“开始分析”查看结果
</div>
</
template
>
</el-empty>
</div>
<div
class=
"right-main-content-footer"
>
<div
class=
"footer-item1"
>
<div
class=
"footer-item1-bottom"
>
<div
class=
"icon"
>
<img
src=
"../../../../assets/images/warning.png"
alt=
""
/>
<div
class=
"relation-content"
v-else
>
<!-- 绑定转换后的 graphNodes 和 graphLinks -->
<GraphChart
:nodes=
"graphNodes"
:links=
"graphLinks"
@
handleClickNode=
"handleClickNode"
/>
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.upstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.upstreamInternalRate
)}%),受制裁${cnEntityOnChainData.upstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.upstreamEntityRate
)}%)`
}}
</div>
</AnalysisBox>
</div>
<div
class=
"footer-item1-top"
>
{{ "上游" }}
</div>
<el-dialog
v-model=
"visible"
:title=
"curNode.name"
width=
"960"
>
<div
class=
"dialog-content"
>
<div
class=
"content-item"
>
<div
class=
"item-label"
>
制裁标题:
</div>
<div
class=
"item-desc item-label"
>
{{ vertexInfo.name }}
</div>
<div
class=
"footer-item2"
>
<div
class=
"footer-item2-bottom"
>
<div
class=
"icon"
>
<img
src=
"../../../../assets/images/warning.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.midstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.midstreamInternalRate
)}%),受制裁${cnEntityOnChainData.midstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.midstreamEntityRate
)}%)`
}}
<div
class=
"content-item"
>
<div
class=
"item-label"
>
制裁领域:
</div>
<div
class=
"item-desc"
>
<AreaTag
v-for=
"item in vertexInfo.domainList"
:key=
"item"
:tagName=
"item"
/>
</div>
</div>
<div
class=
"footer-item2-top"
>
{{ "中游" }}
</div>
<div
class=
"content-item"
>
<div
class=
"item-label"
>
依托文件:
</div>
<div
class=
"item-desc"
>
<div
class=
"item-file"
v-for=
"item in vertexInfo.relyFileList"
:key=
"item.id"
>
{{ item.name }}
</div>
</div>
<div
class=
"footer-item3"
>
<div
class=
"footer-item3-bottom"
>
<div
class=
"icon"
>
<img
src=
"../../../../assets/images/warning.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.downstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.downstreamInternalRate
)}%),受制裁${cnEntityOnChainData.downstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.downstreamEntityRate
)}%)`
}}
<div
class=
"content-item"
>
<div
class=
"item-label"
>
依托制裁:
</div>
<div
class=
"item-desc"
>
<div
class=
"item-file"
v-for=
"item in vertexInfo.relySanList"
:key=
"item.id"
>
{{ item.title }}
</div>
</div>
</div>
<div
class=
"footer-item3-top"
>
{{ "下游" }}
</div>
<div
class=
"content-item"
>
<div
class=
"item-label"
>
制裁原因:
</div>
<div
class=
"item-desc"
>
<div
class=
"item-file"
v-for=
"item in vertexInfo.sanReasonList"
:key=
"item"
>
{{ item }}
</div>
</div>
</div>
<div
class=
"content-item"
>
<div
class=
"item-label"
>
制裁对象:
</div>
<div
class=
"item-desc"
>
<span
class=
"item-table-desc"
v-if=
"vertexInfo.addObjectList?.length || vertexInfo.delObjectList?.length"
>
{{ formatChangeSummary(vertexInfo.addObjectList, vertexInfo.delObjectList) }}
</span>
<el-table
:data=
"vertexInfo.sanList"
stripe
>
<el-table-column
property=
"entityNameZh"
label=
"制裁对象"
width=
"350"
/>
<el-table-column
property=
"domainNames"
label=
"所属领域"
width=
"400"
>
<
template
#
default=
"scope"
>
<AreaTag
v-for=
"item in scope.row.domainNames"
:key=
"item"
:tagName=
"item"
/>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</AnalysisBox>
</div>
</el-dialog>
</div>
</template>
<
script
setup
>
import
{
ref
,
onMounted
,
nextTick
,
onUnmounted
,
computed
}
from
"vue"
;
import
{
ArrowLeft
,
ArrowRight
}
from
"@element-plus/icons-vue"
;
import
defaultTitle
from
"../../../assets/default-icon2.png"
;
import
{
getDeepMiningSelect
,
getDeepMiningIndustry
,
getDeepMiningIndustryFishbone
,
getDeepMiningIndustryEntity
}
from
"@/api/exportControlV2.0"
;
import
{
ref
,
onMounted
,
computed
}
from
"vue"
;
import
GraphChart
from
"@/components/base/GraphChart/index.vue"
;
import
emptyImg
from
"../assets/empty.png"
;
import
markIcon
from
"../assets/icon-mark.png"
;
// 引入图标
import
{
getSanRecord
,
getRecordRelation
,
getVertexInfo
}
from
"@/api/finance"
;
import
{
useRoute
}
from
"vue-router"
;
import
dayjs
from
"dayjs"
;
const
route
=
useRoute
();
// 存储选中的ID列表 (如果需要获取选中项,可以使用这个,或者直接遍历 sanctionList 找 checked=true 的)
const
selectedSanctionIds
=
ref
([]);
// 定义颜色映射
const
colors
=
{
// 相似
similarity
:
{
fontColor
:
"rgba(5, 95, 194)"
,
color
:
"rgb(231, 243, 255)"
,
// 连线颜色 & 标签背景
lineColor
:
"rgba(5, 95, 194)"
// 专门用于连线的颜色,如果需要和背景色区分
},
// 继承
inheritance
:
{
fontColor
:
"rgba(19, 168, 168)"
,
color
:
"rgba(230, 255, 251, 1)"
,
lineColor
:
"rgba(19, 168, 168)"
},
// 冲突
conflict
:
{
fontColor
:
"rgb(206, 79, 81)"
,
color
:
"rgba(255, 241, 240, 1)"
,
// 修正:冲突背景通常偏红/浅红,这里沿用你提供的蓝色背景可能不太符合直觉,但我保留你的配置或微调
lineColor
:
"rgb(206, 79, 81)"
}
};
// 计算属性:是否全选
// 辅助函数:获取关系类型对应的颜色配置
const
getRelationStyle
=
relationType
=>
{
switch
(
relationType
)
{
case
"相似"
:
return
colors
.
similarity
;
case
"继承"
:
return
colors
.
inheritance
;
case
"冲突"
:
return
colors
.
conflict
;
default
:
return
{
fontColor
:
"#666"
,
color
:
"#f0f2f5"
,
lineColor
:
"#AED6FF"
};
}
};
const
selectedSanctionIds
=
ref
([]);
const
isAllSelected
=
computed
({
get
()
{
return
sanctionList
.
value
.
length
>
0
&&
sanctionList
.
value
.
every
(
item
=>
item
.
checked
);
},
set
(
val
)
{
// set 方法由 handleCheckAllChange 处理,这里主要是为了配合 v-model 的读取
}
});
// 全选/取消全选
const
handleCheckAllChange
=
val
=>
{
sanctionList
.
value
.
forEach
(
item
=>
{
item
.
checked
=
val
;
...
...
@@ -272,167 +177,235 @@ const handleCheckAllChange = val => {
updateSelectedIds
();
};
// 单个选中变化
const
handleCheckOneChange
=
(
val
,
item
)
=>
{
item
.
checked
=
val
;
updateSelectedIds
();
};
// 点击行触发复选框切换
const
handleItemClick
=
item
=>
{
item
.
checked
=
!
item
.
checked
;
updateSelectedIds
();
};
// 更新选中ID数组(供外部业务使用,如获取选中数据进行查询等)
const
updateSelectedIds
=
()
=>
{
selectedSanctionIds
.
value
=
sanctionList
.
value
.
filter
(
item
=>
item
.
checked
).
map
(
item
=>
item
.
id
);
// 如果业务需要:当选中项变化时,可能需要触发右侧图表更新?
// 原逻辑是点击选中一个就更新右侧。现在如果是多选,右侧图表如何展示?
// 假设:如果只选中了一个,更新右侧;如果选中多个或没选中,保持现状或显示提示?
// 这里暂时保留原逻辑的触发点,但建议根据实际需求调整。
// 例如:只有当选中项为1个时,才调用 getFishboneData
if
(
selectedSanctionIds
.
value
.
length
===
1
)
{
currentSanctionId
.
value
=
selectedSanctionIds
.
value
[
0
];
getFishboneData
();
getCnEntityOnChainData
();
}
else
if
(
selectedSanctionIds
.
value
.
length
===
0
)
{
// 可选:清空右侧或显示默认状态
}
};
// 计算属性:是否半选(不确定状态)
const
indeterminate
=
computed
(()
=>
{
const
checkedCount
=
sanctionList
.
value
.
filter
(
item
=>
item
.
checked
).
length
;
return
checkedCount
>
0
&&
checkedCount
<
sanctionList
.
value
.
length
;
});
// 实体清单-深度挖掘-产业链中国企业实体信息查询
const
getCnEntityOnChainData
=
async
()
=>
{
const
currentSanction
=
sanctionList
.
value
.
find
(
item
=>
item
.
id
===
currentSanctionId
.
value
);
const
date
=
currentSanction
?
currentSanction
.
date
:
""
;
// 确保 date 格式正确
const
formattedDate
=
date
&&
date
.
includes
(
"年"
)
?
date
.
replace
(
"年"
,
"-"
).
replace
(
"月"
,
"-"
).
replace
(
"日"
,
""
)
:
date
;
const
params
=
{
date
:
formattedDate
};
if
(
selectedIndustryId
.
value
)
{
params
.
chainId
=
selectedIndustryId
.
value
;
const
recordRelation
=
ref
({
noRelationVertices
:
[],
relationVoList
:
[]
});
// 【修改】处理图表节点数据
const
graphNodes
=
computed
(()
=>
{
const
nodesMap
=
new
Map
();
// 1. 处理无关联节点
if
(
recordRelation
.
value
.
noRelationVertices
)
{
recordRelation
.
value
.
noRelationVertices
.
forEach
(
node
=>
{
if
(
!
nodesMap
.
has
(
node
.
id
))
{
nodesMap
.
set
(
node
.
id
,
{
id
:
node
.
id
,
// name: node.name,
name
:
dayjs
(
node
.
date
).
format
(
"YYYY年MM月DD日"
)
+
" "
+
"SDN清单更新"
,
symbol
:
`image://
${
markIcon
}
`
,
// 设置自定义图标
symbolSize
:
[
50
,
50
],
// 根据图标实际大小调整
itemStyle
:
{
color
:
"#91cc75"
}
try
{
const
res
=
await
getDeepMiningIndustryEntity
(
params
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
cnEntityOnChainData
.
value
=
res
.
data
;
}
else
{
cnEntityOnChainData
.
value
=
{};
});
}
}
catch
(
error
)
{
console
.
error
(
"获取产业链中国企业实体信息失败:"
,
error
);
cnEntityOnChainData
.
value
=
{};
});
}
};
// 实体清单-深度挖掘-产业链鱼骨图信息
const
fishboneDataList
=
ref
([]);
const
getFishboneData
=
async
()
=>
{
const
currentSanction
=
sanctionList
.
value
.
find
(
item
=>
item
.
id
===
currentSanctionId
.
value
);
const
date
=
currentSanction
?
currentSanction
.
date
:
""
;
// 确保 date 格式正确
const
formattedDate
=
date
&&
date
.
includes
(
"年"
)
?
date
.
replace
(
"年"
,
"-"
).
replace
(
"月"
,
"-"
).
replace
(
"日"
,
""
)
:
date
;
// 2. 处理关联节点
if
(
recordRelation
.
value
.
relationVoList
)
{
recordRelation
.
value
.
relationVoList
.
forEach
(
rel
=>
{
const
from
=
rel
.
fromVertex
;
const
to
=
rel
.
toVertex
;
const
params
=
{
date
:
formattedDate
};
if
(
selectedIndustryId
.
value
)
{
params
.
chainId
=
selectedIndustryId
.
value
;
if
(
from
&&
!
nodesMap
.
has
(
from
.
id
))
{
nodesMap
.
set
(
from
.
id
,
{
id
:
from
.
id
,
// name: from.name,
name
:
dayjs
(
from
.
date
).
format
(
"YYYY年MM月DD日"
)
+
" "
+
"SDN清单更新"
,
symbol
:
`image://
${
markIcon
}
`
,
// 设置自定义图标
symbolSize
:
[
50
,
50
],
itemStyle
:
{
color
:
"#5470c6"
}
try
{
const
res
=
await
getDeepMiningIndustryFishbone
(
params
);
if
(
res
.
code
===
200
&&
res
.
data
&&
res
.
data
.
causes
&&
res
.
data
.
causes
.
length
>
0
)
{
const
rootCauses
=
res
.
data
.
causes
;
if
(
rootCauses
.
length
>
0
&&
rootCauses
[
0
].
causes
)
{
fishboneDataList
.
value
=
rootCauses
.
map
(
group
=>
{
});
}
if
(
to
&&
!
nodesMap
.
has
(
to
.
id
))
{
nodesMap
.
set
(
to
.
id
,
{
id
:
to
.
id
,
// name: to.name,
name
:
dayjs
(
to
.
date
).
format
(
"YYYY年MM月DD日"
)
+
" "
+
"SDN清单更新"
,
symbol
:
`image://
${
markIcon
}
`
,
// 设置自定义图标
symbolSize
:
[
50
,
50
],
itemStyle
:
{
color
:
"#5470c6"
}
});
}
});
}
return
Array
.
from
(
nodesMap
.
values
());
});
// 【修改】处理图表连线数据
const
graphLinks
=
computed
(()
=>
{
if
(
!
recordRelation
.
value
.
relationVoList
)
return
[];
return
recordRelation
.
value
.
relationVoList
.
map
(
rel
=>
{
const
relationType
=
rel
.
edgeInfo
?
rel
.
edgeInfo
.
value
:
""
;
const
style
=
getRelationStyle
(
relationType
);
return
{
causes
:
group
.
causes
||
[]
source
:
rel
.
fromVertex
.
id
,
target
:
rel
.
toVertex
.
id
,
// 将样式信息挂载到 label 或 data 上,供 formatter 和 lineStyle 使用
label
:
{
formatter
:
relationType
,
show
:
true
,
color
:
style
.
fontColor
,
// 字体颜色
backgroundColor
:
style
.
color
,
// 标签背景色
borderColor
:
style
.
color
,
// 标签边框色
padding
:
[
4
,
8
],
borderRadius
:
4
,
fontSize
:
12
},
lineStyle
:
{
color
:
style
.
lineColor
,
// 连线颜色
width
:
2
// curveness: 0.1 // 稍微有点弧度可能更好看
},
// 额外存储原始数据,以备后用
relationType
:
relationType
};
});
});
mainLineLabels
.
value
=
rootCauses
.
map
(
group
=>
group
.
text
||
""
);
}
else
{
fishboneDataList
.
value
=
[];
mainLineLabels
.
value
=
[];
const
handleAssociationClick
=
()
=>
{
console
.
log
(
"handleAssociationClick"
,
selectedSanctionIds
.
value
);
fetchRecordRelation
();
};
const
fetchRecordRelation
=
async
()
=>
{
if
(
selectedSanctionIds
.
value
.
length
===
0
)
{
return
;
}
try
{
const
res
=
await
getRecordRelation
(
selectedSanctionIds
.
value
);
console
.
log
(
"getRecordRelation"
,
res
);
if
(
!!
res
)
{
recordRelation
.
value
=
res
;
}
else
{
fishboneDataList
.
value
=
[];
mainLineLabels
.
value
=
[];
recordRelation
.
value
=
{
noRelationVertices
:
[],
relationVoList
:
[]
};
}
}
catch
(
error
)
{
console
.
error
(
"获取
产业链鱼骨图数据
失败:"
,
error
);
fishboneDataList
.
value
=
[]
;
console
.
error
(
"获取
制裁记录关联信息
失败:"
,
error
);
recordRelation
.
value
=
{
noRelationVertices
:
[],
relationVoList
:
[]
}
;
}
};
// 实体清单-深度挖掘-产业链列表信息
const
industryList
=
ref
([]);
const
selectedIndustryId
=
ref
(
null
);
const
getIndustryList
=
async
()
=>
{
try
{
const
res
=
await
getDeepMiningIndustry
();
if
(
res
.
code
===
200
&&
res
.
data
&&
res
.
data
.
length
>
0
)
{
industryList
.
value
=
res
.
data
;
selectedIndustryId
.
value
=
res
.
data
[
0
].
id
;
getFishboneData
();
getCnEntityOnChainData
();
const
vertexInfo
=
ref
({});
const
curNode
=
ref
({});
const
visible
=
ref
(
false
);
const
handleClickNode
=
node
=>
{
console
.
log
(
"节点点击"
,
node
);
curNode
.
value
=
node
.
data
;
getVertexInfo
(
node
.
data
.
id
).
then
(
res
=>
{
console
.
log
(
"getVertexInfo"
,
res
);
if
(
!!
res
)
{
vertexInfo
.
value
=
res
;
visible
.
value
=
true
;
}
else
{
industryList
.
value
=
[];
selectedIndustryId
.
value
=
null
;
vertexInfo
.
value
=
{};
}
}
catch
(
error
)
{
console
.
error
(
"获取产业链列表数据失败:"
,
error
);
industryList
.
value
=
[];
selectedIndustryId
.
value
=
null
;
});
};
// 【新增/修改】格式化变动 summary 的函数
const
formatChangeSummary
=
(
addList
,
delList
)
=>
{
const
parts
=
[];
// 处理新增列表
if
(
addList
&&
addList
.
length
>
0
)
{
// 将每个对象转换为 "value个实体" 或 "value名个人" 的形式
const
addItems
=
addList
.
map
(
item
=>
{
let
unit
=
"个"
;
let
noun
=
"实体"
;
if
(
item
.
key
===
"人物"
)
{
unit
=
"名"
;
noun
=
"个人"
;
}
else
if
(
item
.
key
===
"机构"
)
{
// 默认机构对应实体,也可以根据需求调整
unit
=
"个"
;
noun
=
"实体"
;
}
return
`
${
item
.
value
}${
unit
}${
noun
}
`
;
});
// 拼接:新增 + item1 + , + item2 ...
parts
.
push
(
`新增
${
addItems
.
join
(
","
)}
`
);
}
// 处理移除列表
if
(
delList
&&
delList
.
length
>
0
)
{
// 将每个对象转换为 "value个实体" 或 "value名个人" 的形式
const
delItems
=
delList
.
map
(
item
=>
{
let
unit
=
"个"
;
let
noun
=
"实体"
;
if
(
item
.
key
===
"人物"
)
{
unit
=
"名"
;
noun
=
"个人"
;
}
else
if
(
item
.
key
===
"机构"
)
{
unit
=
"个"
;
noun
=
"实体"
;
}
return
`
${
item
.
value
}${
unit
}${
noun
}
`
;
});
// 拼接:移除 + item1 + , + item2 ...
// 注意:题目要求“删除”,但之前代码用的是“移除”,这里统一使用“移除”或“删除”。
// 根据题目描述“展示样本为:新增12个实体,3名个人,移除1个实体”,这里使用“移除”更贴切上下文,
// 如果必须用“删除”,请将下面的 '移除' 改为 '删除'。
parts
.
push
(
`移除
${
delItems
.
join
(
","
)}
`
);
}
return
parts
.
length
>
0
?
parts
.
join
(
","
)
:
"无变动"
;
};
// 获取选择制裁
const
loading
=
ref
(
false
);
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
100
);
const
total
=
ref
(
0
);
const
totalPage
=
ref
(
0
);
const
sanctionList
=
ref
([]
);
const
currentSanctionId
=
ref
(
5
);
const
dateRange
=
ref
([
"2025-01-01"
,
"2025-12-31"
]
);
const
sanTypeId
=
ref
(
""
);
const
getDeepMiningSelectData
=
async
()
=>
{
const
fetchSanRecord
=
async
()
=>
{
loading
.
value
=
true
;
const
params
=
{
startDate
:
dateRange
.
value
&&
dateRange
.
value
[
0
]
?
dateRange
.
value
[
0
]
:
""
,
endDate
:
dateRange
.
value
&&
dateRange
.
value
[
1
]
?
dateRange
.
value
[
1
]
:
""
,
// typeName: "实体清单",
isCn
:
false
,
pageNum
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
sanTypeIds
:
[
Number
(
sanTypeId
.
value
)]
||
1
// 实体清单固定1
sanTypeId
:
sanTypeId
.
value
||
1
};
try
{
const
res
=
await
get
DeepMiningSelect
(
params
);
if
(
res
.
code
===
200
&&
res
.
data
&&
res
.
data
.
content
)
{
sanctionList
.
value
=
res
.
data
.
content
const
res
=
await
get
SanRecord
(
params
);
if
(
res
&&
res
.
length
>
0
)
{
sanctionList
.
value
=
res
.
map
(
item
=>
({
id
:
item
.
id
,
date
:
item
.
postDate
,
title
:
item
.
name
,
count
:
item
.
cnEntityCount
,
unit
:
"家中国实体"
,
// 接口未返回单位,暂时固定
summary
:
item
.
summary
,
// 保留额外信息备用
techDomainList
:
item
.
techDomainList
// 保留额外信息备用
id
:
item
.
sanRecordId
,
date
:
item
.
sanRecordDate
,
title
:
item
.
sanRecordName
,
count
:
item
.
cnEntitiesNum
,
unit
:
"家中国实体"
}))
.
reverse
();
// 默认选中第一条
if
(
sanctionList
.
value
.
length
>
0
)
{
currentSanctionId
.
value
=
sanctionList
.
value
[
0
].
id
;
// getFishboneData(); // 这里不需要调用,因为getIndustryList会调用
}
}
else
{
sanctionList
.
value
=
[];
...
...
@@ -445,102 +418,25 @@ const getDeepMiningSelectData = async () => {
}
};
// 日期选择变化
const
handleDateChange
=
()
=>
{
currentPage
.
value
=
1
;
getDeepMiningSelectData
();
};
// ✅ 自动轮播定时器
const
autoPlayTimer
=
ref
(
null
);
// ✅ 自动下一个(支持循环)
const
handleNextClickAuto
=
()
=>
{
const
currentIndex
=
sanctionList
.
value
.
findIndex
(
item
=>
item
.
id
===
currentSanctionId
.
value
);
let
nextItem
;
if
(
currentIndex
<
sanctionList
.
value
.
length
-
1
)
{
nextItem
=
sanctionList
.
value
[
currentIndex
+
1
];
}
else
{
nextItem
=
sanctionList
.
value
[
0
];
// 循环到第一个
}
if
(
nextItem
)
{
handleSanctionSelect
(
nextItem
.
id
);
}
fetchSanRecord
();
};
const
handleSanctionSelect
=
id
=>
{
currentSanctionId
.
value
=
id
;
getFishboneData
();
getCnEntityOnChainData
();
};
const
activeTab
=
ref
([
"制裁时序分析"
,
"限制关联分析"
]);
const
activeIndex
=
ref
(
0
);
const
dateRange
=
ref
([
"2025-01-01"
,
"2025-12-31"
]);
const
sanctionList
=
ref
([]);
const
currentSanctionId
=
ref
(
5
);
const
cnEntityOnChainData
=
ref
({});
const
mainLineLabels
=
ref
([
"关键原材料"
,
"电池材料"
,
"电子元器件"
,
"动力电池"
,
"电子控制系统"
,
"动力电池"
]);
// 获取奇数索引的数据组(放在上方)
const
getOddGroups
=
data
=>
{
return
data
.
filter
((
_
,
index
)
=>
index
%
2
!==
0
);
};
// 获取偶数索引的数据组(放在下方)
const
getEvenGroups
=
data
=>
{
return
data
.
filter
((
_
,
index
)
=>
index
%
2
===
0
);
};
// 获取上方鱼骨图位置类名
const
getTopBoneClass
=
index
=>
{
const
positions
=
[
"top-bone"
,
"top-bone1"
,
"top-bone2"
];
return
positions
[
index
%
3
]
||
"top-bone"
;
};
// 获取下方鱼骨图位置类名
const
getBottomBoneClass
=
index
=>
{
const
positions
=
[
"bottom-bone"
,
"bottom-bone1"
,
"bottom-bone2"
];
return
positions
[
index
%
3
]
||
"bottom-bone"
;
};
// 获取左侧显示的项目(前半部分)
const
getLeftItems
=
items
=>
{
const
midpoint
=
Math
.
ceil
(
items
.
length
/
2
);
return
items
.
slice
(
0
,
midpoint
);
};
// 获取右侧显示的项目(后半部分)
const
getRightItems
=
items
=>
{
const
midpoint
=
Math
.
ceil
(
items
.
length
/
2
);
return
items
.
slice
(
midpoint
);
};
// 格式化比率
const
formatRate
=
rate
=>
{
if
(
rate
===
undefined
||
rate
===
null
)
return
"0.00"
;
return
(
rate
*
100
).
toFixed
(
2
);
};
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
""
;
// 获取选择制裁
getDeepMiningSelectData
();
// 获取产业链信息
getIndustryList
();
fetchSanRecord
();
});
</
script
>
<
style
lang=
"scss"
scoped
>
/* 样式保持不变 */
.main
{
width
:
100%
;
padding-top
:
1
6
px
;
padding-top
:
1
2
px
;
padding-bottom
:
50px
;
display
:
flex
;
justify-content
:
space-between
;
...
...
@@ -554,7 +450,7 @@ onMounted(() => {
padding
:
0
22px
0
23px
;
display
:
flex
;
flex-direction
:
column
;
height
:
calc
(
100%
-
56
px
);
height
:
calc
(
100%
-
25
px
);
.date-picker-box
{
margin-bottom
:
16px
;
...
...
@@ -574,34 +470,6 @@ onMounted(() => {
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
95
,
101
,
108
);
}
.pagination
{
display
:
flex
;
gap
:
12px
;
.page-btn
{
width
:
28px
;
height
:
28px
;
background
:
rgba
(
231
,
243
,
255
,
1
);
border-radius
:
4px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
color
:
rgb
(
5
,
95
,
194
);
font-size
:
16px
;
&
.disabled
{
cursor
:
not
-
allowed
;
background
:
#f5f7fa
;
color
:
#c0c4cc
;
}
&
:not
(
.disabled
)
:hover
{
background
:
#e1eeff
;
}
}
}
}
.list-content
{
...
...
@@ -619,7 +487,6 @@ onMounted(() => {
}
.list-item
{
// height: 60px;
border
:
1px
solid
rgb
(
234
,
236
,
238
);
border-radius
:
4px
;
margin-bottom
:
8px
;
...
...
@@ -631,6 +498,12 @@ onMounted(() => {
transition
:
all
0
.3s
;
position
:
relative
;
background
:
#fff
;
.item-label
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
width
:
100%
;
}
.item-left
{
width
:
260px
;
...
...
@@ -638,6 +511,10 @@ onMounted(() => {
color
:
rgb
(
59
,
65
,
75
);
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
margin-right
:
10px
;
}
.item-right
{
...
...
@@ -645,6 +522,7 @@ onMounted(() => {
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
flex-shrink
:
0
;
}
&
:hover
{
...
...
@@ -668,7 +546,6 @@ onMounted(() => {
bottom
:
10px
;
width
:
4px
;
background-color
:
rgb
(
5
,
95
,
194
);
// border-radius: 4px 0 0 4px;
}
}
}
...
...
@@ -682,508 +559,61 @@ onMounted(() => {
.right-main
{
margin-top
:
11px
;
height
:
calc
(
100%
-
56
px
);
height
:
calc
(
100%
-
10
px
);
padding
:
0
16px
16px
16px
;
.right-main-content
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
.hintWrap
{
display
:
flex
;
align-items
:
center
;
padding
:
7px
12px
;
border
:
1px
solid
rgba
(
231
,
243
,
255
,
1
);
border-radius
:
4px
;
background
:
rgba
(
246
,
250
,
255
,
1
);
margin-bottom
:
9px
;
.icon1
{
width
:
19px
;
height
:
20px
;
background-image
:
url("../assets/ai.png")
;
background-size
:
100%
100%
;
flex-shrink
:
0
;
}
.title
{
color
:
rgb
(
5
,
95
,
194
);
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
margin-left
:
13px
;
flex
:
1
;
}
.icon2Wrap
{
width
:
24px
;
height
:
24px
;
background-color
:
rgba
(
231
,
243
,
255
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
border-radius
:
12px
;
margin-left
:
20px
;
flex-shrink
:
0
;
.icon2
{
width
:
24px
;
height
:
24px
;
background-image
:
url("../assets/right.png")
;
background-size
:
100%
100%
;
}
}
}
.right-main-content-main
{
flex
:
1
;
// border: 1px solid #eaecee;
// border-radius: 4px;
// background: #f7f8f9;
position
:
relative
;
overflow
:
hidden
;
.fishbone-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
.fishbone-scroll-container
{
.relation-empty
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
width
:
100%
;
height
:
100%
;
overflow-x
:
auto
;
overflow-y
:
hidden
;
scrollbar-width
:
thin
;
scrollbar-color
:
rgba
(
144
,
202
,
249
,
0
.5
)
transparent
;
&
:
:-
webkit-scrollbar
{
height
:
6px
;
}
&
:
:-
webkit-scrollbar-track
{
background
:
transparent
;
}
&
:
:-
webkit-scrollbar-thumb
{
background-color
:
rgba
(
144
,
202
,
249
,
0
.5
);
border-radius
:
3px
;
height
:
300px
;
width
:
300px
;
.empty
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
Source
Han
Sans
CN
;
color
:
rgb
(
132
,
136
,
142
);
line-height
:
30px
;
}
}
.fishbone
{
position
:
relative
;
width
:
fit-content
;
height
:
100%
;
margin-top
:
40px
;
min-width
:
100%
;
padding-left
:
275px
;
margin-left
:
40px
;
.main-line
{
margin-top
:
280px
;
width
:
1888px
;
height
:
3px
;
background
:
rgb
(
230
,
231
,
232
);
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
0
100px
;
// 虚线
&
:
:
after
{
content
:
""
;
position
:
absolute
;
top
:
0
;
left
:
0
;
.relation-content
{
width
:
100%
;
height
:
100%
;
// background: repeating-linear-gradient(to right, rgba(174, 208, 255, 1) 0, rgba(174, 208, 255, 1) 10px, transparent 10px, transparent 20px);
}
// 添加中间的文字块
.main-line-text
{
position
:
absolute
;
// top: -14px;
font-size
:
16px
;
color
:
#055fc2
;
font-weight
:
bold
;
background-color
:
#f7f8f9
;
padding
:
0
10px
;
z-index
:
2
;
// 箭头背景
height
:
32px
;
line-height
:
32px
;
width
:
160px
;
text-align
:
center
;
background
:
rgba
(
231
,
243
,
255
,
1
);
clip-path
:
polygon
(
0%
0%
,
90%
0%
,
100%
50%
,
90%
100%
,
0%
100%
,
10%
50%
);
&
.blue-theme
{
background
:
rgba
(
231
,
243
,
255
,
1
);
color
:
rgba
(
22
,
119
,
255
,
1
);
}
&
.green-theme
{
background
:
rgba
(
225
,
255
,
251
,
1
);
color
:
rgba
(
19
,
168
,
168
,
1
);
}
&
.purple-theme
{
background
:
rgba
(
246
,
235
,
255
,
1
);
color
:
rgba
(
146
,
84
,
222
,
1
);
}
}
}
}
.company-icon
{
width
:
16px
;
height
:
16px
;
margin
:
0
4px
;
object-fit
:
contain
;
}
.top-bone
{
position
:
absolute
;
top
:
20px
;
right
:
200px
;
width
:
3px
;
height
:
260px
;
background
:
rgb
(
230
,
231
,
232
);
transform
:
skew
(
30deg
);
z-index
:
1
;
.left-bone
{
color
:
#777
;
position
:
absolute
;
top
:
0
;
left
:
-150px
;
width
:
150px
;
height
:
50px
;
// overflow: hidden;
.left-bone-item
{
transform
:
skew
(
-30deg
);
height
:
45px
;
margin-bottom
:
2px
;
margin-top
:
2px
;
display
:
flex
;
justify-content
:
flex-end
;
align-items
:
center
;
.text
{
margin-left
:
4px
;
height
:
25px
;
line-height
:
25px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.line
{
margin-left
:
7px
;
width
:
40px
;
height
:
2px
;
background
:
rgb
(
230
,
231
,
232
);
}
}
}
.right-bone
{
color
:
#777
;
position
:
absolute
;
top
:
0
;
right
:
-150px
;
width
:
150px
;
height
:
210px
;
overflow
:
hidden
;
.right-bone-item
{
transform
:
skew
(
-30deg
);
height
:
39px
;
margin-bottom
:
2px
;
margin-top
:
2px
;
display
:
flex
;
justify-content
:
flex-start
;
align-items
:
center
;
.line
{
margin-right
:
7px
;
width
:
30px
;
height
:
2px
;
background
:
rgb
(
230
,
231
,
232
);
}
.text
{
max-width
:
100px
;
margin-right
:
4px
;
height
:
25px
;
line-height
:
25px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
}
}
}
.top-bone1
{
@extend
.top-bone
;
right
:
500px
;
}
.top-bone2
{
@extend
.top-bone
;
right
:
800px
;
}
.bottom-bone
{
position
:
absolute
;
top
:
280px
;
right
:
360px
;
width
:
3px
;
height
:
260px
;
background
:
rgb
(
230
,
231
,
232
);
transform
:
skew
(
-30deg
);
z-index
:
1
;
.left-bone
{
color
:
#777
;
position
:
absolute
;
top
:
50px
;
left
:
-150px
;
width
:
150px
;
height
:
260px
;
.left-bone-item
{
transform
:
skew
(
30deg
);
height
:
39px
;
margin-bottom
:
2px
;
margin-top
:
2px
;
}
.main-association
{
padding-top
:
12px
!
important
;
justify-content
:
flex-start
!
important
;
gap
:
16px
;
}
.dialog-content
{
padding
:
20px
;
display
:
flex
;
justify-content
:
flex-end
;
align-items
:
center
;
.text
{
margin-left
:
4px
;
height
:
25px
;
max-width
:
130px
;
line-height
:
25px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.line
{
margin-left
:
7px
;
width
:
40px
;
height
:
2px
;
background
:
rgb
(
230
,
231
,
232
);
}
}
}
.right-bone
{
color
:
#777
;
position
:
absolute
;
top
:
50px
;
right
:
-150px
;
width
:
150px
;
height
:
260px
;
.right-bone-item
{
transform
:
skew
(
30deg
);
height
:
35px
;
margin-bottom
:
2px
;
margin-top
:
2px
;
flex-direction
:
column
;
gap
:
8px
;
border-top
:
1px
solid
rgb
(
238
,
238
,
238
);
.content-item
{
display
:
flex
;
justify-content
:
flex-start
;
align-items
:
center
;
.line
{
margin-right
:
7px
;
width
:
30px
;
height
:
2px
;
background
:
rgb
(
230
,
231
,
232
);
}
.text
{
max-width
:
100px
;
margin-right
:
4px
;
height
:
25px
;
line-height
:
25px
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
}
}
}
.bottom-bone1
{
@extend
.bottom-bone
;
right
:
660px
;
}
.bottom-bone2
{
@extend
.bottom-bone
;
right
:
960px
;
}
}
.right-main-content-footer
{
height
:
84px
;
margin-top
:
16px
;
display
:
flex
;
justify-content
:
space-between
;
.footer-item1
,
.footer-item2
,
.footer-item3
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
flex-end
;
}
.footer-item1
{
.footer-item1-top
{
height
:
28px
;
text-align
:
center
;
line-height
:
28px
;
background
:
url("../../../../assets/images/bg3.png")
;
background-size
:
100%
100%
;
color
:
rgba
(
22
,
119
,
255
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
700
;
margin-top
:
15px
;
margin-right
:
-10px
;
// Negative margin to overlap/connect
position
:
relative
;
// Ensure z-index works if needed
z-index
:
1
;
}
.footer-item1-bottom
{
display
:
flex
;
justify-content
:
center
;
.icon
{
margin-top
:
9px
;
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
margin-top
:
7px
;
margin-left
:
8px
;
height
:
22px
;
color
:
rgba
(
206
,
79
,
81
,
1
);
font-size
:
14px
;
}
}
}
.footer-item2
{
.footer-item2-top
{
height
:
28px
;
text-align
:
center
;
line-height
:
28px
;
background
:
url("../../../../assets/images/bg2.png")
;
background-size
:
100%
100%
;
color
:
rgba
(
19
,
168
,
168
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
700
;
margin-top
:
15px
;
margin-right
:
-10px
;
// Negative margin to connect with next item
margin-left
:
-10px
;
// Negative margin to connect with prev item
position
:
relative
;
z-index
:
1
;
}
.footer-item2-bottom
{
display
:
flex
;
justify-content
:
center
;
.icon
{
margin-top
:
9px
;
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
margin-top
:
7px
;
margin-left
:
8px
;
height
:
22px
;
color
:
rgba
(
206
,
79
,
81
,
1
);
font-size
:
14px
;
}
}
}
.footer-item3
{
.footer-item3-top
{
height
:
28px
;
text-align
:
center
;
line-height
:
28px
;
background
:
url("../../../../assets/images/bg1.png")
;
background-size
:
100%
100%
;
color
:
rgba
(
146
,
84
,
222
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
gap
:
8px
;
.item-label
{
font-size
:
146x
;
font-weight
:
700
;
margin-top
:
15px
;
margin-left
:
-10px
;
// Negative margin to connect
position
:
relative
;
z-index
:
1
;
}
.footer-item3-bottom
{
display
:
flex
;
justify-content
:
center
;
.icon
{
margin-top
:
9px
;
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
font-family
:
Source
Han
Sans
CN
;
color
:
rgba
(
59
,
65
,
75
,
1
);
line-height
:
24px
;
}
.text
{
margin-top
:
7px
;
margin-left
:
8px
;
height
:
22px
;
color
:
rgba
(
206
,
79
,
81
,
1
);
.item-desc
{
font-size
:
14px
;
font-family
:
Source
Han
Sans
CN
;
margin-top
:
3px
;
}
}
}
}
}
}
}
}
.main-association
{
justify-content
:
flex-start
!
important
;
gap
:
16px
;
}
</
style
>
src/views/finance/entityList/components/deepMining/components/mock.json
0 → 100644
浏览文件 @
9f41b96e
{
"id"
:
2140
,
"name"
:
"美国以伊朗石油非法贸易为由实施制裁,多家中国企业被列入制裁名单"
,
"domainList"
:
[
"海洋"
],
"relyFileList"
:
[
{
"id"
:
null
,
"name"
:
"第13382号行政命令"
},
{
"id"
:
null
,
"name"
:
"第14530号行政命令"
}
],
"relySanList"
:
[
{
"sanTypeId"
:
2
,
"id"
:
2145
,
"title"
:
"OFAC将38个实体及4名个人列入SDN清单,涉及中国关联主体"
,
"postDate"
:
"2025-10-14"
}
],
"sanReasonList"
:
[
"参与了从伊朗购买、收购、销售、运输或营销石油化工产品"
,
"参与了与采购、获取、销售、运输或销售伊朗石油及石油制品相关的重大交易"
],
"addObjectList"
:
[
{
"key"
:
"机构"
,
"value"
:
3
}
],
"delObjectList"
:
[
{
"key"
:
"人物"
,
"value"
:
1
}
],
"sanList"
:
[
{
"entityId"
:
"91310115MA1HBB8PXH"
,
"entityName"
:
"SHANGHAI QIZHANG SHIP MANAGEMENT CO., LTD."
,
"entityNameZh"
:
"上海启章船舶管理有限公司"
,
"entityTypeId"
:
2
,
"entityTypeName"
:
"机构"
,
"domainNames"
:
[
"海洋"
]
},
{
"entityId"
:
"71180883"
,
"entityName"
:
"ALL WIN SHIPPING MANAGEMENT LIMITED"
,
"entityNameZh"
:
"誠安船舶管理有限公司"
,
"entityTypeId"
:
2
,
"entityTypeName"
:
"机构"
,
"domainNames"
:
[
"海洋"
]
},
{
"entityId"
:
"91370211MAEBUA7E2Q"
,
"entityName"
:
"QINGDAO OCEAN KIMO SHIP MANAGEMENT CO LTD"
,
"entityNameZh"
:
"青岛明洋凯茂船舶管理有限公司"
,
"entityTypeId"
:
2
,
"entityTypeName"
:
"机构"
,
"domainNames"
:
[
"海洋"
]
}
]
}
\ No newline at end of file
src/views/finance/entityList/components/deepMining/index.vue
浏览文件 @
9f41b96e
...
...
@@ -13,7 +13,7 @@
</div>
</div>
<div
class=
"main"
>
<div
v-if=
"activeIndex == 0"
>
<div
class=
"sanctionTime"
v-if=
"activeIndex == 0"
>
<div
class=
"left"
>
<AnalysisBox
title=
"选择制裁"
>
<div
class=
"left-main"
>
...
...
@@ -76,7 +76,7 @@
</
template
>
<div
class=
"right-main"
>
<div
class=
"right-main-content"
>
<div
class=
"hintWrap"
>
<
!-- <
div class="hintWrap">
<div class="icon1"></div>
<div class="title">
2025年实体清单制裁范围扩大至芯片制造环节,为中国的芯片制造能力划定“技术天花板”,阻止其向更先进水平发展。制裁范围向上游设备和材料、下游先进封装以及关键工具(如EDA软件)延伸,意图瓦解中国构建自主可控产业链的努力。
...
...
@@ -84,7 +84,7 @@
<div class="icon2Wrap">
<div class="icon2"></div>
</div>
</div>
</div>
-->
<div
class=
"right-main-content-main"
>
<div
class=
"fishbone-wrapper"
>
<div
class=
"fishbone-scroll-container"
ref=
"scrollContainerRef"
>
...
...
@@ -557,8 +557,6 @@ onUnmounted(() => {
</
script
>
<
style
scoped
lang=
"scss"
>
.deep-mining
{
width
:
1601px
;
margin
:
0
auto
;
...
...
@@ -610,6 +608,11 @@ onUnmounted(() => {
padding-bottom
:
50px
;
display
:
flex
;
justify-content
:
space-between
;
.sanctionTime
{
display
:
flex
;
padding-top
:
12px
;
gap
:
10px
;
}
.left
{
width
:
480px
;
...
...
src/views/finance/entityList/components/sanctionsOverview/components/introductionPage/index.vue
浏览文件 @
9f41b96e
...
...
@@ -433,11 +433,6 @@ onMounted(() => {
<
/script
>
<
style
scoped
lang
=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
}
.
introduction
-
page
{
width
:
1601
px
;
margin
:
0
auto
;
...
...
src/views/finance/entityList/components/sanctionsOverview/components/listPage/index.vue
浏览文件 @
9f41b96e
...
...
@@ -532,12 +532,12 @@ watch(customDateRange, () => {
align-items
:
center
;
.search-input
{
width
:
3
88
px
;
width
:
3
60
px
;
height
:
32px
;
:deep
(
.el-input__wrapper
)
{
padding
:
0
11px
;
border
:
1px
solid
rgba
(
170
,
173
,
177
,
1
);
border
:
1px
solid
rgba
(
170
,
173
,
177
,
0
.5
);
background-color
:
#fff
;
border-radius
:
3px
;
}
...
...
@@ -579,7 +579,7 @@ watch(customDateRange, () => {
.left
{
padding-bottom
:
20px
;
width
:
3
88
px
;
width
:
3
60
px
;
height
:
auto
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
...
...
@@ -598,11 +598,12 @@ watch(customDateRange, () => {
.el-checkbox
{
width
:
50%
;
margin-right
:
0
;
margin-bottom
:
4px
;
//
margin-bottom: 4px;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
}
...
...
@@ -649,7 +650,7 @@ watch(customDateRange, () => {
}
.right
{
width
:
1
196
px
;
width
:
1
223
px
;
height
:
auto
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
...
...
src/views/finance/index.vue
浏览文件 @
9f41b96e
...
...
@@ -724,7 +724,7 @@ import {
getCountDomainByYear
,
getEntitiesList
,
getSanctionProcess
,
//
getSanDomainCount,
getSanDomainCount
,
// getRiskSignal,
// getSocialMediaInfo,
// getNewsInfo,
...
...
@@ -738,7 +738,7 @@ import {
getNewsInfo
,
getSocialMediaInfo
,
getReleaseCount
,
getSanDomainCount
,
//
getSanDomainCount,
getAnnualSanDomain
// getSanctionProcess
}
from
"@/api/finance"
;
...
...
@@ -1143,7 +1143,7 @@ const radarOption = ref({
// 获取雷达图数据
const
fetchRadarData
=
async
checked
=>
{
try
{
const
data
=
await
getSanDomainCount
(
checked
,
"export"
);
const
data
=
await
getSanDomainCount
(
checked
,
allSanTypeIds
.
value
.
join
(
","
)
);
if
(
data
&&
Array
.
isArray
(
data
)
&&
data
.
length
>
0
)
{
// 收集所有可能的领域名称
const
allDomains
=
new
Set
();
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论