Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
5fc86ad6
提交
5fc86ad6
authored
3月 21, 2026
作者:
yanpeng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
exportControl api
上级
a3bb0583
隐藏空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
1789 行增加
和
1477 行删除
+1789
-1477
exportControl.js
src/api/exportControl.js
+4
-2
exportControlV2.0.js
src/api/exportControlV2.0.js
+15
-16
index.vue
src/components/Container/index.vue
+3
-2
index.vue
src/views/exportControl/index.vue
+41
-14
index.vue
...s/sanctionsOverview/components/introductionPage/index.vue
+113
-61
index.vue
src/views/exportControl/v2.0CommercialControlList/index.vue
+40
-37
index.vue
...ontrol/v2.0EntityList/components/dataStatistics/index.vue
+34
-19
index.vue
...ortControl/v2.0EntityList/components/deepMining/index.vue
+135
-76
index.vue
...s/sanctionsOverview/components/introductionPage/index.vue
+46
-17
index.vue
...omponents/sanctionsOverview/components/listPage/index.vue
+123
-72
index.vue
...rol/v2.0EntityList/components/sanctionsOverview/index.vue
+75
-69
index.vue
src/views/exportControl/v2.0EntityList/index.vue
+63
-57
index.vue
...ol/v2.0SingleSanction/components/dataStatistics/index.vue
+49
-44
index.vue
...v2.0SingleSanction/components/sanctionsOverview/index.vue
+1048
-991
没有找到文件。
src/api/exportControl.js
浏览文件 @
5fc86ad6
...
...
@@ -4,6 +4,7 @@ import { ElMessage } from "element-plus";
const
request200
=
requestP
=>
{
return
requestP
.
then
(
data
=>
{
if
(
data
.
code
===
200
)
{
console
.
log
(
'返回的数据结构 =>'
,
data
.
data
)
return
data
.
data
;
}
ElMessage
({
...
...
@@ -122,13 +123,14 @@ export function getSanctionsInfoCount() {
* sanReason: string
* }[]>}
*/
export
function
getSanctionProcess
(
typeName
=
"实体清单
"
,
pageNum
=
1
,
pageSize
=
10
,
isCn
=
false
)
{
export
function
getSanctionProcess
(
sanTypeIds
=
"1
"
,
pageNum
=
1
,
pageSize
=
10
,
isCn
=
false
)
{
return
request200
(
request
({
method
:
"POST"
,
url
:
"/api/entitiesDataCount/getSanctionProcess"
,
data
:
{
typeName
,
sanTypeIds
,
// typeName: tabMap[sanTypeId],
pageNum
,
pageSize
,
isCn
...
...
src/api/exportControlV2.0.js
浏览文件 @
5fc86ad6
import
request
from
"@/api/request.js"
;
// 实体清单-制裁概况-获取实体清单基本信息
export
function
getEntityInfo
(
sanType
)
{
export
function
getEntityInfo
(
id
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/baseInfo
/
${
sanType
}
`
url
:
`/api/sanctionList/baseInfo
ById/
${
id
}
`
});
}
...
...
@@ -98,10 +98,10 @@ export function get50PercentEntityCount(data) {
}
// 实体清单-数据统计-总量统计
export
function
getTotalCount
()
{
export
function
getTotalCount
(
id
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/total
`
url
:
`/api/sanctionList/statistics/
total?sanTypeId=
${
id
}
`
});
}
...
...
@@ -113,7 +113,7 @@ export function getTotalCount() {
export
function
getSanctionCountChange
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
num`
,
url
:
`/api/sanctionList/statistics/num`
,
params
});
}
...
...
@@ -128,7 +128,7 @@ export function getSanctionCountChange(params) {
export
function
getRegionCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
region`
,
url
:
`/api/sanctionList/statistics/region`
,
params
});
}
...
...
@@ -143,7 +143,7 @@ export function getRegionCount(params) {
export
function
getTechDomainCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
domain`
,
url
:
`/api/sanctionList/statistics/domain`
,
params
});
}
...
...
@@ -158,7 +158,7 @@ export function getTechDomainCount(params) {
export
function
getEntityTypeCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/e
l/e
ntityType`
,
url
:
`/api/sanctionList/statistics/entityType`
,
params
});
}
...
...
@@ -247,7 +247,7 @@ export function getSingleSanctionOverview(params) {
export
function
getSingleSanctionEntityCountry
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
countryRegion`
,
url
:
`/api/sanctionList/statistics/countryRegion`
,
params
});
}
...
...
@@ -292,11 +292,10 @@ export function getSingleSanctionOverviewList(data) {
* @param {string} params.sanRecordId - 制裁记录ID
* @header token
*/
export
function
getSingleSanctionTotalCount
(
params
)
{
export
function
getSingleSanctionTotalCount
(
id
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/el/total`
,
params
url
:
`/api/sanctionList/statistics/total?sanTypeId=
${
id
}
`
,
});
}
...
...
@@ -311,7 +310,7 @@ export function getSingleSanctionTotalCount(params) {
export
function
getSingleSanctionDomainCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
domain`
,
url
:
`/api/sanctionList/statistics/domain`
,
params
});
}
...
...
@@ -327,7 +326,7 @@ export function getSingleSanctionDomainCount(params) {
export
function
getSingleSanctionEntityTypeCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/e
l/e
ntityType`
,
url
:
`/api/sanctionList/statistics/entityType`
,
params
});
}
...
...
@@ -341,7 +340,7 @@ export function getSingleSanctionEntityTypeCount(params) {
export
function
getSingleSanctionEntityCountryCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
countryRegion`
,
url
:
`/api/sanctionList/statistics/countryRegion`
,
params
});
}
...
...
@@ -357,7 +356,7 @@ export function getSingleSanctionEntityCountryCount(params) {
export
function
getSingleSanctionEntityRegionCount
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
`/api/sanctionList/statistics/
el/
region`
,
url
:
`/api/sanctionList/statistics/region`
,
params
});
}
...
...
src/components/Container/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -135,8 +135,8 @@ const headerTitleClasses = computed(() => [
.header-icon
{
width
:
22px
;
height
:
18px
;
margin-left
:
5
px
;
margin-right
:
1
4
px
;
margin-left
:
0
px
;
margin-right
:
1
0
px
;
}
.blue-title-block
{
...
...
@@ -155,6 +155,7 @@ const headerTitleClasses = computed(() => [
/* color: var(--base-color); */
color
:
$base-color
;
line-height
:
48px
;
padding
:
0
5px
;
// padding: 0 12px;
}
...
...
src/views/exportControl/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -403,7 +403,7 @@
v-for=
"tab in resourceTabs"
:key=
"tab.value"
class=
"resource-tab-item"
:class=
"{ active: activeResourceTab ==
=
tab.value, disabled: tab.disabled }"
:class=
"{ active: activeResourceTab == tab.value, disabled: tab.disabled }"
@
click=
"handleResourceTabClick(tab)"
>
{{ tab.label }}
...
...
@@ -856,7 +856,8 @@ const handleTitleClick = item => {
const
route
=
router
.
resolve
({
path
:
"/exportControl/singleSanction"
,
query
:
{
id
:
item
.
id
id
:
item
.
id
,
sanTypeId
:
item
.
sanTypeId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
...
...
@@ -869,7 +870,8 @@ const handleCompClick = item => {
const
route
=
router
.
resolve
({
name
:
"companyPages"
,
params
:
{
id
:
item
.
id
id
:
item
.
id
,
sanTypeId
:
item
.
sanTypeId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
...
...
@@ -938,7 +940,15 @@ onMounted(async () => {
// 交换第二个和第三个元素
[
dataCount
[
1
],
dataCount
[
2
]]
=
[
dataCount
[
2
],
dataCount
[
1
]];
infoList
.
value
=
dataCount
.
slice
(
0
,
2
);
allSanTypeIds
.
value
=
infoList
.
value
.
map
(
item
=>
item
.
id
);
resourceTabs
.
value
=
infoList
.
value
.
map
(
item
=>
({
label
:
item
.
nameZh
,
value
:
tabMap
[
item
.
id
],
id
:
[
item
.
id
],
disabled
:
item
.
id
==
"13"
// 商业管制清单不展示
}));
resourceTabs
.
value
.
unshift
({
label
:
"全部制裁"
,
value
:
"all"
,
id
:
""
,
disabled
:
false
});
console
.
log
(
"返回的数据结构 infoList =》"
,
infoList
.
value
);
const
entityList
=
_
.
map
(
entitiesDataInfo
?.
sanEntities
??
[],
({
entityNameZh
,
entityName
})
=>
{
return
{
name
:
entityNameZh
,
enName
:
entityName
};
});
...
...
@@ -1123,15 +1133,22 @@ const handleToEntityList = item => {
// 跳转到V2.0实体清单无ID
const
handleToEntityListNoId
=
item
=>
{
console
.
log
(
"这是什么数据 =>"
,
item
);
if
(
item
.
nameZh
==
"实体清单"
)
{
const
routeData
=
router
.
resolve
({
path
:
"/exportControl/entityList"
path
:
"/exportControl/entityList"
,
query
:
{
sanTypeId
:
item
.
id
}
});
// 打开一个新页面
window
.
open
(
routeData
.
href
,
"_blank"
);
}
else
if
(
item
.
nameZh
==
"商业管制清单"
)
{
const
routeData
=
router
.
resolve
({
path
:
"/exportControl/commercialControlList"
path
:
"/exportControl/commercialControlList"
,
query
:
{
sanTypeId
:
item
.
id
}
});
// 打开一个新页面
window
.
open
(
routeData
.
href
,
"_blank"
);
...
...
@@ -1146,6 +1163,7 @@ const curBillListIndex = ref(0);
const
searchExportControlText
=
ref
(
""
);
const
infoListColor
=
ref
([
"rgba(206, 79, 81, 1)"
,
"rgba(114, 46, 209, 1)"
,
"rgba(132, 136, 142, 1)"
,
"rgba(132, 136, 142, 1)"
]);
const
infoList
=
ref
([]);
const
allSanTypeIds
=
ref
([
"1"
,
"13"
]);
// 雷达图
const
domainChecked
=
ref
(
false
);
...
...
@@ -1324,7 +1342,8 @@ const fetchSanctionList = async () => {
techDomainIds
:
techDomains
,
years
:
years
,
isCn
:
false
,
typeName
:
"实体清单"
// typeName: "实体清单"
sanTypeIds
:
allSanTypeIds
.
value
};
const
res
=
await
getExportControlList
(
params
);
...
...
@@ -1454,7 +1473,8 @@ const fetchEntitiesList = async (page = 1, size = 10) => {
const
handleGetMore
=
async
()
=>
{
sanctionPage
.
value
++
;
try
{
const
res
=
await
getSanctionProcess
(
"实体清单"
,
sanctionPage
.
value
,
10
);
const
sanTypeid
=
activeResourceTabItem
.
value
.
id
?
activeResourceTabItem
.
value
.
id
:
allSanTypeIds
.
value
;
const
res
=
await
getSanctionProcess
(
sanTypeid
,
sanctionPage
.
value
,
10
);
if
(
res
&&
res
.
content
)
{
// 将新数据合并到现有列表中
const
newData
=
res
.
content
.
map
(
item
=>
({
...
...
@@ -1479,7 +1499,7 @@ const handleGetMore = async () => {
// 获取历次制裁过程数据
const
fetchSanctionProcess
=
async
(
page
=
1
,
size
=
10
)
=>
{
try
{
const
res
=
await
getSanctionProcess
(
"实体清单"
,
page
,
size
);
const
res
=
await
getSanctionProcess
(
allSanTypeIds
.
value
,
page
,
size
);
if
(
res
)
{
sanctionProcessList
.
value
=
res
.
content
.
map
(
item
=>
({
...
item
,
...
...
@@ -1504,18 +1524,25 @@ const handlePageChange = page => {
const
searchKeyword
=
ref
(
""
);
// 资源库 Tab 数据
const
resourceTabs
=
[
{
label
:
"全部制裁"
,
value
:
"all"
,
disabled
:
false
},
{
label
:
"实体清单"
,
value
:
"entity"
,
disabled
:
false
},
{
label
:
"商业管制清单"
,
value
:
"commerce"
,
disabled
:
true
}
const
resourceTabs
=
ref
(
[
//
{ label: "全部制裁", value: "all", disabled: false },
//
{ label: "实体清单", value: "entity", disabled: false },
//
{ label: "商业管制清单", value: "commerce", disabled: true }
// { label: "关键与新兴技术清单", value: "tech", disabled: true },
// { label: "军事最终用户清单", value: "military", disabled: true }
];
]
)
;
const
activeResourceTab
=
ref
(
"all"
);
const
activeResourceTabItem
=
ref
({});
// 数据对应,便宜行事
const
tabMap
=
{
1
:
"entity"
,
13
:
"commerce"
};
const
handleResourceTabClick
=
tab
=>
{
if
(
tab
.
disabled
)
return
;
activeResourceTab
.
value
=
tab
.
value
;
activeResourceTabItem
.
value
=
tab
;
};
const
strengthLabels
=
{
...
...
src/views/exportControl/v2.0CommercialControlList/components/sanctionsOverview/components/introductionPage/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -25,8 +25,12 @@
<div
class=
"left-center"
>
<AnalysisBox
title=
"出口管制分类编码(ECCN)"
:showAllBtn=
"false"
>
<div
class=
"button-list"
>
<div
:class=
"['button',
{ 'click': item.isClick }]" @click="changeECCN(item)" v-for="(item, i) in ECCNList"
:key="i">
<div
:class=
"['button',
{ click: item.isClick }]"
@click="changeECCN(item)"
v-for="(item, i) in ECCNList"
:key="i"
>
<span>
第
{{
item
.
ranking
}}
位
{{
item
.
name
}}
</span>
</div>
</div>
...
...
@@ -44,9 +48,17 @@
<AnalysisBox
title=
"商业管制清单更新历史"
:showAllBtn=
"false"
>
<template
#
header-btn
>
<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>
...
...
@@ -59,30 +71,43 @@
</div>
<div
class=
"img-zone"
>
<img
:src=
"item.icon || title"
alt
/>
<div
v-if=
"i < sanctionList.length - 1"
:class=
"['img-line', { 'img-line-last': i === sanctionList.length - 1 }]"
>
</div>
<div
v-if=
"i < sanctionList.length - 1"
:class=
"['img-line', { 'img-line-last': i === sanctionList.length - 1 }]"
></div>
</div>
<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"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<el-tooltip
effect=
"dark"
:content=
"item.summary"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<div
class=
"main-desc"
>
{{ item.summary }}
</div>
</el-tooltip>
<div
class=
"tag-box"
>
<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>
<div
class=
"left-footer"
>
<div
class=
"total-count"
>
共 {{ totalAll }} 项
</div>
<el-pagination
v-model:current-page=
"currentPageAll"
:page-size=
"pageSizeAll"
:total=
"totalAll"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChangeAll"
/>
<el-pagination
v-model:current-page=
"currentPageAll"
:page-size=
"pageSizeAll"
:total=
"totalAll"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChangeAll"
/>
</div>
</AnalysisBox>
</div>
...
...
@@ -110,17 +135,31 @@
<span>
关键人物
</span>
</div>
<div
class=
"key-person-list"
>
<div
class=
"person-item"
v-for=
"(item, index) in publishInfo.personList"
:key=
"index"
@
click=
"handlePerClick(item)"
>
<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"
>
<el-tooltip
effect=
"dark"
:content=
"item.name"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<el-tooltip
effect=
"dark"
:content=
"item.name"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<div
class=
"name"
>
{{ item.name }}
</div>
</el-tooltip>
<el-tooltip
effect=
"dark"
:content=
"item.position"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<el-tooltip
effect=
"dark"
:content=
"item.position"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<div
class=
"title1"
>
{{ item.position }}
</div>
</el-tooltip>
</div>
...
...
@@ -166,9 +205,18 @@ 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
{
ElTable
,
ElTableColumn
,
ElButton
,
ElSelect
,
ElOption
,
ElInput
}
from
'element-plus'
import
{
getCCLInfo
,
getECCN
,
getECCNList
,
getPublishInfo
,
getPublishOrgInfo
,
getEntityUpdateInfo
}
from
"@/api/exportControlV2.0.js"
;
import
{
ElTable
,
ElTableColumn
,
ElButton
,
ElSelect
,
ElOption
,
ElInput
}
from
"element-plus"
;
import
{
getCCLInfo
,
getECCN
,
getECCNList
,
getPublishInfo
,
getPublishOrgInfo
,
getEntityUpdateInfo
}
from
"@/api/exportControlV2.0.js"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 处理点击发布机构的方法
const
handleClickOrg
=
item
=>
{
// console.log("点击了发布机构:", item);
...
...
@@ -238,13 +286,14 @@ const getSanctionUpdate = async () => {
isCn
:
onlyChina
.
value
,
techDomainIds
:
selectedDomain
.
value
?
[
selectedDomain
.
value
]
:
[],
// typeName: "实体清单",
typeName
:
"商业管制清单"
,
//
typeName: "商业管制清单",
pageNum
:
currentPageAll
.
value
,
pageSize
:
pageSizeAll
.
value
pageSize
:
pageSizeAll
.
value
,
sanTypeIds
:
[
Number
(
sanTypeId
.
value
)]
||
13
// 商业管制清单固定13
};
try
{
const
res
=
await
getEntityUpdateInfo
(
data
);
console
.
log
(
'-----getSanctionUpdate'
,
res
)
console
.
log
(
"-----getSanctionUpdate"
,
res
);
if
(
res
&&
res
.
code
===
200
)
{
console
.
log
(
res
.
data
.
content
);
sanctionList
.
value
=
(
res
.
data
.
content
||
[]).
map
(
item
=>
({
...
...
@@ -288,13 +337,13 @@ const handlePageChangeAll = val => {
const
publishInfo
=
ref
({});
const
getPublishInfoFn
=
async
()
=>
{
const
params
=
{
sanTypeId
:
13
// 商业管制清单固定13
sanTypeId
:
sanTypeId
.
value
||
13
// 商业管制清单固定13
};
try
{
const
res
=
await
getPublishInfo
(
params
);
if
(
res
&&
res
.
code
===
200
)
{
publishInfo
.
value
=
res
.
data
;
console
.
log
(
'------getPublishInfoFn'
,
res
.
data
)
console
.
log
(
"------getPublishInfoFn"
,
res
.
data
);
// 获取发布机构动态
getPublishOrgInfoFn
();
}
...
...
@@ -348,12 +397,12 @@ const CCLInfo = ref({
description
:
"xxxxx"
,
legalBasis
:
"法律依据"
,
controlledObject
:
"管制对象"
,
name
:
''
,
orgId
:
''
,
orgLogoUrl
:
''
,
orgName
:
''
,
originalName
:
''
,
shortName
:
''
,
name
:
""
,
orgId
:
""
,
orgLogoUrl
:
""
,
orgName
:
""
,
originalName
:
""
,
shortName
:
""
,
restrictiveMeasure
:
"核心限制措施"
});
const
getCCLInfoFn
=
async
()
=>
{
...
...
@@ -361,7 +410,7 @@ const getCCLInfoFn = async () => {
const
res
=
await
getCCLInfo
();
if
(
res
&&
res
.
code
===
200
)
{
CCLInfo
.
value
=
res
.
data
;
console
.
log
(
'getCCLInfoFn'
,
CCLInfo
.
value
)
console
.
log
(
"getCCLInfoFn"
,
CCLInfo
.
value
);
}
}
catch
(
error
)
{
console
.
error
(
"获取商业管制清单基本信息失败:"
,
error
);
...
...
@@ -371,27 +420,28 @@ const getCCLInfoFn = async () => {
// 定义ECCN列表
const
ECCNList
=
ref
([
{
name
:
'第1位: 物项类别'
,
name
:
"第1位: 物项类别"
,
isClick
:
true
,
description
:
'ECCN的第一位是一个数字,它指明了受控物项所属的技术或行业类别,对于快速定位物项在《商业管制清单》中的位置至关重要'
,
key
:
'wxlb'
// 用于和后端对接的
description
:
"ECCN的第一位是一个数字,它指明了受控物项所属的技术或行业类别,对于快速定位物项在《商业管制清单》中的位置至关重要"
,
key
:
"wxlb"
// 用于和后端对接的
}
])
])
;
const
getCCL_ECCN_Type
=
async
()
=>
{
try
{
const
res
=
await
getECCN
();
if
(
res
&&
res
.
code
===
200
)
{
ECCNList
.
value
=
res
.
data
;
console
.
log
(
'getCCL_ECCN_Type'
,
ECCNList
.
value
)
console
.
log
(
"getCCL_ECCN_Type"
,
ECCNList
.
value
);
// 为每个对象增加是否点击对象
ECCNList
.
value
.
forEach
(
(
item
)
=>
{
item
.
isClick
=
false
})
ECCNList
.
value
.
forEach
(
item
=>
{
item
.
isClick
=
false
;
})
;
// 默认点击第一个
ECCNList
.
value
[
0
].
isClick
=
true
ECCNList
.
value
[
0
].
isClick
=
true
;
// 默认将第一个赋值给currentECCN
currentECCN
.
value
=
ECCNList
.
value
[
0
]
currentECCN
.
value
=
ECCNList
.
value
[
0
]
;
}
}
catch
(
error
)
{
console
.
error
(
"获取商业管制清单出口管制分类编码失败:"
,
error
);
...
...
@@ -401,46 +451,47 @@ const getCCL_ECCN_Type = async () => {
// 表格数据
const
tableData
=
ref
([
{
categoryCode
:
'0'
,
categoryCode
:
"0"
,
categoryId
:
1
,
code
:
null
,
description
:
null
,
descriptionZh
:
null
,
id
:
1
,
name
:
'Nuclear Materials, Facilities and Equipment and Miscellaneous'
,
name
:
"Nuclear Materials, Facilities and Equipment and Miscellaneous"
,
nameZh
:
"核材料、设备设施及其他类似物项"
}
])
])
;
const
currentECCN
=
ref
({
name
:
'第1位: 物项类别'
,
name
:
"第1位: 物项类别"
,
isClick
:
true
,
description
:
'ECCN的第一位是一个数字,它指明了受控物项所属的技术或行业类别,对于快速定位物项在《商业管制清单》中的位置至关重要'
,
description
:
"ECCN的第一位是一个数字,它指明了受控物项所属的技术或行业类别,对于快速定位物项在《商业管制清单》中的位置至关重要"
,
id
:
1
,
ranking
:
1
})
})
;
/**
* 修改ECCN
*/
function
changeECCN
(
item
)
{
ECCNList
.
value
.
forEach
(
(
i
)
=>
i
.
isClick
=
false
)
item
.
isClick
=
true
currentECCN
.
value
=
item
ECCNList
.
value
.
forEach
(
i
=>
(
i
.
isClick
=
false
));
item
.
isClick
=
true
;
currentECCN
.
value
=
item
;
}
// 监听当前的ECCN,更新获取列表数据
watch
(
()
=>
currentECCN
.
value
,
async
(
newValue
)
=>
{
async
newValue
=>
{
// 调用接口更新table数据
const
params
=
{
id
:
currentECCN
.
value
.
id
}
}
;
try
{
const
res
=
await
getECCNList
(
params
);
if
(
res
&&
res
.
code
===
200
)
{
tableData
.
value
=
res
.
data
;
console
.
log
(
'getECCNList'
,
tableData
.
value
)
console
.
log
(
"getECCNList"
,
tableData
.
value
);
}
}
catch
(
error
)
{
console
.
error
(
"获取商业管制清单出口管制分类编码信息列表列表数据:"
,
error
);
...
...
@@ -448,18 +499,19 @@ watch(
},
{
deep
:
true
}
);
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取路由参数sanTypeId
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
"13"
;
console
.
log
(
"商业管制清单介绍页面接收到的 sanTypeId:"
,
sanTypeId
.
value
);
// 获取商业管制清单基本信息
getCCLInfoFn
();
// 获取商业管制清单的分类编码
getCCL_ECCN_Type
()
getCCL_ECCN_Type
()
;
// 获取商业管制清单发布机构
getPublishInfoFn
();
// 获取商业管制清单更新历史
getSanctionUpdate
();
});
</
script
>
...
...
src/views/exportControl/v2.0CommercialControlList/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -10,9 +10,7 @@
</div>
<div
class=
"department"
>
{{
headerTitle
.
department
}}
</div>
</div>
<div
class=
"btn"
>
<img
:src=
"icon01"
alt
/>
切换
</div>
<div
class=
"btn"
><img
:src=
"icon01"
alt
/>
切换
</div>
</div>
<div
class=
"header-nav"
>
<div
...
...
@@ -38,15 +36,16 @@
</
template
>
<
script
setup
>
import
{
ref
}
from
'vue'
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
sanctionsOverview
from
"./components/sanctionsOverview/index.vue"
import
sanctionsOverview
from
"./components/sanctionsOverview/index.vue"
;
// import dataStatistics from "./components/dataStatistics/index.vue"
// import deepMining from "./components/deepMining/index.vue"
// import impactAnalysis from "./components/impactAnalysis/index.vue"
import
title
from
"./assets/title.png"
import
icon01
from
"./assets/icon01.png"
import
title
from
"./assets/title.png"
;
import
icon01
from
"./assets/icon01.png"
;
import
icon1
from
"../assets/icons/icon1.png"
;
import
icon1Active
from
"../assets/icons/icon1_active.png"
;
import
icon5
from
"../assets/icons/icon5.png"
;
...
...
@@ -56,41 +55,45 @@ import icon2Active from "../assets/icons/icon2_active.png";
import
icon3
from
"../assets/icons/icon3.png"
;
import
icon3Active
from
"../assets/icons/icon3_active.png"
;
const
route
=
useRoute
();
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取路由参数sanTypeId
sanTypeId
.
value
=
route
.
query
.
sanTypeId
;
console
.
log
(
"CommercialControlList 页面接收到的 sanTypeId:"
,
sanTypeId
.
value
);
});
const
headerTitle
=
ref
({
img
:
title
,
title
:
"商业管制清单(CCL)"
,
titleEn
:
"Commercial Control List"
,
department
:
"美国商务部工业与安全局"
})
img
:
title
,
title
:
"商业管制清单(CCL)"
,
titleEn
:
"Commercial Control List"
,
department
:
"美国商务部工业与安全局"
})
;
const
activeIndex
=
ref
(
0
)
const
activeIndex
=
ref
(
0
)
;
const
headerNavList
=
ref
([
{
img
:
icon1
,
imgActive
:
icon1Active
,
title
:
"制裁概况"
},
{
img
:
icon5
,
imgActive
:
icon5Active
,
title
:
"数据统计"
},
{
img
:
icon2
,
imgActive
:
icon2Active
,
title
:
"深度挖掘"
},
{
img
:
icon3
,
imgActive
:
icon3Active
,
title
:
"影响分析"
}
])
{
img
:
icon1
,
imgActive
:
icon1Active
,
title
:
"制裁概况"
},
{
img
:
icon5
,
imgActive
:
icon5Active
,
title
:
"数据统计"
},
{
img
:
icon2
,
imgActive
:
icon2Active
,
title
:
"深度挖掘"
},
{
img
:
icon3
,
imgActive
:
icon3Active
,
title
:
"影响分析"
}
]);
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
src/views/exportControl/v2.0EntityList/components/dataStatistics/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -178,8 +178,13 @@
<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 / maxRankValue) * 100 + '%', background: getBarColor(index) }"
></div>
<div
class=
"rank-bar-fill"
:style=
"{
width: (item.value / maxRankValue) * 100 + '%',
background: getBarColor(index)
}"
></div>
</div>
<div
class=
"rank-value"
>
{{ item.value }}家
</div>
</div>
...
...
@@ -302,13 +307,22 @@ import * as echarts from "echarts";
import
chinaJson
from
"../../../utils/China.json"
;
import
ai
from
"./assets/ai.png"
;
import
right
from
"./assets/right.png"
;
import
{
getTotalCount
,
getSanctionCountChange
,
getRegionCount
,
getTechDomainCount
,
getEntityTypeCount
}
from
"@/api/exportControlV2.0"
;
import
{
getTotalCount
,
getSanctionCountChange
,
getRegionCount
,
getTechDomainCount
,
getEntityTypeCount
}
from
"@/api/exportControlV2.0"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 实体清单-数据统计-制裁实体类型分布情况
const
typeData
=
ref
([]);
const
getTypeCountData
=
async
()
=>
{
// 参数
const
param
=
{};
param
.
sanTypeId
=
sanTypeId
.
value
;
if
(
typeTime
.
value
!==
"all"
)
{
param
.
startDate
=
`
${
typeTime
.
value
}
-01-01`
;
...
...
@@ -330,14 +344,12 @@ const getTypeCountData = async () => {
}
};
// 实体清单-数据统计-制裁实体领域分布情况
const
domainData
=
ref
([]);
const
getDomainCountData
=
async
()
=>
{
// 参数
const
param
=
{};
param
.
sanTypeId
=
sanTypeId
.
value
;
if
(
domainTime
.
value
!==
"all"
)
{
param
.
startDate
=
`
${
domainTime
.
value
}
-01-01`
;
...
...
@@ -371,7 +383,7 @@ const maxRankValue = computed(() => {
const
getRegionCountData
=
async
()
=>
{
// 参数
const
param
=
{};
param
.
sanTypeId
=
sanTypeId
.
value
;
if
(
regionTime
.
value
!==
"all"
)
{
param
.
startDate
=
`
${
regionTime
.
value
}
-01-01`
;
param
.
endDate
=
`
${
regionTime
.
value
}
-12-31`
;
...
...
@@ -396,14 +408,14 @@ const getRegionCountData = async () => {
}
};
// 实体清单-数据统计-制裁实体数量变化情况
const
sanctionCountChange
=
ref
([]);
// 获取实体清单-数据统计-制裁实体数量变化情况
const
getSanctionCountChangeData
=
async
()
=>
{
// 参数
const
param
=
{
countType
:
activeTab
.
value
===
"year"
?
"year"
:
"record"
countType
:
activeTab
.
value
===
"year"
?
"year"
:
"record"
,
sanTypeId
:
sanTypeId
.
value
};
try
{
const
res
=
await
getSanctionCountChange
(
param
);
...
...
@@ -419,7 +431,7 @@ const totalCount = ref(0);
// 获取实体清单-数据统计-总量统计
const
getTotalCountData
=
async
()
=>
{
try
{
const
res
=
await
getTotalCount
();
const
res
=
await
getTotalCount
(
sanTypeId
.
value
);
totalCount
.
value
=
res
.
data
||
0
;
}
catch
(
error
)
{
console
.
error
(
"获取实体清单-数据统计-总量统计失败:"
,
error
);
...
...
@@ -438,9 +450,7 @@ const typeTime = ref("all");
const
currentYear
=
new
Date
().
getFullYear
();
const
timeOptions
=
[
{
label
:
"全部时间"
,
value
:
"all"
},
];
const
timeOptions
=
[{
label
:
"全部时间"
,
value
:
"all"
}];
for
(
let
i
=
0
;
i
<=
10
;
i
++
)
{
const
year
=
currentYear
-
i
;
...
...
@@ -749,11 +759,13 @@ const updateTypeChart = () => {
chart
=
echarts
.
init
(
typeChartRef
.
value
);
}
let
data
=
typeData
.
value
.
length
?
[...
typeData
.
value
]
:
[
{
value
:
50
,
name
:
"企业"
},
{
value
:
32
,
name
:
"高校"
},
{
value
:
32
,
name
:
"科研院所"
}
];
let
data
=
typeData
.
value
.
length
?
[...
typeData
.
value
]
:
[
{
value
:
50
,
name
:
"企业"
},
{
value
:
32
,
name
:
"高校"
},
{
value
:
32
,
name
:
"科研院所"
}
];
// 2. 聚合逻辑:保留前5项,其余合并为“其他”
data
.
sort
((
a
,
b
)
=>
b
.
value
-
a
.
value
);
...
...
@@ -873,7 +885,10 @@ const initTypeChart = () => {
updateTypeChart
();
};
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
""
;
console
.
log
(
"数据统计页面接收到的 sanTypeId:"
,
sanTypeId
.
value
);
// initSanctionCountChart();
initMapChart
();
initDomainChart
();
...
...
src/views/exportControl/v2.0EntityList/components/deepMining/index.vue
浏览文件 @
5fc86ad6
<
template
>
<div
class=
"deep-mining"
>
<div
class=
"side-nav"
>
<div
v-for=
"(item, index) in activeTab"
:key=
"index"
class=
"tab-item"
:class=
"
{ active: index === activeIndex }"
@click="activeIndex = index">
<div
v-for=
"(item, index) in activeTab"
:key=
"index"
class=
"tab-item"
:class=
"
{ active: index === activeIndex }"
@click="activeIndex = index"
>
{{
item
}}
<span
v-if=
"index === activeIndex"
class=
"arrow"
></span>
</div>
...
...
@@ -12,9 +17,17 @@
<AnalysisBox
title=
"选择制裁"
>
<div
class=
"left-main"
>
<div
class=
"date-picker-box"
>
<el-date-picker
v-model=
"dateRange"
type=
"daterange"
range-separator=
"--"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
value-format=
"YYYY-MM-DD"
style=
"width: 100%"
:clearable=
"false"
@
change=
"handleDateChange"
/>
<el-date-picker
v-model=
"dateRange"
type=
"daterange"
range-separator=
"--"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
value-format=
"YYYY-MM-DD"
style=
"width: 100%"
:clearable=
"false"
@
change=
"handleDateChange"
/>
</div>
<div
class=
"list-header"
>
<div
class=
"count"
>
共
{{
sanctionList
.
length
}}
次制裁
</div>
...
...
@@ -29,8 +42,13 @@
</div>
-->
</div>
<div
class=
"list-content"
v-loading=
"loading"
>
<div
class=
"list-item"
v-for=
"item in sanctionList"
:key=
"item.id"
:class=
"
{ active: currentSanctionId === item.id }" @click="handleSanctionSelect(item.id)">
<div
class=
"list-item"
v-for=
"item in sanctionList"
:key=
"item.id"
:class=
"
{ active: currentSanctionId === item.id }"
@click="handleSanctionSelect(item.id)"
>
<div
class=
"item-left"
>
{{
item
.
date
}}
-
{{
item
.
title
}}
</div>
<div
class=
"item-right"
>
{{
item
.
count
}}{{
item
.
unit
}}
</div>
</div>
...
...
@@ -41,8 +59,17 @@
<div
class=
"right"
>
<AnalysisBox
title=
"制裁产业链时序图"
>
<template
#
header-btn
>
<el-select
v-model=
"selectedIndustryId"
placeholder=
"请选择"
class=
"industry-select"
@
change=
"() =>
{ getFishboneData(); getCnEntityOnChainData(); }">
<el-select
v-model=
"selectedIndustryId"
placeholder=
"请选择"
class=
"industry-select"
@
change=
"
() =>
{
getFishboneData();
getCnEntityOnChainData();
}
"
>
<el-option
v-for=
"item in industryList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</
template
>
...
...
@@ -63,29 +90,44 @@
<div
class=
"fishbone"
ref=
"fishboneRef"
v-if=
"fishboneDataList.length > 0"
>
<div
class=
"main-line"
:style=
"{ width: fishboneDataList.length * 200 + 300 + 'px' }"
>
<!-- 主轴上的标签 -->
<div
class=
"main-line-text"
v-for=
"(item, index) in mainLineLabels"
:key=
"'label-' + index"
<div
class=
"main-line-text"
v-for=
"(item, index) in mainLineLabels"
:key=
"'label-' + index"
:class=
"{
'blue-theme': index < 2,
'green-theme': index >= 2 && index < 4,
'purple-theme': index >= 4
}"
:style=
"{ left: index * 200 + 220 + 'px' }"
>
}"
:style=
"{ left: index * 200 + 220 + 'px' }"
>
{{ item }}
</div>
</div>
<!-- 奇数索引的数据组放在上方 -->
<div
v-for=
"(causeGroup, groupIndex) in getOddGroups(fishboneDataList)"
:key=
"'top-' + groupIndex"
:class=
"getTopBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 420 + 'px' }"
>
<div
v-for=
"(causeGroup, groupIndex) in getOddGroups(fishboneDataList)"
:key=
"'top-' + groupIndex"
:class=
"getTopBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 420 + 'px' }"
>
<div
class=
"left-bone"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-' + index"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-' + index"
>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
<div
class=
"line"
></div>
</div>
</div>
<div
class=
"right-bone"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-' + index"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-' + index"
>
<div
class=
"line"
></div>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
...
...
@@ -94,20 +136,29 @@
</div>
<!-- 偶数索引的数据组放在下方 -->
<div
v-for=
"(causeGroup, groupIndex) in getEvenGroups(fishboneDataList)"
:key=
"'bottom-' + groupIndex"
:class=
"getBottomBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 220 + 'px' }"
>
<div
v-for=
"(causeGroup, groupIndex) in getEvenGroups(fishboneDataList)"
:key=
"'bottom-' + groupIndex"
:class=
"getBottomBoneClass(groupIndex)"
:style=
"{ left: groupIndex * 400 + 220 + 'px' }"
>
<div
class=
"left-bone"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-bottom-' + index"
>
<div
class=
"left-bone-item"
v-for=
"(item, index) in getLeftItems(causeGroup.causes)"
:key=
"'left-bottom-' + index"
>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
<div
class=
"line"
></div>
</div>
</div>
<div
class=
"right-bone"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-bottom-' + index"
>
<div
class=
"right-bone-item"
v-for=
"(item, index) in getRightItems(causeGroup.causes)"
:key=
"'right-bottom-' + index"
>
<div
class=
"line"
></div>
<img
:src=
"defaultTitle || item.picture"
alt=
""
class=
"company-icon"
/>
<div
class=
"text"
:title=
"item.name"
>
{{ item.name }}
</div>
...
...
@@ -115,8 +166,16 @@
</div>
</div>
</div>
<div
v-else
style=
"display: flex; justify-content: center; align-items: center; height: 200px; width: 100%"
>
<div
v-else
style=
"
display: flex;
justify-content: center;
align-items: center;
height: 200px;
width: 100%;
"
>
<el-empty
description=
"暂无相关数据"
/>
</div>
</div>
...
...
@@ -130,9 +189,11 @@
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.upstreamInternalCount ||
0}家(${formatRate(cnEntityOnChainData.upstreamInternalRate)}%),受制裁${cnEntityOnChainData.upstreamEntityCount
|| 0}家(${formatRate(cnEntityOnChainData.upstreamEntityRate)}%)`
`中国企业${cnEntityOnChainData.upstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.upstreamInternalRate
)}%),受制裁${cnEntityOnChainData.upstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.upstreamEntityRate
)}%)`
}}
</div>
</div>
...
...
@@ -145,9 +206,11 @@
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.midstreamInternalCount ||
0}家(${formatRate(cnEntityOnChainData.midstreamInternalRate)}%),受制裁${cnEntityOnChainData.midstreamEntityCount
|| 0}家(${formatRate(cnEntityOnChainData.midstreamEntityRate)}%)`
`中国企业${cnEntityOnChainData.midstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.midstreamInternalRate
)}%),受制裁${cnEntityOnChainData.midstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.midstreamEntityRate
)}%)`
}}
</div>
</div>
...
...
@@ -160,9 +223,11 @@
</div>
<div
class=
"text"
>
{{
`中国企业${cnEntityOnChainData.downstreamInternalCount ||
0}家(${formatRate(cnEntityOnChainData.downstreamInternalRate)}%),受制裁${cnEntityOnChainData.downstreamEntityCount
|| 0}家(${formatRate(cnEntityOnChainData.downstreamEntityRate)}%)`
`中国企业${cnEntityOnChainData.downstreamInternalCount || 0}家(${formatRate(
cnEntityOnChainData.downstreamInternalRate
)}%),受制裁${cnEntityOnChainData.downstreamEntityCount || 0}家(${formatRate(
cnEntityOnChainData.downstreamEntityRate
)}%)`
}}
</div>
</div>
...
...
@@ -181,14 +246,22 @@
import
{
ref
,
onMounted
,
nextTick
}
from
"vue"
;
import
{
ArrowLeft
,
ArrowRight
}
from
"@element-plus/icons-vue"
;
import
defaultTitle
from
"../../assets/default-icon2.png"
;
import
{
getDeepMiningSelect
,
getDeepMiningIndustry
,
getDeepMiningIndustryFishbone
,
getDeepMiningIndustryEntity
}
from
"@/api/exportControlV2.0"
;
import
{
getDeepMiningSelect
,
getDeepMiningIndustry
,
getDeepMiningIndustryFishbone
,
getDeepMiningIndustryEntity
}
from
"@/api/exportControlV2.0"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 实体清单-深度挖掘-产业链中国企业实体信息查询
const
getCnEntityOnChainData
=
async
()
=>
{
const
currentSanction
=
sanctionList
.
value
.
find
(
item
=>
item
.
id
===
currentSanctionId
.
value
);
const
date
=
currentSanction
?
currentSanction
.
date
:
''
;
const
date
=
currentSanction
?
currentSanction
.
date
:
""
;
// 确保 date 格式正确
const
formattedDate
=
date
&&
date
.
includes
(
'年'
)
?
date
.
replace
(
'年'
,
'-'
).
replace
(
'月'
,
'-'
).
replace
(
'日'
,
''
)
:
date
;
const
formattedDate
=
date
&&
date
.
includes
(
"年"
)
?
date
.
replace
(
"年"
,
"-"
).
replace
(
"月"
,
"-"
).
replace
(
"日"
,
""
)
:
date
;
const
params
=
{
date
:
formattedDate
...
...
@@ -207,17 +280,15 @@ const getCnEntityOnChainData = async () => {
console
.
error
(
"获取产业链中国企业实体信息失败:"
,
error
);
cnEntityOnChainData
.
value
=
{};
}
}
};
// 实体清单-深度挖掘-产业链鱼骨图信息
const
fishboneDataList
=
ref
([]);
const
getFishboneData
=
async
()
=>
{
const
currentSanction
=
sanctionList
.
value
.
find
(
item
=>
item
.
id
===
currentSanctionId
.
value
);
const
date
=
currentSanction
?
currentSanction
.
date
:
''
;
const
date
=
currentSanction
?
currentSanction
.
date
:
""
;
// 确保 date 格式正确
const
formattedDate
=
date
&&
date
.
includes
(
'年'
)
?
date
.
replace
(
'年'
,
'-'
).
replace
(
'月'
,
'-'
).
replace
(
'日'
,
''
)
:
date
;
const
formattedDate
=
date
&&
date
.
includes
(
"年"
)
?
date
.
replace
(
"年"
,
"-"
).
replace
(
"月"
,
"-"
).
replace
(
"日"
,
""
)
:
date
;
const
params
=
{
date
:
formattedDate
...
...
@@ -230,14 +301,13 @@ const getFishboneData = async () => {
if
(
res
.
code
===
200
&&
res
.
data
&&
res
.
data
.
causes
&&
res
.
data
.
causes
.
length
>
0
)
{
const
rootCauses
=
res
.
data
.
causes
;
if
(
rootCauses
.
length
>
0
&&
rootCauses
[
0
].
causes
)
{
fishboneDataList
.
value
=
rootCauses
.
map
(
group
=>
{
return
{
causes
:
group
.
causes
||
[]
};
});
mainLineLabels
.
value
=
rootCauses
.
map
(
group
=>
group
.
text
||
''
);
mainLineLabels
.
value
=
rootCauses
.
map
(
group
=>
group
.
text
||
""
);
}
else
{
fishboneDataList
.
value
=
[];
mainLineLabels
.
value
=
[];
...
...
@@ -250,9 +320,7 @@ const getFishboneData = async () => {
console
.
error
(
"获取产业链鱼骨图数据失败:"
,
error
);
fishboneDataList
.
value
=
[];
}
}
};
// 实体清单-深度挖掘-产业链列表信息
const
industryList
=
ref
([]);
...
...
@@ -274,30 +342,25 @@ const getIndustryList = async () => {
industryList
.
value
=
[];
selectedIndustryId
.
value
=
null
;
}
}
};
// 获取选择制裁
const
loading
=
ref
(
false
);
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
100
00
);
const
pageSize
=
ref
(
100
);
const
total
=
ref
(
0
);
const
totalPage
=
ref
(
0
);
const
getDeepMiningSelectData
=
async
()
=>
{
loading
.
value
=
true
;
const
params
=
{
startDate
:
dateRange
.
value
&&
dateRange
.
value
[
0
]
?
dateRange
.
value
[
0
]
:
''
,
endDate
:
dateRange
.
value
&&
dateRange
.
value
[
1
]
?
dateRange
.
value
[
1
]
:
''
,
typeName
:
"实体清单"
,
startDate
:
dateRange
.
value
&&
dateRange
.
value
[
0
]
?
dateRange
.
value
[
0
]
:
""
,
endDate
:
dateRange
.
value
&&
dateRange
.
value
[
1
]
?
dateRange
.
value
[
1
]
:
""
,
//
typeName: "实体清单",
isCn
:
false
,
pageNum
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
pageSize
:
pageSize
.
value
,
sanTypeIds
:
[
Number
(
sanTypeId
.
value
)]
||
1
// 实体清单固定1
};
try
{
const
res
=
await
getDeepMiningSelect
(
params
);
...
...
@@ -307,7 +370,7 @@ const getDeepMiningSelectData = async () => {
date
:
item
.
postDate
,
title
:
item
.
name
,
count
:
item
.
cnEntityCount
,
unit
:
'家中国实体'
,
// 接口未返回单位,暂时固定
unit
:
"家中国实体"
,
// 接口未返回单位,暂时固定
summary
:
item
.
summary
,
// 保留额外信息备用
techDomainList
:
item
.
techDomainList
// 保留额外信息备用
}));
...
...
@@ -326,7 +389,7 @@ const getDeepMiningSelectData = async () => {
}
finally
{
loading
.
value
=
false
;
}
}
}
;
// 日期选择变化
const
handleDateChange
=
()
=>
{
...
...
@@ -335,14 +398,14 @@ const handleDateChange = () => {
};
// 翻页
const
handlePageChange
=
(
page
)
=>
{
const
handlePageChange
=
page
=>
{
if
(
page
<
1
||
page
>
totalPage
.
value
)
return
;
currentPage
.
value
=
page
;
getDeepMiningSelectData
();
};
// 列表项点击事件
const
handleSanctionSelect
=
(
id
)
=>
{
const
handleSanctionSelect
=
id
=>
{
currentSanctionId
.
value
=
id
;
getFishboneData
();
getCnEntityOnChainData
();
...
...
@@ -366,13 +429,7 @@ const currentSanctionId = ref(5);
const
cnEntityOnChainData
=
ref
({});
const
mainLineLabels
=
ref
([
"关键原材料"
,
"电池材料"
,
"电子元器件"
,
"动力电池"
,
"电子控制系统"
,
"动力电池"
]);
const
mainLineLabels
=
ref
([
"关键原材料"
,
"电池材料"
,
"电子元器件"
,
"动力电池"
,
"电子控制系统"
,
"动力电池"
]);
// 获取奇数索引的数据组(放在上方)
const
getOddGroups
=
data
=>
{
...
...
@@ -409,12 +466,15 @@ const getRightItems = items => {
};
// 格式化比率
const
formatRate
=
(
rate
)
=>
{
if
(
rate
===
undefined
||
rate
===
null
)
return
'0.00'
;
const
formatRate
=
rate
=>
{
if
(
rate
===
undefined
||
rate
===
null
)
return
"0.00"
;
return
(
rate
*
100
).
toFixed
(
2
);
};
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
""
;
// 获取选择制裁
getDeepMiningSelectData
();
// 获取产业链信息
...
...
@@ -730,7 +790,7 @@ onMounted(() => {
// 虚线
&
:
:
after
{
content
:
''
;
content
:
""
;
position
:
absolute
;
top
:
0
;
left
:
0
;
...
...
@@ -744,7 +804,7 @@ onMounted(() => {
position
:
absolute
;
// top: -14px;
font-size
:
16px
;
color
:
#055
FC
2
;
color
:
#055
fc
2
;
font-weight
:
bold
;
background-color
:
#f7f8f9
;
padding
:
0
10px
;
...
...
@@ -1235,7 +1295,6 @@ onMounted(() => {
}
}
.industry-select
{
width
:
160px
;
height
:
28px
;
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/introductionPage/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -75,9 +75,17 @@
<
AnalysisBox
title
=
"实体清单更新历史"
:
showAllBtn
=
"false"
>
<
template
#
header
-
btn
>
<
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
>
...
...
@@ -91,8 +99,13 @@
<
img
:
src
=
"item.icon || title"
alt
=
""
/>
<
div
class
=
"main"
>
<
div
class
=
"main-title"
@
click
=
"handleClick(item)"
>
{{
item
.
name
}}
<
/div
>
<
el
-
tooltip
effect
=
"dark"
:
content
=
"item.summary"
popper
-
class
=
"common-prompt-popper"
placement
=
"top"
:
show
-
after
=
"500"
>
<
el
-
tooltip
effect
=
"dark"
:
content
=
"item.summary"
popper
-
class
=
"common-prompt-popper"
placement
=
"top"
:
show
-
after
=
"500"
>
<
div
class
=
"main-desc"
>
{{
item
.
summary
}}
<
/div
>
<
/el-tooltip
>
<
div
class
=
"tag-box"
>
...
...
@@ -107,8 +120,14 @@
<
/div
>
<
div
class
=
"left-footer"
>
<
div
class
=
"total-count"
>
共
{{
totalAll
}}
项
<
/div
>
<
el
-
pagination
v
-
model
:
current
-
page
=
"currentPageAll"
:
page
-
size
=
"pageSizeAll"
:
total
=
"totalAll"
layout
=
"prev, pager, next"
background
@
current
-
change
=
"handlePageChangeAll"
/>
<
el
-
pagination
v
-
model
:
current
-
page
=
"currentPageAll"
:
page
-
size
=
"pageSizeAll"
:
total
=
"totalAll"
layout
=
"prev, pager, next"
background
@
current
-
change
=
"handlePageChangeAll"
/>
<
/div
>
<
/AnalysisBox
>
<
/div
>
...
...
@@ -130,8 +149,12 @@
<
span
>
关键人物
<
/span
>
<
/div
>
<
div
class
=
"key-person-list"
>
<
div
class
=
"person-item"
v
-
for
=
"(item, index) in publishInfo.personList"
:
key
=
"index"
@
click
=
"handlePerClick(item)"
>
<
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"
>
<
CommonPrompt
:
content
=
"item.name"
>
...
...
@@ -183,7 +206,9 @@ import icon02 from "../../assets/icon02.png";
import
{
ArrowDown
}
from
"@element-plus/icons-vue"
;
import
CommonPrompt
from
"../../../../../commonPrompt/index.vue"
;
import
{
getEntityInfo
,
getPublishInfo
,
getPublishOrgInfo
,
getEntityUpdateInfo
}
from
"@/api/exportControlV2.0.js"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 处理点击发布机构的方法
const
handleClickOrg
=
item
=>
{
// console.log("点击了发布机构:", item);
...
...
@@ -275,9 +300,10 @@ const getSanctionUpdate = async () => {
const
data
=
{
isCn
:
onlyChina
.
value
,
techDomainIds
:
selectedDomain
.
value
?
[
selectedDomain
.
value
]
:
[],
typeName
:
"实体清单"
,
//
typeName: "实体清单",
pageNum
:
currentPageAll
.
value
,
pageSize
:
pageSizeAll
.
value
pageSize
:
pageSizeAll
.
value
,
sanTypeIds
:
[
sanTypeId
.
value
]
||
[
"1"
]
// 实体清单固定1
}
;
try
{
const
res
=
await
getEntityUpdateInfo
(
data
);
...
...
@@ -324,7 +350,7 @@ const handlePageChangeAll = val => {
const
publishInfo
=
ref
({
}
);
const
getPublishInfoFn
=
async
()
=>
{
const
params
=
{
sanTypeId
:
1
// 实体清单固定1
sanTypeId
:
sanTypeId
.
value
||
1
// 实体清单固定1
}
;
try
{
const
res
=
await
getPublishInfo
(
params
);
...
...
@@ -379,22 +405,25 @@ const handleLoadMoreDynamic = () => {
// 获取实体清单基本信息
const
entityInfo
=
ref
({
}
);
const
emit
=
defineEmits
([
'update-entity-info'
]);
const
getEntityInfoFn
=
async
()
=>
{
const
emit
=
defineEmits
([
"update-entity-info"
]);
const
getEntityInfoFn
=
async
id
=>
{
try
{
const
res
=
await
getEntityInfo
(
"el"
);
const
res
=
await
getEntityInfo
(
id
);
if
(
res
&&
res
.
code
===
200
)
{
entityInfo
.
value
=
res
.
data
;
emit
(
'update-entity-info'
,
res
.
data
);
console
.
log
(
"获取实体清单基本信息成功:"
,
res
.
data
);
emit
(
"update-entity-info"
,
res
.
data
);
}
}
catch
(
error
)
{
console
.
error
(
"获取实体清单基本信息失败:"
,
error
);
}
}
;
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
sanTypeId
.
value
=
route
.
query
.
sanTypeId
;
// 获取实体清单基本信息
getEntityInfoFn
();
getEntityInfoFn
(
sanTypeId
.
value
);
// 获取实体清单发布机构
getPublishInfoFn
();
// 获取实体清单更新历史
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/components/listPage/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -13,27 +13,47 @@
<div
class=
"text"
>
科技领域
</div>
</div>
<div
class=
"checkbox-group"
>
<el-checkbox
v-for=
"(item, index) in techFields"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, techFields, 'tech')"
/>
<el-checkbox
v-for=
"(item, index) in techFields"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, techFields, 'tech')"
/>
</div>
<div
class=
"title"
>
<div
class=
"box"
></div>
<div
class=
"text"
>
实体类型
</div>
</div>
<div
class=
"checkbox-group"
>
<el-checkbox
v-for=
"(item, index) in entityTypes"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, entityTypes, 'type')"
/>
<el-checkbox
v-for=
"(item, index) in entityTypes"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, entityTypes, 'type')"
/>
</div>
<div
class=
"title"
>
<div
class=
"box"
></div>
<div
class=
"text"
>
制裁时间
</div>
</div>
<div
class=
"checkbox-group"
>
<el-checkbox
v-for=
"(item, index) in sanctionTimes"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, sanctionTimes, 'time')"
/>
<el-checkbox
v-for=
"(item, index) in sanctionTimes"
:key=
"index"
v-model=
"item.checked"
:label=
"item.label"
@
change=
"handleFilterChange(item, sanctionTimes, 'time')"
/>
<div
v-if=
"sanctionTimes.find(i => i.value === 'custom' && i.checked)"
class=
"custom-date-picker"
>
<el-date-picker
v-model=
"customDateRange"
type=
"daterange"
range-separator=
"-"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
/>
<el-date-picker
v-model=
"customDateRange"
type=
"daterange"
range-separator=
"-"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
/>
</div>
</div>
</div>
...
...
@@ -106,33 +126,47 @@
<
template
#
header-btn
>
<div
class=
"stats"
>
<div
class=
"dot"
></div>
<div
class=
"count-text"
>
共
<span
class=
"highlight"
>
{{
ruleCount
.
totalCount
}}
</span>
家
</div>
<div
class=
"rule-text"
>
(50%规则涉及
<span
class=
"highlight"
>
{{
ruleCount
.
ruleCount
}}
</span>
家)
</div>
<div
class=
"count-text"
>
共
<span
class=
"highlight"
>
{{
ruleCount
.
totalCount
}}
</span>
家
</div>
<div
class=
"rule-text"
>
(50%规则涉及
<span
class=
"highlight"
>
{{
ruleCount
.
ruleCount
}}
</span
>
家)
</div>
</div>
</
template
>
<div
class=
"right-table"
>
<el-table
:data=
"entityRows"
table-layout=
"fixed"
:row-class-name=
"tableRowClassName"
:header-cell-style=
"{ background: '#fff' }"
>
<el-table
:data=
"entityRows"
table-layout=
"fixed"
:row-class-name=
"tableRowClassName"
:header-cell-style=
"{ background: '#fff' }"
>
<el-table-column
label=
"实体名称"
min-width=
"200"
>
<
template
#
default=
"{ row }"
>
<div
class=
"entity-name-cell"
@
click=
"handleCompClick(row)"
>
<el-image
v-if=
"row.img"
class=
"avatar"
:src=
"row.img"
alt=
""
></el-image>
<div
v-else
class=
"avatar-undefined"
>
{{
(
row
.
entityNameZh
||
row
.
entityName
)?.
match
(
/
[\u
4e00-
\u
9fa5a-zA-Z0-9
]
/
)?.[
0
]
}}
{{
(
row
.
entityNameZh
||
row
.
entityName
)?.
match
(
/
[\u
4e00-
\u
9fa5a-zA-Z0-9
]
/
)?.[
0
]
}}
</div>
<CommonPrompt
:content=
"row.entityNameZh || row.entityName"
style=
"flex: 1; overflow: hidden"
/>
<CommonPrompt
:content=
"row.entityNameZh || row.entityName"
style=
"flex: 1; overflow: hidden"
/>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"涉及领域"
min-width=
"150"
>
<
template
#
default=
"{ row }"
>
<div
class=
"domain-cell"
>
<el-tag
v-for=
"tag in row.techDomains"
:key=
"tag"
class=
"domain-tag"
effect=
"plain"
:disable-transitions=
"true"
:style=
"getTagStyle(tag)"
>
<el-tag
v-for=
"tag in row.techDomains"
:key=
"tag"
class=
"domain-tag"
effect=
"plain"
:disable-transitions=
"true"
:style=
"getTagStyle(tag)"
>
{{
tag
}}
</el-tag>
</div>
...
...
@@ -143,10 +177,11 @@
<
template
#
default=
"{ row }"
>
<div
class=
"rule-cell"
v-if=
"row.ruleOrgCount > 0"
>
<div
class=
"rule-text"
:title=
"row.ruleOrgList?.[0]?.orgName || ''"
>
{{
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>
<el-link
class=
"rule-link"
type=
"primary"
:underline=
"false"
@
click=
"handleRuleClick(row)"
>
{{
row
.
ruleOrgCount
}}
家 >
</el-link
>
</div>
</
template
>
</el-table-column>
...
...
@@ -154,15 +189,26 @@
</div>
<div
class=
"tight-footer"
>
<div
class=
"total-text"
>
共 {{ total }} 项
</div>
<el-pagination
:current-page=
"currentPage"
v-model:page-size=
"pageSize"
:total=
"total"
layout=
"prev, pager, next"
prev-text=
"<"
next-text=
">"
@
current-change=
"handleCurrentChange"
/>
<el-pagination
:current-page=
"currentPage"
v-model:page-size=
"pageSize"
:total=
"total"
layout=
"prev, pager, next"
prev-text=
"<"
next-text=
">"
@
current-change=
"handleCurrentChange"
/>
</div>
</AnalysisBox>
</div>
</div>
</div>
<RuleSubsidiaryDialog
v-model=
"ruleDialogVisible"
:company-name=
"currentRuleCompany"
:total-count=
"currentRuleCount"
:data-list=
"currentRuleList"
/>
<RuleSubsidiaryDialog
v-model=
"ruleDialogVisible"
:company-name=
"currentRuleCompany"
:total-count=
"currentRuleCount"
:data-list=
"currentRuleList"
/>
</template>
<
script
setup
>
...
...
@@ -171,15 +217,15 @@ import { useRouter } from "vue-router";
import
{
Search
}
from
"@element-plus/icons-vue"
;
import
defaultIcon
from
"../../../../../assets/icons/default-avatar.png"
;
import
RuleSubsidiaryDialog
from
"./RuleSubsidiaryDialog.vue"
;
import
{
getExportControlList
,
get50PercentEntityCount
}
from
"@/api/exportControlV2.0.js"
import
CommonPrompt
from
'@/views/exportControl/commonPrompt/index.vue'
import
{
getExportControlList
,
get50PercentEntityCount
}
from
"@/api/exportControlV2.0.js"
;
import
CommonPrompt
from
"@/views/exportControl/commonPrompt/index.vue"
;
const
router
=
useRouter
();
// 跳转公司详情页
const
handleCompClick
=
item
=>
{
console
.
log
(
"item"
,
item
);
window
.
sessionStorage
.
setItem
(
'curTabName'
,
item
.
entityNameZh
||
item
.
entityName
)
window
.
sessionStorage
.
setItem
(
"curTabName"
,
item
.
entityNameZh
||
item
.
entityName
);
const
route
=
router
.
resolve
({
name
:
"companyPages"
,
params
:
{
...
...
@@ -199,7 +245,7 @@ const total = ref(0);
const
entityRows
=
computed
(()
=>
mainList
.
value
);
const
handleCurrentChange
=
(
val
)
=>
{
const
handleCurrentChange
=
val
=>
{
if
(
val
===
currentPage
.
value
)
return
;
currentPage
.
value
=
val
;
getExportControlListApi
();
...
...
@@ -275,7 +321,7 @@ const currentRuleCompany = ref("");
const
currentRuleCount
=
ref
(
0
);
const
currentRuleList
=
ref
([]);
const
handleRuleClick
=
(
row
)
=>
{
const
handleRuleClick
=
row
=>
{
currentRuleCompany
.
value
=
row
.
entityNameZh
||
row
.
entityName
;
currentRuleCount
.
value
=
row
.
ruleOrgCount
;
currentRuleList
.
value
=
row
.
ruleOrgList
||
[];
...
...
@@ -285,11 +331,11 @@ const handleRuleClick = (row) => {
// 筛选逻辑处理
const
handleFilterChange
=
(
item
,
list
,
type
)
=>
{
// 如果点击的是"全部"
if
(
item
.
value
===
'all'
)
{
if
(
item
.
value
===
"all"
)
{
if
(
item
.
checked
)
{
// 选中全部,取消其他所有
list
.
forEach
(
i
=>
{
if
(
i
.
value
!==
'all'
)
i
.
checked
=
false
;
if
(
i
.
value
!==
"all"
)
i
.
checked
=
false
;
});
}
else
{
// 取消全部(通常不允许全部取消,至少得选一个,这里如果取消全部,就默认为全部选中)
...
...
@@ -299,17 +345,17 @@ const handleFilterChange = (item, list, type) => {
// 点击的是具体项
if
(
item
.
checked
)
{
// 选中具体项,取消"全部"
const
allItem
=
list
.
find
(
i
=>
i
.
value
===
'all'
);
const
allItem
=
list
.
find
(
i
=>
i
.
value
===
"all"
);
if
(
allItem
)
allItem
.
checked
=
false
;
// 特殊处理制裁时间的自定义和其他年份互斥
if
(
type
===
'time'
)
{
if
(
item
.
value
===
'custom'
)
{
if
(
type
===
"time"
)
{
if
(
item
.
value
===
"custom"
)
{
list
.
forEach
(
i
=>
{
if
(
i
.
value
!==
'custom'
&&
i
.
value
!==
'all'
)
i
.
checked
=
false
;
if
(
i
.
value
!==
"custom"
&&
i
.
value
!==
"all"
)
i
.
checked
=
false
;
});
}
else
{
const
customItem
=
list
.
find
(
i
=>
i
.
value
===
'custom'
);
const
customItem
=
list
.
find
(
i
=>
i
.
value
===
"custom"
);
if
(
customItem
)
customItem
.
checked
=
false
;
}
}
...
...
@@ -317,7 +363,7 @@ const handleFilterChange = (item, list, type) => {
// 取消具体项,检查是否还有选中的
const
anyChecked
=
list
.
some
(
i
=>
i
.
checked
);
if
(
!
anyChecked
)
{
const
allItem
=
list
.
find
(
i
=>
i
.
value
===
'all'
);
const
allItem
=
list
.
find
(
i
=>
i
.
value
===
"all"
);
if
(
allItem
)
allItem
.
checked
=
true
;
}
}
...
...
@@ -337,50 +383,54 @@ let abortController = null;
const
getExportControlListApi
=
async
()
=>
{
// 取消上一轮未完成的请求
if
(
abortController
)
{
try
{
abortController
.
abort
();
}
catch
{
}
try
{
abortController
.
abort
();
}
catch
{}
}
abortController
=
new
AbortController
();
isFetching
.
value
=
true
;
// 处理科技领域筛选
let
techDomains
=
[];
const
allTech
=
techFields
.
value
.
find
(
item
=>
item
.
value
===
'all'
);
const
allTech
=
techFields
.
value
.
find
(
item
=>
item
.
value
===
"all"
);
if
(
!
allTech
||
!
allTech
.
checked
)
{
techDomains
=
techFields
.
value
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
'all'
)
.
map
(
item
=>
item
.
value
);
techDomains
=
techFields
.
value
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
"all"
).
map
(
item
=>
item
.
value
);
}
// 处理实体类型筛选
let
typeIds
=
[];
const
allType
=
entityTypes
.
value
.
find
(
item
=>
item
.
value
===
'all'
);
const
allType
=
entityTypes
.
value
.
find
(
item
=>
item
.
value
===
"all"
);
if
(
!
allType
||
!
allType
.
checked
)
{
typeIds
=
entityTypes
.
value
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
'all'
)
.
map
(
item
=>
Number
(
item
.
value
));
typeIds
=
entityTypes
.
value
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
"all"
).
map
(
item
=>
Number
(
item
.
value
));
}
// 处理制裁时间筛选
let
years
=
[];
let
startDate
=
undefined
;
let
endDate
=
undefined
;
const
allTime
=
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
'all'
);
const
allTime
=
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
"all"
);
if
(
!
allTime
||
!
allTime
.
checked
)
{
years
=
sanctionTimes
.
value
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
'all'
&&
item
.
value
!==
'custom'
)
.
filter
(
item
=>
item
.
checked
&&
item
.
value
!==
"all"
&&
item
.
value
!==
"custom"
)
.
map
(
item
=>
Number
(
item
.
value
));
const
customTime
=
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
'custom'
);
const
customTime
=
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
"custom"
);
if
(
customTime
&&
customTime
.
checked
&&
customDateRange
.
value
&&
customDateRange
.
value
.
length
===
2
)
{
const
start
=
new
Date
(
customDateRange
.
value
[
0
]);
const
end
=
new
Date
(
customDateRange
.
value
[
1
]);
startDate
=
`
${
start
.
getFullYear
()}
-
${
String
(
start
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)}
-
${
String
(
start
.
getDate
()).
padStart
(
2
,
'0'
)}
`
;
endDate
=
`
${
end
.
getFullYear
()}
-
${
String
(
end
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)}
-
${
String
(
end
.
getDate
()).
padStart
(
2
,
'0'
)}
`
;
startDate
=
`
${
start
.
getFullYear
()}
-
${
String
(
start
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)}
-
${
String
(
start
.
getDate
()
).
padStart
(
2
,
"0"
)}
`
;
endDate
=
`
${
end
.
getFullYear
()}
-
${
String
(
end
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)}
-
${
String
(
end
.
getDate
()).
padStart
(
2
,
"0"
)}
`
;
}
}
const
data
=
{
typeName
:
"实体清单"
,
// typeName: "实体清单",
sanTypeId
:
sanTypeId
.
value
||
1
,
isCn
:
onlyChina
.
value
,
techDomains
:
techDomains
.
length
>
0
?
techDomains
:
undefined
,
entityTypes
:
typeIds
.
length
>
0
?
typeIds
:
undefined
,
...
...
@@ -404,14 +454,14 @@ const getExportControlListApi = async () => {
total
.
value
=
res
.
data
.
totalElements
;
}
}
catch
(
error
)
{
if
(
!
error
||
(
error
.
code
!==
'ERR_CANCELED'
&&
error
.
name
!==
'CanceledError'
&&
error
.
name
!==
'AbortError'
))
{
if
(
!
error
||
(
error
.
code
!==
"ERR_CANCELED"
&&
error
.
name
!==
"CanceledError"
&&
error
.
name
!==
"AbortError"
))
{
console
.
error
(
error
);
}
}
finally
{
isFetching
.
value
=
false
;
abortController
=
null
;
}
}
}
;
watch
(
onlyChina
,
()
=>
{
currentPage
.
value
=
1
;
...
...
@@ -428,13 +478,14 @@ watch(searchKeyword, () => {
getExportControlListApi
();
},
300
);
});
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
sanTypeId
.
value
=
router
.
currentRoute
.
value
.
query
.
sanTypeId
||
"1"
;
getExportControlListApi
();
});
watch
(
customDateRange
,
()
=>
{
if
(
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
'custom'
&&
item
.
checked
))
{
if
(
sanctionTimes
.
value
.
find
(
item
=>
item
.
value
===
"custom"
&&
item
.
checked
))
{
currentPage
.
value
=
1
;
getExportControlListApi
();
}
...
...
@@ -795,40 +846,40 @@ watch(customDateRange, () => {
.total-text
{
font-size
:
14px
;
font-weight
:
400
;
color
:
#5
F656C
;
color
:
#5
f656c
;
font-family
:
"Microsoft YaHei"
;
}
:deep
(
.el-pagination
)
{
--el-pagination-button-bg-color
:
#fff
;
--el-pagination-hover-color
:
#0
E78F
1
;
--el-pagination-hover-color
:
#0
e78f
1
;
--el-pagination-font-size
:
14px
;
.el-pager
li
{
border
:
1px
solid
#
DCDFE
6
;
border
:
1px
solid
#
dcdfe
6
;
border-radius
:
4px
;
margin
:
0
4px
;
font-weight
:
400
;
color
:
#5
F656C
;
color
:
#5
f656c
;
min-width
:
32px
;
height
:
32px
;
line-height
:
30px
;
&
.is-active
{
background-color
:
#0
E78F
1
;
background-color
:
#0
e78f
1
;
color
:
#fff
;
border-color
:
#0
E78F
1
;
border-color
:
#0
e78f
1
;
}
&
:hover:not
(
.is-active
)
{
color
:
#0
E78F
1
;
border-color
:
#0
E78F
1
;
color
:
#0
e78f
1
;
border-color
:
#0
e78f
1
;
}
}
.btn-prev
,
.btn-next
{
border
:
1px
solid
#
DCDFE
6
;
border
:
1px
solid
#
dcdfe
6
;
border-radius
:
4px
;
padding
:
0
;
margin
:
0
4px
;
...
...
@@ -838,13 +889,13 @@ watch(customDateRange, () => {
text-align
:
center
;
&
:hover
{
color
:
#0
E78F
1
;
border-color
:
#0
E78F
1
;
color
:
#0
e78f
1
;
border-color
:
#0
e78f
1
;
}
&
[
disabled
]
{
border-color
:
#
E4E7ED
;
color
:
#
C0C4CC
;
border-color
:
#
e4e7ed
;
color
:
#
c0c4cc
;
}
}
}
...
...
src/views/exportControl/v2.0EntityList/components/sanctionsOverview/index.vue
浏览文件 @
5fc86ad6
<
template
>
<div
class=
"sanctions-overview"
>
<div
class=
"side-nav"
>
<div
v-for=
"(item, index) in activeTab"
:key=
"index"
class=
"tab-item"
:class=
"
{'active': index === activeIndex}" @click="activeIndex = index">
{{
item
}}
<span
v-if=
"index === activeIndex"
class=
"arrow"
></span>
</div>
</div>
<div
class=
"content-box"
>
<introductionPage
v-if=
"activeIndex === 0"
@
update-entity-info=
"(data) => $emit('update-entity-info', data)"
></introductionPage>
<listPage
v-if=
"activeIndex === 1"
></listPage>
</div>
</div>
<div
class=
"sanctions-overview"
>
<div
class=
"side-nav"
>
<div
v-for=
"(item, index) in activeTab"
:key=
"index"
class=
"tab-item"
:class=
"
{ active: index === activeIndex }"
@click="activeIndex = index"
>
{{
item
}}
<span
v-if=
"index === activeIndex"
class=
"arrow"
></span>
</div>
</div>
<div
class=
"content-box"
>
<introductionPage
v-show=
"activeIndex === 1"
@
update-entity-info=
"data => $emit('update-entity-info', data)"
></introductionPage>
<listPage
v-show=
"activeIndex === 0"
></listPage>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
,
defineEmits
}
from
'vue'
import
introductionPage
from
"./components/introductionPage/index.vue"
import
listPage
from
"./components/listPage/index.vue"
const
emit
=
defineEmits
([
'update-entity-info'
])
const
activeTab
=
ref
([
"实体清单简介"
,
"实体清单列表"
])
const
activeIndex
=
ref
(
0
)
import
{
ref
,
defineEmits
}
from
"vue"
;
import
introductionPage
from
"./components/introductionPage/index.vue"
;
import
listPage
from
"./components/listPage/index.vue"
;
const
emit
=
defineEmits
([
"update-entity-info"
]);
const
activeTab
=
ref
([
"实体清单列表"
,
"实体清单简介"
]);
const
activeIndex
=
ref
(
0
);
</
script
>
<
style
scoped
lang=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
*
{
margin
:
0
;
padding
:
0
;
}
.sanctions-overview
{
width
:
1601px
;
margin
:
0
auto
;
position
:
relative
;
// min-height: 800px;
.side-nav
{
position
:
absolute
;
top
:
27px
;
right
:
100%
;
margin-right
:
12px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
.tab-item
{
cursor
:
pointer
;
padding
:
4px
20px
;
border-radius
:
22px
;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
white-space
:
nowrap
;
display
:
flex
;
align-items
:
center
;
&
.active
{
background-color
:
rgb
(
5
,
95
,
194
);
color
:
#fff
;
.arrow
{
display
:
inline-block
;
width
:
0
;
height
:
0
;
border-top
:
5px
solid
transparent
;
border-bottom
:
5px
solid
transparent
;
border-left
:
6px
solid
#fff
;
margin-left
:
8px
;
}
}
}
}
.content-box
{
width
:
100%
;
}
.sanctions-overview
{
width
:
1601px
;
margin
:
0
auto
;
position
:
relative
;
// min-height: 800px;
.side-nav
{
position
:
absolute
;
top
:
27px
;
right
:
100%
;
margin-right
:
12px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
.tab-item
{
cursor
:
pointer
;
padding
:
4px
20px
;
border-radius
:
22px
;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
white-space
:
nowrap
;
display
:
flex
;
align-items
:
center
;
&
.active
{
background-color
:
rgb
(
5
,
95
,
194
);
color
:
#fff
;
.arrow
{
display
:
inline-block
;
width
:
0
;
height
:
0
;
border-top
:
5px
solid
transparent
;
border-bottom
:
5px
solid
transparent
;
border-left
:
6px
solid
#fff
;
margin-left
:
8px
;
}
}
}
}
.content-box
{
width
:
100%
;
}
}
</
style
>
src/views/exportControl/v2.0EntityList/index.vue
浏览文件 @
5fc86ad6
<
template
>
<div
class=
"entity-list"
>
<div
class=
"header"
>
<div
class=
"header-title"
>
<img
:src=
"headerTitle.img"
alt=
""
>
<div>
<div
class=
"title"
>
{{
headerTitle
.
title
}}
<span>
{{
headerTitle
.
titleEn
}}
</span>
</div>
<div
class=
"department"
>
{{
headerTitle
.
department
}}
</div>
</div>
<div
class=
"btn"
>
<img
:src=
"icon01"
alt=
""
>
切换
</div>
</div>
<div
class=
"header-nav"
>
<div
class=
"nav-item"
v-for=
"(item, index) in headerNavList"
:key=
"index"
:class=
"
{ active: activeIndex === index }"
@click="activeIndex = index"
>
<img
:src=
"activeIndex === index ? item.imgActive : item.img"
alt=
""
>
<span>
{{
item
.
title
}}
</span>
<div
class=
"active-line"
v-if=
"activeIndex === index"
></div>
</div>
</div>
</div>
<div
class=
"main"
>
<sanctions-overview
v-if=
"activeIndex === 0"
@
update-entity-info=
"handleUpdateEntityInfo"
></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>
</div>
</div>
<div
class=
"entity-list"
>
<div
class=
"header"
>
<div
class=
"header-title"
>
<img
:src=
"headerTitle.img"
alt=
""
/>
<div>
<div
class=
"title"
>
{{
headerTitle
.
title
}}
<span>
{{
headerTitle
.
titleEn
}}
</span>
</div>
<div
class=
"department"
>
{{
headerTitle
.
department
}}
</div>
</div>
<div
class=
"btn"
><img
:src=
"icon01"
alt=
""
/>
切换
</div>
</div>
<div
class=
"header-nav"
>
<div
class=
"nav-item"
v-for=
"(item, index) in headerNavList"
:key=
"index"
:class=
"
{ active: activeIndex === index }"
@click="activeIndex = index"
>
<img
:src=
"activeIndex === index ? item.imgActive : item.img"
alt=
""
/>
<span>
{{
item
.
title
}}
</span>
<div
class=
"active-line"
v-if=
"activeIndex === index"
></div>
</div>
</div>
</div>
<div
class=
"main"
>
<sanctions-overview
v-if=
"activeIndex === 0"
@
update-entity-info=
"handleUpdateEntityInfo"
></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>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
sanctionsOverview
from
"./components/sanctionsOverview/index.vue"
;
import
dataStatistics
from
"./components/dataStatistics/index.vue"
;
...
...
@@ -58,27 +57,34 @@ import icon2Active from "../assets/icons/icon2_active.png";
import
icon3
from
"../assets/icons/icon3.png"
;
import
icon3Active
from
"../assets/icons/icon3_active.png"
;
const
route
=
useRoute
();
const
headerTitle
=
ref
({
// img: title,
// title: "实体清单",
// titleEn: "Entity List",
// department: "美国商务部工业与安全局"
})
// img: title,
// title: "实体清单",
// titleEn: "Entity List",
// department: "美国商务部工业与安全局"
})
;
const
handleUpdateEntityInfo
=
(
data
)
=>
{
if
(
data
)
{
headerTitle
.
value
=
{
...
headerTitle
.
value
,
title
:
data
.
name
,
titleEn
:
data
.
originalName
,
department
:
data
.
orgName
,
departId
:
data
.
orgId
,
img
:
data
.
orgLogoUrl
||
title
}
}
}
onMounted
(()
=>
{
// 获取路由参数id
const
id
=
route
.
query
.
id
;
console
.
log
(
"EntityList 页面接收到的 id:"
,
id
);
});
const
handleUpdateEntityInfo
=
data
=>
{
console
.
log
(
"更新实体清单基本信息:"
,
data
);
if
(
data
)
{
headerTitle
.
value
=
{
...
headerTitle
.
value
,
title
:
data
.
name
,
titleEn
:
data
.
originalName
,
department
:
data
.
orgName
,
departId
:
data
.
orgId
,
img
:
data
.
orgLogoUrl
||
title
};
}
};
const
activeIndex
=
ref
(
0
)
const
activeIndex
=
ref
(
0
)
;
const
headerNavList
=
ref
([
{
...
...
src/views/exportControl/v2.0SingleSanction/components/dataStatistics/index.vue
浏览文件 @
5fc86ad6
...
...
@@ -137,15 +137,18 @@
<AnalysisBox
title=
"制裁实体国家分布情况"
>
<div
class=
"country-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in countryDistribution"
:key=
"index"
>
<img
:src=
"flag"
alt=
""
class=
"flag"
>
<img
:src=
"flag"
alt=
""
class=
"flag"
/
>
<div
class=
"country-name"
>
{{
item
.
name
}}
</div>
<div
class=
"progress-bar-container"
>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}">
</div>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}"
>
</div>
</div>
<div
class=
"count"
:class=
"
{
'highlight'
: index === 0 }">
{{
item
.
count
}}
家
</div>
<div
class=
"count"
:class=
"
{
highlight
: index === 0 }">
{{
item
.
count
}}
家
</div>
</div>
</div>
<div
class=
"bottom"
>
...
...
@@ -170,9 +173,13 @@
<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: (maxRegionCount > 0 ? (item.count / maxRegionCount) * 100 : 0) + '%', background: getBarColor(index) }">
</div>
<div
class=
"rank-bar-fill"
:style=
"
{
width: (maxRegionCount > 0 ? (item.count / maxRegionCount) * 100 : 0) + '%',
background: getBarColor(index)
}"
>
</div>
</div>
<div
class=
"rank-value"
>
{{
item
.
count
}}
家
</div>
</div>
...
...
@@ -201,10 +208,18 @@ 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
flag
from
"../../assets/default-icon2.png"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
getSingleSanctionTotalCount
,
getSingleSanctionDomainCount
,
getSingleSanctionEntityTypeCount
,
getSingleSanctionEntityCountryCount
,
getSingleSanctionEntityRegionCount
}
from
"@/api/exportControlV2.0"
;
import
{
getSingleSanctionTotalCount
,
getSingleSanctionDomainCount
,
getSingleSanctionEntityTypeCount
,
getSingleSanctionEntityCountryCount
,
getSingleSanctionEntityRegionCount
}
from
"@/api/exportControlV2.0"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 单次制裁-数据统计-制裁实体地域分布情况
const
regionDistribution
=
ref
([]);
const
maxRegionCount
=
ref
(
0
);
...
...
@@ -212,8 +227,8 @@ const maxRegionCount = ref(0);
const
getRegionData
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
regionTime
.
value
&&
regionTime
.
value
!==
'all'
)
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
,
sanTypeId
:
route
.
query
.
sanTypeId
||
"1"
};
if
(
regionTime
.
value
&&
regionTime
.
value
!==
"all"
)
{
params
.
startDate
=
`
${
regionTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
regionTime
.
value
}
-12-31`
;
}
...
...
@@ -226,9 +241,7 @@ const getRegionData = async () => {
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
};
// 单次制裁-数据统计-制裁实体国家分布情况
const
countryDistribution
=
ref
([]);
...
...
@@ -236,7 +249,7 @@ const countryDistribution = ref([]);
const
getCountryCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
const
params
=
{
sanRecordId
:
sanRecordId
.
value
,
sanTypeId
:
sanTypeId
.
value
};
const
res
=
await
getSingleSanctionEntityCountryCount
(
params
);
if
(
res
.
code
===
200
)
{
const
rawData
=
res
.
data
||
[];
...
...
@@ -245,7 +258,7 @@ const getCountryCount = async () => {
countryDistribution
.
value
=
rawData
.
map
((
item
,
index
)
=>
{
// 计算宽度,最大值对应 80%
const
width
=
maxCount
>
0
?
(
item
.
count
/
maxCount
)
*
80
+
'%'
:
'0%'
;
const
width
=
maxCount
>
0
?
(
item
.
count
/
maxCount
)
*
80
+
"%"
:
"0%"
;
// 根据索引分配渐变色
let
gradient
=
""
;
...
...
@@ -267,9 +280,7 @@ const getCountryCount = async () => {
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
};
// 单次制裁-数据统计-制裁实体类型分布情况
const
entityTypeCount
=
ref
([]);
...
...
@@ -277,8 +288,8 @@ const entityTypeCount = ref([]);
const
getEntityTypeCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
typeTime
.
value
&&
typeTime
.
value
!==
'all'
)
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
,
sanTypeId
:
sanTypeId
.
value
};
if
(
typeTime
.
value
&&
typeTime
.
value
!==
"all"
)
{
params
.
startDate
=
`
${
typeTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
typeTime
.
value
}
-12-31`
;
}
...
...
@@ -290,10 +301,7 @@ const getEntityTypeCount = async () => {
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
};
// 单次制裁-数据统计-制裁实体领域分布情况
const
domainCount
=
ref
([]);
...
...
@@ -301,8 +309,8 @@ const domainCount = ref([]);
const
getDomainCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
};
if
(
domainTime
.
value
&&
domainTime
.
value
!==
'all'
)
{
const
params
=
{
sanRecordId
:
sanRecordId
.
value
,
sanTypeId
:
sanTypeId
.
value
};
if
(
domainTime
.
value
&&
domainTime
.
value
!==
"all"
)
{
params
.
startDate
=
`
${
domainTime
.
value
}
-01-01`
;
params
.
endDate
=
`
${
domainTime
.
value
}
-12-31`
;
}
...
...
@@ -314,8 +322,7 @@ const getDomainCount = async () => {
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
};
// 单次制裁-数据统计-总量统计
const
totalCount
=
ref
({});
...
...
@@ -323,22 +330,21 @@ const totalCount = ref({});
const
getTotalCount
=
async
()
=>
{
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionTotalCount
(
{
sanRecordId
:
sanRecordId
.
value
}
);
const
res
=
await
getSingleSanctionTotalCount
(
route
.
query
.
sanTypeId
);
if
(
res
.
code
===
200
)
{
totalCount
.
value
=
res
.
data
||
{};
}
}
catch
(
error
)
{
console
.
log
(
error
);
}
}
};
const
router
=
useRouter
();
const
sanRecordId
=
ref
(
""
)
const
sanRecordId
=
ref
(
""
)
;
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
;
}
;
const
regionTime
=
ref
(
"all"
);
const
domainTime
=
ref
(
"all"
);
...
...
@@ -355,11 +361,7 @@ watch(regionTime, () => {
getRegionData
();
});
const
timeOptions
=
[
{
label
:
"全部时间"
,
value
:
"all"
}
];
const
timeOptions
=
[{
label
:
"全部时间"
,
value
:
"all"
}];
// 生成2000-2025年的选项
for
(
let
i
=
2025
;
i
>=
2000
;
i
--
)
{
timeOptions
.
push
({
label
:
`
${
i
}
年`
,
value
:
`
${
i
}
`
});
...
...
@@ -640,8 +642,11 @@ const initTypeChart = () => {
chart
.
resize
();
});
};
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取路由参数id
sanTypeId
.
value
=
route
.
query
.
sanTypeId
;
console
.
log
(
"页面接收到的 sanTypeId:"
,
sanTypeId
.
value
);
// 获取url参数
getUrlParams
();
// 单次制裁-数据统计-总量统计-请求
...
...
src/views/exportControl/v2.0SingleSanction/components/sanctionsOverview/index.vue
浏览文件 @
5fc86ad6
<
template
>
<div
class=
"sanctions-overview"
>
<div
class=
"main"
>
<div
class=
"left"
>
<div
class=
"left-top"
>
<AnalysisBox
title=
"基本信息"
:showAllBtn=
"false"
>
<div
class=
"left-top-title"
>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布时间:
</div>
<div
class=
"value"
>
{{
formattedData
.
postDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
生效时间:
</div>
<div
class=
"value"
>
{{
formattedData
.
effectiveDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布文件:
</div>
<div
class=
"value"
>
{{
formattedData
.
fileCode
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
案卷号:
</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
@
click=
"handleClick"
>
{{
formattedData
.
postPersonName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
制裁领域:
</div>
<div
class=
"value tags"
>
<span
class=
"tag"
v-for=
"(domain, index) in formattedData.domains"
:key=
"index"
>
{{
domain
}}
</span>
</div>
</div>
</div>
<div
class=
"left-top-content"
>
<div
class=
"content-title"
>
制裁实体分布:
</div>
<div
class=
"distribution-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
>
<img
:src=
"flag"
alt=
""
class=
"flag"
>
<div
class=
"country-name"
>
{{
item
.
name
}}
</div>
<div
class=
"progress-bar-container"
>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}">
</div>
</div>
<div
class=
"count"
:class=
"
{ 'highlight': index === 0 }">
{{
item
.
count
}}
家
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
<div
class=
"left-bottom"
>
<AnalysisBox
title=
"制裁背景"
:showAllBtn=
"false"
>
<div
class=
"left-bottom-content"
>
<div
class=
"timeline-list"
>
<div
class=
"timeline-item"
v-for=
"(item, index) in timelineData"
:key=
"index"
>
<div
class=
"date-row"
>
<div
class=
"dot"
></div>
<div
class=
"date"
>
{{
item
.
date
}}
</div>
</div>
<div
class=
"content"
>
{{
item
.
content
}}
</div>
</div>
</div>
<div
class=
"view-more"
v-if=
"hasMore"
@
click=
"loadMore"
>
查看更多
<el-icon
class=
"icon-more"
>
<DArrowRight
/>
</el-icon>
</div>
</div>
</AnalysisBox>
</div>
</div>
<div
class=
"right"
v-loading=
"isLoading"
>
<AnalysisBox
title=
"制裁清单"
:showAllBtn=
"false"
>
<div
class=
"right-title"
>
<div
class=
"stats-row"
>
<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
class=
"stats-info"
>
<div
class=
"stat-item"
>
<span
class=
"dot red"
></span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span>
家)
</span>
</div>
<div
class=
"stat-item"
>
<span
class=
"dot green"
></span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span>
家)
</span>
</div>
</div>
</div>
<div
class=
"filter-row"
>
<div
class=
"filter-left"
>
<!--
<el-select
v-model=
"filterEntity"
placeholder=
"受制裁实体"
style=
"width: 184px"
>
<div
class=
"sanctions-overview"
>
<div
class=
"main"
>
<div
class=
"left"
>
<div
class=
"left-top"
>
<AnalysisBox
title=
"基本信息"
:showAllBtn=
"false"
>
<div
class=
"left-top-title"
>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
/>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布时间:
</div>
<div
class=
"value"
>
{{
formattedData
.
postDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
生效时间:
</div>
<div
class=
"value"
>
{{
formattedData
.
effectiveDate
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
发布文件:
</div>
<div
class=
"value"
>
{{
formattedData
.
fileCode
}}
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
案卷号:
</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
@
click=
"handleClick"
>
{{
formattedData
.
postPersonName
}}
>
</span>
</div>
</div>
<div
class=
"info-row"
>
<div
class=
"label"
>
制裁领域:
</div>
<div
class=
"value tags"
>
<span
class=
"tag"
v-for=
"(domain, index) in formattedData.domains"
:key=
"index"
>
{{
domain
}}
</span>
</div>
</div>
</div>
<div
class=
"left-top-content"
>
<div
class=
"content-title"
>
制裁实体分布:
</div>
<div
class=
"distribution-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
>
<img
:src=
"flag"
alt=
""
class=
"flag"
/>
<div
class=
"country-name"
>
{{
item
.
name
}}
</div>
<div
class=
"progress-bar-container"
>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}"
>
</div>
</div>
<div
class=
"count"
:class=
"
{ highlight: index === 0 }">
{{
item
.
count
}}
家
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
<div
class=
"left-bottom"
>
<AnalysisBox
title=
"制裁背景"
:showAllBtn=
"false"
>
<div
class=
"left-bottom-content"
>
<div
class=
"timeline-list"
>
<div
class=
"timeline-item"
v-for=
"(item, index) in timelineData"
:key=
"index"
>
<div
class=
"date-row"
>
<div
class=
"dot"
></div>
<div
class=
"date"
>
{{
item
.
date
}}
</div>
</div>
<div
class=
"content"
>
{{
item
.
content
}}
</div>
</div>
</div>
<div
class=
"view-more"
v-if=
"hasMore"
@
click=
"loadMore"
>
查看更多
<el-icon
class=
"icon-more"
>
<DArrowRight
/>
</el-icon>
</div>
</div>
</AnalysisBox>
</div>
</div>
<div
class=
"right"
v-loading=
"isLoading"
>
<AnalysisBox
title=
"制裁清单"
:showAllBtn=
"false"
>
<div
class=
"right-title"
>
<div
class=
"stats-row"
>
<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
class=
"stats-info"
>
<div
class=
"stat-item"
>
<span
class=
"dot red"
></span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span
>
家)
</span
>
</div>
<div
class=
"stat-item"
>
<span
class=
"dot green"
></span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span
>
家)
</span
>
</div>
</div>
</div>
<div
class=
"filter-row"
>
<div
class=
"filter-left"
>
<!--
<el-select
v-model=
"filterEntity"
placeholder=
"受制裁实体"
style=
"width: 184px"
>
<el-option
label=
"受制裁实体"
value=
"1"
/>
</el-select>
-->
</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=
""
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px"
:suffix-icon=
"Search"
/>
</div>
</div>
</div>
<div
class=
"right-content"
>
<div
class=
"sanction-group-list"
>
<div
class=
"sanction-group"
v-for=
"(group, index) in sanctionList"
:key=
"index"
>
<el-table
:data=
"group.entities"
style=
"width: 100%"
>
<el-table-column
label=
"实体名称"
min-width=
"280"
>
<template
#
default=
"scope"
>
<div
class=
"name-cell"
>
<div
class=
"dot"
></div>
<img
:src=
"defaultTitle"
class=
"company-icon"
/>
<span
class=
"company-name"
@
click=
"handleCompClick(scope.row)"
>
{{
scope
.
row
.
name
}}
</span>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"涉及领域"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<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=
"90"
align=
"center"
/>
<el-table-column
prop=
"date"
label=
"制裁时间"
width=
"150"
align=
"center"
/>
<el-table-column
prop=
"revenue"
label=
"营收(亿元)"
width=
"110"
align=
"center"
/>
<el-table-column
label=
"50%规则子企业"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
{{
scope
.
row
.
subsidiaryText
}}
<span
class=
"blue-text"
>
{{
scope
.
row
.
subsidiaryCount
}}
家 >
</span>
</span>
<span
v-else
>
--
</span>
</
template
>
</el-table-column>
</el-table>
<div
class=
"reason-box"
>
{{ group.reason }}
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</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=
""
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px"
:suffix-icon=
"Search"
/>
</div>
</div>
</div>
<div
class=
"right-content"
>
<div
class=
"sanction-group-list"
>
<div
class=
"sanction-group"
v-for=
"(group, index) in sanctionList"
:key=
"index"
>
<el-table
:data=
"group.entities"
style=
"width: 100%"
>
<el-table-column
label=
"实体名称"
min-width=
"280"
>
<template
#
default=
"scope"
>
<div
class=
"name-cell"
>
<div
class=
"dot"
></div>
<img
:src=
"defaultTitle"
class=
"company-icon"
/>
<span
class=
"company-name"
@
click=
"handleCompClick(scope.row)"
>
{{
scope
.
row
.
name
}}
</span>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"涉及领域"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<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=
"90"
align=
"center"
/>
<el-table-column
prop=
"date"
label=
"制裁时间"
width=
"150"
align=
"center"
/>
<el-table-column
prop=
"revenue"
label=
"营收(亿元)"
width=
"110"
align=
"center"
/>
<el-table-column
label=
"50%规则子企业"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
{{
scope
.
row
.
subsidiaryText
}}
<span
class=
"blue-text"
>
{{
scope
.
row
.
subsidiaryCount
}}
家 >
</span>
</span>
<span
v-else
>
--
</span>
</
template
>
</el-table-column>
</el-table>
<div
class=
"reason-box"
>
{{ group.reason }}
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</div>
</template>
<
script
setup
>
...
...
@@ -172,34 +219,40 @@ import { useRouter } from "vue-router";
import
{
ElMessage
}
from
"element-plus"
;
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"
;
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"
;
import
RuleSubsidiaryDialog
from
"../../../v2.0EntityList/components/sanctionsOverview/components/listPage/RuleSubsidiaryDialog.vue"
;
import
{
useRoute
}
from
"vue-router"
;
const
route
=
useRoute
();
// 跳转公司详情页
const
handleCompClick
=
item
=>
{
if
(
!
item
.
entityId
)
{
ElMessage
.
warning
(
"暂无数据"
);
return
;
}
window
.
sessionStorage
.
setItem
(
'curTabName'
,
item
.
name
)
const
curRoute
=
router
.
resolve
({
name
:
"companyPages"
,
params
:
{
id
:
item
.
entityId
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
if
(
!
item
.
entityId
)
{
ElMessage
.
warning
(
"暂无数据"
);
return
;
}
window
.
sessionStorage
.
setItem
(
"curTabName"
,
item
.
name
);
const
curRoute
=
router
.
resolve
({
name
:
"companyPages"
,
params
:
{
id
:
item
.
entityId
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
};
// 跳转发布机构详情页
const
handleClickDp
=
()
=>
{
// console.log("点击了发布机构:", props.data);
window
.
sessionStorage
.
setItem
(
'curTabName'
,
props
.
data
.
postOrgName
)
const
route
=
router
.
resolve
({
path
:
"/institution"
,
query
:
{
id
:
props
.
data
.
postOrgId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
// console.log("点击了发布机构:", props.data);
window
.
sessionStorage
.
setItem
(
"curTabName"
,
props
.
data
.
postOrgName
);
const
route
=
router
.
resolve
({
path
:
"/institution"
,
query
:
{
id
:
props
.
data
.
postOrgId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
};
// 50%规则子企业弹框逻辑
...
...
@@ -208,63 +261,65 @@ const currentSubsidiaryCompanyName = ref("");
const
currentSubsidiaryCount
=
ref
(
0
);
const
currentSubsidiaryList
=
ref
([]);
const
handleSubsidiaryClick
=
(
row
)
=>
{
currentSubsidiaryCompanyName
.
value
=
row
.
name
;
currentSubsidiaryCount
.
value
=
row
.
subsidiaryCount
;
currentSubsidiaryList
.
value
=
row
.
ruleOrgList
||
[];
subsidiaryDialogVisible
.
value
=
true
;
const
handleSubsidiaryClick
=
row
=>
{
currentSubsidiaryCompanyName
.
value
=
row
.
name
;
currentSubsidiaryCount
.
value
=
row
.
subsidiaryCount
;
currentSubsidiaryList
.
value
=
row
.
ruleOrgList
||
[];
subsidiaryDialogVisible
.
value
=
true
;
};
// 单次制裁-制裁概况-制裁清单
const
sanctionList
=
ref
([])
const
addCount
=
ref
(
0
)
const
addRuleCount
=
ref
(
0
)
const
removeCount
=
ref
(
0
)
const
removeRuleCount
=
ref
(
0
)
const
sanctionList
=
ref
([])
;
const
addCount
=
ref
(
0
)
;
const
addRuleCount
=
ref
(
0
)
;
const
removeCount
=
ref
(
0
)
;
const
removeRuleCount
=
ref
(
0
)
;
const
isLoading
=
ref
(
false
)
const
isLoading
=
ref
(
false
)
;
// 调用单次制裁-制裁概况-制裁清单接口
const
getSanctionOverviewList
=
async
()
=>
{
isLoading
.
value
=
true
try
{
const
res
=
await
getSingleSanctionOverviewList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
onlyChina
.
value
,
domainId
:
filterField
.
value
||
undefined
,
searchText
:
searchKeyword
.
value
||
undefined
,
searchType
:
searchType
.
value
})
isLoading
.
value
=
false
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
=>
({
...
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
)
}
}
isLoading
.
value
=
true
;
try
{
const
res
=
await
getSingleSanctionOverviewList
({
sanRecordId
:
sanRecordId
.
value
,
isOnlyCn
:
onlyChina
.
value
,
domainId
:
filterField
.
value
||
undefined
,
searchText
:
searchKeyword
.
value
||
undefined
,
searchType
:
searchType
.
value
});
isLoading
.
value
=
false
;
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
=>
({
...
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
);
...
...
@@ -272,811 +327,814 @@ 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
)
}
}
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
);
timelinePage
.
value
++
;
getSanctionBackground
(
true
);
};
const
router
=
useRouter
();
const
sanRecordId
=
ref
(
""
)
const
sanRecordId
=
ref
(
""
)
;
const
getUrlParams
=
()
=>
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
sanRecordId
.
value
=
urlParams
.
get
(
"id"
)
||
""
}
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
)
}
}
try
{
const
res
=
await
getSingleSanctionEntityCountry
({
sanRecordId
:
sanRecordId
.
value
,
sanTypeId
:
sanTypeId
.
value
||
1
// 实体清单固定1
});
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
:
()
=>
({})
}
data
:
{
type
:
Object
,
default
:
()
=>
({})
}
});
// 跳转到人物页
const
handleClick
=
()
=>
{
window
.
sessionStorage
.
setItem
(
"curTabName"
,
props
.
data
.
postPersonName
)
const
route
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
// type: props.data.type,
personId
:
props
.
data
.
postPersonId
}
});
window
.
open
(
route
.
href
,
"_blank"
);
window
.
sessionStorage
.
setItem
(
"curTabName"
,
props
.
data
.
postPersonName
);
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
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
(
false
)
const
filterField
=
ref
(
''
)
const
searchKeyword
=
ref
(
''
)
const
activeTab
=
ref
(
'add'
)
const
searchType
=
computed
(()
=>
activeTab
.
value
)
const
filterEntity
=
ref
(
""
);
const
onlyChina
=
ref
(
false
)
;
const
filterField
=
ref
(
""
);
const
searchKeyword
=
ref
(
""
);
const
activeTab
=
ref
(
"add"
);
const
searchType
=
computed
(()
=>
activeTab
.
value
)
;
// 监听筛选条件变化
watch
([
onlyChina
,
filterField
,
activeTab
],
()
=>
{
getSanctionOverviewList
()
})
getSanctionOverviewList
();
})
;
// 搜索框防抖
const
debouncedSearch
=
debounce
(()
=>
{
getSanctionOverviewList
()
},
500
)
getSanctionOverviewList
();
},
500
)
;
watch
(
searchKeyword
,
()
=>
{
debouncedSearch
()
})
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"
}
{
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
{
bg
:
'rgb(231, 243, 255)'
,
border
:
'rgb(186, 224, 255)'
,
text
:
'rgb(5, 95, 194)'
},
// Blue
{
bg
:
'rgb(255, 247, 230)'
,
border
:
'rgb(255, 213, 145)'
,
text
:
'rgb(250, 140, 22)'
},
// Orange
{
bg
:
'rgb(255, 241, 240)'
,
border
:
'rgb(255, 204, 199)'
,
text
:
'rgb(245, 34, 45)'
},
// Red
{
bg
:
'rgb(246, 255, 237)'
,
border
:
'rgb(183, 235, 143)'
,
text
:
'rgb(82, 196, 26)'
},
// Green
{
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
{
bg
:
"rgb(231, 243, 255)"
,
border
:
"rgb(186, 224, 255)"
,
text
:
"rgb(5, 95, 194)"
},
// Blue
{
bg
:
"rgb(255, 247, 230)"
,
border
:
"rgb(255, 213, 145)"
,
text
:
"rgb(250, 140, 22)"
},
// Orange
{
bg
:
"rgb(255, 241, 240)"
,
border
:
"rgb(255, 204, 199)"
,
text
:
"rgb(245, 34, 45)"
},
// Red
{
bg
:
"rgb(246, 255, 237)"
,
border
:
"rgb(183, 235, 143)"
,
text
:
"rgb(82, 196, 26)"
}
// Green
];
const
getTagStyle
=
(
tagName
)
=>
{
if
(
!
tagName
)
return
{};
let
hash
=
0
;
for
(
let
i
=
0
;
i
<
tagName
.
length
;
i
++
)
{
hash
=
tagName
.
charCodeAt
(
i
)
+
((
hash
<<
5
)
-
hash
);
}
const
index
=
Math
.
abs
(
hash
)
%
tagColors
.
length
;
const
color
=
tagColors
[
index
];
return
{
backgroundColor
:
color
.
bg
,
borderColor
:
color
.
border
,
color
:
color
.
text
};
}
const
getTagStyle
=
tagName
=>
{
if
(
!
tagName
)
return
{};
let
hash
=
0
;
for
(
let
i
=
0
;
i
<
tagName
.
length
;
i
++
)
{
hash
=
tagName
.
charCodeAt
(
i
)
+
((
hash
<<
5
)
-
hash
);
}
const
index
=
Math
.
abs
(
hash
)
%
tagColors
.
length
;
const
color
=
tagColors
[
index
];
return
{
backgroundColor
:
color
.
bg
,
borderColor
:
color
.
border
,
color
:
color
.
text
};
}
;
const
timelineData
=
ref
([]);
const
entityDistribution
=
ref
([
{
name
:
"中国"
,
count
:
24
,
width
:
"100%"
,
gradient
:
"linear-gradient(270deg, rgba(206,79,81,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"沙特阿拉伯"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(270deg, rgba(255,170,0,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"俄罗斯"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(270deg, rgba(255,170,0,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"中国香港"
,
count
:
1
,
width
:
"50%"
,
gradient
:
"linear-gradient(270deg, rgba(5,95,194,1) 0%, rgba(255,255,255,0) 100%)"
}
])
{
name
:
"中国"
,
count
:
24
,
width
:
"100%"
,
gradient
:
"linear-gradient(270deg, rgba(206,79,81,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"沙特阿拉伯"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(270deg, rgba(255,170,0,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"俄罗斯"
,
count
:
2
,
width
:
"60%"
,
gradient
:
"linear-gradient(270deg, rgba(255,170,0,1) 0%, rgba(255,255,255,0) 100%)"
},
{
name
:
"中国香港"
,
count
:
1
,
width
:
"50%"
,
gradient
:
"linear-gradient(270deg, rgba(5,95,194,1) 0%, rgba(255,255,255,0) 100%)"
}
])
;
const
sanTypeId
=
ref
(
""
);
onMounted
(()
=>
{
// 获取URL参数
getUrlParams
()
// 单次制裁-制裁概况-制裁实体国家分布
getEntityCountry
()
// 单次制裁-制裁概况-制裁背景
getSanctionBackground
()
// 单次制裁-制裁概况-制裁清单
getSanctionOverviewList
()
})
// 获取路由参数中的sanTypeId
sanTypeId
.
value
=
route
.
query
.
sanTypeId
||
"1"
;
// 获取URL参数
getUrlParams
();
// 单次制裁-制裁概况-制裁实体国家分布
getEntityCountry
();
// 单次制裁-制裁概况-制裁背景
getSanctionBackground
();
// 单次制裁-制裁概况-制裁清单
getSanctionOverviewList
();
});
</
script
>
<
style
scoped
lang=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
margin
:
0
;
padding
:
0
;
}
.sanctions-overview
{
width
:
1601px
;
margin
:
0
auto
;
padding-top
:
16px
;
padding-bottom
:
16px
;
.main
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
justify-content
:
space-between
;
.left
{
width
:
520px
;
// height: 1119px;
.left-top
{
width
:
100%
;
height
:
auto
;
// padding-bottom: 20px;
// border-radius: 10px;
// box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
// background-color: #fff;
margin-bottom
:
16px
;
.left-top-title
{
padding
:
22px
20px
22px
27px
;
width
:
100%
;
height
:
auto
;
border-bottom
:
1px
solid
rgb
(
234
,
236
,
238
);
display
:
flex
;
flex-direction
:
column
;
// justify-content: space-between;
gap
:
12px
;
.info-row
{
display
:
flex
;
align-items
:
center
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
.label
{
min-width
:
92px
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
700
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
text-align
:
left
;
}
.value
{
display
:
flex
;
align-items
:
center
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
400
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
overflow
:
hidden
;
&
.link
{
color
:
rgb
(
5
,
95
,
194
);
cursor
:
pointer
;
}
.icon
{
width
:
20px
;
height
:
20px
;
margin-right
:
8px
;
object-fit
:
contain
;
&
.avatar
{
border-radius
:
50%
;
width
:
24px
;
height
:
24px
;
}
}
&
.tags
{
gap
:
8px
;
flex-wrap
:
wrap
;
overflow
:
visible
;
.tag
{
padding
:
1px
8px
;
background
:
rgba
(
246
,
250
,
255
,
1
);
border-radius
:
4px
;
border
:
1px
solid
rgba
(
231
,
243
,
255
,
1
);
color
:
rgb
(
5
,
95
,
194
);
font-size
:
14px
;
line-height
:
20px
;
}
}
}
}
}
.left-top-content
{
width
:
100%
;
height
:
234px
;
padding
:
19px
29px
22px
27px
;
overflow
:
auto
;
.content-title
{
font-size
:
16px
;
font-weight
:
700
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-bottom
:
12px
;
text-align
:
left
;
}
.distribution-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
overflow
:
auto
;
.list-item
{
display
:
flex
;
align-items
:
center
;
height
:
24px
;
.flag
{
width
:
24px
;
height
:
24px
;
margin-right
:
16px
;
border-radius
:
50%
;
object-fit
:
cover
;
}
.country-name
{
width
:
96px
;
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-right
:
12px
;
text-align
:
left
;
}
.progress-bar-container
{
flex
:
1
;
height
:
8px
;
// background: #f0f2f5;
// border-radius: 4px;
// overflow: hidden;
margin-right
:
24px
;
display
:
flex
;
align-items
:
center
;
.progress-bar
{
height
:
100%
;
border-radius
:
4px
;
}
}
.count
{
// width: 40px;
text-align
:
right
;
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
&
.highlight
{
color
:
rgb
(
206
,
79
,
81
);
font-weight
:
700
;
}
}
}
}
}
}
.left-bottom
{
width
:
100%
;
height
:
521px
;
.left-bottom-content
{
padding
:
20px
25px
0
25px
;
height
:
calc
(
100%
-
45px
);
// 减去标题高度
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
{
display
:
flex
;
flex-direction
:
column
;
// margin-left: 12px;
position
:
relative
;
&
:
:
before
{
content
:
""
;
position
:
absolute
;
left
:
6px
;
top
:
12px
;
height
:
calc
(
100%
+
24px
);
width
:
1px
;
background-color
:
rgb
(
234
,
236
,
238
);
transform
:
translateX
(
-50%
);
}
&
:last-child::before
{
display
:
none
;
}
}
.date-row
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
3px
;
}
.dot
{
width
:
12px
;
height
:
12px
;
border-radius
:
50%
;
background-color
:
#fff
;
border
:
3px
solid
rgb
(
5
,
95
,
194
);
box-sizing
:
border-box
;
margin-right
:
13px
;
position
:
relative
;
z-index
:
1
;
}
.date
{
font-size
:
16px
;
font-weight
:
700
;
color
:
rgb
(
5
,
95
,
194
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
// margin-bottom: 3px;
}
.content
{
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
line-height
:
24px
;
margin-left
:
25px
;
text-align
:
left
;
font-family
:
"Microsoft YaHei"
;
}
.view-more
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
font-size
:
14px
;
font-weight
:
400
;
color
:
rgb
(
5
,
95
,
194
);
cursor
:
pointer
;
margin-top
:
24px
;
font-family
:
"Microsoft YaHei"
;
.icon-more
{
transform
:
rotate
(
90deg
);
margin-left
:
8px
;
}
}
}
}
}
.right
{
width
:
1064px
;
// height: auto;
// border-radius: 10px;
// box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
// background-color: #fff;
.right-title
{
width
:
100%
;
// height: 107px;
padding
:
4px
20px
20px
20px
;
box-sizing
:
border-box
;
.filter-row
{
display
:
flex
;
justify-content
:
right
;
align-items
:
center
;
// margin-bottom: 20px;
:deep
(
.el-input__inner
)
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
&
:
:
placeholder
{
color
:
rgb
(
95
,
101
,
108
);
}
}
:deep
(
.el-checkbox__label
)
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
}
.filter-right
{
display
:
flex
;
align-items
:
center
;
}
}
.stats-row
{
display
:
flex
;
margin-bottom
:
20px
;
justify-content
:
space-between
;
align-items
:
center
;
.tabs
{
display
:
flex
;
gap
:
8px
;
.tab-btn
{
padding
:
4px
8px
;
border
:
1px
solid
rgb
(
230
,
231
,
232
);
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
18px
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
400
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
&
.active
{
color
:
rgb
(
5
,
95
,
194
);
border-color
:
rgb
(
5
,
95
,
194
);
font-weight
:
700
;
}
}
}
.stats-info
{
display
:
flex
;
gap
:
24px
;
align-items
:
center
;
.stat-item
{
display
:
flex
;
align-items
:
center
;
font-size
:
18px
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-right
:
34px
;
.dot
{
width
:
8px
;
height
:
8px
;
border-radius
:
50%
;
margin-right
:
8px
;
&
.red
{
background-color
:
rgb
(
206
,
79
,
81
);
}
&
.green
{
background-color
:
rgb
(
33
,
129
,
57
);
}
}
.text
{
font-size
:
18px
;
font-weight
:
700
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
59
,
65
,
75
);
.num
{
font-weight
:
700
;
&
.red
{
color
:
rgb
(
206
,
79
,
81
);
}
&
.green
{
color
:
rgb
(
33
,
129
,
57
);
}
}
}
}
}
}
}
.right-content
{
padding
:
0
20px
17px
20px
;
.sanction-group-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
.sanction-group
{
border
:
1px
solid
rgb
(
234
,
236
,
238
);
border-radius
:
8px
;
overflow
:
hidden
;
:deep
(
.el-table__header-wrapper
)
{
th
{
background-color
:
var
(
--
color-primary-100
)
!
important
;
height
:
48px
;
padding
:
0
;
color
:
#fff
;
font-weight
:
700
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
border-bottom
:
none
;
}
th
:first-child
{
border-top-left-radius
:
8px
;
}
th
:last-child
{
border-top-right-radius
:
8px
;
}
}
:deep
(
.el-table__body-wrapper
)
{
tr
:nth-child
(
odd
)
td
{
background-color
:
rgba
(
248
,
249
,
250
,
1
);
}
tr
:nth-child
(
even
)
td
{
background-color
:
#fff
;
}
td
{
height
:
48px
;
padding
:
0
;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
95
,
101
,
108
);
line-height
:
24px
;
}
}
.name-cell
{
display
:
flex
;
align-items
:
center
;
.dot
{
width
:
8px
;
height
:
8px
;
border-radius
:
50%
;
background-color
:
rgb
(
206
,
79
,
81
);
margin-right
:
16px
;
flex-shrink
:
0
;
}
.company-icon
{
width
:
24px
;
height
:
24px
;
margin-right
:
12px
;
border-radius
:
50%
;
flex-shrink
:
0
;
}
.company-name
{
font-size
:
16px
;
font-weight
:
700
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
color
:
rgba
(
59
,
65
,
75
,
1
);
cursor
:
pointer
;
}
}
.tag
{
padding
:
1px
8px
;
border-radius
:
4px
;
font-size
:
14px
;
display
:
inline-block
;
border
:
1px
solid
transparent
;
}
.subsidiary-link
{
cursor
:
pointer
;
font-size
:
14px
;
color
:
rgb
(
59
,
65
,
75
);
.blue-text
{
color
:
rgb
(
5
,
95
,
194
);
}
}
.reason-box
{
padding
:
12px
19px
14px
39px
;
background-color
:
#fff
;
border-top
:
1px
solid
rgb
(
234
,
236
,
238
);
font-size
:
16px
;
color
:
rgb
(
59
,
65
,
75
);
line-height
:
28px
;
font-family
:
"Microsoft YaHei"
;
text-align
:
left
;
}
}
}
}
}
}
width
:
1601px
;
margin
:
0
auto
;
padding-top
:
16px
;
padding-bottom
:
16px
;
.main
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
justify-content
:
space-between
;
.left
{
width
:
520px
;
// height: 1119px;
.left-top
{
width
:
100%
;
height
:
auto
;
// padding-bottom: 20px;
// border-radius: 10px;
// box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
// background-color: #fff;
margin-bottom
:
16px
;
.left-top-title
{
padding
:
22px
20px
22px
27px
;
width
:
100%
;
height
:
auto
;
border-bottom
:
1px
solid
rgb
(
234
,
236
,
238
);
display
:
flex
;
flex-direction
:
column
;
// justify-content: space-between;
gap
:
12px
;
.info-row
{
display
:
flex
;
align-items
:
center
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
.label
{
min-width
:
92px
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
700
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
text-align
:
left
;
}
.value
{
display
:
flex
;
align-items
:
center
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
400
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
overflow
:
hidden
;
&
.link
{
color
:
rgb
(
5
,
95
,
194
);
cursor
:
pointer
;
}
.icon
{
width
:
20px
;
height
:
20px
;
margin-right
:
8px
;
object-fit
:
contain
;
&
.avatar
{
border-radius
:
50%
;
width
:
24px
;
height
:
24px
;
}
}
&
.tags
{
gap
:
8px
;
flex-wrap
:
wrap
;
overflow
:
visible
;
.tag
{
padding
:
1px
8px
;
background
:
rgba
(
246
,
250
,
255
,
1
);
border-radius
:
4px
;
border
:
1px
solid
rgba
(
231
,
243
,
255
,
1
);
color
:
rgb
(
5
,
95
,
194
);
font-size
:
14px
;
line-height
:
20px
;
}
}
}
}
}
.left-top-content
{
width
:
100%
;
height
:
234px
;
padding
:
19px
29px
22px
27px
;
overflow
:
auto
;
.content-title
{
font-size
:
16px
;
font-weight
:
700
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-bottom
:
12px
;
text-align
:
left
;
}
.distribution-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
overflow
:
auto
;
.list-item
{
display
:
flex
;
align-items
:
center
;
height
:
24px
;
.flag
{
width
:
24px
;
height
:
24px
;
margin-right
:
16px
;
border-radius
:
50%
;
object-fit
:
cover
;
}
.country-name
{
width
:
96px
;
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-right
:
12px
;
text-align
:
left
;
}
.progress-bar-container
{
flex
:
1
;
height
:
8px
;
// background: #f0f2f5;
// border-radius: 4px;
// overflow: hidden;
margin-right
:
24px
;
display
:
flex
;
align-items
:
center
;
.progress-bar
{
height
:
100%
;
border-radius
:
4px
;
}
}
.count
{
// width: 40px;
text-align
:
right
;
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
&
.highlight
{
color
:
rgb
(
206
,
79
,
81
);
font-weight
:
700
;
}
}
}
}
}
}
.left-bottom
{
width
:
100%
;
height
:
521px
;
.left-bottom-content
{
padding
:
20px
25px
0
25px
;
height
:
calc
(
100%
-
45px
);
// 减去标题高度
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
{
display
:
flex
;
flex-direction
:
column
;
// margin-left: 12px;
position
:
relative
;
&
:
:
before
{
content
:
""
;
position
:
absolute
;
left
:
6px
;
top
:
12px
;
height
:
calc
(
100%
+
24px
);
width
:
1px
;
background-color
:
rgb
(
234
,
236
,
238
);
transform
:
translateX
(
-50%
);
}
&
:last-child::before
{
display
:
none
;
}
}
.date-row
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
3px
;
}
.dot
{
width
:
12px
;
height
:
12px
;
border-radius
:
50%
;
background-color
:
#fff
;
border
:
3px
solid
rgb
(
5
,
95
,
194
);
box-sizing
:
border-box
;
margin-right
:
13px
;
position
:
relative
;
z-index
:
1
;
}
.date
{
font-size
:
16px
;
font-weight
:
700
;
color
:
rgb
(
5
,
95
,
194
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
// margin-bottom: 3px;
}
.content
{
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
line-height
:
24px
;
margin-left
:
25px
;
text-align
:
left
;
font-family
:
"Microsoft YaHei"
;
}
.view-more
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
font-size
:
14px
;
font-weight
:
400
;
color
:
rgb
(
5
,
95
,
194
);
cursor
:
pointer
;
margin-top
:
24px
;
font-family
:
"Microsoft YaHei"
;
.icon-more
{
transform
:
rotate
(
90deg
);
margin-left
:
8px
;
}
}
}
}
}
.right
{
width
:
1064px
;
// height: auto;
// border-radius: 10px;
// box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
// background-color: #fff;
.right-title
{
width
:
100%
;
// height: 107px;
padding
:
4px
20px
20px
20px
;
box-sizing
:
border-box
;
.filter-row
{
display
:
flex
;
justify-content
:
right
;
align-items
:
center
;
// margin-bottom: 20px;
:deep
(
.el-input__inner
)
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
&
:
:
placeholder
{
color
:
rgb
(
95
,
101
,
108
);
}
}
:deep
(
.el-checkbox__label
)
{
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
color
:
rgb
(
95
,
101
,
108
);
}
.filter-right
{
display
:
flex
;
align-items
:
center
;
}
}
.stats-row
{
display
:
flex
;
margin-bottom
:
20px
;
justify-content
:
space-between
;
align-items
:
center
;
.tabs
{
display
:
flex
;
gap
:
8px
;
.tab-btn
{
padding
:
4px
8px
;
border
:
1px
solid
rgb
(
230
,
231
,
232
);
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
18px
;
color
:
rgb
(
59
,
65
,
75
);
font-weight
:
400
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
&
.active
{
color
:
rgb
(
5
,
95
,
194
);
border-color
:
rgb
(
5
,
95
,
194
);
font-weight
:
700
;
}
}
}
.stats-info
{
display
:
flex
;
gap
:
24px
;
align-items
:
center
;
.stat-item
{
display
:
flex
;
align-items
:
center
;
font-size
:
18px
;
color
:
rgb
(
59
,
65
,
75
);
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
margin-right
:
34px
;
.dot
{
width
:
8px
;
height
:
8px
;
border-radius
:
50%
;
margin-right
:
8px
;
&
.red
{
background-color
:
rgb
(
206
,
79
,
81
);
}
&
.green
{
background-color
:
rgb
(
33
,
129
,
57
);
}
}
.text
{
font-size
:
18px
;
font-weight
:
700
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
59
,
65
,
75
);
.num
{
font-weight
:
700
;
&
.red
{
color
:
rgb
(
206
,
79
,
81
);
}
&
.green
{
color
:
rgb
(
33
,
129
,
57
);
}
}
}
}
}
}
}
.right-content
{
padding
:
0
20px
17px
20px
;
.sanction-group-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
.sanction-group
{
border
:
1px
solid
rgb
(
234
,
236
,
238
);
border-radius
:
8px
;
overflow
:
hidden
;
:deep
(
.el-table__header-wrapper
)
{
th
{
background-color
:
var
(
--
color-primary-100
)
!
important
;
height
:
48px
;
padding
:
0
;
color
:
#fff
;
font-weight
:
700
;
font-size
:
16px
;
font-family
:
"Microsoft YaHei"
;
border-bottom
:
none
;
}
th
:first-child
{
border-top-left-radius
:
8px
;
}
th
:last-child
{
border-top-right-radius
:
8px
;
}
}
:deep
(
.el-table__body-wrapper
)
{
tr
:nth-child
(
odd
)
td
{
background-color
:
rgba
(
248
,
249
,
250
,
1
);
}
tr
:nth-child
(
even
)
td
{
background-color
:
#fff
;
}
td
{
height
:
48px
;
padding
:
0
;
font-size
:
16px
;
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
color
:
rgb
(
95
,
101
,
108
);
line-height
:
24px
;
}
}
.name-cell
{
display
:
flex
;
align-items
:
center
;
.dot
{
width
:
8px
;
height
:
8px
;
border-radius
:
50%
;
background-color
:
rgb
(
206
,
79
,
81
);
margin-right
:
16px
;
flex-shrink
:
0
;
}
.company-icon
{
width
:
24px
;
height
:
24px
;
margin-right
:
12px
;
border-radius
:
50%
;
flex-shrink
:
0
;
}
.company-name
{
font-size
:
16px
;
font-weight
:
700
;
line-height
:
24px
;
font-family
:
"Microsoft YaHei"
;
color
:
rgba
(
59
,
65
,
75
,
1
);
cursor
:
pointer
;
}
}
.tag
{
padding
:
1px
8px
;
border-radius
:
4px
;
font-size
:
14px
;
display
:
inline-block
;
border
:
1px
solid
transparent
;
}
.subsidiary-link
{
cursor
:
pointer
;
font-size
:
14px
;
color
:
rgb
(
59
,
65
,
75
);
.blue-text
{
color
:
rgb
(
5
,
95
,
194
);
}
}
.reason-box
{
padding
:
12px
19px
14px
39px
;
background-color
:
#fff
;
border-top
:
1px
solid
rgb
(
234
,
236
,
238
);
font-size
:
16px
;
color
:
rgb
(
59
,
65
,
75
);
line-height
:
28px
;
font-family
:
"Microsoft YaHei"
;
text-align
:
left
;
}
}
}
}
}
}
}
.title-com
{
width
:
100%
;
height
:
56px
;
display
:
flex
;
align-items
:
center
;
padding
:
14px
12px
16px
0
;
.box
{
width
:
8px
;
height
:
20px
;
background-color
:
rgb
(
5
,
95
,
194
);
border-bottom-right-radius
:
4px
;
border-top-right-radius
:
4px
;
margin-right
:
14px
;
}
.text
{
font-size
:
20px
;
font-weight
:
700
;
font-family
:
"Microsoft YaHei"
;
line-height
:
26px
;
color
:
rgb
(
5
,
95
,
194
);
}
.btn
{
width
:
60px
;
height
:
28px
;
margin-left
:
auto
;
img
{
width
:
28px
;
height
:
28px
;
cursor
:
pointer
;
}
img
:first-child
{
margin-right
:
4px
;
}
}
width
:
100%
;
height
:
56px
;
display
:
flex
;
align-items
:
center
;
padding
:
14px
12px
16px
0
;
.box
{
width
:
8px
;
height
:
20px
;
background-color
:
rgb
(
5
,
95
,
194
);
border-bottom-right-radius
:
4px
;
border-top-right-radius
:
4px
;
margin-right
:
14px
;
}
.text
{
font-size
:
20px
;
font-weight
:
700
;
font-family
:
"Microsoft YaHei"
;
line-height
:
26px
;
color
:
rgb
(
5
,
95
,
194
);
}
.btn
{
width
:
60px
;
height
:
28px
;
margin-left
:
auto
;
img
{
width
:
28px
;
height
:
28px
;
cursor
:
pointer
;
}
img
:first-child
{
margin-right
:
4px
;
}
}
}
</
style
>
\ No newline at end of file
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论