Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
500577d5
提交
500577d5
authored
12月 31, 2025
作者:
李智林
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
出口管制2.0更新
上级
a94961db
显示空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
2349 行增加
和
1087 行删除
+2349
-1087
exportControl.js
src/api/exportControl.js
+10
-11
exportControlV2.0.js
src/api/exportControlV2.0.js
+342
-0
index.vue
src/views/exportControl/index.vue
+21
-29
index.vue
...s/sanctionsOverview/components/introductionPage/index.vue
+125
-104
RuleSubsidiaryDialog.vue
...ionsOverview/components/listPage/RuleSubsidiaryDialog.vue
+17
-84
index.vue
...omponents/sanctionsOverview/components/listPage/index.vue
+6
-3
index.vue
...ol/v2.0SingleSanction/components/dataStatistics/index.vue
+215
-95
index.vue
...ontrol/v2.0SingleSanction/components/deepMining/index.vue
+438
-145
index.vue
...ents/impactAnalysis/components/industrialImpact/index.vue
+546
-145
index.vue
...onents/impactAnalysis/components/researchImpact/index.vue
+282
-157
index.vue
...v2.0SingleSanction/components/sanctionsOverview/index.vue
+291
-307
index.vue
src/views/exportControl/v2.0SingleSanction/index.vue
+56
-7
没有找到文件。
src/api/exportControl.js
浏览文件 @
500577d5
...
...
@@ -842,23 +842,21 @@ export function getScientificImpactEntityList(startTime) {
/**
* 概览页--获取出口管制制裁措施
* @param {Object} data
* @param {string} [data.typeName="实体清单"] - 类型名称
* @param {boolean} [data.isCn=true] - 是否只看中国实体
* @param {string[]} [data.techDomains] - 科技领域ID列表
* @param {number[]} [data.years] - 制裁年份列表
* @param {string} [data.startDate] - 开始时间
* @param {string} [data.endDate] - 结束时间
* @param {string} [data.keyword] - 搜索字段
* @param {number[]} [data.entityTypes] - 实体类型(2:科研院所;3:高校;4:企业)
* @param {number} [data.pageNum=1] - 页码
* @param {number} [data.pageSize=10] - 每页数量
* @param {string} [data.typeName="实体清单"] - 类型名称(实体清单、商业管制清单、军事最终用户清单)
* @param {boolean} [data.isCn=true] - 是否只看涉华
* @param {Array<string>} [data.techDomainIds] - 科技领域ID
* @param {Array<number>} [data.years] - 制裁年份
*/
export
function
getExportControlList
(
data
)
{
return
request200
(
request
({
method
:
"POST"
,
url
:
"/api/
sanctionList/pageQuery
"
,
url
:
"/api/
entitiesDataCount/getSanctionProcess
"
,
data
})
);
}
//
\ No newline at end of file
src/api/exportControlV2.0.js
浏览文件 @
500577d5
...
...
@@ -225,3 +225,344 @@ export function getDeepMiningIndustryEntity(params) {
params
,
})
}
// 单次制裁-制裁概况-基本信息
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @header token
*/
export
function
getSingleSanctionOverview
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/record/getDetail`
,
params
,
})
}
// 单次制裁-制裁概况-制裁实体国家分布
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @header token
*/
export
function
getSingleSanctionEntityCountry
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/countryRegion`
,
params
,
})
}
// 单次制裁-制裁概况-制裁背景
/**
* @param {Object} data
* @param {string} data.sanRecordId - 制裁记录ID
* @param {number} [data.pageNum=1] - 页码
* @param {number} [data.pageSize=3] - 每页数量
* @header token
*/
export
function
getSingleSanctionBackground
(
data
)
{
return
request
({
method
:
'POST'
,
url
:
`/api/sanctionList/record/background`
,
data
,
})
}
// 单次制裁-制裁概况-制裁清单
/**
* @param {Object} data
* @param {Integer} data.sanRecordId - 制裁记录ID
* @param {Boolean} [data.isOnlyCn=true] - 是否仅查询中国数据
* @param {String} [data.domainId] - 科技领域ID
* @param {String} [data.searchText] - 搜索文本
* @header token
*/
export
function
getSingleSanctionOverviewList
(
data
)
{
return
request
({
method
:
'POST'
,
url
:
`/api/sanctionList/record/sanList`
,
data
,
})
}
// 单次制裁-数据统计-总量统计
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @header token
*/
export
function
getSingleSanctionTotalCount
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/total`
,
params
,
})
}
// 单次制裁-数据统计-制裁实体领域分布情况
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @param {string} [params.startDate] - 开始时间
* @param {string} [params.endDate] - 结束时间
* @header token
*/
export
function
getSingleSanctionDomainCount
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/domain`
,
params
,
})
}
// 单次制裁-数据统计-制裁实体类型分布情况
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @param {string} [params.startDate] - 开始时间
* @param {string} [params.endDate] - 结束时间
* @header token
*/
export
function
getSingleSanctionEntityTypeCount
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/entityType`
,
params
,
})
}
// 单次制裁-数据统计-制裁实体国家分布
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @header token
*/
export
function
getSingleSanctionEntityCountryCount
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/countryRegion`
,
params
,
})
}
// 单次制裁-数据统计-制裁实体地域分布情况
/**
* @param {Object} params
* @param {string} params.sanRecordId - 制裁记录ID
* @param {string} [params.startDate] - 开始时间
* @param {string} [params.endDate] - 结束时间
* @header token
*/
export
function
getSingleSanctionEntityRegionCount
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/statistics/el/region`
,
params
,
})
}
// 单次制裁-深度挖掘-本次制裁实体清单列表
/**
* @param {Object} params
* @param {Integer} data.sanRecordId - 制裁记录ID
* @param {Boolean} [data.isOnlyCn=true] - 是否仅查询中国数据
* @param {String} [data.domainId] - 科技领域ID
* @param {String} [data.searchText] - 搜索文本
* @header token
*/
export
function
getSingleSanctionEntityList
(
data
)
{
return
request
({
method
:
'POST'
,
url
:
`/api/sanctionList/record/sanListByType`
,
data
,
})
}
// 单次制裁-深度挖掘-制裁实体供应链信息
/**
* @param {Object} params
* @param {Integer} params.orgId - 组织ID
* @header token
*/
export
function
getSingleSanctionEntitySupplyChain
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/record/supplyChain`
,
params
,
})
}
// 单次制裁-深度挖掘-制裁实体股权信息
/**
* @param {Object} params
* @param {Integer} params.orgId - 组织ID
* @param {Boolean} params.rule - 是否遵循规则
* @header token
*/
export
function
getSingleSanctionEntityEquity
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/sanctionList/record/shareholding`
,
params
,
})
}
// 单次制裁-影响分析-企业规模-营收
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityRevenue
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/scale/revenue`
,
params
,
})
}
// 单次制裁-影响分析-企业规模-净利润
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityNetProfit
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/scale/netProfit`
,
params
,
})
}
// 单次制裁-影响分析-企业规模-人员
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityPersonnel
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/scale/personnel`
,
params
,
})
}
// 单次制裁-影响分析-企业市值变化
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityMarketValue
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/marketValue`
,
params
,
})
}
// 单次制裁-影响分析-企业研发投入
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityRDInvestment
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/rdInvestment`
,
params
,
})
}
// 单次制裁-影响分析-企业市场占比
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityMarketShare
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/marketShare`
,
params
,
})
}
// 单次制裁-影响分析-科研仪器对美依赖情况
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityRDInstrumentDependency
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/instrument/getDependencyUS`
,
params
,
})
}
// 单次制裁-影响分析-科研仪器进口国分布
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @header token
*/
export
function
getSingleSanctionEntityRDInstrumentImportCountry
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/instrument/getOriginCount`
,
params
,
})
}
// 单次制裁-影响分析-新增国际合作项目
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @param {String} params.domainId - 科技领域ID
* @header token
*/
export
function
getSingleSanctionEntityInternationalCooperation
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/project/icCount`
,
params
,
})
}
// 单次制裁-影响分析-新增国际合作论文
/**
* @param {Object} params
* @param {Integer} params.id - 机构ID
* @param {String} params.domainId - 科技领域ID
* @header token
*/
export
function
getSingleSanctionEntityInternationalPaper
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/organization/paper/icCount`
,
params
,
})
}
\ No newline at end of file
src/views/exportControl/index.vue
浏览文件 @
500577d5
...
...
@@ -741,26 +741,8 @@ import listIcon from "./assets/images/icon-list.png";
import
dotIcon
from
"./assets/images/info2-icon.png"
;
import
entityIcon
from
"./assets/images/icon-entity.png"
;
import
comTitle
from
"./assets/images/panel1_1.png"
;
import
newsImg
from
"@/assets/images/news-img.png"
;
import
newsImg1
from
"@/assets/images/newsImg1.png"
;
import
getMultiLineChart
from
"./utils/multiLineChart"
;
import
bill1
from
"./assets/images/bill1.png"
;
import
bill2
from
"./assets/images/bill2.png"
;
import
bill3
from
"./assets/images/bill3.png"
;
import
bill4
from
"./assets/images/bill4.png"
;
import
bill5
from
"./assets/images/bill5.png"
;
import
bill6
from
"./assets/images/bill6.png"
;
import
bill7
from
"./assets/images/bill7.png"
;
import
bill8
from
"./assets/images/bill8.png"
;
import
bill9
from
"./assets/images/bill9.png"
;
import
bill10
from
"./assets/images/bill10.png"
;
import
bill11
from
"./assets/images/bill11.png"
;
import
bill12
from
"./assets/images/bill12.png"
;
import
icon01
from
"./assets/images/jianzhu.png"
;
import
{
getEntitiesDataCount
,
getEntitiesDataInfo
,
...
...
@@ -795,7 +777,7 @@ const techOptions = [
{
label
:
"太空"
,
value
:
13
},
{
label
:
"核"
,
value
:
14
}
];
const
timeOptions
=
[
"全部时间"
,
"2025年"
,
"2024年"
,
"2023年"
,
"2022年"
,
"2021年"
];
const
timeOptions
=
[
"全部时间"
,
"2025年"
,
"2024年"
,
"2023年"
,
"2022年"
,
"2021年"
,
"2020年"
,
"2019年"
,
"2018年"
,
"2017年"
,
"2016年"
,
"2015年"
,
"2014年"
,
"2013年"
,
"2012年"
,
"2011年"
,
"2010年"
,
"2009年"
,
"2008年"
,
"2007年"
,
"2006年"
,
"2005年"
,
"2004年"
,
"2003年"
,
"2002年"
,
"2001年"
];
const
checkedTech
=
ref
([
0
]);
const
checkedTime
=
ref
([
"全部时间"
]);
...
...
@@ -1246,24 +1228,30 @@ const fetchSanctionList = async () => {
const
params
=
{
pageNum
:
currentPageAll
.
value
,
pageSize
:
pageSizeAll
.
value
,
techDomains
:
techDomains
,
techDomain
Id
s
:
techDomains
,
years
:
years
,
isCn
:
false
,
typeName
:
""
typeName
:
"
实体清单
"
};
const
res
=
await
getExportControlList
(
params
);
if
(
res
&&
res
.
content
)
{
sanctionList
.
value
=
res
.
content
.
map
(
item
=>
{
const
tags
=
Array
.
isArray
(
item
.
techDomains
)
?
item
.
techDomains
:
item
.
techDomain
?
[
item
.
techDomain
]
:
[];
const
tags
=
Array
.
isArray
(
item
.
techDomains
)
?
item
.
techDomains
:
item
.
techDomain
?
[
item
.
techDomain
]
:
item
.
techDomainList
||
[];
const
fullTime
=
item
.
startTime
?
formatAnyDateToChinese
(
item
.
startTime
)
:
item
.
publishDate
||
item
.
date
;
const
fullTime
=
item
.
startTime
?
formatAnyDateToChinese
(
item
.
startTime
)
:
item
.
p
ostDate
||
item
.
p
ublishDate
||
item
.
date
;
let
year
=
""
;
let
dateStr
=
fullTime
;
if
(
typeof
fullTime
===
"string"
&&
fullTime
.
includes
(
"年"
))
{
if
(
typeof
fullTime
===
"string"
)
{
if
(
fullTime
.
includes
(
"年"
))
{
const
parts
=
fullTime
.
split
(
"年"
);
year
=
parts
[
0
];
dateStr
=
parts
[
1
].
replace
(
/
\s
+/g
,
""
);
}
else
if
(
fullTime
.
includes
(
"-"
))
{
const
parts
=
fullTime
.
split
(
"-"
);
year
=
parts
[
0
];
dateStr
=
parts
.
slice
(
1
).
join
(
"-"
);
}
}
return
{
...
...
@@ -1274,7 +1262,7 @@ const fetchSanctionList = async () => {
title
:
item
.
entityNameZh
||
item
.
entityName
||
item
.
title
||
item
.
name
,
desc
:
item
.
sanReason
||
item
.
description
||
item
.
summary
||
item
.
content
,
tags
:
tags
,
countTag
:
item
.
ruleOrgCount
?
`
${
item
.
ruleOrgCount
}
家关联实体`
:
item
.
countTag
||
""
countTag
:
item
.
cnEntityCount
?
`
${
item
.
cnEntityCount
}
家中国实体`
:
item
.
ruleOrgCount
?
`
${
item
.
ruleOrgCount
}
家关联实体`
:
item
.
countTag
||
""
};
});
totalAll
.
value
=
res
.
totalElements
;
...
...
@@ -1456,12 +1444,14 @@ const fetchSocialMediaInfo = async () => {
try
{
const
data
=
await
getSocialMediaInfo
();
if
(
data
&&
Array
.
isArray
(
data
))
{
// console.log(data);
socialMediaList
.
value
=
data
.
map
(
item
=>
({
avatar
:
item
.
personImage
,
name
:
item
.
personName
,
time
:
formatTime
(
item
.
time
),
source
:
item
.
orgName
,
content
:
item
.
remarks
content
:
item
.
remarks
,
personId
:
item
.
personId
}));
}
}
catch
(
err
)
{
...
...
@@ -1493,7 +1483,8 @@ const handlePerClick = item => {
const
route
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
type
:
item
.
type
||
[
1
,
2
,
3
][
Math
.
floor
(
Math
.
random
()
*
3
)]
type
:
item
.
type
||
[
1
,
2
,
3
][
Math
.
floor
(
Math
.
random
()
*
3
)],
personId
:
item
.
personId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
...
...
@@ -1661,7 +1652,7 @@ const handleClose = () => {
dialogVisible
.
value
=
false
;
};
const
handleOrgClick
=
item
=>
{
console
.
log
(
item
,
item
.
name
);
//
console.log(item, item.name);
currentOrgList
.
value
=
item
.
ruleOrgList
;
dialogVisible
.
value
=
true
;
};
...
...
@@ -1672,7 +1663,7 @@ const handleMediaClose = () => {
mediaVisible
.
value
=
false
;
};
const
handleMediaClick
=
item
=>
{
console
.
log
(
item
,
item
.
name
);
//
console.log(item, item.name);
currentMedia
.
value
=
item
.
content
;
mediaVisible
.
value
=
true
;
};
...
...
@@ -3070,6 +3061,7 @@ const handleMediaClick = item => {
padding-top
:
14px
;
position
:
relative
;
.main-title
{
width
:
800px
;
font-size
:
20px
;
font-weight
:
700
;
line-height
:
26px
;
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/introductionPage/index.vue
浏览文件 @
500577d5
...
...
@@ -17,27 +17,19 @@
<div
class=
"left-top-main-content"
>
<div
class=
"content-item"
>
<span
class=
"label"
>
常见列入原因:
</span>
<span
class=
"text"
>
{{
entityInfo
.
commonReason
}}
</span
>
<span
class=
"text"
>
{{
entityInfo
.
commonReason
}}
</span>
</div>
<div
class=
"content-item"
>
<span
class=
"label"
>
核心限制措施:
</span>
<span
class=
"text"
>
{{
entityInfo
.
restrictiveMeasure
}}
</span
>
<span
class=
"text"
>
{{
entityInfo
.
restrictiveMeasure
}}
</span>
</div>
<div
class=
"content-item"
>
<span
class=
"label"
>
负责机构:
</span>
<span
class=
"text"
>
{{
entityInfo
.
responsibleOrganization
}}
</span
>
<span
class=
"text"
>
{{
entityInfo
.
responsibleOrganization
}}
</span>
</div>
<div
class=
"content-item"
>
<span
class=
"label"
>
移除机制:
</span>
<span
class=
"text"
>
{{
entityInfo
.
removalMechanism
}}
</span
>
<span
class=
"text"
>
{{
entityInfo
.
removalMechanism
}}
</span>
</div>
</div>
</div>
...
...
@@ -47,13 +39,12 @@
<div
class=
"box"
></div>
<div
class=
"text"
>
实体清单更新历史
</div>
<div
class=
"filters"
>
<el-select
v-model=
"selectedDomain"
placeholder=
"Select"
style=
"width: 150px; height: 32px; margin-right: 16px"
>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-select
v-model=
"selectedDomain"
placeholder=
"Select"
style=
"width: 150px; height: 32px; margin-right: 16px"
>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-checkbox
v-model=
"onlyChina"
>
只看涉华动态
</el-checkbox>
</div>
...
...
@@ -70,7 +61,7 @@
</div>
<img
:src=
"item.icon || title"
alt=
""
/>
<div
class=
"main"
>
<div
class=
"main-title"
>
{{
item
.
name
}}
</div>
<div
class=
"main-title"
@
click=
"handleClick(item)"
>
{{
item
.
name
}}
</div>
<el-tooltip
effect=
"dark"
:content=
"item.summary"
...
...
@@ -84,7 +75,9 @@
<div
v-for=
"tag in item.techDomainList"
:key=
"tag"
class=
"tag-item"
>
{{
tag
}}
</div>
</div>
<div
:class=
"
{'count-tag': item.cnEntityCount}">
{{
item
.
cnEntityCount
?
`${item.cnEntityCount
}
家中国实体`
:
''
}}
<
/div
>
<div
:class=
"
{ 'count-tag': item.cnEntityCount }">
{{
item
.
cnEntityCount
?
`${item.cnEntityCount
}
家中国实体`
:
""
}}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
...
...
@@ -112,21 +105,26 @@
<
/div
>
<
div
class
=
"right-main"
>
<
div
class
=
"right-main-title"
>
<
img
:
src
=
"publishInfo.imageUrl"
alt
=
""
>
<
img
:
src
=
"publishInfo.imageUrl"
alt
=
""
/
>
<
div
>
<
div
class
=
"title-text"
>
{{
publishInfo
.
orgNameZh
}}
><
/div
>
<
div
class
=
"title-entext"
>
{{
publishInfo
.
orgName
}}
<
/div
>
<
div
class
=
"title-text"
>
{{
publishInfo
.
orgNameZh
}}
><
/div
>
<
div
class
=
"title-entext"
>
{{
publishInfo
.
orgName
}}
<
/div
>
<
/div
>
<
/div
>
<!--
关键人物
-->
<
div
class
=
"right-main-key-person"
>
<
div
class
=
"key-person-title"
>
<
img
:
src
=
"icon01"
alt
=
""
>
<
img
:
src
=
"icon01"
alt
=
""
/
>
<
span
>
关键人物
<
/span
>
<
/div
>
<
div
class
=
"key-person-list"
>
<
div
class
=
"person-item"
v
-
for
=
"(item, index) in publishInfo.personList"
:
key
=
"index"
>
<
img
:
src
=
"item.imageUrl"
alt
=
""
>
<
div
class
=
"person-item"
v
-
for
=
"(item, index) in publishInfo.personList"
:
key
=
"index"
@
click
=
"handlePerClick(item)"
>
<
img
:
src
=
"item.imageUrl"
alt
=
""
/>
<
div
class
=
"person-info"
>
<
div
class
=
"name"
>
{{
item
.
name
}}
<
/div
>
<
div
class
=
"title1"
>
{{
item
.
position
}}
<
/div
>
...
...
@@ -137,7 +135,7 @@
<!--
机构动态
-->
<
div
class
=
"right-main-dynamic"
>
<
div
class
=
"dynamic-title"
>
<
img
:
src
=
"icon02"
alt
=
""
>
<
img
:
src
=
"icon02"
alt
=
""
/
>
<
span
>
机构动态
<
/span
>
<
/div
>
<
div
class
=
"dynamic-list"
>
...
...
@@ -164,31 +162,56 @@
<
script
setup
>
import
{
ref
,
onMounted
,
watch
}
from
"vue"
;
import
title
from
"../../../../assets/title.png"
import
defaultIcon
from
"../../../../../assets/icons/default-avatar.png"
import
icon01
from
"../../assets/icon01.png"
import
icon02
from
"../../assets/icon02.png"
import
{
ArrowDown
}
from
'@element-plus/icons-vue'
import
{
getEntityInfo
,
getPublishInfo
,
getPublishOrgInfo
,
getEntityUpdateInfo
}
from
"@/api/exportControlV2.0.js"
import
router
from
"@/router"
;
import
title
from
"../../../../assets/title.png"
;
import
defaultIcon
from
"../../../../../assets/icons/default-avatar.png"
;
import
icon01
from
"../../assets/icon01.png"
;
import
icon02
from
"../../assets/icon02.png"
;
import
{
ArrowDown
}
from
"@element-plus/icons-vue"
;
import
{
getEntityInfo
,
getPublishInfo
,
getPublishOrgInfo
,
getEntityUpdateInfo
}
from
"@/api/exportControlV2.0.js"
;
// 跳转到详情页
const
handleClick
=
item
=>
{
const
route
=
router
.
resolve
({
path
:
"/exportControl/singleSanction"
,
query
:
{
id
:
item
.
id
}
}
);
window
.
open
(
route
.
href
,
"_blank"
);
}
;
// 处理点击关键人物的方法
const
handlePerClick
=
item
=>
{
console
.
log
(
"点击了关键人物:"
,
item
);
const
route
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
type
:
item
.
type
,
personId
:
item
.
id
}
}
);
window
.
open
(
route
.
href
,
"_blank"
);
}
;
const
selectedDomain
=
ref
(
0
);
const
onlyChina
=
ref
(
false
);
const
domainOptions
=
[
{
label
:
'全部领域'
,
value
:
0
}
,
{
label
:
'人工智能'
,
value
:
1
}
,
{
label
:
'生物科技'
,
value
:
2
}
,
{
label
:
'新一代信息技术'
,
value
:
3
}
,
{
label
:
'量子科技'
,
value
:
4
}
,
{
label
:
'新能源'
,
value
:
5
}
,
{
label
:
'集成电路'
,
value
:
6
}
,
{
label
:
'海洋'
,
value
:
7
}
,
{
label
:
'先进制造'
,
value
:
8
}
,
{
label
:
'新材料'
,
value
:
9
}
,
{
label
:
'航空航天'
,
value
:
10
}
,
{
label
:
'深海'
,
value
:
11
}
,
{
label
:
'极地'
,
value
:
12
}
,
{
label
:
'太空'
,
value
:
13
}
,
{
label
:
'核'
,
value
:
14
}
{
label
:
"全部领域"
,
value
:
0
}
,
{
label
:
"人工智能"
,
value
:
1
}
,
{
label
:
"生物科技"
,
value
:
2
}
,
{
label
:
"新一代信息技术"
,
value
:
3
}
,
{
label
:
"量子科技"
,
value
:
4
}
,
{
label
:
"新能源"
,
value
:
5
}
,
{
label
:
"集成电路"
,
value
:
6
}
,
{
label
:
"海洋"
,
value
:
7
}
,
{
label
:
"先进制造"
,
value
:
8
}
,
{
label
:
"新材料"
,
value
:
9
}
,
{
label
:
"航空航天"
,
value
:
10
}
,
{
label
:
"深海"
,
value
:
11
}
,
{
label
:
"极地"
,
value
:
12
}
,
{
label
:
"太空"
,
value
:
13
}
,
{
label
:
"核"
,
value
:
14
}
];
// const keyPersonList = [
...
...
@@ -227,59 +250,58 @@ const getSanctionUpdate = async () => {
typeName
:
"实体清单"
,
pageNum
:
currentPageAll
.
value
,
pageSize
:
pageSizeAll
.
value
}
}
;
try
{
const
res
=
await
getEntityUpdateInfo
(
data
)
const
res
=
await
getEntityUpdateInfo
(
data
);
if
(
res
&&
res
.
code
===
200
)
{
console
.
log
(
res
.
data
.
content
)
console
.
log
(
res
.
data
.
content
)
;
sanctionList
.
value
=
(
res
.
data
.
content
||
[]).
map
(
item
=>
({
...
item
,
year
:
item
.
postDate
?
item
.
postDate
.
split
(
'-'
)[
0
]
:
''
,
date
:
item
.
postDate
?
`${item.postDate.split('-')[1]
}
月${item.postDate.split('-')[2]
}
日`
:
''
,
year
:
item
.
postDate
?
item
.
postDate
.
split
(
"-"
)[
0
]
:
""
,
date
:
item
.
postDate
?
`${item.postDate.split("-")[1]
}
月${item.postDate.split("-")[2]
}
日`
:
""
,
techDomainList
:
item
.
techDomainList
||
[],
icon
:
''
}
))
totalAll
.
value
=
res
.
data
.
totalElements
||
0
icon
:
""
}
));
totalAll
.
value
=
res
.
data
.
totalElements
||
0
;
}
}
catch
(
error
)
{
console
.
error
(
"获取实体清单更新历史失败:"
,
error
)
console
.
error
(
"获取实体清单更新历史失败:"
,
error
);
}
}
}
;
// 监听筛选条件变化
watch
([
selectedDomain
,
onlyChina
],
()
=>
{
currentPageAll
.
value
=
1
getSanctionUpdate
()
}
)
currentPageAll
.
value
=
1
;
getSanctionUpdate
();
}
)
;
const
handlePageChangeAll
=
val
=>
{
currentPageAll
.
value
=
val
getSanctionUpdate
()
currentPageAll
.
value
=
val
;
getSanctionUpdate
();
}
;
// 获取实体清单发布机构
const
publishInfo
=
ref
({
}
)
const
publishInfo
=
ref
({
}
)
;
const
getPublishInfoFn
=
async
()
=>
{
const
params
=
{
sanTypeId
:
1
// 实体清单固定1
}
}
;
try
{
const
res
=
await
getPublishInfo
(
params
)
const
res
=
await
getPublishInfo
(
params
);
if
(
res
&&
res
.
code
===
200
)
{
publishInfo
.
value
=
res
.
data
publishInfo
.
value
=
res
.
data
;
// 获取发布机构动态
getPublishOrgInfoFn
()
getPublishOrgInfoFn
()
;
}
}
catch
(
error
)
{
console
.
error
(
"获取实体清单发布机构失败:"
,
error
)
console
.
error
(
"获取实体清单发布机构失败:"
,
error
);
}
}
}
;
// 获取发布机构动态
const
publishOrgInfo
=
ref
([])
const
dynamicPage
=
ref
(
1
)
const
dynamicPageSize
=
ref
(
3
)
const
dynamicTotal
=
ref
(
0
)
const
publishOrgInfo
=
ref
([])
;
const
dynamicPage
=
ref
(
1
)
;
const
dynamicPageSize
=
ref
(
3
)
;
const
dynamicTotal
=
ref
(
0
)
;
const
getPublishOrgInfoFn
=
async
(
isLoadMore
=
false
)
=>
{
if
(
publishInfo
.
value
&&
!
publishInfo
.
value
.
id
)
return
;
...
...
@@ -287,61 +309,59 @@ const getPublishOrgInfoFn = async (isLoadMore = false) => {
const
params
=
{
orgId
:
publishInfo
.
value
.
id
,
pageNum
:
dynamicPage
.
value
,
pageSize
:
dynamicPageSize
.
value
,
}
pageSize
:
dynamicPageSize
.
value
}
;
try
{
const
res
=
await
getPublishOrgInfo
(
params
)
const
res
=
await
getPublishOrgInfo
(
params
);
if
(
res
&&
res
.
code
===
200
)
{
const
newRows
=
(
res
.
data
.
content
||
[]).
map
(
item
=>
({
...
item
,
publishDate
:
item
.
newsDate
,
title
:
item
.
newsTitle
}
))
}
));
if
(
isLoadMore
)
{
publishOrgInfo
.
value
=
[...
publishOrgInfo
.
value
,
...
newRows
]
publishOrgInfo
.
value
=
[...
publishOrgInfo
.
value
,
...
newRows
];
}
else
{
publishOrgInfo
.
value
=
newRows
publishOrgInfo
.
value
=
newRows
;
}
dynamicTotal
.
value
=
res
.
data
.
totalElements
||
0
dynamicTotal
.
value
=
res
.
data
.
totalElements
||
0
;
}
}
catch
(
error
)
{
console
.
error
(
"获取发布机构动态失败:"
,
error
)
console
.
error
(
"获取发布机构动态失败:"
,
error
);
}
}
}
;
const
handleLoadMoreDynamic
=
()
=>
{
dynamicPage
.
value
++
getPublishOrgInfoFn
(
true
)
}
dynamicPage
.
value
++
;
getPublishOrgInfoFn
(
true
);
}
;
// 获取实体清单基本信息
const
entityInfo
=
ref
({
}
)
const
entityInfo
=
ref
({
}
)
;
const
getEntityInfoFn
=
async
()
=>
{
try
{
const
res
=
await
getEntityInfo
()
const
res
=
await
getEntityInfo
();
if
(
res
&&
res
.
code
===
200
)
{
entityInfo
.
value
=
res
.
data
entityInfo
.
value
=
res
.
data
;
}
}
catch
(
error
)
{
console
.
error
(
"获取实体清单基本信息失败:"
,
error
)
console
.
error
(
"获取实体清单基本信息失败:"
,
error
);
}
}
}
;
onMounted
(()
=>
{
// 获取实体清单基本信息
getEntityInfoFn
()
getEntityInfoFn
();
// 获取实体清单发布机构
getPublishInfoFn
()
getPublishInfoFn
();
// 获取实体清单更新历史
getSanctionUpdate
()
}
)
getSanctionUpdate
();
}
)
;
<
/script
>
<
style
scoped
lang
=
"scss"
>
*
{
<
style
scoped
lang
=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
}
...
...
@@ -459,6 +479,7 @@ onMounted(() => {
padding
-
top
:
14
px
;
position
:
relative
;
.
main
-
title
{
cursor
:
pointer
;
width
:
700
px
;
font
-
size
:
20
px
;
font
-
weight
:
700
;
...
...
@@ -531,7 +552,7 @@ onMounted(() => {
}
.
right
{
width
:
520
px
;
height
:
941
px
;
height
:
1020
px
;
border
-
radius
:
10
px
;
padding
-
bottom
:
20
px
;
box
-
shadow
:
0
px
0
px
20
px
0
px
rgba
(
25
,
69
,
130
,
0.1
);
...
...
@@ -540,7 +561,7 @@ onMounted(() => {
padding
:
7
px
24
px
0
px
23
px
;
.
right
-
main
-
title
{
width
:
473
px
;
height
:
110
px
;
height
:
auto
;
border
-
radius
:
4
px
;
background
-
color
:
rgb
(
247
,
248
,
249
);
border
:
1
px
solid
rgb
(
234
,
236
,
238
);
...
...
@@ -595,7 +616,7 @@ onMounted(() => {
.
person
-
item
{
width
:
185
px
;
// height: 49px
;
cursor
:
pointer
;
display
:
flex
;
align
-
items
:
center
;
margin
-
bottom
:
16
px
;
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/listPage/RuleSubsidiaryDialog.vue
浏览文件 @
500577d5
...
...
@@ -94,6 +94,10 @@ const props = defineProps({
totalCount
:
{
type
:
Number
,
default
:
0
},
dataList
:
{
type
:
Array
,
default
:
()
=>
[]
}
});
...
...
@@ -107,92 +111,21 @@ const visible = computed({
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
// Mock Data
const
tableData
=
ref
([
{
id
:
1
,
name
:
"讯飞智元信息科技有限公司"
,
domains
:
[
"人工智能"
],
equityRatio
:
"100%"
,
location
:
"深圳"
,
revenue
:
325
},
{
id
:
2
,
name
:
"科大讯飞(北京)有限公司"
,
domains
:
[
"通信网络"
,
"集成电路"
],
equityRatio
:
"100%"
,
location
:
"--"
,
revenue
:
305
},
{
id
:
3
,
name
:
"深圳讯飞智慧科技有限公司"
,
domains
:
[
"人工智能"
],
equityRatio
:
"100%"
,
location
:
"深圳"
,
revenue
:
325
},
{
id
:
4
,
name
:
"安徽讯飞寰语科技有限公司"
,
domains
:
[
"通信网络"
,
"集成电路"
],
equityRatio
:
"100%"
,
location
:
"--"
,
revenue
:
305
},
{
id
:
5
,
name
:
"天津讯飞信息科技有限公司"
,
domains
:
[
"航空航天"
],
equityRatio
:
"100%"
,
location
:
"--"
,
revenue
:
289
},
{
id
:
6
,
name
:
"安徽听见科技有限公司"
,
domains
:
[
"人工智能"
],
equityRatio
:
"100%"
,
location
:
"深圳"
,
revenue
:
270
},
{
id
:
7
,
name
:
"广州市讯飞樽鸿信息技术有限公司"
,
domains
:
[
"航空航天"
],
equityRatio
:
"69.8%"
,
location
:
"--"
,
revenue
:
289
},
{
id
:
8
,
name
:
"苏州科大讯飞教育科技有限公司"
,
domains
:
[
"人工智能"
],
equityRatio
:
"66.3%"
,
location
:
"深圳"
,
revenue
:
270
},
{
id
:
9
,
name
:
"深圳讯飞互动电子有限公司"
,
domains
:
[
"人工智能"
],
equityRatio
:
"57.1%"
,
location
:
"香港"
,
revenue
:
255
},
{
id
:
10
,
name
:
"讯飞医疗科技股份有限公司"
,
domains
:
[
"生物技术"
],
equityRatio
:
"51%"
,
location
:
"深圳"
,
revenue
:
270
}
]);
const
tableData
=
computed
(()
=>
{
const
start
=
(
currentPage
.
value
-
1
)
*
pageSize
.
value
;
const
end
=
start
+
pageSize
.
value
;
return
props
.
dataList
.
slice
(
start
,
end
).
map
(
item
=>
({
...
item
,
name
:
item
.
orgName
,
domains
:
item
.
techDomains
||
[],
equityRatio
:
item
.
equityRatio
?
(
item
.
equityRatio
*
100
).
toFixed
(
2
)
+
'%'
:
'--'
,
location
:
'--'
,
revenue
:
item
.
revenue
||
'--'
}));
});
const
handleCurrentChange
=
val
=>
{
c
onsole
.
log
(
"Page changed:"
,
val
)
;
c
urrentPage
.
value
=
val
;
};
const
tableRowClassName
=
({
rowIndex
})
=>
{
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/listPage/index.vue
浏览文件 @
500577d5
...
...
@@ -104,8 +104,8 @@
<el-table-column
label=
"50%规则子企业"
min-width=
"280"
show-overflow-tooltip
align=
"right"
>
<
template
#
default=
"{ row }"
>
<div
class=
"rule-cell"
v-if=
"row.ruleOrgCount > 0"
>
<div
class=
"rule-text"
:title=
"row.ruleOrgList
&& row.ruleOrgList.length > 0 ? row.ruleOrgList[0].orgName :
''"
>
{{
row
.
ruleOrgList
&&
row
.
ruleOrgList
.
length
>
0
?
row
.
ruleOrgList
[
0
].
orgName
:
''
}}
...等
<div
class=
"rule-text"
:title=
"row.ruleOrgList
?.[0]?.orgName ||
''"
>
{{
row
.
ruleOrgList
?.[
0
]?.
orgName
||
''
}}
...等
</div>
<el-link
class=
"rule-link"
type=
"primary"
:underline=
"false"
@
click=
"handleRuleClick(row)"
>
{{
row
.
ruleOrgCount
}}
家 >
</el-link>
</div>
...
...
@@ -132,6 +132,7 @@
v-model=
"ruleDialogVisible"
:company-name=
"currentRuleCompany"
:total-count=
"currentRuleCount"
:data-list=
"currentRuleList"
/>
</template>
...
...
@@ -227,10 +228,12 @@ const customDateRange = ref("");
const
ruleDialogVisible
=
ref
(
false
);
const
currentRuleCompany
=
ref
(
""
);
const
currentRuleCount
=
ref
(
0
);
const
currentRuleList
=
ref
([]);
const
handleRuleClick
=
(
row
)
=>
{
currentRuleCompany
.
value
=
row
.
entityName
;
currentRuleCompany
.
value
=
row
.
entityName
Zh
||
row
.
entityName
;
currentRuleCount
.
value
=
row
.
ruleOrgCount
;
currentRuleList
.
value
=
row
.
ruleOrgList
||
[];
ruleDialogVisible
.
value
=
true
;
};
...
...
src/views/exportControl/v2.0SingleSanction/components/dataStatistics/index.vue
浏览文件 @
500577d5
...
...
@@ -8,7 +8,7 @@
<div
class=
"title"
>
中国实体数量
</div>
</div>
<div
class=
"number"
>
<span
class=
"num"
>
24
</span>
<span
class=
"num"
>
{{
totalCount
.
cnCount
}}
</span>
<span
class=
"unit"
>
家
</span>
</div>
</div>
...
...
@@ -21,7 +21,7 @@
<div
class=
"subtitle"
>
50%规则涉及
</div>
</div>
<div
class=
"number"
>
<span
class=
"num"
>
102
</span>
<span
class=
"num"
>
{{
totalCount
.
cn50RuleCount
}}
</span>
<span
class=
"unit"
>
家
</span>
</div>
</div>
...
...
@@ -33,7 +33,7 @@
<div
class=
"title"
>
涉及领域
</div>
</div>
<div
class=
"number"
>
<span
class=
"num"
>
3
</span>
<span
class=
"num"
>
{{
totalCount
.
techDomainCount
}}
</span>
<span
class=
"unit"
>
家
</span>
</div>
</div>
...
...
@@ -154,16 +154,16 @@
<div
class=
"map-wrapper"
>
<div
class=
"map-chart"
ref=
"mapChartRef"
></div>
<div
class=
"rank-list"
>
<div
class=
"rank-item"
v-for=
"(item, index) in r
ankData
"
:key=
"index"
>
<div
class=
"rank-item"
v-for=
"(item, index) in r
egionDistribution
"
:key=
"index"
>
<div
class=
"rank-index"
:class=
"'rank-' + (index + 1)"
>
{{
index
+
1
}}
</div>
<div
class=
"rank-name"
>
{{
item
.
name
}}
</div>
<div
class=
"rank-bar-bg"
>
<div
class=
"rank-bar-fill"
:style=
"
{ width: (
item.value / 50) * 100
+ '%', background: getBarColor(index) }"
:style=
"
{ width: (
maxRegionCount > 0 ? (item.count / maxRegionCount) * 100 : 0)
+ '%', background: getBarColor(index) }"
>
</div>
</div>
<div
class=
"rank-value"
>
{{
item
.
value
}}
家
</div>
<div
class=
"rank-value"
>
{{
item
.
count
}}
家
</div>
</div>
</div>
</div>
...
...
@@ -184,108 +184,210 @@
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
ref
,
onMounted
,
watch
}
from
"vue"
;
import
*
as
echarts
from
"echarts"
;
import
chinaJson
from
"../../../utils/China.json"
;
import
ai
from
"./assets/ai.png"
;
import
right
from
"./assets/right.png"
;
import
flag
from
"../../assets/default-icon2.png"
import
{
useRouter
}
from
"vue-router"
;
import
{
getSingleSanctionTotalCount
,
getSingleSanctionDomainCount
,
getSingleSanctionEntityTypeCount
,
getSingleSanctionEntityCountryCount
,
getSingleSanctionEntityRegionCount
}
from
"@/api/exportControlV2.0"
;
// 单次制裁-数据统计-制裁实体地域分布情况
const
regionDistribution
=
ref
([]);
const
maxRegionCount
=
ref
(
0
);
// 单次制裁-数据统计-制裁实体地域分布情况-请求
const
getRegionData
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
regionTime
.
value
&&
regionTime
.
value
!==
'all'
)
{
params
.
startDate
=
`
${
regionTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
regionTime
.
value
}
-12-31`
;
}
const
res
=
await
getSingleSanctionEntityRegionCount
(
params
);
if
(
res
.
code
===
200
)
{
regionDistribution
.
value
=
res
.
data
||
[];
maxRegionCount
.
value
=
Math
.
max
(...
regionDistribution
.
value
.
map
(
item
=>
item
.
count
),
0
);
initMapChart
();
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-数据统计-制裁实体国家分布情况
const
countryDistribution
=
ref
([]);
// 单次制裁-数据统计-制裁实体国家分布情况-请求
const
getCountryCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
const
res
=
await
getSingleSanctionEntityCountryCount
(
params
);
if
(
res
.
code
===
200
)
{
const
rawData
=
res
.
data
||
[];
// 找出最大值用于计算比例
const
maxCount
=
Math
.
max
(...
rawData
.
map
(
item
=>
item
.
count
),
0
);
countryDistribution
.
value
=
rawData
.
map
((
item
,
index
)
=>
{
// 计算宽度,最大值对应 80%
const
width
=
maxCount
>
0
?
(
item
.
count
/
maxCount
)
*
80
+
'%'
:
'0%'
;
// 根据索引分配渐变色
let
gradient
=
""
;
if
(
index
===
0
)
{
gradient
=
"linear-gradient(90deg, rgba(205, 66, 70, 0) 0%, rgba(205, 66, 70, 1) 100%)"
;
}
else
if
(
index
===
1
||
index
===
2
)
{
gradient
=
"linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)"
;
}
else
{
gradient
=
"linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)"
;
}
return
{
...
item
,
width
,
gradient
};
});
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-数据统计-制裁实体类型分布情况
const
entityTypeCount
=
ref
([]);
// 单次制裁-数据统计-制裁实体类型分布情况-请求
const
getEntityTypeCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
typeTime
.
value
&&
typeTime
.
value
!==
'all'
)
{
params
.
startDate
=
`
${
typeTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
typeTime
.
value
}
-12-31`
;
}
const
res
=
await
getSingleSanctionEntityTypeCount
(
params
);
if
(
res
.
code
===
200
)
{
entityTypeCount
.
value
=
res
.
data
||
[];
initTypeChart
();
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-数据统计-制裁实体领域分布情况
const
domainCount
=
ref
([]);
// 单次制裁-数据统计-制裁实体领域分布情况-请求
const
getDomainCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
domainTime
.
value
&&
domainTime
.
value
!==
'all'
)
{
params
.
startDate
=
`
${
domainTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
domainTime
.
value
}
-12-31`
;
}
const
res
=
await
getSingleSanctionDomainCount
(
params
);
if
(
res
.
code
===
200
)
{
domainCount
.
value
=
res
.
data
||
[];
initDomainChart
();
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-数据统计-总量统计
const
totalCount
=
ref
({});
// 单次制裁-数据统计-总量统计-请求
const
getTotalCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionTotalCount
({
sanRecordId
:
sanRecordId
.
value
});
if
(
res
.
code
===
200
)
{
totalCount
.
value
=
res
.
data
||
{};
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
const
router
=
useRouter
();
const
sanRecordId
=
ref
(
""
)
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
const
regionTime
=
ref
(
"all"
);
const
domainTime
=
ref
(
"all"
);
const
typeTime
=
ref
(
"all"
);
watch
(
domainTime
,
()
=>
{
getDomainCount
();
});
watch
(
typeTime
,
()
=>
{
getEntityTypeCount
();
});
watch
(
regionTime
,
()
=>
{
getRegionData
();
});
const
timeOptions
=
[
{
label
:
"全部时间"
,
value
:
"all"
},
{
label
:
"2024年"
,
value
:
"2024"
},
{
label
:
"2023年"
,
value
:
"2023"
}
{
label
:
"全部时间"
,
value
:
"all"
}
];
// 生成2000-2025年的选项
for
(
let
i
=
2025
;
i
>=
2000
;
i
--
)
{
timeOptions
.
push
({
label
:
`
${
i
}
年`
,
value
:
`
${
i
}
`
});
}
const
countryDistribution
=
[
{
name
:
"中国"
,
count
:
24
,
width
:
"80%"
,
gradient
:
"linear-gradient(90deg, rgba(205, 66, 70, 0) 0%, rgba(205, 66, 70, 1) 100%)"
},
{
name
:
"沙特阿拉伯"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)"
},
{
name
:
"伊朗"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)"
},
{
name
:
"俄罗斯"
,
count
:
2
,
width
:
"55%"
,
gradient
:
"linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)"
},
{
name
:
"中国香港"
,
count
:
1
,
width
:
"40%"
,
gradient
:
"linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)"
}
];
//
const countryDistribution = [
//
{ name: "中国", count: 24, width: "80%", gradient: "linear-gradient(90deg, rgba(205, 66, 70, 0) 0%, rgba(205, 66, 70, 1) 100%)" },
//
{ name: "沙特阿拉伯", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
//
{ name: "伊朗", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
//
{ name: "俄罗斯", count: 2, width: "55%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
//
{ name: "中国香港", count: 1, width: "40%", gradient: "linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)" }
//
];
const
mapChartRef
=
ref
(
null
);
const
domainChartRef
=
ref
(
null
);
const
typeChartRef
=
ref
(
null
);
const
rankData
=
[
{
name
:
"广东省"
,
value
:
42
},
{
name
:
"上海市"
,
value
:
35
},
{
name
:
"浙江省"
,
value
:
28
},
{
name
:
"江苏省"
,
value
:
19
},
{
name
:
"山东省"
,
value
:
15
},
{
name
:
"福建省"
,
value
:
14
},
{
name
:
"中国香港"
,
value
:
13
}
];
const
getBarColor
=
index
=>
{
if
(
index
===
0
)
return
"linear-gradient(90deg, rgba(255, 77, 79, 0) 0%, rgba(255, 77, 79, 1) 100%)"
;
if
(
index
===
1
||
index
===
2
)
return
"linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)"
;
return
"linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)"
;
};
const
geoCoordMap
=
{
广东省
:
[
113.280637
,
23.125178
],
上海市
:
[
121.472644
,
31.231706
],
浙江省
:
[
120.153576
,
30.287459
],
江苏省
:
[
118.767413
,
32.041544
],
山东省
:
[
117.000923
,
36.675807
],
福建省
:
[
119.306239
,
26.075302
],
中国香港
:
[
114.173355
,
22.320048
],
北京市
:
[
116.405285
,
39.904989
],
天津市
:
[
117.190182
,
39.125596
],
重庆市
:
[
106.504962
,
29.533155
],
河北省
:
[
114.502461
,
38.045474
],
山西省
:
[
112.549248
,
37.857014
],
辽宁省
:
[
123.429096
,
41.796767
],
吉林省
:
[
125.3245
,
43.886841
],
黑龙江省
:
[
126.642464
,
45.756967
],
安徽省
:
[
117.283042
,
31.86119
],
江西省
:
[
115.892151
,
28.676493
],
河南省
:
[
113.665412
,
34.757975
],
湖北省
:
[
114.298572
,
30.584355
],
湖南省
:
[
112.982279
,
28.19409
],
海南省
:
[
110.33119
,
20.031971
],
四川省
:
[
104.065735
,
30.659462
],
贵州省
:
[
106.713478
,
26.578343
],
云南省
:
[
102.712251
,
25.040609
],
陕西省
:
[
108.948024
,
34.263161
],
甘肃省
:
[
103.823557
,
36.058039
],
青海省
:
[
101.778916
,
36.623178
],
内蒙古自治区
:
[
111.670801
,
40.818311
],
广西壮族自治区
:
[
108.320004
,
22.82402
],
西藏自治区
:
[
91.132212
,
29.660361
],
宁夏回族自治区
:
[
106.278179
,
38.46637
],
新疆维吾尔自治区
:
[
87.617733
,
43.792818
],
台湾省
:
[
121.509062
,
25.044332
],
澳门特别行政区
:
[
113.54909
,
22.198951
]
};
const
convertData
=
data
=>
{
const
res
=
[];
for
(
let
i
=
0
;
i
<
data
.
length
;
i
++
)
{
const
geoCoord
=
geoCoordMap
[
data
[
i
].
name
];
if
(
geoCoord
)
{
res
.
push
({
name
:
data
[
i
].
name
,
value
:
geoCoord
.
concat
(
data
[
i
].
value
)
});
}
}
return
res
;
};
const
initMapChart
=
()
=>
{
if
(
!
mapChartRef
.
value
)
return
;
// 销毁旧实例
const
oldChart
=
echarts
.
getInstanceByDom
(
mapChartRef
.
value
);
if
(
oldChart
)
{
oldChart
.
dispose
();
}
const
chart
=
echarts
.
init
(
mapChartRef
.
value
);
echarts
.
registerMap
(
"china"
,
chinaJson
);
const
scatterData
=
regionDistribution
.
value
.
map
(
item
=>
{
return
{
name
:
item
.
name
,
value
:
[
item
.
lon
,
item
.
lat
,
item
.
count
]
};
});
const
option
=
{
tooltip
:
{
show
:
false
...
...
@@ -317,7 +419,7 @@ const initMapChart = () => {
{
type
:
"scatter"
,
coordinateSystem
:
"geo"
,
data
:
convertData
(
rankData
)
,
data
:
scatterData
,
symbolSize
:
8
,
itemStyle
:
{
color
:
"#ff4d4f"
,
...
...
@@ -343,17 +445,17 @@ const initMapChart = () => {
const
initDomainChart
=
()
=>
{
if
(
!
domainChartRef
.
value
)
return
;
// 销毁旧实例,防止内存泄漏或状态残留
const
oldChart
=
echarts
.
getInstanceByDom
(
domainChartRef
.
value
);
if
(
oldChart
)
{
oldChart
.
dispose
();
}
const
chart
=
echarts
.
init
(
domainChartRef
.
value
);
const
data
=
[
{
value
:
215
,
name
:
"集成电路"
},
{
value
:
198
,
name
:
"人工智能"
},
{
value
:
117
,
name
:
"通信网络"
},
{
value
:
98
,
name
:
"量子科技"
},
{
value
:
91
,
name
:
"先进制造"
},
{
value
:
80
,
name
:
"新材料"
},
{
value
:
62
,
name
:
"航空航天"
}
];
const
data
=
domainCount
.
value
.
map
(
item
=>
({
value
:
item
.
count
,
name
:
item
.
name
}));
const
option
=
{
tooltip
:
{
...
...
@@ -438,13 +540,17 @@ const initDomainChart = () => {
const
initTypeChart
=
()
=>
{
if
(
!
typeChartRef
.
value
)
return
;
// 销毁旧实例,防止内存泄漏或状态残留
const
oldChart
=
echarts
.
getInstanceByDom
(
typeChartRef
.
value
);
if
(
oldChart
)
{
oldChart
.
dispose
();
}
const
chart
=
echarts
.
init
(
typeChartRef
.
value
);
const
data
=
[
{
value
:
50
,
name
:
"企业"
},
{
value
:
32
,
name
:
"高校"
},
{
value
:
32
,
name
:
"科研院所"
}
];
const
data
=
entityTypeCount
.
value
.
map
(
item
=>
({
value
:
item
.
count
,
name
:
item
.
name
}));
const
option
=
{
tooltip
:
{
...
...
@@ -524,9 +630,22 @@ const initTypeChart = () => {
};
onMounted
(()
=>
{
// 获取url参数
getUrlParams
();
// 单次制裁-数据统计-总量统计-请求
getTotalCount
();
// 单次制裁-数据统计-制裁实体领域分布情况-请求
getDomainCount
();
// 单次制裁-数据统计-制裁实体类型分布情况-请求
getEntityTypeCount
();
// 单次制裁-数据统计-制裁实体国家分布情况-请求
getCountryCount
();
// 单次制裁-数据统计-制裁实体地域分布情况-请求
getRegionData
();
initMapChart
();
initDomainChart
();
initTypeChart
();
// initDomainChart(); // 移到 getDomainCount 回调中初始化
// initTypeChart(); // 移到 getEntityTypeCount 回调中初始化
});
</
script
>
...
...
@@ -626,6 +745,7 @@ onMounted(() => {
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
border-radius
:
10px
;
.country-list
{
overflow
:
auto
;
width
:
100%
;
height
:
280px
;
padding
:
0
20px
;
...
...
src/views/exportControl/v2.0SingleSanction/components/deepMining/index.vue
浏览文件 @
500577d5
...
...
@@ -29,8 +29,20 @@
<div
class=
"filter-bar"
>
<el-select
v-model=
"searchDomain"
placeholder=
"全部领域"
class=
"domain-select"
>
<el-option
label=
"全部领域"
value=
""
/>
<el-option
label=
"集成电路"
value=
"集成电路"
/>
<el-option
label=
"人工智能"
value=
"人工智能"
/>
<el-option
label=
"人工智能"
value=
"1"
/>
<el-option
label=
"生物科技"
value=
"2"
/>
<el-option
label=
"新一代信息技术"
value=
"3"
/>
<el-option
label=
"量子科技"
value=
"4"
/>
<el-option
label=
"新能源"
value=
"5"
/>
<el-option
label=
"集成电路"
value=
"6"
/>
<el-option
label=
"海洋"
value=
"7"
/>
<el-option
label=
"先进制造"
value=
"8"
/>
<el-option
label=
"新材料"
value=
"9"
/>
<el-option
label=
"航空航天"
value=
"10"
/>
<el-option
label=
"深海"
value=
"11"
/>
<el-option
label=
"极地"
value=
"12"
/>
<el-option
label=
"太空"
value=
"13"
/>
<el-option
label=
"核"
value=
"14"
/>
</el-select>
<el-input
v-model=
"searchText"
placeholder=
"搜索实体"
class=
"search-input"
>
<template
#
suffix
>
...
...
@@ -89,12 +101,26 @@
<div
class=
"rule-checkbox"
v-if=
"rightActiveTab === 'equity'"
>
<el-checkbox
v-model=
"is50PercentRule"
size=
"large"
>
50%规则涉及实体
</el-checkbox>
</div>
<el-select
v-model=
"filterType"
placeholder=
"全部类型"
class=
"header-select"
>
<
!-- <
el-select v-model="filterType" placeholder="全部类型" class="header-select">
<el-option label="全部类型" value="" />
</el-select>
<el-select v-model="filterDomain" placeholder="全部领域" class="header-select last-select">
<el-option label="全部领域" value="" />
</el-select>
<el-option label="人工智能" value="1" />
<el-option label="生物科技" value="2" />
<el-option label="新一代信息技术" value="3" />
<el-option label="量子科技" value="4" />
<el-option label="新能源" value="5" />
<el-option label="集成电路" value="6" />
<el-option label="海洋" value="7" />
<el-option label="先进制造" value="8" />
<el-option label="新材料" value="9" />
<el-option label="航空航天" value="10" />
<el-option label="深海" value="11" />
<el-option label="极地" value="12" />
<el-option label="太空" value="13" />
<el-option label="核" value="14" />
</el-select> -->
<div
class=
"btn"
>
<img
src=
"../../assets/数据库按钮.png"
alt=
""
/>
<img
src=
"../../assets/下载按钮.png"
alt=
""
/>
...
...
@@ -124,10 +150,14 @@
<div
class=
"popup-body"
>
<div
class=
"tag-row"
>
<span
class=
"red-dot"
></span>
<span
class=
"red-text"
>
2025年7月15日 《实体清单》
</span>
<span
class=
"red-text"
>
<!-- 2025年7月15日 《实体清单》 -->
暂无数据
</span>
</div>
<div
class=
"desc"
>
因获取和试图获取美国原产物品以支持中国军事和国防相关空间领域活动以及中国量子技术能力而被列入。
<!-- 因获取和试图获取美国原产物品以支持中国军事和国防相关空间领域活动以及中国量子技术能力而被列入。 -->
暂无数据
</div>
</div>
</div>
...
...
@@ -140,8 +170,9 @@
<
script
setup
>
import
{
ref
,
onMounted
,
nextTick
,
watch
,
onUnmounted
}
from
"vue"
;
import
{
debounce
}
from
"lodash"
;
import
*
as
echarts
from
"echarts"
;
import
{
ArrowLeft
,
ArrowRight
,
Search
,
CaretRight
,
Close
,
CircleCheckFilled
}
from
"@element-plus/icons-vue"
;
import
{
Search
,
CaretRight
,
Close
}
from
"@element-plus/icons-vue"
;
import
defaultTitle
from
"../../assets/default-icon2.png"
;
import
icon01
from
"./assets/icon01.png"
import
icon02
from
"./assets/icon02.png"
...
...
@@ -152,6 +183,94 @@ import echartsIcon02 from "./assets/echartsIcon02.png"
import
echartsIcon03
from
"./assets/echartsIcon03.png"
import
company
from
"./assets/company.png"
import
companyActive
from
"./assets/company-active.png"
import
{
getSingleSanctionEntityList
,
getSingleSanctionEntitySupplyChain
,
getSingleSanctionEntityEquity
}
from
"@/api/exportControlV2.0"
;
// 单次制裁-深度挖掘-制裁实体股权信息-列表
const
singleSanctionEntityEquityData
=
ref
(
null
);
// 单次制裁-深度挖掘-制裁实体股权信息-请求
const
getSingleSanctionEntityEquityRequest
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityEquity
({
orgId
:
activeEntityId
.
value
,
rule
:
is50PercentRule
.
value
,
})
if
(
res
.
code
===
200
)
{
singleSanctionEntityEquityData
.
value
=
res
.
data
||
null
;
initChart
();
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-深度挖掘-制裁实体供应链信息-列表
const
singleSanctionEntitySupplyChainData
=
ref
(
null
);
// 单次制裁-深度挖掘-制裁实体供应链信息-请求
const
getSingleSanctionEntitySupplyChainRequest
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntitySupplyChain
({
orgId
:
activeEntityId
.
value
,
})
if
(
res
.
code
===
200
)
{
singleSanctionEntitySupplyChainData
.
value
=
res
.
data
||
null
;
initChart
();
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-深度挖掘-本次制裁实体清单列表
const
singleSanctionEntityList
=
ref
([]);
// 单次制裁-深度挖掘-本次制裁实体清单列表-请求
const
getSingleSanctionEntityListRequest
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
false
,
domainId
:
searchDomain
.
value
||
undefined
,
searchText
:
searchText
.
value
||
undefined
,
})
if
(
res
.
code
===
200
)
{
entityList
.
value
=
(
res
.
data
||
[]).
map
((
group
,
index
)
=>
({
id
:
`group-
${
index
}
`
,
name
:
group
.
orgType
,
count
:
group
.
orgInfoList
?
group
.
orgInfoList
.
length
:
0
,
expanded
:
index
===
0
,
// 默认展开第一个分组
children
:
(
group
.
orgInfoList
||
[]).
map
(
org
=>
({
id
:
org
.
id
,
name
:
org
.
orgNameZh
}))
}));
// 如果有数据,且当前没有选中的实体,默认选中第一个分组的第一个实体
if
(
entityList
.
value
.
length
>
0
&&
entityList
.
value
[
0
].
children
&&
entityList
.
value
[
0
].
children
.
length
>
0
)
{
const
firstEntity
=
entityList
.
value
[
0
].
children
[
0
];
if
(
!
activeEntityId
.
value
)
{
activeEntityId
.
value
=
firstEntity
.
id
;
currentEntityName
.
value
=
firstEntity
.
name
;
}
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
const
sanRecordId
=
ref
(
""
)
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
const
activeTab
=
ref
([
"实体穿透分析"
,
"重点实体识别"
]);
const
activeIndex
=
ref
(
0
);
...
...
@@ -163,41 +282,9 @@ const filterType = ref("");
const
filterDomain
=
ref
(
""
);
const
activeEntityId
=
ref
(
""
);
const
currentEntityName
=
ref
(
""
);
const
is50PercentRule
=
ref
(
tru
e
);
const
is50PercentRule
=
ref
(
fals
e
);
const
entityList
=
ref
([
{
id
:
"group1"
,
name
:
"企业"
,
count
:
13
,
expanded
:
true
,
children
:
[
{
id
:
"1"
,
name
:
"北京复旦微电子技术有限公司"
},
{
id
:
"2"
,
name
:
"上海复旦微电子股份有限公司"
},
{
id
:
"3"
,
name
:
"深圳复旦微电子有限公司"
},
{
id
:
"4"
,
name
:
"上海复控华龙微系统技术有限公司"
},
{
id
:
"5"
,
name
:
"上海富伟迅捷数字技术有限公司"
},
{
id
:
"6"
,
name
:
"中芯国际集成电路制造有限公司"
},
{
id
:
"7"
,
name
:
"上海复旦微电子(香港)有限公司"
},
{
id
:
"8"
,
name
:
"华科供应链(香港)有限公司"
},
{
id
:
"9"
,
name
:
"华科物流(香港)有限公司"
},
{
id
:
"10"
,
name
:
"长沙网迅电子科技有限公司"
},
{
id
:
"11"
,
name
:
"香港 DEMX 有限公司"
},
{
id
:
"12"
,
name
:
"北京天一辉远生物技术有限公司"
},
{
id
:
"13"
,
name
:
"上海索辰信息技术有限公司"
}
]
},
{
id
:
"group2"
,
name
:
"科研机构"
,
count
:
2
,
expanded
:
false
,
children
:
[
{
id
:
"14"
,
name
:
"中国科学院微电子研究所"
},
{
id
:
"15"
,
name
:
"国家超级计算中心"
}
]
}
]);
const
entityList
=
ref
([]);
const
toggleGroup
=
(
group
)
=>
{
group
.
expanded
=
!
group
.
expanded
;
...
...
@@ -206,16 +293,6 @@ const toggleGroup = (group) => {
const
selectEntity
=
(
item
)
=>
{
activeEntityId
.
value
=
item
.
id
;
currentEntityName
.
value
=
item
.
name
;
initChart
();
};
const
formatEntityName
=
(
name
)
=>
{
if
(
!
name
)
return
''
;
if
(
name
.
length
>
8
)
{
const
mid
=
Math
.
ceil
(
name
.
length
/
2
);
return
name
.
slice
(
0
,
mid
)
+
'
\
n'
+
name
.
slice
(
mid
);
}
return
name
;
};
const
chartRef
=
ref
(
null
);
...
...
@@ -257,48 +334,149 @@ const initChart = () => {
};
const
getSupplyChainOption
=
()
=>
{
const
centerName
=
formatEntityName
(
currentEntityName
.
value
);
const
nodes
=
[
{
id
:
'0'
,
name
:
centerName
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
550
,
y
:
400
,
symbolSize
:
50
,
label
:
{
fontSize
:
16
,
fontWeight
:
'bold'
,
color
:
'#055FC2'
}
},
{
id
:
'1'
,
name
:
'上海华虹(集团)
\
n有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
100
,
y
:
300
},
{
id
:
'2'
,
name
:
'南京芯全信息科技
\
n有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
250
,
y
:
300
},
{
id
:
'3'
,
name
:
'上海伟测半导体科
\
n技股份有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
400
,
y
:
250
},
{
id
:
'4'
,
name
:
'上海宏测半导体科
\
n技有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
400
,
y
:
100
},
{
id
:
'5'
,
name
:
'德耐尔节能科技
\
n(上海)股份有限
\
n公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
250
,
y
:
100
},
{
id
:
'6'
,
name
:
'台湾积体电路制造
\
n股份有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
550
,
y
:
250
},
{
id
:
'7'
,
name
:
'江苏长电科技股份
\
n有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
700
,
y
:
250
},
{
id
:
'8'
,
name
:
'杭州士兰微电子股
\
n份有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
850
,
y
:
300
},
// Customers
{
id
:
'9'
,
name
:
'上海复微迅捷数字
\
n技术有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
200
,
y
:
600
},
{
id
:
'10'
,
name
:
'上海索辰信息技术
\
n有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
400
,
y
:
600
},
{
id
:
'11'
,
name
:
'上海复旦微电子
\
n(香港)有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
600
,
y
:
600
},
{
id
:
'12'
,
name
:
'上海华岭集成电路
\
n技术股份有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
800
,
y
:
600
},
{
id
:
'13'
,
name
:
'上海复旦通讯股份
\
n有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
950
,
y
:
600
},
];
const
nodeCategoryMap
=
nodes
.
reduce
((
acc
,
node
)
=>
{
acc
[
node
.
id
]
=
node
.
category
;
return
acc
;
},
{});
const
links
=
[
{
source
:
'1'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'2'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'3'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'4'
,
target
:
'3'
,
value
:
'供应商'
},
{
source
:
'5'
,
target
:
'3'
,
value
:
'供应商'
},
{
source
:
'6'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'7'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'8'
,
target
:
'0'
,
value
:
'供应商'
},
{
source
:
'0'
,
target
:
'9'
,
value
:
'客户'
},
{
source
:
'0'
,
target
:
'10'
,
value
:
'客户'
},
{
source
:
'0'
,
target
:
'11'
,
value
:
'客户'
},
{
source
:
'0'
,
target
:
'12'
,
value
:
'客户'
},
{
source
:
'0'
,
target
:
'13'
,
value
:
'客户'
},
];
if
(
!
singleSanctionEntitySupplyChainData
.
value
)
return
{};
const
data
=
singleSanctionEntitySupplyChainData
.
value
;
const
nodes
=
[];
const
links
=
[];
const
centerX
=
550
;
const
centerY
=
400
;
// 中心节点
nodes
.
push
({
id
:
'0'
,
name
:
data
.
orgName
,
category
:
0
,
// 强制为制裁中
symbol
:
'image://'
+
companyActive
,
// 强制使用制裁中图标
x
:
centerX
,
y
:
centerY
,
symbolSize
:
50
,
label
:
{
fontSize
:
16
,
fontWeight
:
'bold'
,
color
:
'#055FC2'
,
// 使用制裁蓝,在图标下方更清晰
position
:
'bottom'
,
distance
:
10
,
width
:
150
,
overflow
:
'break'
}
});
// 父级节点 (供应商)
const
parentList
=
data
.
parentOrgList
||
[];
parentList
.
forEach
((
item
,
index
)
=>
{
// 使用 (index + 0.5) 使节点在半圆弧内居中分布
const
angle
=
-
Math
.
PI
+
((
index
+
0.5
)
/
parentList
.
length
)
*
Math
.
PI
;
// 交错半径:偶数索引使用 340,奇数索引使用 440,拉开垂直距离
const
radius
=
index
%
2
===
0
?
340
:
440
;
const
x
=
centerX
+
radius
*
Math
.
cos
(
angle
);
const
y
=
centerY
+
radius
*
Math
.
sin
(
angle
);
// 动态计算标签位置:根据余弦值判断左右,根据正弦值判断上下
let
position
=
'right'
;
let
align
=
'left'
;
const
cosA
=
Math
.
cos
(
angle
);
const
sinA
=
Math
.
sin
(
angle
);
if
(
Math
.
abs
(
cosA
)
<
0.3
)
{
// 顶部区域
position
=
'top'
;
align
=
'center'
;
}
else
if
(
cosA
<
0
)
{
// 左侧区域
position
=
'left'
;
align
=
'right'
;
}
nodes
.
push
({
id
:
`p-
${
item
.
id
}
`
,
name
:
item
.
name
,
category
:
item
.
isSanctioned
?
0
:
1
,
symbol
:
'image://'
+
(
item
.
isSanctioned
?
companyActive
:
company
),
x
:
x
,
y
:
y
,
isSanctioned
:
item
.
isSanctioned
,
label
:
{
position
:
position
,
align
:
align
,
distance
:
8
,
width
:
110
,
overflow
:
'break'
,
lineHeight
:
14
,
fontSize
:
11
}
});
links
.
push
({
source
:
`p-
${
item
.
id
}
`
,
target
:
'0'
,
value
:
'供应商'
,
isSanctioned
:
item
.
isSanctioned
&&
data
.
isSanctioned
});
});
// 子级节点 (客户)
const
childList
=
data
.
childrenOrgList
||
[];
childList
.
forEach
((
item
,
index
)
=>
{
const
angle
=
((
index
+
0.5
)
/
childList
.
length
)
*
Math
.
PI
;
// 交错半径
const
radius
=
index
%
2
===
0
?
340
:
440
;
const
x
=
centerX
+
radius
*
Math
.
cos
(
angle
);
const
y
=
centerY
+
radius
*
Math
.
sin
(
angle
);
// 动态计算标签位置
let
position
=
'right'
;
let
align
=
'left'
;
const
cosA
=
Math
.
cos
(
angle
);
const
sinA
=
Math
.
sin
(
angle
);
if
(
Math
.
abs
(
cosA
)
<
0.3
)
{
// 底部区域
position
=
'bottom'
;
align
=
'center'
;
}
else
if
(
cosA
<
0
)
{
// 左侧区域
position
=
'left'
;
align
=
'right'
;
}
nodes
.
push
({
id
:
`c-
${
item
.
id
}
`
,
name
:
item
.
name
,
category
:
item
.
isSanctioned
?
0
:
1
,
symbol
:
'image://'
+
(
item
.
isSanctioned
?
companyActive
:
company
),
x
:
x
,
y
:
y
,
isSanctioned
:
item
.
isSanctioned
,
label
:
{
position
:
position
,
align
:
align
,
distance
:
8
,
width
:
110
,
overflow
:
'break'
,
lineHeight
:
14
,
fontSize
:
11
}
});
links
.
push
({
source
:
'0'
,
target
:
`c-
${
item
.
id
}
`
,
value
:
'客户'
,
isSanctioned
:
item
.
isSanctioned
&&
data
.
isSanctioned
});
});
return
{
tooltip
:
{
show
:
false
},
tooltip
:
{
show
:
true
,
formatter
:
(
params
)
=>
{
if
(
params
.
dataType
===
'node'
)
{
return
`<div style="padding: 8px; max-width: 300px; white-space: normal; word-break: break-all;">
${
params
.
data
.
name
}
</div>`
;
}
return
''
;
}
},
series
:
[
{
type
:
'graph'
,
...
...
@@ -307,10 +485,9 @@ const getSupplyChainOption = () => {
roam
:
true
,
label
:
{
show
:
true
,
position
:
'bottom'
,
formatter
:
'{b}'
,
fontSize
:
12
,
lineHeight
:
16
hideOverlap
:
true
},
edgeSymbol
:
[
'none'
,
'arrow'
],
edgeSymbolSize
:
[
4
,
8
],
...
...
@@ -336,15 +513,14 @@ const getSupplyChainOption = () => {
}
})),
links
:
links
.
map
(
link
=>
{
const
isSanctioned
=
nodeCategoryMap
[
link
.
source
]
===
0
&&
nodeCategoryMap
[
link
.
target
]
===
0
;
return
{
...
link
,
lineStyle
:
{
color
:
isSanctioned
?
'rgba(100, 180, 255, 1)'
:
'rgb(180, 181, 182)'
,
color
:
link
.
isSanctioned
?
'rgba(100, 180, 255, 1)'
:
'rgb(180, 181, 182)'
,
width
:
1
,
curveness
:
0
},
label
:
isSanctioned
?
{
label
:
link
.
isSanctioned
?
{
show
:
true
,
formatter
:
'{c}'
,
backgroundColor
:
'rgba(231, 243, 255, 1)'
,
...
...
@@ -364,47 +540,116 @@ const getSupplyChainOption = () => {
};
const
getEquityOption
=
()
=>
{
const
centerName
=
formatEntityName
(
currentEntityName
.
value
);
const
nodes
=
[
{
id
:
'0'
,
name
:
centerName
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
550
,
y
:
350
,
symbolSize
:
50
,
label
:
{
fontSize
:
16
,
fontWeight
:
'bold'
,
color
:
'#055FC2'
}
},
// Shareholders (Top)
{
id
:
'1'
,
name
:
'上海复芯凡高集成
\
n电路技术有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
150
,
y
:
50
},
{
id
:
'2'
,
name
:
'中信证券股份有限
\
n公司-嘉实上证科创
\
n板芯片交易型开...'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
350
,
y
:
50
},
{
id
:
'3'
,
name
:
'上海复旦复控科技
\
n产业控股有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
550
,
y
:
50
},
{
id
:
'4'
,
name
:
'香港中央结算(代
\
n理人)有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
750
,
y
:
50
},
{
id
:
'5'
,
name
:
'中国农业银行股份
\
n有限公司-南方军工
\
n改革灵活配置混...'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
950
,
y
:
50
},
// Investments (Bottom)
{
id
:
'6'
,
name
:
'上海复龙鸿芯微系
\
n统技术有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
50
,
y
:
650
},
{
id
:
'7'
,
name
:
'上海复微迅捷数字
\
n技术有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
200
,
y
:
650
},
{
id
:
'8'
,
name
:
'上海索辰信息技术
\
n有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
350
,
y
:
650
},
{
id
:
'9'
,
name
:
'上海复旦微电子
\
n(香港)有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
500
,
y
:
650
},
{
id
:
'10'
,
name
:
'北京复旦微电子技
\
n术有限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
650
,
y
:
650
},
{
id
:
'11'
,
name
:
'深圳复旦微电子有
\
n限公司'
,
category
:
0
,
symbol
:
'image://'
+
companyActive
,
x
:
800
,
y
:
650
},
{
id
:
'12'
,
name
:
'上海华岭集成电路
\
n技术股份有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
950
,
y
:
650
},
{
id
:
'13'
,
name
:
'上海复旦通讯股份
\
n有限公司'
,
category
:
1
,
symbol
:
'image://'
+
company
,
x
:
1100
,
y
:
650
},
];
const
links
=
[
{
source
:
'1'
,
target
:
'0'
,
value
:
'持股51%'
,
isSanctioned
:
true
},
{
source
:
'2'
,
target
:
'0'
,
value
:
'持股12%'
,
isSanctioned
:
false
},
{
source
:
'3'
,
target
:
'0'
,
value
:
'持股19%'
,
isSanctioned
:
true
},
{
source
:
'4'
,
target
:
'0'
,
value
:
'持股12%'
,
isSanctioned
:
false
},
{
source
:
'5'
,
target
:
'0'
,
value
:
'持股12%'
,
isSanctioned
:
false
},
{
source
:
'0'
,
target
:
'6'
,
value
:
'持股85%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'7'
,
value
:
'持股60%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'8'
,
value
:
'持股54%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'9'
,
value
:
'持股51%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'10'
,
value
:
'持股51%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'11'
,
value
:
'持股51%'
,
isSanctioned
:
true
},
{
source
:
'0'
,
target
:
'12'
,
value
:
'持股15%'
,
isSanctioned
:
false
},
{
source
:
'0'
,
target
:
'13'
,
value
:
'持股12%'
,
isSanctioned
:
false
},
];
if
(
!
singleSanctionEntityEquityData
.
value
)
return
{};
const
data
=
singleSanctionEntityEquityData
.
value
;
const
nodes
=
[];
const
links
=
[];
const
centerX
=
550
;
const
centerY
=
400
;
// 中心节点
nodes
.
push
({
id
:
'0'
,
name
:
data
.
orgName
,
category
:
0
,
// 强制为制裁中
symbol
:
'image://'
+
companyActive
,
// 强制使用制裁中图标
x
:
centerX
,
y
:
centerY
,
symbolSize
:
50
,
label
:
{
fontSize
:
16
,
fontWeight
:
'bold'
,
color
:
'#055FC2'
,
// 使用制裁蓝,在图标下方更清晰
position
:
'bottom'
,
distance
:
10
,
width
:
150
,
overflow
:
'break'
}
});
// 父级节点 (股东)
const
parentList
=
data
.
parentOrgList
||
[];
parentList
.
forEach
((
item
,
index
)
=>
{
// 在顶部水平排列
const
total
=
parentList
.
length
;
const
gap
=
200
;
const
startX
=
centerX
-
((
total
-
1
)
*
gap
)
/
2
;
const
x
=
startX
+
index
*
gap
;
const
y
=
centerY
-
250
;
// 向上偏移
nodes
.
push
({
id
:
`p-
${
index
}
`
,
name
:
item
.
name
,
category
:
item
.
isSanctioned
?
0
:
1
,
symbol
:
'image://'
+
(
item
.
isSanctioned
?
companyActive
:
company
),
x
:
x
,
y
:
y
,
isSanctioned
:
item
.
isSanctioned
,
label
:
{
position
:
'top'
,
distance
:
8
,
width
:
110
,
overflow
:
'break'
,
lineHeight
:
14
,
fontSize
:
11
}
});
links
.
push
({
source
:
`p-
${
index
}
`
,
target
:
'0'
,
value
:
item
.
type
||
'持股'
,
// 使用 type 字段
isSanctioned
:
item
.
isSanctioned
// 中心节点强制制裁,高亮取决于对方
});
});
// 子级节点 (对外投资)
const
childList
=
data
.
childrenOrgList
||
[];
childList
.
forEach
((
item
,
index
)
=>
{
// 在底部水平排列
const
total
=
childList
.
length
;
const
gap
=
240
;
// 稍微拉开一点间距
const
startX
=
centerX
-
((
total
-
1
)
*
gap
)
/
2
;
const
x
=
startX
+
index
*
gap
;
const
y
=
centerY
+
250
;
// 向下偏移
nodes
.
push
({
id
:
`c-
${
index
}
`
,
name
:
item
.
name
,
category
:
item
.
isSanctioned
?
0
:
1
,
symbol
:
'image://'
+
(
item
.
isSanctioned
?
companyActive
:
company
),
x
:
x
,
y
:
y
,
isSanctioned
:
item
.
isSanctioned
,
label
:
{
position
:
'bottom'
,
distance
:
8
,
width
:
110
,
overflow
:
'break'
,
lineHeight
:
14
,
fontSize
:
11
}
});
links
.
push
({
source
:
'0'
,
target
:
`c-
${
index
}
`
,
value
:
item
.
type
||
'持股'
,
// 使用 type 字段
isSanctioned
:
item
.
isSanctioned
// 中心节点强制制裁,高亮取决于对方
});
});
return
{
tooltip
:
{
show
:
false
},
tooltip
:
{
show
:
true
,
formatter
:
(
params
)
=>
{
if
(
params
.
dataType
===
'node'
)
{
return
`<div style="padding: 8px; max-width: 300px; white-space: normal; word-break: break-all;">
${
params
.
data
.
name
}
</div>`
;
}
return
''
;
}
},
series
:
[
{
type
:
'graph'
,
...
...
@@ -413,10 +658,9 @@ const getEquityOption = () => {
roam
:
true
,
label
:
{
show
:
true
,
position
:
'bottom'
,
formatter
:
'{b}'
,
fontSize
:
12
,
lineHeight
:
16
hideOverlap
:
true
},
edgeSymbol
:
[
'none'
,
'arrow'
],
edgeSymbolSize
:
[
4
,
8
],
...
...
@@ -468,24 +712,70 @@ const getEquityOption = () => {
};
};
watch
(
rightActiveTab
,
(
val
)
=>
{
const
debouncedGetList
=
debounce
(()
=>
{
getSingleSanctionEntityListRequest
();
},
1000
);
watch
(
searchText
,
()
=>
{
debouncedGetList
();
});
watch
(
searchDomain
,
()
=>
{
getSingleSanctionEntityListRequest
();
});
watch
(
activeIndex
,
(
val
)
=>
{
if
(
val
===
0
)
{
nextTick
(()
=>
{
if
(
activeEntityId
.
value
)
{
if
(
rightActiveTab
.
value
===
'supplyChain'
)
{
getSingleSanctionEntitySupplyChainRequest
();
}
else
{
getSingleSanctionEntityEquityRequest
();
}
}
initChart
();
});
},
{
immediate
:
true
});
}
});
watch
(
activeEntityId
,
(
val
)
=>
{
if
(
val
)
{
if
(
rightActiveTab
.
value
===
'supplyChain'
)
{
getSingleSanctionEntitySupplyChainRequest
();
}
else
{
getSingleSanctionEntityEquityRequest
();
}
initChart
();
}
});
onMounted
(()
=>
{
// 默认选中第一个实体
if
(
entityList
.
value
.
length
>
0
&&
entityList
.
value
[
0
].
children
&&
entityList
.
value
[
0
].
children
.
length
>
0
)
{
const
firstEntity
=
entityList
.
value
[
0
].
children
[
0
];
activeEntityId
.
value
=
firstEntity
.
id
;
currentEntityName
.
value
=
firstEntity
.
name
;
watch
(
is50PercentRule
,
()
=>
{
if
(
rightActiveTab
.
value
===
'equity'
&&
activeEntityId
.
value
)
{
getSingleSanctionEntityEquityRequest
();
}
});
watch
(
rightActiveTab
,
(
val
)
=>
{
if
(
activeEntityId
.
value
)
{
if
(
val
===
'supplyChain'
)
{
getSingleSanctionEntitySupplyChainRequest
();
}
else
{
getSingleSanctionEntityEquityRequest
();
}
}
nextTick
(()
=>
{
initChart
();
});
},
{
immediate
:
true
});
onMounted
(()
=>
{
// 获取URL参数
getUrlParams
();
// 单次制裁-深度挖掘-本次制裁实体清单列表-请求
getSingleSanctionEntityListRequest
();
window
.
addEventListener
(
'resize'
,
handleResize
);
});
...
...
@@ -493,6 +783,9 @@ onUnmounted(() => {
if
(
chartInstance
.
value
)
{
chartInstance
.
value
.
dispose
();
}
if
(
debouncedGetList
&&
debouncedGetList
.
cancel
)
{
debouncedGetList
.
cancel
();
}
window
.
removeEventListener
(
'resize'
,
handleResize
);
});
...
...
src/views/exportControl/v2.0SingleSanction/components/impactAnalysis/components/industrialImpact/index.vue
浏览文件 @
500577d5
...
...
@@ -15,8 +15,22 @@
</div>
<div
class=
"left-main"
>
<div
class=
"top-bar"
>
<el-select
v-model=
"selectedDomain"
class=
"domain-select"
placeholder=
"Select"
>
<el-option
label=
"全部领域"
value=
"全部领域"
/>
<el-select
v-model=
"searchDomain"
placeholder=
"全部领域"
class=
"domain-select"
>
<el-option
label=
"全部领域"
value=
""
/>
<el-option
label=
"人工智能"
value=
"1"
/>
<el-option
label=
"生物科技"
value=
"2"
/>
<el-option
label=
"新一代信息技术"
value=
"3"
/>
<el-option
label=
"量子科技"
value=
"4"
/>
<el-option
label=
"新能源"
value=
"5"
/>
<el-option
label=
"集成电路"
value=
"6"
/>
<el-option
label=
"海洋"
value=
"7"
/>
<el-option
label=
"先进制造"
value=
"8"
/>
<el-option
label=
"新材料"
value=
"9"
/>
<el-option
label=
"航空航天"
value=
"10"
/>
<el-option
label=
"深海"
value=
"11"
/>
<el-option
label=
"极地"
value=
"12"
/>
<el-option
label=
"太空"
value=
"13"
/>
<el-option
label=
"核"
value=
"14"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
class=
"search-input"
placeholder=
"搜索实体"
:suffix-icon=
"Search"
/>
</div>
...
...
@@ -26,7 +40,7 @@
<div
class=
"company-item"
:class=
"
{ active: selectedCompanyId === item.id }"
v-for="item in
compan
yList"
v-for="item in
entit
yList"
:key="item.id"
@click="selectedCompanyId = item.id"
>
...
...
@@ -51,7 +65,7 @@
:class=
"
{ active: activeScale === item }"
v-for="item in scaleOptions"
:key="item"
@click="
activeScale = item
"
@click="
handleScaleClick(item)
"
>
{{
item
}}
</div>
...
...
@@ -163,12 +177,298 @@
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
,
nextTick
,
computed
}
from
"vue"
;
import
{
ref
,
onMounted
,
nextTick
,
watch
}
from
"vue"
;
import
{
debounce
}
from
"lodash"
;
import
*
as
echarts
from
"echarts"
;
import
{
Search
}
from
"@element-plus/icons-vue"
;
import
defaultTitle
from
"../../../../assets/default-icon2.png"
;
import
ai
from
"../../assets/ai.png"
;
import
right
from
"../../assets/right.png"
;
import
{
getSingleSanctionEntityList
,
getSingleSanctionEntityRevenue
,
getSingleSanctionEntityNetProfit
,
getSingleSanctionEntityPersonnel
,
getSingleSanctionEntityMarketValue
,
getSingleSanctionEntityRDInvestment
,
getSingleSanctionEntityMarketShare
}
from
"@/api/exportControlV2.0"
;
// 单次制裁-影响分析-企业市场占比
const
marketShareData
=
ref
([]);
const
getMarketShare
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityMarketShare
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
marketShareData
.
value
=
res
.
data
||
[];
// 格式化市场占比数据
const
sortedData
=
[...
marketShareData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
if
(
sortedData
.
length
>
0
)
{
shareChartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
2
)
||
0
)),
unit
:
"%"
,
sanctionDate
:
"2024
\
nQ1"
// 默认制裁日期
};
nextTick
(()
=>
{
initShareChart
();
});
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-影响分析-企业研发投入
const
rdInvestmentData
=
ref
([]);
const
getRDInvestment
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityRDInvestment
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
rdInvestmentData
.
value
=
res
.
data
||
[];
// 格式化研发投入数据
const
sortedData
=
[...
rdInvestmentData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
if
(
sortedData
.
length
>
0
)
{
const
lastItem
=
sortedData
[
sortedData
.
length
-
1
];
rdChartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
2
)
||
0
)),
unit
:
" 万"
,
endValue
:
Number
(
lastItem
.
count
?.
toFixed
(
2
)
||
0
),
sanctionDate
:
"2024
\
nQ1"
// 默认制裁日期
};
nextTick
(()
=>
{
initRDChart
();
});
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-影响分析-企业市值变化
const
marketValueData
=
ref
([]);
const
getMarketValue
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityMarketValue
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
marketValueData
.
value
=
res
.
data
||
[];
// 格式化市值数据
const
sortedData
=
[...
marketValueData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
if
(
sortedData
.
length
>
0
)
{
const
lastItem
=
sortedData
[
sortedData
.
length
-
1
];
marketChartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
2
)
||
0
)),
unit
:
" 万"
,
endValue
:
Number
(
lastItem
.
count
?.
toFixed
(
2
)
||
0
),
sanctionDate
:
"2024
\
nQ1"
// 默认制裁日期
};
nextTick
(()
=>
{
initMarketChart
();
});
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-影响分析-企业规模-人员
const
personnelData
=
ref
([]);
// 单次制裁-影响分析-企业规模-人员-调查
const
getPersonnel
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityPersonnel
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
personnelData
.
value
=
res
.
data
||
[];
// 将数据格式化为图表所需格式
const
sortedData
=
[...
personnelData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
chartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
0
)
||
0
)),
// 人员通常是整数
unit
:
" 人"
,
endValue
:
Number
(
sortedData
[
sortedData
.
length
-
1
]?.
count
||
0
)
};
// 重新初始化图表
nextTick
(()
=>
{
initRevenueChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-影响分析-企业规模-净利润
const
netProfitData
=
ref
([]);
// 单次制裁-影响分析-企业规模-净利润-调查
const
getNetProfitData
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityNetProfit
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
netProfitData
.
value
=
res
.
data
||
[];
// 将数据格式化为图表所需格式
const
sortedData
=
[...
netProfitData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
chartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
2
)
||
0
)),
unit
:
" 万"
,
endValue
:
Number
(
sortedData
[
sortedData
.
length
-
1
]?.
count
.
toFixed
(
2
)
||
0
)
};
// 重新初始化图表
nextTick
(()
=>
{
initRevenueChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-影响分析-制裁企业列表
const
entityList
=
ref
([]);
const
searchDomain
=
ref
(
""
);
const
searchKeyword
=
ref
(
""
);
const
selectedCompanyId
=
ref
(
null
);
const
sanRecordId
=
ref
(
""
);
// 单次制裁-影响分析-企业规模-营收
const
revenueData
=
ref
([]);
// 单次制裁-影响分析-企业规模-营收-查询
const
getRevenueData
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionEntityRevenue
({
id
:
selectedCompanyId
.
value
,
})
if
(
res
.
code
===
200
)
{
revenueData
.
value
=
res
.
data
||
[];
// 将数据格式化为图表所需格式
const
sortedData
=
[...
revenueData
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
chartData
.
value
=
{
dates
:
sortedData
.
map
(
item
=>
item
.
year
.
toString
()),
values
:
sortedData
.
map
(
item
=>
Number
(
item
.
count
?.
toFixed
(
2
)
||
0
)),
// 防御空值
unit
:
" 万"
,
endValue
:
Number
(
sortedData
[
sortedData
.
length
-
1
]?.
count
.
toFixed
(
2
)
||
0
)
};
// 重新初始化图表
nextTick
(()
=>
{
initRevenueChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
const
handleScaleClick
=
(
item
)
=>
{
activeScale
.
value
=
item
;
if
(
item
===
'营收'
)
{
getRevenueData
();
}
else
if
(
item
===
'净利润'
)
{
getNetProfitData
();
}
else
if
(
item
===
'人员'
)
{
getPersonnel
();
}
}
watch
(
selectedCompanyId
,
(
val
)
=>
{
if
(
val
)
{
// 切换企业时,根据当前激活的按钮更新企业规模数据
if
(
activeScale
.
value
===
'营收'
)
{
getRevenueData
();
}
else
if
(
activeScale
.
value
===
'净利润'
)
{
getNetProfitData
();
}
else
if
(
activeScale
.
value
===
'人员'
)
{
getPersonnel
();
}
// 更新其他图表数据
getMarketValue
();
getRDInvestment
();
getMarketShare
();
}
});
// 单次制裁-影响分析-制裁企业列表-查询
const
getEntityList
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
false
,
domainId
:
searchDomain
.
value
||
undefined
,
searchText
:
searchKeyword
.
value
||
undefined
,
})
if
(
res
.
code
===
200
)
{
entityList
.
value
=
(
res
.
data
||
[]).
reduce
((
acc
,
group
)
=>
{
if
(
group
.
orgType
===
"企业"
&&
group
.
orgInfoList
)
{
acc
.
push
(...
group
.
orgInfoList
.
map
(
org
=>
({
id
:
org
.
id
,
name
:
org
.
orgNameZh
})));
}
return
acc
;
},
[]);
// 默认选中第一个
if
(
entityList
.
value
.
length
>
0
&&
!
selectedCompanyId
.
value
)
{
selectedCompanyId
.
value
=
entityList
.
value
[
0
].
id
;
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
const
debouncedGetEntityList
=
debounce
(()
=>
{
getEntityList
();
},
500
);
watch
(
searchDomain
,
()
=>
{
getEntityList
();
});
watch
(
searchKeyword
,
()
=>
{
debouncedGetEntityList
();
});
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
const
activeScale
=
ref
(
"营收"
);
const
scaleOptions
=
[
"营收"
,
"净利润"
,
"人员"
];
...
...
@@ -179,23 +479,6 @@ const rdOptions = ["研发经费"];
const
activeMarketShare
=
ref
(
"总体市场份额"
);
const
marketShareOptions
=
[
"总体市场份额"
];
const
selectedDomain
=
ref
(
"全部领域"
);
const
searchKeyword
=
ref
(
""
);
const
selectedCompanyId
=
ref
(
1
);
const
companyList
=
ref
([
{
id
:
1
,
name
:
"比亚迪股份有限公司"
},
{
id
:
2
,
name
:
"宁德时代新能源科技股份有限公司"
},
{
id
:
3
,
name
:
"隆基绿能科技股份有限公司"
},
{
id
:
4
,
name
:
"晶科能源控股有限公司"
},
{
id
:
5
,
name
:
"厦门海辰储能科技股份有限公司"
},
{
id
:
6
,
name
:
"国轩高科股份有限公司"
},
{
id
:
7
,
name
:
"远景科技集团"
},
{
id
:
8
,
name
:
"惠州亿纬锂能股份有限公司"
},
{
id
:
9
,
name
:
"天合光能股份有限公司"
},
{
id
:
10
,
name
:
"晶澳太阳能科技股份有限公司"
}
]);
const
chartData
=
ref
({
dates
:
[
"2023
\
nQ3"
,
...
...
@@ -248,7 +531,6 @@ const rdChartData = ref({
values
:
[
62
,
65
,
60
,
55
,
62
,
68
,
83
,
92
,
89
,
92
],
sanctionDate
:
"2024
\
nQ1"
,
endValue
:
92
,
maxY
:
100
});
const
shareChartData
=
ref
({
...
...
@@ -266,9 +548,7 @@ const shareChartData = ref({
],
values
:
[
65
,
70
,
72
,
70
,
58
,
65
,
68
,
72
,
72
,
68
],
sanctionDate
:
"2024
\
nQ1"
,
type
:
"bar"
,
unit
:
"%"
,
maxY
:
100
});
const
chartRef
=
ref
(
null
);
...
...
@@ -276,154 +556,162 @@ const marketChartRef = ref(null);
const
rdChartRef
=
ref
(
null
);
const
shareChartRef
=
ref
(
null
);
const
initChart
=
(
domRef
,
data
)
=>
{
if
(
!
domRef
)
return
;
const
myChart
=
echarts
.
init
(
domRef
);
const
lastDate
=
data
.
dates
[
data
.
dates
.
length
-
1
];
const
lastSeriesValue
=
data
.
values
[
data
.
values
.
length
-
1
];
const
maxY
=
data
.
maxY
||
1000
;
const
chartType
=
data
.
type
||
"line"
;
const
unit
=
data
.
unit
||
" 亿元"
;
const
option
=
{
// 提取通用配置生成器
const
getBaseOption
=
(
data
)
=>
{
return
{
tooltip
:
{
trigger
:
"axis"
,
valueFormatter
:
value
=>
value
+
unit
valueFormatter
:
value
=>
value
+
(
data
.
unit
||
" 亿元"
)
},
grid
:
{
top
:
"15%"
,
left
:
"
3
%"
,
top
:
"15%"
,
// 默认顶部留白
left
:
"
2
%"
,
right
:
"4%"
,
bottom
:
"
3
%"
,
bottom
:
"
5
%"
,
containLabel
:
true
},
xAxis
:
{
type
:
"category"
,
boundaryGap
:
chartType
===
"bar"
,
boundaryGap
:
true
,
data
:
data
.
dates
,
axisLine
:
{
lineStyle
:
{
color
:
"#E6EBF5"
}
},
axisLine
:
{
lineStyle
:
{
color
:
"#E6EBF5"
}
},
axisLabel
:
{
color
:
"#606266"
,
fontSize
:
12
,
lineHeight
:
18
fontSize
:
10
,
interval
:
0
,
rotate
:
45
,
hideOverlap
:
false
},
axisTick
:
{
show
:
false
}
axisTick
:
{
show
:
false
}
},
yAxis
:
{
type
:
"value"
,
min
:
0
,
max
:
maxY
,
interval
:
maxY
/
5
,
name
:
chartType
===
"bar"
?
"百分比"
:
""
,
nameTextStyle
:
{
align
:
"right"
,
padding
:
[
0
,
8
,
0
,
0
],
color
:
"#606266"
},
splitNumber
:
5
,
axisLabel
:
{
color
:
"#606266"
,
fontSize
:
12
fontSize
:
12
,
formatter
:
(
value
)
=>
value
.
toLocaleString
()
},
splitLine
:
{
lineStyle
:
{
type
:
"dashed"
,
color
:
"rgba(231, 243, 255, 1)"
}
splitLine
:
{
lineStyle
:
{
type
:
"dashed"
,
color
:
"rgba(231, 243, 255, 1)"
}
}
}
},
series
:
[
{
};
};
// 1. 初始化企业营收图表 (应用原型图置顶样式)
const
initRevenueChart
=
()
=>
{
const
dom
=
chartRef
.
value
;
const
data
=
chartData
.
value
;
if
(
!
dom
||
!
data
.
values
)
return
;
let
myChart
=
echarts
.
getInstanceByDom
(
dom
)
||
echarts
.
init
(
dom
);
const
lastDate
=
data
.
dates
[
data
.
dates
.
length
-
1
];
const
lastValue
=
data
.
values
[
data
.
values
.
length
-
1
];
const
unit
=
data
.
unit
||
" 亿元"
;
const
maxVal
=
Math
.
max
(...
data
.
values
.
map
(
v
=>
Number
(
v
)
||
0
));
const
maxY
=
Math
.
ceil
((
maxVal
*
1.5
)
/
100
)
*
100
||
100
;
const
option
=
getBaseOption
(
data
);
option
.
grid
.
top
=
"20%"
;
// 增加顶部空间
option
.
yAxis
.
max
=
maxY
;
option
.
series
=
[{
data
:
data
.
values
,
type
:
chartType
,
type
:
"line"
,
symbol
:
"none"
,
smooth
:
false
,
barWidth
:
16
,
itemStyle
:
chartType
===
"bar"
?
{
lineStyle
:
{
color
:
"#055FC2"
,
width
:
2
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"#055FC2
"
},
{
offset
:
0
,
color
:
"rgba(5, 95, 194, 0.2)
"
},
{
offset
:
1
,
color
:
"rgba(5, 95, 194, 0)"
}
]),
borderRadius
:
[
8
,
8
,
0
,
0
]
}
:
undefined
,
lineStyle
:
chartType
===
"line"
?
{
color
:
"#055FC2"
,
width
:
2
}
:
undefined
,
areaStyle
:
chartType
===
"line"
?
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"rgba(5, 95, 194, 0.2)"
},
{
offset
:
1
,
color
:
"rgba(5, 95, 194, 0)"
}
])
}
:
undefined
,
},
markLine
:
{
symbol
:
"none"
,
data
:
[
{
data
.
sanctionDate
?
{
xAxis
:
data
.
sanctionDate
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
rotate
:
0
,
color
:
"#F56C6C"
,
backgroundColor
:
"rgba(255, 238, 238, 1)"
,
borderRadius
:
4
,
padding
:
[
4
,
8
],
offset
:
[
0
,
0
]
padding
:
[
4
,
8
]
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
,
width
:
1
}
},
chartType
===
"line"
?
[
{
coord
:
[
lastDate
,
lastSeriesValue
],
symbol
:
"none"
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
,
width
:
1
}
}
:
null
,
[
{
coord
:
[
lastDate
,
lastValue
],
symbol
:
"none"
},
{
coord
:
[
lastDate
,
maxY
],
symbol
:
"none"
,
lineStyle
:
{
color
:
"#055FC2"
,
type
:
"dotted"
}
}
]
].
filter
(
Boolean
)
},
{
coord
:
[
lastDate
,
maxY
],
symbol
:
"none"
,
lineStyle
:
{
markPoint
:
{
symbol
:
"circle"
,
symbolSize
:
6
,
itemStyle
:
{
color
:
"#055FC2"
},
label
:
{
show
:
true
,
position
:
"left"
,
formatter
:
"{c}"
+
unit
.
trim
(),
color
:
"#055FC2"
,
type
:
"dotted"
,
width
:
1
}
fontSize
:
16
,
fontWeight
:
"bold"
,
offset
:
[
-
10
,
0
]
},
data
:
[{
coord
:
[
lastDate
,
maxY
],
value
:
data
.
endValue
||
lastValue
}]
}
}];
myChart
.
setOption
(
option
,
true
);
};
// 2. 初始化企业市值图表
const
initMarketChart
=
()
=>
{
const
dom
=
marketChartRef
.
value
;
const
data
=
marketChartData
.
value
;
if
(
!
dom
||
!
data
.
values
)
return
;
let
myChart
=
echarts
.
getInstanceByDom
(
dom
)
||
echarts
.
init
(
dom
);
const
lastDate
=
data
.
dates
[
data
.
dates
.
length
-
1
];
const
lastValue
=
data
.
values
[
data
.
values
.
length
-
1
];
const
unit
=
data
.
unit
||
" 万"
;
const
maxVal
=
Math
.
max
(...
data
.
values
.
map
(
v
=>
Number
(
v
)
||
0
));
const
maxY
=
Math
.
ceil
((
maxVal
*
1.5
)
/
100
)
*
100
||
100
;
const
option
=
getBaseOption
(
data
);
option
.
grid
.
top
=
"20%"
;
// 增加顶部空间
option
.
yAxis
.
max
=
maxY
;
option
.
series
=
[{
data
:
data
.
values
,
type
:
"line"
,
symbol
:
"none"
,
lineStyle
:
{
color
:
"#055FC2"
,
width
:
2
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"rgba(5, 95, 194, 0.2)"
},
{
offset
:
1
,
color
:
"rgba(5, 95, 194, 0)"
}
])
},
markLine
:
{
symbol
:
"none"
,
data
:
[
data
.
sanctionDate
?
{
xAxis
:
data
.
sanctionDate
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
color
:
"#F56C6C"
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
}
}
:
null
,
[
{
coord
:
[
lastDate
,
lastValue
],
symbol
:
"none"
},
{
coord
:
[
lastDate
,
maxY
],
symbol
:
"none"
,
lineStyle
:
{
color
:
"#055FC2"
,
type
:
"dotted"
}
}
]
:
null
].
filter
(
Boolean
)
},
markPoint
:
chartType
===
"line"
?
{
markPoint
:
{
symbol
:
"circle"
,
symbolSize
:
6
,
itemStyle
:
{
color
:
"#055FC2"
},
itemStyle
:
{
color
:
"#055FC2"
},
label
:
{
show
:
true
,
position
:
"left"
,
...
...
@@ -431,31 +719,144 @@ const initChart = (domRef, data) => {
color
:
"#055FC2"
,
fontSize
:
16
,
fontWeight
:
"bold"
,
offset
:
[
-
5
,
0
]
offset
:
[
-
10
,
0
]
},
data
:
[
{
coord
:
[
lastDate
,
maxY
],
value
:
data
.
endValue
||
lastSeriesValue
data
:
[{
coord
:
[
lastDate
,
maxY
],
value
:
data
.
endValue
||
lastValue
}]
}
}];
myChart
.
setOption
(
option
,
true
);
};
// 3. 初始化研发投入图表
const
initRDChart
=
()
=>
{
const
dom
=
rdChartRef
.
value
;
const
data
=
rdChartData
.
value
;
if
(
!
dom
||
!
data
.
values
)
return
;
let
myChart
=
echarts
.
getInstanceByDom
(
dom
)
||
echarts
.
init
(
dom
);
const
lastDate
=
data
.
dates
[
data
.
dates
.
length
-
1
];
const
lastValue
=
data
.
values
[
data
.
values
.
length
-
1
];
const
unit
=
data
.
unit
||
" 万"
;
// 计算 Y 轴最大值,留出顶部空间给数值标签
const
maxVal
=
Math
.
max
(...
data
.
values
.
map
(
v
=>
Number
(
v
)
||
0
));
const
maxY
=
Math
.
ceil
((
maxVal
*
1.5
)
/
100
)
*
100
||
100
;
const
option
=
getBaseOption
(
data
);
option
.
grid
.
top
=
"20%"
;
// 增加顶部空间
option
.
yAxis
.
max
=
maxY
;
option
.
series
=
[{
data
:
data
.
values
,
type
:
"line"
,
symbol
:
"none"
,
lineStyle
:
{
color
:
"#055FC2"
,
width
:
2
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"rgba(5, 95, 194, 0.2)"
},
{
offset
:
1
,
color
:
"rgba(5, 95, 194, 0)"
}
])
},
markLine
:
{
symbol
:
"none"
,
data
:
[
data
.
sanctionDate
?
{
xAxis
:
data
.
sanctionDate
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
color
:
"#F56C6C"
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
}
}
:
null
,
[
{
coord
:
[
lastDate
,
lastValue
],
symbol
:
"none"
},
{
coord
:
[
lastDate
,
maxY
],
symbol
:
"none"
,
lineStyle
:
{
color
:
"#055FC2"
,
type
:
"dotted"
}
}
]
].
filter
(
Boolean
)
},
markPoint
:
{
symbol
:
"circle"
,
symbolSize
:
6
,
itemStyle
:
{
color
:
"#055FC2"
},
label
:
{
show
:
true
,
position
:
"left"
,
formatter
:
"{c}"
+
unit
.
trim
(),
color
:
"#055FC2"
,
fontSize
:
16
,
fontWeight
:
"bold"
,
offset
:
[
-
10
,
0
]
},
data
:
[{
coord
:
[
lastDate
,
maxY
],
value
:
data
.
endValue
||
lastValue
}]
}
:
undefined
}];
myChart
.
setOption
(
option
,
true
);
};
// 4. 初始化市场占比图表 (柱状图)
const
initShareChart
=
()
=>
{
const
dom
=
shareChartRef
.
value
;
const
data
=
shareChartData
.
value
;
if
(
!
dom
||
!
data
.
values
||
data
.
values
.
length
===
0
)
return
;
let
myChart
=
echarts
.
getInstanceByDom
(
dom
)
||
echarts
.
init
(
dom
);
const
option
=
getBaseOption
(
data
);
// 针对柱状图优化 X 轴
option
.
xAxis
.
boundaryGap
=
true
;
option
.
xAxis
.
axisLabel
.
interval
=
'auto'
;
// 自动计算间隔,避免标签重叠导致柱子变细
option
.
yAxis
.
name
=
"百分比"
;
// 动态计算 Y 轴最大值,避免数据太小时展示不明显
const
maxVal
=
Math
.
max
(...
data
.
values
);
const
maxY
=
maxVal
===
0
?
100
:
maxVal
*
1.5
;
option
.
yAxis
.
max
=
maxY
;
option
.
series
=
[{
data
:
data
.
values
,
type
:
"bar"
,
// 移除固定 barWidth,让其根据数据量自适应,或设置一个合理的最小值
barMaxWidth
:
20
,
barMinHeight
:
2
,
// 确保即使数值极小也能看到一点
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"#055FC2"
},
{
offset
:
1
,
color
:
"rgba(5, 95, 194, 0.1)"
}
]),
borderRadius
:
[
8
,
8
,
0
,
0
]
},
markLine
:
{
symbol
:
"none"
,
data
:
[
data
.
sanctionDate
?
{
xAxis
:
data
.
sanctionDate
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
color
:
"#F56C6C"
,
backgroundColor
:
"rgba(245, 108, 108, 0.1)"
,
padding
:
[
2
,
4
],
borderRadius
:
10
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
}
}
:
null
].
filter
(
Boolean
)
}
]
};
myChart
.
setOption
(
option
);
window
.
addEventListener
(
"resize"
,
()
=>
{
myChart
.
resize
();
});
}];
myChart
.
setOption
(
option
,
true
);
};
onMounted
(()
=>
{
onMounted
(
async
()
=>
{
getUrlParams
();
// 单次制裁-影响分析-制裁企业列表-查询 (获取到默认的 selectedCompanyId 后会触发 watch 加载其他数据)
await
getEntityList
();
// 初始化阶段默认调用营收数据
if
(
selectedCompanyId
.
value
)
{
getRevenueData
();
}
nextTick
(()
=>
{
init
Chart
(
chartRef
.
value
,
chartData
.
value
);
init
Chart
(
marketChartRef
.
value
,
marketChartData
.
value
);
init
Chart
(
rdChartRef
.
value
,
rdChartData
.
value
);
init
Chart
(
shareChartRef
.
value
,
shareChartData
.
value
);
init
RevenueChart
(
);
init
MarketChart
(
);
init
RDChart
(
);
init
ShareChart
(
);
});
});
</
script
>
...
...
src/views/exportControl/v2.0SingleSanction/components/impactAnalysis/components/researchImpact/index.vue
浏览文件 @
500577d5
...
...
@@ -7,15 +7,15 @@
<div
class=
"text"
>
制裁科研机构列表
</div>
<div
class=
"right-group"
>
<el-select
v-model=
"
activeDependency
"
v-model=
"
searchDomain
"
class=
"dependency-select"
placeholder=
"
Select
"
placeholder=
"
全部领域
"
>
<el-option
v-for=
"item in d
ependency
Options"
:key=
"item"
:label=
"item"
:value=
"item"
v-for=
"item in d
omain
Options"
:key=
"item
.value
"
:label=
"item
.label
"
:value=
"item
.value
"
/>
</el-select>
<div
class=
"btn-com"
>
...
...
@@ -31,7 +31,7 @@
<div
class=
"company-item"
:class=
"
{ active: selectedCompanyId === item.id }"
v-for="item in
compan
yList"
v-for="item in
entit
yList"
:key="item.id"
@click="selectedCompanyId = item.id"
>
...
...
@@ -71,7 +71,7 @@
<div
class=
"box"
></div>
<div
class=
"text"
>
科研仪器进口国分布
</div>
<div
class=
"right-group"
>
<el-select
<
!--
<
el-select
v-model=
"activeInstrument"
class=
"instrument-select"
placeholder=
"Select"
...
...
@@ -82,7 +82,7 @@
:label=
"item"
:value=
"item"
/>
</el-select>
</el-select>
-->
<div
class=
"btn"
>
<img
src=
"../../../../assets/数据库按钮.png"
alt=
""
/>
<img
src=
"../../../../assets/下载按钮.png"
alt=
""
/>
...
...
@@ -107,13 +107,13 @@
<el-select
v-model=
"activeProjectDomain"
class=
"project-domain-select"
placeholder=
"
Select
"
placeholder=
"
全部领域
"
>
<el-option
v-for=
"item in projectDomainOptions"
:key=
"item"
:label=
"item"
:value=
"item"
:key=
"item
.value
"
:label=
"item
.label
"
:value=
"item
.value
"
/>
</el-select>
<div
class=
"btn"
>
...
...
@@ -140,13 +140,13 @@
<el-select
v-model=
"activePaperDomain"
class=
"paper-domain-select"
placeholder=
"
Select
"
placeholder=
"
全部领域
"
>
<el-option
v-for=
"item in paperDomainOptions"
:key=
"item"
:label=
"item"
:value=
"item"
:key=
"item
.value
"
:label=
"item
.label
"
:value=
"item
.value
"
/>
</el-select>
<div
class=
"btn"
>
...
...
@@ -171,11 +171,197 @@
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
,
nextTick
}
from
"vue"
;
import
{
ref
,
onMounted
,
nextTick
,
watch
,
onBeforeUnmount
}
from
"vue"
;
import
*
as
echarts
from
"echarts"
;
import
defaultTitle
from
"../../../../assets/default-icon2.png"
;
import
ai
from
"../../assets/ai.png"
;
import
right
from
"../../assets/right.png"
;
import
{
getSingleSanctionEntityList
,
getSingleSanctionEntityRDInstrumentDependency
,
getSingleSanctionEntityRDInstrumentImportCountry
,
getSingleSanctionEntityInternationalCooperation
,
getSingleSanctionEntityInternationalPaper
}
from
"@/api/exportControlV2.0"
;
const
domainOptions
=
[
{
label
:
"全部领域"
,
value
:
""
},
{
label
:
"人工智能"
,
value
:
"1"
},
{
label
:
"生物科技"
,
value
:
"2"
},
{
label
:
"新一代信息技术"
,
value
:
"3"
},
{
label
:
"量子科技"
,
value
:
"4"
},
{
label
:
"新能源"
,
value
:
"5"
},
{
label
:
"集成电路"
,
value
:
"6"
},
{
label
:
"海洋"
,
value
:
"7"
},
{
label
:
"先进制造"
,
value
:
"8"
},
{
label
:
"新材料"
,
value
:
"9"
},
{
label
:
"航空航天"
,
value
:
"10"
},
{
label
:
"深海"
,
value
:
"11"
},
{
label
:
"极地"
,
value
:
"12"
},
{
label
:
"太空"
,
value
:
"13"
},
{
label
:
"核"
,
value
:
"14"
}
];
const
activeInstrument
=
ref
(
"电子测量仪器"
);
const
instrumentOptions
=
[
"电子测量仪器"
];
const
activeProjectDomain
=
ref
(
""
);
const
projectDomainOptions
=
domainOptions
;
const
activePaperDomain
=
ref
(
""
);
const
paperDomainOptions
=
domainOptions
;
const
searchDomain
=
ref
(
""
);
const
selectedCompanyId
=
ref
(
null
);
const
sanRecordId
=
ref
(
""
);
const
entityList
=
ref
([]);
const
internationalCooperation
=
ref
([]);
const
rdInstrumentImportCountry
=
ref
([]);
const
rdInstrumentDependency
=
ref
([]);
// 单次制裁-影响分析-新增国际合著论文
const
internationalPaper
=
ref
([]);
const
getInternationalPaper
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
params
=
{
id
:
selectedCompanyId
.
value
};
if
(
activePaperDomain
.
value
)
{
params
.
domainId
=
activePaperDomain
.
value
;
}
const
res
=
await
getSingleSanctionEntityInternationalPaper
(
params
);
if
(
res
.
code
===
200
)
{
internationalPaper
.
value
=
res
.
data
||
[];
nextTick
(()
=>
{
initShareChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-影响分析-新增国际合作项目
const
getInternationalCooperation
=
async
()
=>
{
if
(
!
selectedCompanyId
.
value
)
return
;
try
{
const
params
=
{
id
:
selectedCompanyId
.
value
};
if
(
activeProjectDomain
.
value
)
{
params
.
domainId
=
activeProjectDomain
.
value
;
}
const
res
=
await
getSingleSanctionEntityInternationalCooperation
(
params
);
if
(
res
.
code
===
200
)
{
internationalCooperation
.
value
=
res
.
data
||
[];
nextTick
(()
=>
{
initRdChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-影响分析-科研仪器进口国分布
const
getRDInstrumentImportCountry
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityRDInstrumentImportCountry
({
id
:
selectedCompanyId
.
value
});
if
(
res
.
code
===
200
)
{
rdInstrumentImportCountry
.
value
=
res
.
data
||
[];
nextTick
(()
=>
{
initMarketChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-影响分析-科研仪器对美依赖情况
const
getRDInstrumentDependency
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityRDInstrumentDependency
({
id
:
selectedCompanyId
.
value
});
if
(
res
.
code
===
200
)
{
rdInstrumentDependency
.
value
=
res
.
data
||
[];
nextTick
(()
=>
{
initChart
();
});
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
// 单次制裁-影响分析-科研机构列表-查询
const
getEntityList
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
false
,
domainId
:
searchDomain
.
value
||
undefined
});
if
(
res
.
code
===
200
)
{
entityList
.
value
=
(
res
.
data
||
[]).
reduce
((
acc
,
group
)
=>
{
if
(
group
.
orgType
===
"科研机构"
&&
group
.
orgInfoList
)
{
acc
.
push
(
...
group
.
orgInfoList
.
map
((
org
)
=>
({
id
:
org
.
id
,
name
:
org
.
orgNameZh
}))
);
}
return
acc
;
},
[]);
// 默认选中第一个
if
(
entityList
.
value
.
length
>
0
&&
!
selectedCompanyId
.
value
)
{
selectedCompanyId
.
value
=
entityList
.
value
[
0
].
id
;
}
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
};
watch
(
selectedCompanyId
,
(
val
)
=>
{
if
(
val
)
{
getRDInstrumentDependency
();
getRDInstrumentImportCountry
();
getInternationalCooperation
();
getInternationalPaper
();
}
});
watch
(
activeProjectDomain
,
()
=>
{
getInternationalCooperation
();
});
watch
(
activePaperDomain
,
()
=>
{
getInternationalPaper
();
});
watch
(
searchDomain
,
()
=>
{
getEntityList
();
});
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
const
chartRef
=
ref
(
null
);
const
marketChartRef
=
ref
(
null
);
...
...
@@ -183,17 +369,14 @@ const rdChartRef = ref(null);
const
shareChartRef
=
ref
(
null
);
const
initChart
=
()
=>
{
if
(
!
chartRef
.
value
)
return
;
const
myChart
=
echarts
.
init
(
chartRef
.
value
);
const
data
=
[
{
name
:
"电子测量仪器"
,
value
:
109
},
{
name
:
"物理性能测试仪器"
,
value
:
95
},
{
name
:
"激光器"
,
value
:
79
},
{
name
:
"分析仪器"
,
value
:
25
},
{
name
:
"计量仪器"
,
value
:
21
},
{
name
:
"计算机及其配套设备"
,
value
:
10
},
{
name
:
"地球探测仪器"
,
value
:
10
}
];
if
(
!
chartRef
.
value
||
rdInstrumentDependency
.
value
.
length
===
0
)
return
;
const
myChart
=
echarts
.
getInstanceByDom
(
chartRef
.
value
)
||
echarts
.
init
(
chartRef
.
value
);
// 处理接口数据,映射为图表所需的 name 和 value
const
chartData
=
rdInstrumentDependency
.
value
.
map
(
item
=>
({
name
:
item
.
name
,
value
:
item
.
count
}));
const
option
=
{
grid
:
{
...
...
@@ -203,6 +386,15 @@ const initChart = () => {
bottom
:
"0%"
,
containLabel
:
true
},
dataZoom
:
[
{
type
:
"inside"
,
orient
:
"vertical"
,
start
:
0
,
end
:
chartData
.
length
>
7
?
(
7
/
chartData
.
length
)
*
100
:
100
,
zoomLock
:
true
}
],
xAxis
:
{
show
:
false
,
type
:
"value"
...
...
@@ -210,7 +402,7 @@ const initChart = () => {
yAxis
:
[
{
type
:
"category"
,
data
:
d
ata
.
map
((
item
)
=>
item
.
name
),
data
:
chartD
ata
.
map
((
item
)
=>
item
.
name
),
axisLine
:
{
show
:
false
},
axisTick
:
{
show
:
false
},
axisLabel
:
{
...
...
@@ -225,7 +417,7 @@ const initChart = () => {
},
{
type
:
"category"
,
data
:
d
ata
.
map
((
item
)
=>
item
.
value
),
data
:
chartD
ata
.
map
((
item
)
=>
item
.
value
),
axisLine
:
{
show
:
false
},
axisTick
:
{
show
:
false
},
axisLabel
:
{
...
...
@@ -245,7 +437,7 @@ const initChart = () => {
series
:
[
{
type
:
"bar"
,
data
:
d
ata
.
map
((
item
,
index
)
=>
{
data
:
chartD
ata
.
map
((
item
,
index
)
=>
{
return
{
value
:
item
.
value
,
itemStyle
:
{
...
...
@@ -278,23 +470,17 @@ const initChart = () => {
]
};
myChart
.
setOption
(
option
);
window
.
addEventListener
(
"resize"
,
()
=>
{
myChart
.
resize
();
});
};
const
initMarketChart
=
()
=>
{
if
(
!
marketChartRef
.
value
)
return
;
const
myChart
=
echarts
.
init
(
marketChartRef
.
value
);
const
data
=
[
{
name
:
"美国"
,
value
:
27
},
{
name
:
"日本"
,
value
:
22
},
{
name
:
"德国"
,
value
:
18
},
{
name
:
"英国"
,
value
:
15
},
{
name
:
"韩国"
,
value
:
12
},
{
name
:
"荷兰"
,
value
:
8
},
{
name
:
"其他"
,
value
:
7
}
];
if
(
!
marketChartRef
.
value
||
rdInstrumentImportCountry
.
value
.
length
===
0
)
return
;
const
myChart
=
echarts
.
getInstanceByDom
(
marketChartRef
.
value
)
||
echarts
.
init
(
marketChartRef
.
value
);
const
chartData
=
rdInstrumentImportCountry
.
value
.
map
(
item
=>
({
name
:
item
.
name
,
value
:
item
.
count
}));
const
colors
=
[
"#66b1ff"
,
"#ffba63"
,
...
...
@@ -312,14 +498,18 @@ const initMarketChart = () => {
formatter
:
"{b}: {c}%"
},
legend
:
{
type
:
"scroll"
,
orient
:
"vertical"
,
right
:
"
1
5%"
,
right
:
"5%"
,
top
:
"middle"
,
bottom
:
"20"
,
itemGap
:
20
,
icon
:
"circle"
,
formatter
:
function
(
name
)
{
const
item
=
data
.
find
((
i
)
=>
i
.
name
===
name
);
return
`{name|
${
name
}
} {value|
${
item
.
value
}
%}`
;
const
item
=
chartData
.
find
((
i
)
=>
i
.
name
===
name
);
const
total
=
chartData
.
reduce
((
sum
,
i
)
=>
sum
+
i
.
value
,
0
);
const
percent
=
total
>
0
?
((
item
.
value
/
total
)
*
100
).
toFixed
(
0
)
:
0
;
return
`{name|
${
name
}
} {value|
${
percent
}
%}`
;
},
textStyle
:
{
rich
:
{
...
...
@@ -357,32 +547,25 @@ const initMarketChart = () => {
labelLine
:
{
show
:
false
},
data
:
d
ata
data
:
chartD
ata
}
]
};
myChart
.
setOption
(
option
);
window
.
addEventListener
(
"resize"
,
()
=>
{
myChart
.
resize
();
});
};
const
initRdChart
=
()
=>
{
if
(
!
rdChartRef
.
value
)
return
;
const
myChart
=
echarts
.
init
(
rdChartRef
.
value
);
const
data
=
[
6
,
7
,
6
,
6
,
6
,
7
,
7
,
6
,
5
,
4
];
const
years
=
[
"2016"
,
"2017"
,
"2018"
,
"2019"
,
"2020"
,
"2021"
,
"2022"
,
"2023"
,
"2024"
,
"2025"
];
const
myChart
=
echarts
.
getInstanceByDom
(
rdChartRef
.
value
)
||
echarts
.
init
(
rdChartRef
.
value
);
// 处理接口返回的数据,按年份升序排序
const
chartData
=
[...
internationalCooperation
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
const
years
=
chartData
.
map
((
item
)
=>
item
.
year
.
toString
());
const
data
=
chartData
.
map
((
item
)
=>
item
.
count
);
// 动态计算 Y 轴最大值和间隔
const
maxVal
=
Math
.
max
(...
data
,
10
);
const
yMax
=
Math
.
ceil
(
maxVal
/
5
)
*
5
;
const
option
=
{
tooltip
:
{
...
...
@@ -417,8 +600,8 @@ const initRdChart = () => {
yAxis
:
{
type
:
"value"
,
min
:
0
,
max
:
10
,
interval
:
2
,
max
:
yMax
,
interval
:
yMax
/
5
,
axisLabel
:
{
color
:
"#606266"
,
fontSize
:
12
,
...
...
@@ -452,54 +635,25 @@ const initRdChart = () => {
color
:
"rgba(5, 95, 194, 0)"
}
])
},
markLine
:
{
symbol
:
"none"
,
data
:
[
{
xAxis
:
"2023"
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
rotate
:
0
,
color
:
"#F56C6C"
,
backgroundColor
:
"rgba(255, 238, 238, 1)"
,
borderRadius
:
4
,
padding
:
[
4
,
8
]
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
,
width
:
1
}
}
]
}
}
]
};
myChart
.
setOption
(
option
);
window
.
addEventListener
(
"resize"
,
()
=>
{
myChart
.
resize
();
});
};
const
initShareChart
=
()
=>
{
if
(
!
shareChartRef
.
value
)
return
;
const
myChart
=
echarts
.
init
(
shareChartRef
.
value
);
const
data
=
[
61
,
65
,
59
,
46
,
46
,
41
,
46
,
51
,
21
,
24
];
const
years
=
[
"2016"
,
"2017"
,
"2018"
,
"2019"
,
"2020"
,
"2021"
,
"2022"
,
"2023"
,
"2024"
,
"2025"
];
const
myChart
=
echarts
.
getInstanceByDom
(
shareChartRef
.
value
)
||
echarts
.
init
(
shareChartRef
.
value
);
// 处理接口返回的数据,按年份升序排序
const
chartData
=
[...
internationalPaper
.
value
].
sort
((
a
,
b
)
=>
a
.
year
-
b
.
year
);
const
years
=
chartData
.
map
((
item
)
=>
item
.
year
.
toString
());
const
data
=
chartData
.
map
((
item
)
=>
item
.
count
);
// 动态计算 Y 轴最大值和间隔
const
maxVal
=
Math
.
max
(...
data
,
10
);
const
yMax
=
Math
.
ceil
(
maxVal
/
5
)
*
5
;
const
option
=
{
tooltip
:
{
...
...
@@ -534,8 +688,8 @@ const initShareChart = () => {
yAxis
:
{
type
:
"value"
,
min
:
0
,
max
:
100
,
interval
:
20
,
max
:
yMax
,
interval
:
yMax
/
5
,
axisLabel
:
{
color
:
"#606266"
,
fontSize
:
12
,
...
...
@@ -569,64 +723,35 @@ const initShareChart = () => {
color
:
"rgba(5, 95, 194, 0)"
}
])
},
markLine
:
{
symbol
:
"none"
,
data
:
[
{
xAxis
:
"2023"
,
label
:
{
formatter
:
"列入实体清单"
,
position
:
"end"
,
rotate
:
0
,
color
:
"#F56C6C"
,
backgroundColor
:
"rgba(255, 238, 238, 1)"
,
borderRadius
:
4
,
padding
:
[
4
,
8
]
},
lineStyle
:
{
color
:
"#F56C6C"
,
type
:
"dotted"
,
width
:
1
}
}
]
}
}
]
};
myChart
.
setOption
(
option
);
window
.
addEventListener
(
"resize"
,
()
=>
{
};
const
handleResize
=
()
=>
{
[
chartRef
,
marketChartRef
,
rdChartRef
,
shareChartRef
].
forEach
(
refItem
=>
{
if
(
refItem
.
value
)
{
const
myChart
=
echarts
.
getInstanceByDom
(
refItem
.
value
);
if
(
myChart
)
{
myChart
.
resize
();
}
}
});
};
onMounted
(()
=>
{
initChart
();
initMarketChart
();
initRdChart
();
initShareChart
();
});
const
selectedCompanyId
=
ref
(
1
);
const
activeDependency
=
ref
(
"对外依赖"
);
const
dependencyOptions
=
[
"对外依赖"
];
const
activeInstrument
=
ref
(
"电子测量仪器"
);
const
instrumentOptions
=
[
"电子测量仪器"
];
const
activeProjectDomain
=
ref
(
"全部领域"
);
const
projectDomainOptions
=
[
"全部领域"
];
const
activePaperDomain
=
ref
(
"全部领域"
);
const
paperDomainOptions
=
[
"全部领域"
];
const
companyList
=
ref
([
{
id
:
1
,
name
:
"空天信息创新研究院"
},
{
id
:
2
,
name
:
"中国科学院国家授时中心"
}
]);
onMounted
(
async
()
=>
{
getUrlParams
();
// 单次制裁-影响分析-科研机构列表-查询 (等待获取到默认的 selectedCompanyId 后会触发 watch 加载其他数据)
await
getEntityList
();
window
.
addEventListener
(
"resize"
,
handleResize
);
});
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
"resize"
,
handleResize
);
});
</
script
>
...
...
src/views/exportControl/v2.0SingleSanction/components/sanctionsOverview/index.vue
浏览文件 @
500577d5
...
...
@@ -16,38 +16,40 @@
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
>
<span>
商务部工业与安全局
>
</span>
<span>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布时间:
</div>
<div
class=
"value"
>
2025年9月16日
</div>
<div
class=
"value"
>
{{
formattedData
.
postDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
生效时间:
</div>
<div
class=
"value"
>
2025年9月12日
</div>
<div
class=
"value"
>
{{
formattedData
.
effectiveDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布文件:
</div>
<div
class=
"value"
>
2025-17893 (90 FR 44496)
</div>
<div
class=
"value"
>
{{
formattedData
.
fileCode
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
案卷号:
</div>
<div
class=
"value"
>
No. 250912-0152
</div>
<div
class=
"value"
>
{{
formattedData
.
administrativeOrderId
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布人:
</div>
<div
class=
"value link"
>
<img
:src=
"defaultTitle"
alt=
""
class=
"icon avatar"
>
<span
>
朱莉哑·A·赫尔松斯基 (战略贸易副助理部长)
>
</span>
<span
@
click=
"handleClick"
>
{{
formattedData
.
postPersonName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
制裁领域:
</div>
<div
class=
"value tags"
>
<span
class=
"tag"
>
量子科技
</span>
<span
class=
"tag"
>
人工智能
</span>
<span
class=
"tag"
>
集成电路
</span>
<span
class=
"tag"
v-for=
"(domain, index) in formattedData.domains"
:key=
"index"
>
{{
domain
}}
</span>
</div>
</div>
</div>
...
...
@@ -98,7 +100,7 @@
<div
class=
"content"
>
{{
item
.
content
}}
</div>
</div>
</div>
<div
class=
"view-more"
>
<div
class=
"view-more"
v-if=
"hasMore"
@
click=
"loadMore"
>
查看更多
<el-icon
class=
"icon-more"
><DArrowRight
/></el-icon>
</div>
</div>
...
...
@@ -115,15 +117,16 @@
</div>
<div
class=
"right-title"
>
<div
class=
"filter-row"
>
<div
class=
"filter-left"
>
<
!--
<
div
class=
"filter-left"
>
<el-select
v-model=
"filterEntity"
placeholder=
"受制裁实体"
style=
"width: 184px"
>
<el-option
label=
"受制裁实体"
value=
"1"
/>
</el-select>
</div>
</div>
-->
<div
class=
"filter-right"
>
<el-checkbox
v-model=
"onlyChina"
label=
"只看中国实体"
/>
<el-select
v-model=
"filterField"
placeholder=
"全部领域"
style=
"width: 150px; margin: 0 12px 0 16px"
>
<el-option
label=
"全部领域"
value=
"1"
/>
<el-option
label=
"全部领域"
value=
""
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
...
...
@@ -134,19 +137,19 @@
</div>
</div>
<div
class=
"stats-row"
>
<div
class=
"tabs"
>
<
!--
<
div
class=
"tabs"
>
<div
class=
"tab-btn"
:class=
"
{ active: activeTab === 'add' }" @click="activeTab = 'add'">新增实体
</div>
<div
class=
"tab-btn"
:class=
"
{ active: activeTab === 'remove' }" @click="activeTab = 'remove'">移除实体
</div>
</div>
</div>
-->
<div
class=
"stats-info"
>
<div
class=
"stat-item"
>
<span
class=
"dot red"
></span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
24
</span>
家 (50%规则涉及
<span
class=
"num red"
>
51
</span>
家)
</span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span>
家)
</span>
</div>
<div
class=
"stat-item"
>
<!--
<div
class=
"stat-item"
>
<span
class=
"dot green"
></span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
4
</span>
家 (50%规则涉及
<span
class=
"num green"
>
6
</span>
家)
</span>
</div
>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span>
家)
</span>
</div>
--
>
</div>
</div>
</div>
...
...
@@ -166,14 +169,20 @@
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"涉及领域"
width=
"1
2
0"
align=
"center"
>
<el-table-column
label=
"涉及领域"
width=
"1
8
0"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
class=
"tag"
:style=
"getTagStyle(scope.row.field)"
>
{{
scope
.
row
.
field
}}
</span>
<span
v-for=
"(item, index) in scope.row.fields"
:key=
"index"
class=
"tag"
:style=
"getTagStyle(item)"
style=
"margin: 0 2px;"
>
{{
item
}}
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"location"
label=
"上市地点"
width=
"
10
0"
align=
"center"
/>
<el-table-column
prop=
"date"
label=
"制裁时间"
width=
"1
2
0"
align=
"center"
/>
<el-table-column
prop=
"revenue"
label=
"营收(亿元)"
width=
"1
2
0"
align=
"center"
/>
<el-table-column
prop=
"location"
label=
"上市地点"
width=
"
9
0"
align=
"center"
/>
<el-table-column
prop=
"date"
label=
"制裁时间"
width=
"1
5
0"
align=
"center"
/>
<el-table-column
prop=
"revenue"
label=
"营收(亿元)"
width=
"1
1
0"
align=
"center"
/>
<el-table-column
label=
"50%规则子企业"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
>
...
...
@@ -195,18 +204,220 @@
</template>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
{
ref
,
defineProps
,
computed
,
onMounted
,
watch
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
DArrowRight
,
Search
}
from
"@element-plus/icons-vue"
;
import
{
debounce
}
from
"lodash"
;
import
title
from
"../../assets/title.png"
import
defaultTitle
from
"../../assets/default-icon1.png"
import
flag
from
"../../assets/default-icon2.png"
import
{
getSingleSanctionEntityCountry
,
getSingleSanctionBackground
,
getSingleSanctionOverviewList
}
from
"@/api/exportControlV2.0"
;
// 单次制裁-制裁概况-制裁清单
const
sanctionList
=
ref
([])
const
addCount
=
ref
(
0
)
const
addRuleCount
=
ref
(
0
)
const
removeCount
=
ref
(
0
)
const
removeRuleCount
=
ref
(
0
)
// 调用单次制裁-制裁概况-制裁清单接口
const
getSanctionOverviewList
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionOverviewList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
onlyChina
.
value
,
domainId
:
filterField
.
value
||
undefined
,
searchText
:
searchKeyword
.
value
||
undefined
,
})
if
(
res
.
code
===
200
)
{
const
data
=
res
.
data
||
{}
addCount
.
value
=
data
.
addCount
||
0
addRuleCount
.
value
=
data
.
addRuleCount
||
0
removeCount
.
value
=
data
.
removeCount
||
0
removeRuleCount
.
value
=
data
.
removeRuleCount
||
0
const
list
=
data
.
sanList
||
[]
sanctionList
.
value
=
list
.
map
(
item
=>
({
reason
:
item
.
sanReason
,
entities
:
(
item
.
orgList
||
[]).
map
(
org
=>
({
name
:
org
.
entityNameZh
||
org
.
entityName
,
fields
:
org
.
techDomains
||
[],
location
:
"--"
,
// 接口暂无数据
date
:
org
.
startTime
?
org
.
startTime
.
replace
(
/^
(\d{4})
-
(\d{2})
-
(\d{2})
.*$/
,
'$1年$2月$3日'
)
:
'--'
,
// 格式化日期
revenue
:
"--"
,
// 接口暂无数据
subsidiaryCount
:
org
.
ruleOrgCount
||
0
,
subsidiaryText
:
org
.
ruleOrgList
&&
org
.
ruleOrgList
.
length
>
0
?
(
org
.
ruleOrgList
[
0
].
orgName
.
length
>
10
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
'...'
:
org
.
ruleOrgList
[
0
].
orgName
)
+
'...等'
:
''
}))
}))
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
// 单次制裁-制裁概况-制裁背景
const
timelinePage
=
ref
(
1
);
const
timelinePageSize
=
ref
(
3
);
const
hasMore
=
ref
(
true
);
const
getSanctionBackground
=
async
(
isLoadMore
=
false
)
=>
{
try
{
const
res
=
await
getSingleSanctionBackground
({
sanRecordId
:
sanRecordId
.
value
,
pageNum
:
timelinePage
.
value
,
pageSize
:
timelinePageSize
.
value
,
})
if
(
res
.
code
===
200
)
{
const
list
=
res
.
data
.
content
||
[];
const
formattedList
=
list
.
map
(
item
=>
({
...
item
,
date
:
item
.
createTime
?
item
.
createTime
.
split
(
' '
)[
0
]
:
''
,
// 提取日期部分
content
:
item
.
summaryZh
}));
if
(
isLoadMore
)
{
timelineData
.
value
=
[...
timelineData
.
value
,
...
formattedList
];
}
else
{
timelineData
.
value
=
formattedList
;
}
// 判断是否还有更多数据
const
totalElements
=
res
.
data
.
totalElements
||
0
;
hasMore
.
value
=
timelineData
.
value
.
length
<
totalElements
;
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
const
loadMore
=
()
=>
{
timelinePage
.
value
++
;
getSanctionBackground
(
true
);
};
const
router
=
useRouter
();
const
sanRecordId
=
ref
(
""
)
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
// 单次制裁-制裁概况-制裁实体国家分布
const
getEntityCountry
=
async
()
=>
{
try
{
const
res
=
await
getSingleSanctionEntityCountry
({
sanRecordId
:
sanRecordId
.
value
})
if
(
res
.
code
===
200
)
{
const
rawData
=
res
.
data
||
[];
const
maxCount
=
Math
.
max
(...
rawData
.
map
(
item
=>
item
.
count
||
0
),
1
);
// 颜色池 - 用于循环分配
const
gradientPool
=
[
"linear-gradient(270deg, rgba(255,170,0,1) 0%, rgba(255,255,255,0) 100%)"
,
// 橙
"linear-gradient(270deg, rgba(5,95,194,1) 0%, rgba(255,255,255,0) 100%)"
,
// 蓝
"linear-gradient(270deg, rgba(114,46,209,1) 0%, rgba(255,255,255,0) 100%)"
,
// 紫
"linear-gradient(270deg, rgba(16,178,166,1) 0%, rgba(255,255,255,0) 100%)"
// 青
];
// 固定颜色映射 - 特定国家使用固定颜色
const
fixedColorMap
=
{
"中国"
:
"linear-gradient(270deg, rgba(206,79,81,1) 0%, rgba(255,255,255,0) 100%)"
// 红
};
entityDistribution
.
value
=
rawData
.
map
((
item
,
index
)
=>
({
...
item
,
width
:
`
${((
item
.
count
||
0
)
/
maxCount
)
*
100
}
%`
,
// 优先使用固定颜色,否则循环使用颜色池中的颜色
gradient
:
fixedColorMap
[
item
.
name
]
||
gradientPool
[
index
%
gradientPool
.
length
]
}));
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
const
props
=
defineProps
({
data
:
{
type
:
Object
,
default
:
()
=>
({})
}
});
// 跳转到人物页
const
handleClick
=
()
=>
{
const
route
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
// type: props.data.type,
personId
:
props
.
data
.
postPersonId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
};
// 计算属性处理数据
const
formattedData
=
computed
(()
=>
{
const
info
=
props
.
data
||
{};
// 日期格式化
const
formatDate
=
(
dateStr
)
=>
{
if
(
!
dateStr
)
return
''
;
const
date
=
new
Date
(
dateStr
);
if
(
isNaN
(
date
.
getTime
()))
return
dateStr
;
return
`
${
date
.
getFullYear
()}
年
${
date
.
getMonth
()
+
1
}
月
${
date
.
getDate
()}
日`
;
};
return
{
postOrgName
:
info
.
postOrgName
,
postDate
:
formatDate
(
info
.
postDate
),
effectiveDate
:
formatDate
(
info
.
effectiveDate
),
fileCode
:
info
.
fileCode
?
`
${
info
.
fileCode
}
`
:
''
,
administrativeOrderId
:
info
.
administrativeOrderId
?
`No.
${
info
.
administrativeOrderId
}
`
:
''
,
postPersonName
:
info
.
postPersonName
,
domains
:
info
.
domainNames
};
});
const
filterEntity
=
ref
(
''
)
const
onlyChina
=
ref
(
tru
e
)
const
onlyChina
=
ref
(
fals
e
)
const
filterField
=
ref
(
''
)
const
searchKeyword
=
ref
(
''
)
const
activeTab
=
ref
(
'add'
)
// 监听筛选条件变化
watch
([
onlyChina
,
filterField
],
()
=>
{
getSanctionOverviewList
()
})
// 搜索框防抖
const
debouncedSearch
=
debounce
(()
=>
{
getSanctionOverviewList
()
},
500
)
watch
(
searchKeyword
,
()
=>
{
debouncedSearch
()
})
const
domainOptions
=
ref
([
{
label
:
"人工智能"
,
value
:
"1"
},
{
label
:
"生物科技"
,
value
:
"2"
},
{
label
:
"新一代信息技术"
,
value
:
"3"
},
{
label
:
"量子科技"
,
value
:
"4"
},
{
label
:
"新能源"
,
value
:
"5"
},
{
label
:
"集成电路"
,
value
:
"6"
},
{
label
:
"海洋"
,
value
:
"7"
},
{
label
:
"先进制造"
,
value
:
"8"
},
{
label
:
"新材料"
,
value
:
"9"
},
{
label
:
"航空航天"
,
value
:
"10"
},
{
label
:
"深海"
,
value
:
"11"
},
{
label
:
"极地"
,
value
:
"12"
},
{
label
:
"太空"
,
value
:
"13"
},
{
label
:
"核"
,
value
:
"14"
}
]);
const
tagColors
=
[
{
bg
:
'rgb(242, 235, 255)'
,
border
:
'rgb(211, 190, 255)'
,
text
:
'rgb(114, 46, 209)'
},
// Purple
{
bg
:
'rgb(225, 250, 248)'
,
border
:
'rgb(178, 242, 238)'
,
text
:
'rgb(16, 178, 166)'
},
// Cyan
...
...
@@ -231,282 +442,7 @@ const getTagStyle = (tagName) => {
};
}
const
sanctionList
=
ref
([
{
reason
:
"该实体因获取和试图获取美国原产物品以支持中国军事和国防相关空间领域活动以及中国量子技术能力而被列入;鉴于量子技术的军事应用,这些行动对美国国家安全造成严重影响。"
,
entities
:
[
{
name
:
"中国科学院国家授时中心"
,
field
:
"量子科技"
,
fieldClass
:
"purple"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
}
]
},
{
reason
:
"这些实体被列入清单是因为其获取和试图获取美国原产物品以支持中国军事现代化,参与中国先进计算和集成电路制造及分销领域,以及直接供应中国军事、政府和安全机构。上海复旦微电子股份有限公司和中芯国际集成电路制造有限公司将获得脚注 4 的指定。这些实体参与高性能计算(HPC)芯片的生产,包括人工智能和其他双重用途应用。此外,上海复旦微电子股份有限公司已向俄罗斯军事最终用户提供技术。"
,
entities
:
[
{
name
:
"北京复旦微电子技术有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"上海复旦微电子股份有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"北京复旦微电子技术有...等"
,
subsidiaryCount
:
4
},
{
name
:
"深圳复旦微电子有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"上海复控华龙微系统技术有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"上海富伟迅捷数字技术有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"中芯国际集成电路制造有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"上海"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"中芯协成投资(背景)...等"
,
subsidiaryCount
:
5
},
{
name
:
"上海复旦微电子(香港)有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
}
]
},
{
reason
:
"这些实体因参与支持受制裁实体(包括伊朗军方)的两用物品转售而被添加。"
,
entities
:
[
{
name
:
"华科供应链(香港)有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"香港"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
2
},
{
name
:
"华科物流(香港)有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"深圳新利康供应链管理有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
}
]
},
{
reason
:
"该实体被添加是因为其与支持中国高空气球计划的公司有关联。"
,
entities
:
[
{
name
:
"中国科学院空天信息研究院"
,
field
:
"航空航天"
,
fieldClass
:
"blue"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
}
]
},
{
reason
:
"此次列入的原因是这两家公司采购了原产于美国的物项,并将其转售给实体清单上的实体。具体而言,这两家公司在未获得美国商务部工业安全局(BIS)的必要许可或授权的情况下,为实体清单上的两家实体——中芯国际北方集成电路制造(北京)有限公司和中芯国际(北京)股份有限公司——采购了原产于美国的半导体制造设备。"
,
entities
:
[
{
name
:
"GMC 半导体技术(无锡)有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"GMC集成电路技术有...等"
,
subsidiaryCount
:
1
},
{
name
:
"济村半导体技术(上海)有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
}
]
},
{
reason
:
"这些实体构成不可接受的风险,可能将原产于美国的物项用于或转移给中国人民解放军军事医学科学院。"
,
entities
:
[
{
name
:
"北京天一辉远生物技术有限公司"
,
field
:
"生物科技"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"北京天一辉远制药生产...等"
,
subsidiaryCount
:
1
},
{
name
:
"北京擎科生物技术有限公司"
,
field
:
"生物科技"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryCount
:
0
},
{
name
:
"上海生工生物工程股份有限公司"
,
field
:
"生物科技"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
3
}
]
},
{
reason
:
"它们为中国军工联合体的关键客户(包括美国商务部工业安全局(BIS)实体清单上的客户)开发计算机辅助工程软件。"
,
entities
:
[
{
name
:
"上海索辰信息技术有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
},
{
name
:
"香港德迈克斯有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
}
]
},
{
reason
:
"因为它们存在规避出口管制和将产品转移至被列入实体清单实体的风险。"
,
entities
:
[
{
name
:
"长沙网发电子科技有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
},
{
name
:
"常州网发微电子有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
},
{
name
:
"成都网发微电子有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"深圳"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
},
{
name
:
"深圳网发微电子有限公司"
,
field
:
"集成电路"
,
fieldClass
:
"cyan"
,
location
:
"--"
,
date
:
"2025年9月"
,
revenue
:
"325"
,
subsidiaryText
:
"讯飞智元信息科技有限公司...等"
,
subsidiaryCount
:
15
}
]
}
])
const
timelineData
=
ref
([
{
date
:
"2025-07-31"
,
content
:
"美商务部发布指南,警告全球企业使用华为昇腾芯片可能违反美国出口管制。意在限制中国AI产业发展,阻碍其获得先进算力。"
},
{
date
:
"2025-07-30"
,
content
:
"美商务部持续对多种中国产品发起“双反”(反倾销、反补贴)调查并作出裁决,涉及产品从工业原料到日常用品,且裁定的税率普遍较高。"
},
{
date
:
"2025-07-30"
,
content
:
"美商务部进一步收紧对华先进半导体出口管制,将更多中国实体列入“实体清单”。限制14纳米及以下先进芯片、DRAM等对华出口"
}
])
const
timelineData
=
ref
([]);
const
entityDistribution
=
ref
([
{
...
...
@@ -534,6 +470,17 @@ const entityDistribution = ref([
gradient
:
"linear-gradient(270deg, rgba(5,95,194,1) 0%, rgba(255,255,255,0) 100%)"
}
])
onMounted
(()
=>
{
// 获取URL参数
getUrlParams
()
// 单次制裁-制裁概况-制裁实体国家分布
getEntityCountry
()
// 单次制裁-制裁概况-制裁背景
getSanctionBackground
()
// 单次制裁-制裁概况-制裁清单
getSanctionOverviewList
()
})
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
@@ -563,7 +510,7 @@ const entityDistribution = ref([
background-color
:
#fff
;
margin-bottom
:
16px
;
.left-top-title
{
padding
:
22px
0
22px
27px
;
padding
:
22px
20px
22px
27px
;
width
:
100%
;
height
:
286px
;
border-bottom
:
1px
solid
rgb
(
234
,
236
,
238
);
...
...
@@ -597,6 +544,7 @@ const entityDistribution = ref([
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
overflow
:
hidden
;
// Ensure it respects the container width
&
.link
{
color
:
rgb
(
5
,
95
,
194
);
...
...
@@ -618,8 +566,24 @@ const entityDistribution = ref([
&
.tags
{
gap
:
8px
;
overflow-x
:
auto
;
// Allow horizontal scrolling
white-space
:
nowrap
;
// Prevent wrapping
padding-bottom
:
4px
;
// Add some space for scrollbar
/* Custom Scrollbar */
&
:
:-
webkit-scrollbar
{
height
:
4px
;
}
&
:
:-
webkit-scrollbar-thumb
{
background
:
#ccc
;
border-radius
:
2px
;
}
&
:
:-
webkit-scrollbar-track
{
background
:
transparent
;
}
.tag
{
flex-shrink
:
0
;
// Prevent tags from shrinking
padding
:
1px
8px
;
background
:
rgba
(
246
,
250
,
255
,
1
);
border-radius
:
4px
;
...
...
@@ -636,7 +600,7 @@ const entityDistribution = ref([
width
:
100%
;
height
:
234px
;
padding
:
19px
29px
22px
27px
;
overflow
:
auto
;
.content-title
{
font-size
:
16px
;
font-weight
:
700
;
...
...
@@ -651,7 +615,7 @@ const entityDistribution = ref([
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
overflow
:
auto
;
.list-item
{
display
:
flex
;
align-items
:
center
;
...
...
@@ -719,12 +683,30 @@ const entityDistribution = ref([
background-color
:
#fff
;
.left-bottom-content
{
padding
:
26px
30px
0
25px
;
height
:
calc
(
100%
-
56px
);
// 减去标题高度
display
:
flex
;
flex-direction
:
column
;
.timeline-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
24px
;
margin-bottom
:
24px
;
overflow-y
:
auto
;
// 允许垂直滚动
flex
:
1
;
// 占据剩余空间
padding-right
:
10px
;
// 防止滚动条遮挡内容
/* Custom Scrollbar */
&
:
:-
webkit-scrollbar
{
width
:
6px
;
}
&
:
:-
webkit-scrollbar-thumb
{
background
:
#ccc
;
border-radius
:
3px
;
}
&
:
:-
webkit-scrollbar-track
{
background
:
transparent
;
}
}
.timeline-item
{
...
...
@@ -819,7 +801,8 @@ const entityDistribution = ref([
.filter-row
{
display
:
flex
;
justify-content
:
space-between
;
// 隐藏使用right,解除使用space-between
justify-content
:
right
;
align-items
:
center
;
margin-bottom
:
20px
;
...
...
@@ -851,7 +834,8 @@ const entityDistribution = ref([
.stats-row
{
display
:
flex
;
justify-content
:
space-between
;
// 隐藏使用right,解除使用space-between
justify-content
:
right
;
align-items
:
center
;
.tabs
{
...
...
src/views/exportControl/v2.0SingleSanction/index.vue
浏览文件 @
500577d5
...
...
@@ -6,7 +6,7 @@
<div>
<div
class=
"title"
>
{{
headerTitle
.
title
}}
<
span>
{{
headerTitle
.
titleEn
}}
</span
>
<
!--
<span>
{{
headerTitle
.
titleEn
}}
</span>
--
>
</div>
<div
class=
"department"
>
{{
headerTitle
.
department
}}
...
...
@@ -35,7 +35,10 @@
</div>
</div>
<div
class=
"main"
>
<sanctions-overview
v-if=
"activeIndex === 0"
></sanctions-overview>
<sanctions-overview
v-if=
"activeIndex === 0"
:data=
"singleSanctionOverview"
></sanctions-overview>
<data-statistics
v-if=
"activeIndex === 1"
></data-statistics>
<deep-mining
v-if=
"activeIndex === 2"
></deep-mining>
<impact-analysis
v-if=
"activeIndex === 3"
></impact-analysis>
...
...
@@ -44,7 +47,7 @@
</
template
>
<
script
setup
>
import
{
ref
}
from
'vue'
import
{
ref
,
onMounted
}
from
'vue'
import
sanctionsOverview
from
"./components/sanctionsOverview/index.vue"
import
dataStatistics
from
"./components/dataStatistics/index.vue"
...
...
@@ -62,12 +65,55 @@ import icon2Active from "../assets/icons/icon2_active.png";
import
icon3
from
"../assets/icons/icon3.png"
;
import
icon3Active
from
"../assets/icons/icon3_active.png"
;
import
{
getSingleSanctionOverview
}
from
"@/api/exportControlV2.0.js"
// 获取URL参数
const
sanRecordId
=
ref
(
""
)
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
// console.log(sanRecordId.value)
// 单次制裁-制裁概况-基本信息
const
singleSanctionOverview
=
ref
({})
const
getSingleSanctionOverviewData
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
try
{
const
res
=
await
getSingleSanctionOverview
({
sanRecordId
:
sanRecordId
.
value
})
if
(
res
.
code
===
200
)
{
singleSanctionOverview
.
value
=
res
.
data
||
{}
// 格式化日期
let
dateStr
=
""
;
if
(
singleSanctionOverview
.
value
.
postDate
)
{
const
date
=
new
Date
(
singleSanctionOverview
.
value
.
postDate
);
if
(
!
isNaN
(
date
.
getTime
()))
{
dateStr
=
`
${
date
.
getFullYear
()}
年
${
date
.
getMonth
()
+
1
}
月
${
date
.
getDate
()}
日`
;
}
else
{
dateStr
=
singleSanctionOverview
.
value
.
postDate
;
}
}
// 更新头部信息
headerTitle
.
value
=
{
...
headerTitle
.
value
,
title
:
`
${
dateStr
}
《
${
singleSanctionOverview
.
value
.
sanTitleZh
||
singleSanctionOverview
.
value
.
sanTitle
}
》`
,
titleEn
:
singleSanctionOverview
.
value
.
sanTitle
||
""
,
department
:
singleSanctionOverview
.
value
.
fileCode
||
""
}
}
}
catch
(
error
)
{
console
.
error
(
"获取制裁概况失败:"
,
error
)
}
}
const
headerTitle
=
ref
({
img
:
title
,
title
:
"2025年9月12日《对实体清单的添加和修订 》"
,
titleEn
:
""
,
department
:
"2025-17893(90 FR 44496)"
})
const
activeIndex
=
ref
(
0
)
...
...
@@ -96,7 +142,10 @@ const headerNavList = ref([
])
onMounted
(()
=>
{
getUrlParams
()
getSingleSanctionOverviewData
()
})
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论