Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
a37f484f
提交
a37f484f
authored
4月 22, 2026
作者:
朱政
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'pre' into zz-dev
上级
ee169074
4a4ab848
流水线
#603
已通过 于阶段
in 1 分 30 秒
变更
18
流水线
1
隐藏空白字符变更
内嵌
并排
正在显示
18 个修改的文件
包含
1125 行增加
和
469 行删除
+1125
-469
index.js
src/router/index.js
+1
-1
goToPage.js
src/utils/goToPage.js
+1
-1
setChart.js
src/utils/setChart.js
+0
-1
index.vue
src/views/dataLibrary/company/index.vue
+23
-11
index.vue
src/views/dataLibrary/decree/index.vue
+23
-11
index.vue
src/views/dataLibrary/thinkTank/index.vue
+27
-15
ChartAiAnalysis.vue
src/views/exportControl/components/ChartAiAnalysis.vue
+128
-0
title.vue
src/views/exportControl/components/title.vue
+2
-2
index.vue
src/views/exportControl/index.vue
+357
-183
index.vue
...ol/v2.0SingleSanction/components/dataStatistics/index.vue
+234
-130
index.vue
...v2.0SingleSanction/components/sanctionsOverview/index.vue
+71
-34
index.vue
...ews/exportControl/v2.0SingleSanction/originPage/index.vue
+4
-4
title.vue
src/views/finance/components/title.vue
+2
-2
index.vue
src/views/finance/index.vue
+169
-31
index.vue
...nce/singleSanction/components/sanctionsOverview/index.vue
+78
-36
index.vue
src/views/finance/singleSanction/index.vue
+1
-1
index.vue
src/views/finance/singleSanction/originPage/index.vue
+4
-4
index.vue
src/views/thinkTank/ThinkTankDetail/index.vue
+0
-2
没有找到文件。
src/router/index.js
浏览文件 @
a37f484f
...
...
@@ -147,7 +147,7 @@ const router = createRouter({
// 2)登录成功回跳带 ?token=:先 setToken 并同步 bootId,再去掉 URL 中的 token(须先于 clearTokenIfNewDevBoot,避免误清刚写入的登录态)
// 3)已有本地 token:正常走前端路由
router
.
beforeEach
((
to
,
from
,
next
)
=>
{
//
【新增】
在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 这能防止旧页面的数据回来覆盖新页面,也能减少服务器压力
cancelAllRequests
();
if
(
import
.
meta
.
env
.
DEV
)
{
...
...
src/utils/goToPage.js
浏览文件 @
a37f484f
...
...
@@ -380,7 +380,7 @@ export const goToSearch = (tabName, areaName, billSearchType) => {
}
// 跳转数据资源库
// 跳转数据资源库
-国家法案
export
const
goToDataCountryBill
=
(
selectParam
)
=>
{
// const codeParam = new URLSearchParams(selectParam)
// JSON -> Base64
...
...
src/utils/setChart.js
浏览文件 @
a37f484f
...
...
@@ -151,7 +151,6 @@ const setChart = (option, chartId, allowClick, selectParam) => {
// 容器可能受布局/异步渲染影响,强制一次 resize 保证 canvas 与容器一致
setTimeout
(()
=>
{
chart
.
resize
();
},
0
);
return
chart
;
};
...
...
src/views/dataLibrary/company/index.vue
浏览文件 @
a37f484f
...
...
@@ -3,7 +3,7 @@
<div
class=
"header-box"
>
<div
class=
"header-top"
>
<SelectBox
:placeholder-name=
"areaPlaceHolder"
select-title=
"科技领域"
:select-list=
"areaList"
:select-name=
"selectedArea"
@
update:select-text=
"handleSelectArea"
/>
:select-name=
"selectedArea"
:is-multiple=
"true"
@
update:select-text=
"handleSelectArea"
/>
<SelectBox
:placeholder-name=
"DatePlaceHolder"
select-title=
"成立时间"
:select-list=
"dateList"
:select-name=
"selectedDate"
:custom-time=
"customTime"
@
update:select-text=
"handleSelectDate"
@
update:custom-time=
"handleCustomDate"
/>
...
...
@@ -388,11 +388,11 @@ const handleChangeTime = value => {
// 激活的标签列表
const
activeTagList
=
computed
(()
=>
{
const
arr
=
[]
if
(
selectedArea
.
value
&&
selectedArea
.
value
!==
'全部领域'
)
{
if
(
selectedArea
.
value
&&
selectedArea
.
value
[
0
]
!==
'全部领域'
)
{
arr
.
push
(
{
tag
:
'科技领域'
,
name
:
selectedArea
.
value
name
:
selectedArea
.
value
.
join
(
'、'
)
}
)
}
...
...
@@ -439,7 +439,7 @@ const activeTagList = computed(() => {
const
handleCloseCurTag
=
(
tag
,
index
)
=>
{
switch
(
tag
.
tag
)
{
case
'科技领域'
:
selectedArea
.
value
=
'全部领域'
selectedArea
.
value
=
[
'全部领域'
]
break
case
'成立时间'
:
selectedDate
.
value
=
''
...
...
@@ -493,7 +493,7 @@ const operationList = ref([
// 科技领域
const
areaPlaceHolder
=
ref
(
'请选择领域'
)
const
selectedArea
=
ref
(
'全部领域'
)
const
selectedArea
=
ref
(
[
'全部领域'
]
)
const
areaList
=
ref
([
{
name
:
'全部领域'
,
...
...
@@ -561,7 +561,11 @@ const areaList = ref([
}
,
])
const
handleSelectArea
=
(
value
)
=>
{
selectedArea
.
value
=
value
if
(
value
[
value
.
length
-
1
]
===
'全部领域'
)
{
selectedArea
.
value
=
[
'全部领域'
]
return
}
selectedArea
.
value
=
value
.
length
>
1
&&
value
.
includes
(
'全部领域'
)
?
value
.
filter
(
item
=>
item
!==
'全部领域'
)
:
value
;
}
// 提出时间
...
...
@@ -678,7 +682,7 @@ const isSanctioned = ref(false)
// 清空条件
const
handleClear
=
()
=>
{
selectedArea
.
value
=
'全部领域'
selectedArea
.
value
=
[
'全部领域'
]
selectedDate
.
value
=
''
customTime
.
value
=
[]
selectedCountry
.
value
=
'全部国家地区'
...
...
@@ -739,7 +743,7 @@ const fetchTableData = async () => {
page
:
currentPage
.
value
,
size
:
pageSize
.
value
,
type
:
5
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单【制裁记录】 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
'全部领域'
?
null
:
[
selectedArea
.
value
]
,
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
]
?
customTime
.
value
[
0
]
:
null
,
proposedDateEnd
:
customTime
.
value
[
1
]
?
customTime
.
value
[
1
]
:
null
,
countryId
:
selectedCountry
.
value
===
'全部国家地区'
?
null
:
selectedCountry
.
value
,
...
...
@@ -852,7 +856,7 @@ const fetchAllData = async () => {
page
:
1
,
size
:
9999
,
type
:
5
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单【制裁记录】 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
'全部领域'
?
null
:
[
selectedArea
.
value
]
,
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
]
?
customTime
.
value
[
0
]
:
null
,
proposedDateEnd
:
customTime
.
value
[
1
]
?
customTime
.
value
[
1
]
:
null
,
countryId
:
selectedCountry
.
value
===
'全部国家地区'
?
null
:
selectedCountry
.
value
,
...
...
@@ -1008,7 +1012,11 @@ const handleDownloadCurChartData = () => {
const
initParam
=
()
=>
{
const
hasQuery
=
Object
.
keys
(
route
.
query
).
length
>
0
;
if
(
hasQuery
)
{
selectedArea
.
value
=
route
.
query
.
domains
?
route
.
query
.
domains
:
'全部领域'
if
(
route
.
query
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
route
.
query
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
route
.
query
.
domains
?
[
route
.
query
.
domains
]
:
[
'全部领域'
]
}
if
(
route
.
query
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
route
.
query
.
selectedDate
))
&&
JSON
.
parse
(
route
.
query
.
selectedDate
).
length
)
{
selectedDate
.
value
=
'自定义'
...
...
@@ -1027,7 +1035,11 @@ const initParam = () => {
}
}
else
{
const
savedQuery
=
JSON
.
parse
(
sessionStorage
.
getItem
(
'dataCompanyRouteQuery'
)
||
'{
}
'
);
selectedArea
.
value
=
savedQuery
.
domains
?
savedQuery
.
domains
:
'全部领域'
if
(
savedQuery
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
savedQuery
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
savedQuery
.
domains
?
[
savedQuery
.
domains
]
:
[
'全部领域'
]
}
if
(
savedQuery
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
savedQuery
.
selectedDate
))
&&
JSON
.
parse
(
savedQuery
.
selectedDate
).
length
)
{
selectedDate
.
value
=
'自定义'
customTime
.
value
=
JSON
.
parse
(
savedQuery
.
selectedDate
)
...
...
src/views/dataLibrary/decree/index.vue
浏览文件 @
a37f484f
...
...
@@ -3,7 +3,7 @@
<div
class=
"header-box"
>
<div
class=
"header-top"
>
<SelectBox
:placeholder-name=
"areaPlaceHolder"
select-title=
"科技领域"
:select-list=
"areaList"
:select-name=
"selectedArea"
@
update:select-text=
"handleSelectArea"
/>
:select-name=
"selectedArea"
:is-multiple=
"true"
@
update:select-text=
"handleSelectArea"
/>
<SelectBox
:placeholder-name=
"DatePlaceHolder"
select-title=
"发布时间"
:select-list=
"dateList"
:select-name=
"selectedDate"
:custom-time=
"customTime"
@
update:select-text=
"handleSelectDate"
@
update:custom-time=
"handleCustomDate"
/>
...
...
@@ -331,11 +331,11 @@ const handleChangeTime = value => {
// 激活的标签列表
const
activeTagList
=
computed
(()
=>
{
const
arr
=
[]
if
(
selectedArea
.
value
&&
selectedArea
.
value
!==
'全部领域'
)
{
if
(
selectedArea
.
value
&&
selectedArea
.
value
[
0
]
!==
'全部领域'
)
{
arr
.
push
(
{
tag
:
'科技领域'
,
name
:
selectedArea
.
value
name
:
selectedArea
.
value
.
join
(
'、'
)
}
)
}
...
...
@@ -394,7 +394,7 @@ const activeTagList = computed(() => {
const
handleCloseCurTag
=
(
tag
,
index
)
=>
{
switch
(
tag
.
tag
)
{
case
'科技领域'
:
selectedArea
.
value
=
'全部领域'
selectedArea
.
value
=
[
'全部领域'
]
break
case
'发布时间'
:
selectedDate
.
value
=
''
...
...
@@ -493,7 +493,7 @@ const operationList = ref([
// 科技领域
const
areaPlaceHolder
=
ref
(
'请选择领域'
)
const
selectedArea
=
ref
(
'全部领域'
)
const
selectedArea
=
ref
(
[
'全部领域'
]
)
const
areaList
=
ref
([
{
name
:
'全部领域'
,
...
...
@@ -561,7 +561,11 @@ const areaList = ref([
}
,
])
const
handleSelectArea
=
(
value
)
=>
{
selectedArea
.
value
=
value
if
(
value
[
value
.
length
-
1
]
===
'全部领域'
)
{
selectedArea
.
value
=
[
'全部领域'
]
return
}
selectedArea
.
value
=
value
.
length
>
1
&&
value
.
includes
(
'全部领域'
)
?
value
.
filter
(
item
=>
item
!==
'全部领域'
)
:
value
;
}
// 提出时间
...
...
@@ -668,7 +672,7 @@ const isInvolveTechnology = ref(false)
// 清空条件
const
handleClear
=
()
=>
{
selectedArea
.
value
=
'全部领域'
selectedArea
.
value
=
[
'全部领域'
]
selectedDate
.
value
=
''
customTime
.
value
=
[]
selectedIns
.
value
=
'全部机构'
...
...
@@ -731,7 +735,7 @@ const fetchTableData = async () => {
size
:
pageSize
.
value
,
// keyword: '',
type
:
2
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单【制裁记录】 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
'全部领域'
?
null
:
[
selectedArea
.
value
]
,
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
]
?
customTime
.
value
[
0
]
:
null
,
proposedDateEnd
:
customTime
.
value
[
1
]
?
customTime
.
value
[
1
]
:
null
,
organizationName
:
selectedIns
.
value
===
'全部机构'
?
null
:
selectedIns
.
value
,
...
...
@@ -816,7 +820,7 @@ const fetchAllData = async () => {
size
:
9999
,
// keyword: '',
type
:
2
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单【制裁记录】 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
'全部领域'
?
null
:
[
selectedArea
.
value
]
,
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
],
proposedDateEnd
:
customTime
.
value
[
1
],
organizationName
:
selectedIns
.
value
===
'全部机构'
?
null
:
selectedIns
.
value
,
...
...
@@ -973,7 +977,11 @@ const handleDownloadCurChartData = () => {
const
initParam
=
()
=>
{
const
hasQuery
=
Object
.
keys
(
route
.
query
).
length
>
0
;
if
(
hasQuery
)
{
selectedArea
.
value
=
route
.
query
.
domains
?
route
.
query
.
domains
:
'全部领域'
if
(
route
.
query
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
route
.
query
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
route
.
query
.
domains
?
[
route
.
query
.
domains
]
:
[
'全部领域'
]
}
if
(
route
.
query
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
route
.
query
.
selectedDate
))
&&
JSON
.
parse
(
route
.
query
.
selectedDate
).
length
)
{
selectedDate
.
value
=
'自定义'
...
...
@@ -994,7 +1002,11 @@ const initParam = () => {
}
}
else
{
const
savedQuery
=
JSON
.
parse
(
sessionStorage
.
getItem
(
'decreeRouteQuery'
)
||
'{
}
'
);
selectedArea
.
value
=
savedQuery
.
domains
?
savedQuery
.
domains
:
'全部领域'
if
(
savedQuery
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
savedQuery
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
savedQuery
.
domains
?
[
savedQuery
.
domains
]
:
[
'全部领域'
]
}
if
(
savedQuery
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
savedQuery
.
selectedDate
))
&&
JSON
.
parse
(
savedQuery
.
selectedDate
).
length
)
{
selectedDate
.
value
=
'自定义'
customTime
.
value
=
JSON
.
parse
(
savedQuery
.
selectedDate
)
...
...
src/views/dataLibrary/thinkTank/index.vue
浏览文件 @
a37f484f
...
...
@@ -2,8 +2,8 @@
<div
class=
"thinktank-wrapper"
>
<div
class=
"header-box"
>
<div
class=
"header-top"
>
<SelectBox
:placeholder-name=
"areaPlaceHolder"
select-title=
"科技领域"
:select-list=
"areaList"
:select-name=
"selectedArea"
@
update:select-text=
"handleSelectArea"
/>
<SelectBox
:placeholder-name=
"areaPlaceHolder"
select-title=
"科技领域"
:select-list=
"areaList"
:select-name=
"selectedArea"
:is-multiple=
"true"
@
update:select-text=
"handleSelectArea"
/>
<SelectBox
:placeholder-name=
"DatePlaceHolder"
select-title=
"发布时间"
:select-list=
"dateList"
:select-name=
"selectedDate"
:custom-time=
"customTime"
@
update:select-text=
"handleSelectDate"
@
update:custom-time=
"handleCustomDate"
/>
...
...
@@ -322,10 +322,10 @@ const handleChangeTime = value => {
// 激活的标签列表
const
activeTagList
=
computed
(()
=>
{
const
arr
=
[];
if
(
selectedArea
.
value
&&
selectedArea
.
value
!==
"全部领域"
)
{
if
(
selectedArea
.
value
&&
selectedArea
.
value
[
0
]
!==
"全部领域"
)
{
arr
.
push
({
tag
:
"科技领域"
,
name
:
selectedArea
.
value
name
:
selectedArea
.
value
.
join
(
'、'
)
}
);
}
if
(
selectedDate
.
value
===
"自定义"
)
{
...
...
@@ -362,7 +362,7 @@ const activeTagList = computed(() => {
const
handleCloseCurTag
=
(
tag
,
index
)
=>
{
switch
(
tag
.
tag
)
{
case
"科技领域"
:
selectedArea
.
value
=
"全部领域"
;
selectedArea
.
value
=
[
'全部领域'
]
break
;
case
"发布时间"
:
selectedDate
.
value
=
""
;
...
...
@@ -450,7 +450,7 @@ const operationList = ref([
// 科技领域
const
areaPlaceHolder
=
ref
(
"请选择领域"
);
const
selectedArea
=
ref
(
"全部领域"
);
const
selectedArea
=
ref
(
[
'全部领域'
]
);
const
areaList
=
ref
([
{
name
:
"全部领域"
,
...
...
@@ -517,9 +517,13 @@ const areaList = ref([
id
:
"其他"
}
]);
const
handleSelectArea
=
value
=>
{
selectedArea
.
value
=
value
;
}
;
const
handleSelectArea
=
(
value
)
=>
{
if
(
value
[
value
.
length
-
1
]
===
'全部领域'
)
{
selectedArea
.
value
=
[
'全部领域'
]
return
}
selectedArea
.
value
=
value
.
length
>
1
&&
value
.
includes
(
'全部领域'
)
?
value
.
filter
(
item
=>
item
!==
'全部领域'
)
:
value
;
}
// 提出时间
const
DatePlaceHolder
=
ref
(
"请选择时间"
);
...
...
@@ -585,7 +589,7 @@ const handleGetThinkTankList = async () => {
}
;
}
);
thinkTankList
.
value
=
[...
thinkTankList
.
value
,
...
arr
];
}
catch
(
error
)
{
}
catch
(
error
)
{
}
finally
{
loading
.
value
=
false
...
...
@@ -616,7 +620,7 @@ const isInvolveCn = ref(false);
// 清空条件
const
handleClear
=
()
=>
{
selectedArea
.
value
=
"全部领域"
;
selectedArea
.
value
=
[
'全部领域'
]
selectedDate
.
value
=
""
;
customTime
.
value
=
[];
selectedThinkTank
.
value
=
"全部智库"
;
...
...
@@ -676,7 +680,7 @@ const fetchTableData = async () => {
size
:
pageSize
.
value
,
// keyword: '',
type
:
4
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
"全部领域"
?
null
:
[
selectedArea
.
value
],
// 领域
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
]
?
customTime
.
value
[
0
]
:
null
,
// 开始日期
proposedDateEnd
:
customTime
.
value
[
1
]
?
customTime
.
value
[
1
]
:
null
,
// 结束日期
organizationName
:
selectedThinkTank
.
value
===
"全部智库"
?
null
:
selectedThinkTank
.
value
,
// 智库名称
...
...
@@ -758,7 +762,7 @@ const fetchAllData = async () => {
size
:
9999
,
// keyword: '',
type
:
4
,
// type 1= 法案 2= 政令 3 =智库 4=智库报告 5=实体清单【制裁记录】 6= 人物 7= 机构 8=新闻 9= 社媒
domains
:
selectedArea
.
value
===
"全部领域"
?
null
:
[
selectedArea
.
value
]
,
domains
:
selectedArea
.
value
[
0
]
===
'全部领域'
?
null
:
selectedArea
.
value
,
proposedDateStart
:
customTime
.
value
[
0
],
proposedDateEnd
:
customTime
.
value
[
1
],
organizationName
:
selectedThinkTank
.
value
===
"全部智库"
?
null
:
selectedThinkTank
.
value
,
...
...
@@ -908,7 +912,11 @@ const handleDownloadCurChartData = () => {
const
initParam
=
()
=>
{
const
hasQuery
=
Object
.
keys
(
route
.
query
).
length
>
0
;
if
(
hasQuery
)
{
selectedArea
.
value
=
route
.
query
.
domains
?
route
.
query
.
domains
:
"全部领域"
;
if
(
route
.
query
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
route
.
query
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
route
.
query
.
domains
?
[
route
.
query
.
domains
]
:
[
'全部领域'
]
}
if
(
route
.
query
.
selectedDate
&&
...
...
@@ -931,7 +939,11 @@ const initParam = () => {
}
}
else
{
const
savedQuery
=
JSON
.
parse
(
sessionStorage
.
getItem
(
"thinktankRouteQuery"
)
||
"{
}
"
);
selectedArea
.
value
=
savedQuery
.
domains
?
savedQuery
.
domains
:
"全部领域"
;
if
(
savedQuery
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
savedQuery
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
savedQuery
.
domains
?
[
savedQuery
.
domains
]
:
[
'全部领域'
]
}
if
(
savedQuery
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
savedQuery
.
selectedDate
))
&&
...
...
src/views/exportControl/components/ChartAiAnalysis.vue
0 → 100644
浏览文件 @
a37f484f
<
template
>
<div
class=
"chart-ai-analysis"
@
mouseenter=
"handleMouseEnter"
@
mouseleave=
"handleMouseLeave"
>
<!-- 触发按钮 -->
<div
class=
"ai-button-wrapper"
>
<AiButton
/>
</div>
<!-- AI 内容面板 -->
<transition
name=
"ai-fade"
>
<div
v-if=
"isVisible"
class=
"ai-pane-wrapper"
>
<div
class=
"ai-pane-content"
>
<!-- 1. Loading 状态 -->
<div
v-if=
"hookInstance.loading"
class=
"ai-loading-text"
>
智能总结生成中...
</div>
<!-- 2. 错误状态 -->
<div
v-else-if=
"hookInstance.error"
class=
"ai-error-text"
>
{{
hookInstance
.
error
}}
</div>
<!-- 3. 成功状态 -->
<div
v-else-if=
"hookInstance.interpretation"
class=
"ai-success-content"
>
<AiPane
:aiContent=
"hookInstance.interpretation"
/>
</div>
<!-- 4. 初始空状态 (可选,防止闪烁) -->
<div
v-else
class=
"ai-empty-text"
>
暂无数据
</div>
</div>
</div>
</transition>
</div>
</
template
>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
AiButton
from
"@/components/base/Ai/AiButton/index.vue"
;
import
AiPane
from
"@/components/base/Ai/AiPane/index.vue"
;
const
props
=
defineProps
({
// 接收由 useChartInterpretation() 创建的实例对象
hookInstance
:
{
type
:
Object
,
required
:
true
},
// 图表数据 Payload,用于首次请求
payload
:
{
type
:
Object
,
required
:
true
}
});
const
isVisible
=
ref
(
false
);
const
hasRequested
=
ref
(
false
);
const
handleMouseEnter
=
()
=>
{
isVisible
.
value
=
true
;
// 如果还没有请求过数据,则发起请求
if
(
!
hasRequested
.
value
&&
!
props
.
hookInstance
.
loading
)
{
hasRequested
.
value
=
true
;
// 调用 Hook 中的 interpret 方法
props
.
hookInstance
.
interpret
(
props
.
payload
);
}
};
const
handleMouseLeave
=
()
=>
{
isVisible
.
value
=
false
;
};
</
script
>
<
style
lang=
"scss"
scoped
>
.chart-ai-analysis
{
position
:
absolute
;
right
:
0px
;
bottom
:
15px
;
z-index
:
999
;
width
:
auto
;
height
:
auto
;
.ai-button-wrapper
{
display
:
flex
;
justify-content
:
flex-end
;
}
.ai-pane-wrapper
{
position
:
absolute
;
bottom
:
0
;
right
:
0
;
width
:
100%
;
// 确保面板出现在按钮上方或覆盖区域,根据原有 CSS 调整
// 原有 .ai-pane:hover 逻辑是宽度变宽,这里我们直接显示完整面板
background
:
#fff
;
border-radius
:
4px
;
box-shadow
:
0
-2px
12px
rgba
(
0
,
0
,
0
,
0
.1
);
padding
:
10px
;
box-sizing
:
border-box
;
.ai-pane-content
{
min-height
:
40px
;
.ai-loading-text
,
.ai-error-text
,
.ai-empty-text
{
font-size
:
14px
;
color
:
var
(
--
text-primary-50-color
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
40px
;
}
.ai-error-text
{
color
:
var
(
--
color-red-100
);
}
}
}
}
// 复用原有的淡入淡出动画
.ai-fade-enter-active
,
.ai-fade-leave-active
{
transition
:
opacity
0
.3s
ease
;
}
.ai-fade-enter-from
,
.ai-fade-leave-to
{
opacity
:
0
;
}
</
style
>
src/views/exportControl/components/title.vue
浏览文件 @
a37f484f
...
...
@@ -43,8 +43,8 @@ defineProps({
.title-text
{
color
:
rgba
(
10
,
18
,
30
,
1
);
font-size
:
32px
;
font-family
:
$base-font-family
;
font-weight
:
bold
;
font-family
:
"Microsoft YaHei"
;
font-weight
:
700
;
margin-left
:
20px
;
white-space
:
nowrap
;
}
...
...
src/views/exportControl/index.vue
浏览文件 @
a37f484f
...
...
@@ -3,12 +3,25 @@
<div
class=
"home-main"
ref=
"homeMainRef"
>
<div
class=
"home-top-bg"
></div>
<div
class=
"home-main-header"
>
<SearchContainer
style=
"margin-bottom: 0; margin-top: 48px; height: fit-content"
v-if=
"homeMainRef"
placeholder=
"搜索出口管制"
:containerRef=
"homeMainRef"
areaName=
"实体清单"
/>
<SearchContainer
style=
"margin-bottom: 0; margin-top: 48px; height: fit-content"
v-if=
"homeMainRef"
placeholder=
"搜索出口管制"
:containerRef=
"homeMainRef"
areaName=
"实体清单"
/>
<div
class=
"home-main-header-footer-info"
>
<InfoCard
v-for=
"(item, index) in infoList"
:key=
"item.id"
:title=
"item.nameZh"
:subtitle=
"item.nameAbbr"
:description=
"item.description"
:quantity=
"item.postCount"
:unit=
"item.unit"
:color=
"infoListColor[index]"
@
click=
"handleToEntityListNoId(item)"
/>
<InfoCard
v-for=
"(item, index) in infoList"
:key=
"item.id"
:title=
"item.nameZh"
:subtitle=
"item.nameAbbr"
:description=
"item.description"
:quantity=
"item.postCount"
:unit=
"item.unit"
:color=
"infoListColor[index]"
@
click=
"handleToEntityListNoId(item)"
/>
</div>
</div>
...
...
@@ -71,10 +84,18 @@
<div
class=
"box1-bottom-sanTypeId"
v-if=
"item.sanEntities?.length"
>
<div
class=
"box1-bottom-title"
>
· 涉及主要实体:
</div>
<div
class=
"box1-bottom-content"
>
<div
class=
"box1-bottom-content-item"
v-for=
"(ett, index) in item.sanEntities"
:key=
"index"
@
click=
"handleEntityClick(ett)"
>
<el-image
v-if=
"ett.img"
class=
"box1-bottom-content-item-img"
:src=
"ett.img"
alt=
""
></el-image>
<div
class=
"box1-bottom-content-item"
v-for=
"(ett, index) in item.sanEntities"
:key=
"index"
@
click=
"handleEntityClick(ett)"
>
<el-image
v-if=
"ett.img"
class=
"box1-bottom-content-item-img"
:src=
"ett.img"
alt=
""
></el-image>
<div
v-else
class=
"box1-bottom-content-item-imgUndefined"
>
{{
(
ett
.
orgName
||
ett
.
orgNameZh
)?.
match
(
...
...
@@ -91,8 +112,12 @@
<div
class=
"box1-bottom-sanTypeId"
v-if=
"item.sanItems?.length > 0"
>
<div
class=
"box1-bottom-title"
>
· 涉及管制物项:
</div>
<div
class=
"box1-bottom-content__wx"
>
<div
class=
"box1-bottom-content__wx-item"
v-for=
"(ett, index) in item.sanItems"
:key=
"index"
@
click=
"handleWxClick(item)"
>
<div
class=
"box1-bottom-content__wx-item"
v-for=
"(ett, index) in item.sanItems"
:key=
"index"
@
click=
"handleWxClick(item)"
>
<div
class=
"box1-bottom-content__wx-item-id"
>
{{
ett
.
id
}}
</div>
...
...
@@ -124,86 +149,35 @@
</custom-container>
</el-col>
<el-col
:span=
"8"
style=
"padding-right: 0px"
>
<!-- <custom-container
titleType="danger"
title="风险信号"
:headerNum="warningList.length"
:titleIcon="dangerIcon"
height="450px"
>
<template #default>
<div class="box2-main">
<div
class="box2-main-item"
v-for="(item, index) in warningList"
:key="index"
@click="handleToRiskSignalDetail"
>
<div
class="item-left"
:class="{
itemLeftStatus1: item.status === '一般风险',
itemLeftStatus2: item.status === '重大风险'
}"
>
{{ item.status }}
</div>
<div class="item-right">
<div class="text">
<CommonPrompt :content="item.title">
{{ item.title }}
</CommonPrompt>
</div>
<div class="time">{{ item.time }}</div>
</div>
</div>
<div class="box2-footer" @click="handleToMoreRiskSignal">
<div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" />
</div>
<div class="text">{{ "查看更多" }}</div>
</div>
</div>
</template>
</custom-container> -->
<RiskSignal
:list=
"warningList"
@
item-click=
"handleToRiskSignalDetail"
@
more-click=
"handleToMoreRiskSignal"
riskLevel=
"signalLevel"
postDate=
"signalTime"
name=
"signalTitle"
/>
<RiskSignal
:list=
"warningList"
@
item-click=
"handleToRiskSignalDetail"
@
more-click=
"handleToMoreRiskSignal"
riskLevel=
"signalLevel"
postDate=
"signalTime"
name=
"signalTitle"
/>
</el-col>
</el-row>
<el-row
:gutter=
"16"
style=
"width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px"
>
<CustomTitle
id=
"position2"
title=
"资讯要闻"
/>
</el-row>
<!-- <el-col :span="12">
<custom-container title="新闻资讯" :titleIcon="newsIcon" height="450px">
<template #header-right>
<el-button type="primary" link @click="handleToMoreNews">
{{ "更多 +" }}
</el-button>
</template>
<template #default>
<div class="news-list">
<NewsList :list-data="newsList" @item-click="item => handleNewsInfoClick(item)" />
</div>
</template>
</custom-container>
</el-col> -->
<div
class=
"center-center"
>
<NewsList
:newsList=
"newsList"
@
item-click=
"handleNewsInfoClick"
@
more-click=
"handleToMoreNews"
content=
"newsContent"
/>
<MessageBubble
:messageList=
"socialMediaList"
@
person-click=
"handlePerClick"
imageUrl=
"avatar"
@
more-click=
"handleToSocialDetail"
/>
<!-- <custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px">
<template #default>
<div class="dialog-list">
<MessageBubble v-for="(item, index) in socialMediaList" @click="handlePerClick(item)"
@info-click="handleMediaClick(item)" :key="index" :avatar="item.avatar" :name="item.name"
:time="item.time" :source="item.source" :content="item.content" />
</div>
</template>
</custom-container> -->
<NewsList
:newsList=
"newsList"
@
item-click=
"handleNewsInfoClick"
@
more-click=
"handleToMoreNews"
content=
"newsContent"
/>
<MessageBubble
:messageList=
"socialMediaList"
@
person-click=
"handlePerClick"
imageUrl=
"avatar"
@
more-click=
"handleToSocialDetail"
/>
</div>
<el-row
:gutter=
"16"
style=
"width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px"
>
...
...
@@ -214,24 +188,31 @@
<div
class=
"box3"
>
<div
class=
"box3-content"
>
<div
class=
"box3-content-title"
>
实体清单发布频次统计
</div>
<el-table
:data=
"entityListReleaseFreq"
stripe
style=
"width: 100%"
@
row-click=
"handleEntityRowClick"
>
<el-table
:data=
"entityListReleaseFreq"
stripe
style=
"width: 100%"
@
row-click=
"handleEntityRowClick"
>
<el-table-column
prop=
"year"
label=
"年份"
width=
"200"
/>
<el-table-column
label=
"发布次数"
width=
"300"
>
<template
#
default=
"scope"
>
<div
style=
"display: flex; align-items: center"
>
<span
style=
"margin-right: 10px; width: 40px"
>
{{
scope
.
row
.
num
}}
次
</span>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"重点领域"
width=
"220"
align=
"center"
>
<
template
#
default=
"scope"
>
<div
style=
"display: flex; justify-content: center; align-items: center; gap: 5px"
>
<div
style=
"display: flex; justify-content: center; align-items: center; gap: 5px"
>
<AreaTag
v-for=
"tag in scope.row.tags"
:key=
"tag"
:tagName=
"tag"
/>
<!--
<el-tag
v-for=
"tag in scope.row.tags"
:key=
"tag"
:type=
"getTagType(tag)"
>
{{
tag
}}
</el-tag>
-->
</div>
</
template
>
</el-table-column>
...
...
@@ -240,34 +221,44 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布实体清单的频次,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"entityListReleaseFreqChart.interpretation"
/>
<AiButton
@
mouseenter=
"handleShowAiPane('entityListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.entityListReleaseFreqChart"
:aiContent=
"overviewAiContent.entityListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('entityListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
>
<div
class=
"box3-content-title"
>
商业管制清单发布频次统计
</div>
<el-table
:data=
"commerceControlListReleaseFreq"
stripe
style=
"width: 100%"
@
row-click=
"handleCommercialRowClick"
>
<el-table
:data=
"commerceControlListReleaseFreq"
stripe
style=
"width: 100%"
@
row-click=
"handleCommercialRowClick"
>
<el-table-column
prop=
"year"
label=
"年份"
width=
"200"
/>
<el-table-column
label=
"发布次数"
width=
"300"
>
<
template
#
default=
"scope"
>
<div
style=
"display: flex; align-items: center"
>
<span
style=
"margin-right: 10px; width: 40px"
>
{{
scope
.
row
.
num
}}
次
</span>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"重点领域"
width=
"220"
align=
"center"
>
<
template
#
default=
"scope"
>
<div
style=
"display: flex; justify-content: center; align-items: center; gap: 5px"
>
<div
style=
"display: flex; justify-content: center; align-items: center; gap: 5px"
>
<AreaTag
v-for=
"tag in scope.row.tags"
:key=
"tag"
:tagName=
"tag"
/>
<!--
<el-tag
v-for=
"tag in scope.row.tags"
:key=
"tag"
:type=
"getTagType(tag)"
>
{{
tag
}}
</el-tag>
-->
</div>
</
template
>
</el-table-column>
...
...
@@ -276,13 +267,15 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布商业管制清单的频次,数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"commerceControlListReleaseFreqChart.interpretation"
/>
<AiButton
@
mouseenter=
"handleShowAiPane('commerceControlListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.commerceControlListReleaseFreqChart"
:aiContent=
"overviewAiContent.commerceControlListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('commerceControlListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
style=
"display: none"
>
...
...
@@ -293,8 +286,11 @@
<
template
#
default=
"scope"
>
<div
style=
"display: flex; align-items: center"
>
<span
style=
"margin-right: 10px; width: 40px"
>
{{
scope
.
row
.
num
}}
次
</span>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
<el-progress
:percentage=
"scope.row.percent * 100"
:show-text=
"false"
:status=
"getStatus(scope.row.percent)"
/>
</div>
</
template
>
</el-table-column>
...
...
@@ -322,17 +318,21 @@
<el-checkbox
v-model=
"domainChecked"
label=
"50%规则"
size=
"large"
/>
</
template
>
<
template
#
default
>
<EChart
:option=
"radarOption"
autoresize
:style=
"
{ height: '420px' }"
@chart-click="handleRadarChartClick" />
<EChart
:option=
"radarOption"
autoresize
:style=
"
{ height: '420px' }"
@chart-click="handleRadarChartClick"
/>
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入实体清单的中国实体领域分布情况,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"
radarChart.interpretation
"
/>
<AiButton
@
mouseenter=
"handleShowAiPane('radarChart')"
/>
<AiPane
:aiContent=
"
overviewAiContent.radarChart"
@
mouseleave=
"handleHideAiPane('radarChart')
"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -341,24 +341,37 @@
<custom-container
title=
"制裁清单数量增长趋势"
:titleIcon=
"qushiIcon"
height=
"540px"
>
<
template
#
header-right
>
<div
style=
"display: flex; align-items: center; gap: 16px"
>
<el-checkbox
v-if=
"selectedEntityId != '13'"
v-model=
"trendChecked"
label=
"50%规则"
size=
"large"
/>
<el-checkbox
v-if=
"selectedEntityId != '13'"
v-model=
"trendChecked"
label=
"50%规则"
size=
"large"
/>
<el-select
v-model=
"selectedEntityId"
placeholder=
"请选择清单类型"
style=
"width: 160px"
>
<el-option
v-for=
"item in infoList"
:key=
"item.id"
:label=
"item.nameZh"
:value=
"item.id"
/>
</el-select>
</div>
</
template
>
<
template
#
default
>
<EChart
:option=
"trendOption"
autoresize
:style=
"
{ height: '420px' }"
@chart-click="handleMultiBarChartClick" />
<EChart
:option=
"trendOption"
autoresize
:style=
"
{ height: '420px' }"
@chart-click="handleMultiBarChartClick"
/>
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入实体清单的中国实体数量变化趋势,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"trendChart.interpretation"
/>
<AiButton
@
mouseenter=
"handleShowAiPane('trendChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.trendChart"
:aiContent=
"overviewAiContent.trendChart"
@
mouseleave=
"handleHideAiPane('trendChart')"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -368,9 +381,13 @@
<el-row
:gutter=
"16"
style=
"width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px"
>
<CustomTitle
id=
"position4"
title=
"资源库"
style=
"margin-top: 0px"
/>
<div
class=
"resource-tabs"
>
<div
v-for=
"tab in resourceTabs"
:key=
"tab.value"
class=
"resource-tab-item"
<div
v-for=
"tab in resourceTabs"
:key=
"tab.value"
class=
"resource-tab-item"
:class=
"{ active: activeResourceTab == tab.value, disabled: tab.disabled }"
@
click=
"handleResourceTabClick(tab)"
>
@
click=
"handleResourceTabClick(tab)"
>
{{ tab.label }}
</div>
</div>
...
...
@@ -383,15 +400,25 @@
<div
class=
"box4-item"
v-for=
"(item, idx) in sanctionProcessList"
:key=
"item.title"
>
<div
class=
"box4-item-left"
>
<el-image
:src=
"dotIcon"
alt=
"图片"
class=
"box4-item-left-icon"
/>
<div
class=
"box4-item-left-line"
v-if=
"idx + 1 != sanctionProcessList.length"
></div>
<div
class=
"box4-item-left-line"
v-if=
"idx + 1 != sanctionProcessList.length"
></div>
</div>
<div
class=
"box4-item-right"
>
<div
class=
"box4-item-right-header"
@
click=
"handleSanc(item)"
>
<span
class=
"box4-item-right-header-title"
>
{{
item
.
postDate
}}
—
{{
item
.
title
}}
</span>
<span
class=
"box4-item-right-header-title"
>
{{
item
.
postDate
}}
—
{{
item
.
title
}}
</span
>
<span
class=
"box4-item-right-header-desc"
>
{{
item
.
desc
}}
</span>
</div>
<el-tooltip
effect=
"dark"
:content=
"item.content"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<el-tooltip
effect=
"dark"
:content=
"item.content"
popper-class=
"common-prompt-popper"
placement=
"top"
:show-after=
"500"
>
<div
class=
"box4-item-right-content"
>
{{
item
.
content
}}
</div>
...
...
@@ -399,8 +426,12 @@
</div>
</div>
</div>
<div
class=
"box4-footer"
:style=
"
{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }">
<el-button
type=
"primary"
link
@
click=
"handleGetMore"
>
查看更多
<div
class=
"box4-footer"
:style=
"
{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }"
>
<el-button
type=
"primary"
link
@
click=
"handleGetMore"
>
查看更多
<el-icon>
<DArrowRight
/>
</el-icon>
...
...
@@ -417,13 +448,24 @@
</
template
>
<
template
#
default
>
<div
class=
"box5"
>
<el-table
:data=
"entitiesList"
class=
"sanction-table"
stripe
empty-text=
"暂无数据"
height=
"700px"
header-row-class-name=
"table-header"
row-class-name=
"table-row"
>
<el-table
:data=
"entitiesList"
class=
"sanction-table"
stripe
empty-text=
"暂无数据"
height=
"700px"
header-row-class-name=
"table-header"
row-class-name=
"table-row"
>
<el-table-column
prop=
"name"
label=
"实体名称"
min-width=
"200"
>
<template
#
default=
"scope"
>
<div
class=
"tableName"
@
click=
"handleCompClick(scope.row)"
>
<el-image
v-if=
"scope.row.img"
class=
"box1-bottom-content-item-img"
:src=
"scope.row.img"
alt=
""
></el-image>
<el-image
v-if=
"scope.row.img"
class=
"box1-bottom-content-item-img"
:src=
"scope.row.img"
alt=
""
></el-image>
<div
v-else
class=
"box1-bottom-content-item-imgUndefined"
>
{{
(
scope
.
row
.
name
||
scope
.
row
.
enName
)?.
match
(
...
...
@@ -455,35 +497,22 @@
</
template
>
</el-table-column>
<!-- <el-table-column prop="strength" label="制裁强度" width="120" align="center">
<template #default="scope">
<div class="sanction-strength">
<div :class="['strength-bar', `strength-${scope.row.strength}`]"></div>
<span>{{ strengthLabels[scope.row.strength] }}</span>
</div>
</template>
</el-table-column> -->
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="140" align="right">
<template #default="scope">
<span
:class="['revenue-cell', scope.row.revenue === '无营收数据' ? 'no-revenue' : '']"
>
{{ scope.row.revenue }}
</span>
</template>
</el-table-column> -->
<el-table-column
prop=
"revenue"
label=
"50%规则子企业"
width=
"280"
align=
"right"
>
<
template
#
default=
"scope"
>
<div
class=
"num-item"
v-if=
"scope.row.ruleOrgCount > 0"
>
<div
class=
"name-item"
:class=
"[
'revenue-cell',
scope.row.revenue === '无营收数据' ? 'no-revenue' : ''
]"
>
<div
class=
"name-item"
:class=
"[
'revenue-cell',
scope.row.revenue === '无营收数据' ? 'no-revenue' : ''
]"
>
{{
scope
.
row
.
ruleOrgList
[
0
].
orgName
}}
...等
</div>
<div
style=
"width: 50px; color: #409eff; cursor: pointer"
@
click=
"handleOrgClick(scope.row)"
>
<div
style=
"width: 50px; color: #409eff; cursor: pointer"
@
click=
"handleOrgClick(scope.row)"
>
{{
scope
.
row
.
ruleOrgCount
}}
家>
</div>
</div>
...
...
@@ -495,8 +524,15 @@
<!-- <div class="pagination-info">
第{{ currentPage }}页,共{{ totalPages }}页
</div> -->
<el-pagination
v-model:current-page=
"currentPage"
:page-size=
"pageSize"
:total=
"total"
:pager-count=
"5"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChange"
/>
<el-pagination
v-model:current-page=
"currentPage"
:page-size=
"pageSize"
:total=
"total"
:pager-count=
"5"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChange"
/>
</div>
</div>
</template>
...
...
@@ -562,8 +598,14 @@
</div>
<div
class=
"right-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>
</div>
</div>
...
...
@@ -576,8 +618,12 @@
</
template
>
</el-row>
</div>
<RuleSubsidiaryDialog
v-model=
"dialogVisible"
:company-name=
"currentRuleCompany"
:total-count=
"currentRuleCount"
:data-list=
"currentOrgList"
/>
<RuleSubsidiaryDialog
v-model=
"dialogVisible"
:company-name=
"currentRuleCompany"
:total-count=
"currentRuleCount"
:data-list=
"currentOrgList"
/>
</div>
<el-dialog
v-model=
"mediaVisible"
title=
"社交媒体信息"
width=
"500"
:before-close=
"handleMediaClose"
>
<div
class=
"dialog-content"
>
...
...
@@ -590,32 +636,34 @@
</div>
</
template
>
</el-dialog>
<RiskSignalOverviewDetailDialog
v-model=
"isRiskOverviewDetailOpen"
:row=
"riskOverviewDetailRow"
name-field=
"signalTitle"
post-date-field=
"signalTime"
risk-level-field=
"signalLevel"
/>
<RiskSignalOverviewDetailDialog
v-model=
"isRiskOverviewDetailOpen"
:row=
"riskOverviewDetailRow"
name-field=
"signalTitle"
post-date-field=
"signalTime"
risk-level-field=
"signalLevel"
/>
</template>
<
script
setup
>
//这是一个备注
import
NewsList
from
"@/components/base/newsList/index.vue"
;
import
RiskSignal
from
"@/components/base/riskSignal/index.vue"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
RiskSignalOverviewDetailDialog
from
"@/components/base/RiskSignalOverviewDetailDialog/index.vue"
;
import
{
onMounted
,
ref
,
computed
,
reactive
,
shallowRef
,
watch
,
nextTick
}
from
"vue"
;
import
{
useContainerScroll
}
from
"@/hooks/useScrollShow"
;
const
homeMainRef
=
ref
(
null
);
const
{
isShow
}
=
useContainerScroll
(
homeMainRef
);
import
*
as
echarts
from
"echarts"
;
import
setChart
from
"@/utils/setChart"
;
import
{
ElMessage
,
ElMessageBox
}
from
"element-plus"
;
import
listPage
from
"./v2.0CommercialControlList/components/sanctionsOverview/components/listPage/index.vue"
;
import
{
DArrowRight
,
Warning
,
Search
,
Message
}
from
"@element-plus/icons-vue"
;
import
EChart
from
"@/components/Chart/index.vue"
;
import
tipsIcon
from
"./assets/icons/info-icon.png"
;
import
AiButton
from
"@/components/base/Ai/AiButton/index.vue"
;
import
AiPane
from
"@/components/base/Ai/AiPane/index.vue"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
import
{
useChartInterpretation
}
from
"@/views/exportControl/utils/common"
;
const
sanctionCountChart
=
useChartInterpretation
();
import
{
TAGTYPE
}
from
"@/public/constant"
;
import
{
useGotoCompanyPages
}
from
"@/router/modules/company"
;
import
{
useGotoNewsDetail
}
from
"@/router/modules/news"
;
...
...
@@ -886,11 +934,12 @@ onMounted(async () => {
tags
:
item
.
domain
};
});
entityListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreq
.
value
});
entityListReleaseFreqChartData
.
value
=
entityListReleaseFreq
.
value
;
// entityListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布实体清单的频次",
// data: entityListReleaseFreq.value
// });
commerceControlListReleaseFreq
.
value
=
_
.
map
(
cclList1
,
item
=>
{
return
{
year
:
item
.
year
,
...
...
@@ -899,11 +948,12 @@ onMounted(async () => {
tags
:
item
.
domain
};
});
commerceControlListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreq
.
value
});
commerceControlListReleaseFreqChartData
.
value
=
commerceControlListReleaseFreq
.
value
;
// commerceControlListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布商业管制清单的频次",
// data: commerceControlListReleaseFreq.value
// });
// 获取趋势图数据
await
fetchTrendData
();
...
...
@@ -944,13 +994,136 @@ const fetchTrendData = async () => {
});
if
(
res
&&
res
[
0
]
&&
res
[
0
].
yearDomainCount
)
{
trendOption
.
value
=
processYearDomainCountData
(
res
[
0
].
yearDomainCount
);
trendChart
.
interpret
({
type
:
"柱状图"
,
name
:
"制裁清单数量增长趋势"
,
data
:
res
[
0
].
yearDomainCount
});
trendChartData
.
value
=
res
[
0
].
yearDomainCount
;
// trendChart.interpret({ type: "柱状图", name: "制裁清单数量增长趋势", data: res[0].yearDomainCount });
}
}
catch
(
error
)
{
console
.
error
(
"获取趋势图数据失败:"
,
error
);
}
};
const
requestAiPaneContent
=
async
key
=>
{
if
(
!
key
||
aiPaneLoading
.
value
[
key
]
||
aiPaneFetched
.
value
[
key
])
return
;
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
true
};
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成中..."
};
try
{
const
payload
=
buildAiChartPayload
(
key
);
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
onChunk
:
chunk
=>
{
const
current
=
overviewAiContent
.
value
[
key
];
const
base
=
current
===
"智能总结生成中..."
?
""
:
current
;
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
base
+
chunk
};
}
}
);
const
list
=
res
?.
data
;
const
first
=
Array
.
isArray
(
list
)
?
list
[
0
]
:
null
;
const
interpretation
=
first
?.
解读
||
first
?.[
"解读"
];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if
(
interpretation
)
{
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
interpretation
};
}
aiPaneFetched
.
value
=
{
...
aiPaneFetched
.
value
,
[
key
]:
true
};
}
catch
(
error
)
{
console
.
error
(
"获取图表解读失败"
,
error
);
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成失败"
};
}
finally
{
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
false
};
}
};
const
trendChartData
=
ref
([]);
const
radarChartData
=
ref
([]);
const
entityListReleaseFreqChartData
=
ref
([]);
const
commerceControlListReleaseFreqChartData
=
ref
([]);
const
aiPaneVisible
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
overviewAiContent
=
ref
({
trendChart
:
"智能总结生成中..."
,
radarChart
:
"智能总结生成中..."
,
entityListReleaseFreqChart
:
"智能总结生成中..."
,
commerceControlListReleaseFreqChart
:
"智能总结生成中..."
});
const
aiPaneFetched
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
aiPaneLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
chartLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
buildAiChartPayload
=
key
=>
{
if
(
key
===
"trendChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"制裁清单数量增长趋势"
,
data
:
trendChartData
.
value
};
}
if
(
key
===
"radarChart"
)
{
return
{
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
radarChartData
.
value
};
}
if
(
key
===
"entityListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreqChartData
.
value
};
}
if
(
key
===
"commerceControlListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreqChartData
.
value
};
}
return
{
type
:
""
,
name
:
""
,
data
:
[]
};
};
const
handleShowAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
true
};
requestAiPaneContent
(
key
);
};
const
handleHideAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
false
};
};
watch
(
()
=>
[
trendChecked
.
value
,
selectedEntityId
.
value
],
()
=>
{
...
...
@@ -965,6 +1138,7 @@ const processYearDomainCountData = yearDomainCountData => {
// 提取所有领域名称
const
allDomains
=
[...
new
Set
(
yearDomainCountData
.
flatMap
(
item
=>
item
.
domainCountInfo
.
map
(
domain
=>
domain
.
name
)))];
console
.
log
(
"不同领域的数据 =>"
,
allDomains
);
// 构造 getMultipleBarChart_m 所需的数据结构
const
chartData
=
{
...
...
@@ -991,7 +1165,7 @@ const processYearDomainCountData = yearDomainCountData => {
};
})
};
console
.
log
(
"不同领域的数据 chartData"
,
chartData
);
// 使用 getMultipleBarChart_m 生成图表配置
return
getMultipleBarChart_m
(
chartData
);
};
...
...
@@ -1218,7 +1392,8 @@ const fetchRadarData = async checked => {
}
};
});
radarChart
.
interpret
({
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
data
});
radarChartData
.
value
=
data
;
// radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
}
}
catch
(
error
)
{
console
.
error
(
"获取雷达图数据失败:"
,
error
);
...
...
@@ -1318,7 +1493,7 @@ const fetchSanctionList = async () => {
});
totalAll
.
value
=
res
.
totalElements
;
}
}
catch
(
error
)
{
}
}
catch
(
error
)
{}
};
const
handlePageChangeAll
=
val
=>
{
...
...
@@ -1612,7 +1787,7 @@ const handleGetHylyList = async () => {
hylymc
:
"全部分类"
};
categoryList
.
value
=
[
obj
,
...
categoryList
.
value
];
}
catch
(
error
)
{
}
}
catch
(
error
)
{}
};
const
chart1Data
=
ref
({
...
...
@@ -2218,7 +2393,6 @@ const handleMediaClick = item => {
}
.box3-content
{
// flex: 1;
.el-progress--line
{
width
:
82px
;
...
...
src/views/exportControl/v2.0SingleSanction/components/dataStatistics/index.vue
浏览文件 @
a37f484f
...
...
@@ -26,7 +26,7 @@
</div>
</div>
</div>
<div
class=
"nav-item"
@
click=
"handleToDataLibrary2"
>
<div
class=
"nav-item"
@
click=
"handleToDataLibrary2"
>
<div
class=
"item-position"
></div>
<div
class=
"content"
>
<div
class=
"info"
>
...
...
@@ -54,107 +54,94 @@
</div>
</div>
</div>
-->
<EChart
:option=
"domainChartOption"
autoresize
:style=
"
{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary3" />
<EChart
:option=
"domainChartOption"
autoresize
:style=
"
{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary3"
/>
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入本次实体清单的中国实体领域分布情况,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"domainChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"domainChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('domainChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.domainChart"
:aiContent=
"overviewAiContent.domainChart"
@
mouseleave=
"handleHideAiPane('domainChart')"
/>
</div>
</AnalysisBox>
</div>
<div
class=
"main-item"
>
<!--
<div
class=
"title-com"
>
<div
class=
"box"
></div>
<div
class=
"text"
>
制裁实体类型分布情况
</div>
<div
class=
"right-group"
>
<div
class=
"btn"
>
<img
src=
"../../assets/数据库按钮.png"
alt=
""
/>
<img
src=
"../../assets/下载按钮.png"
alt=
""
/>
<img
src=
"../../assets/收藏按钮.png"
alt=
""
/>
</div>
</div>
</div>
<div
class=
"echarts"
ref=
"typeChartRef"
></div>
<div
class=
"bottom"
>
<div
class=
"ai"
>
<div
class=
"left"
>
<img
:src=
"ai"
alt=
""
class=
"icon1"
/>
<div
class=
"text"
>
我国被制裁实体以企业、科研院所和高校为主。
</div>
</div>
<div
class=
"right"
>
<img
:src=
"right"
alt=
""
class=
"icon2"
/>
</div>
</div>
</div>
-->
<AnalysisBox
title=
"制裁实体类型分布情况"
>
<!--
<div
class=
"echarts"
ref=
"typeChartRef"
></div>
<div
class=
"bottom"
>
<div
class=
"ai"
>
<div
class=
"left"
>
<img
:src=
"ai"
alt=
""
class=
"icon1"
/>
<div
class=
"text"
>
我国被制裁实体以企业、科研院所和高校为主。
</div>
</div>
<div
class=
"right"
>
<img
:src=
"right"
alt=
""
class=
"icon2"
/>
</div>
</div>
</div>
-->
<EChart
:option=
"typeChartOption"
autoresize
:style=
"
{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary4" />
<EChart
:option=
"typeChartOption"
autoresize
:style=
"
{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary4"
/>
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入本次实体清单的中国实体类型分布情况,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"typeChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"typeChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('typeChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.typeChart"
:aiContent=
"overviewAiContent.typeChart"
@
mouseleave=
"handleHideAiPane('typeChart')"
/>
</div>
</AnalysisBox>
</div>
<div
class=
"main-item"
>
<AnalysisBox
title=
"制裁实体国家地区分布情况"
>
<div
class=
"country-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in countryDistribution"
:key=
"index"
@
click=
"handleToDataLibrary5(item)"
>
<div
class=
"list-item"
v-for=
"(item, index) in countryDistribution"
:key=
"index"
@
click=
"handleToDataLibrary5(item)"
>
<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>
</div>
<!--
<div
class=
"bottom"
>
<div
class=
"ai"
>
<div
class=
"left"
>
<img
:src=
"ai"
alt=
""
class=
"icon1"
/>
<div
class=
"text"
>
美国对中国的制裁近年来呈现显著增长趋势。
</div>
</div>
<div
class=
"right"
>
<img
:src=
"right"
alt=
""
class=
"icon2"
/>
</div>
</div>
</div>
-->
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入本次实体清单的实体国家地区分布情况,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"countryDistributionChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"countryDistributionChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('countryDistributionChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.countryDistributionChart"
:aiContent=
"overviewAiContent.countryDistributionChart"
@
mouseleave=
"handleHideAiPane('countryDistributionChart')"
/>
</div>
</AnalysisBox>
</div>
...
...
@@ -163,40 +150,42 @@
<div
class=
"map-wrapper"
>
<div
class=
"map-chart"
ref=
"mapChartRef"
></div>
<div
class=
"rank-list"
>
<div
class=
"rank-item"
v-for=
"(item, index) in regionDistribution"
:key=
"index"
@
click=
"handleToDataLibrary6(item)"
>
<div
class=
"rank-item"
v-for=
"(item, index) in regionDistribution"
:key=
"index"
@
click=
"handleToDataLibrary6(item)"
>
<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>
</div>
</div>
<!--
<div
class=
"bottom"
>
<div
class=
"ai"
>
<div
class=
"left"
>
<img
:src=
"ai"
alt=
""
class=
"icon1"
/>
<div
class=
"text"
>
我国被制裁实体多分布于沿海经济活跃省份。
</div>
</div>
<div
class=
"right"
>
<img
:src=
"right"
alt=
""
class=
"icon2"
/>
</div>
</div>
</div>
-->
<div
class=
"data-origin-box"
>
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入本次实体清单的中国实体各省分布情况,
数据来源:美国商务部官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国商务部官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"regionDistributionChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"regionDistributionChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('regionDistributionChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.regionDistributionChart"
:aiContent=
"overviewAiContent.regionDistributionChart"
@
mouseleave=
"handleHideAiPane('regionDistributionChart')"
/>
</div>
</AnalysisBox>
</div>
...
...
@@ -223,9 +212,9 @@ import {
getSingleSanctionEntityCountryCount
,
getSingleSanctionEntityRegionCount
}
from
"@/api/exportControlV2.0"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
{
useChartInterpretation
}
from
"@/views/exportControl/utils/common"
;
const
sanctionCountChart
=
useChartInterpretation
();
const
domainChart
=
useChartInterpretation
();
const
typeChart
=
useChartInterpretation
();
const
countryDistributionChart
=
useChartInterpretation
();
...
...
@@ -248,8 +237,9 @@ const getRegionData = async () => {
if
(
res
.
code
===
200
)
{
regionDistribution
.
value
=
res
.
data
||
[];
maxRegionCount
.
value
=
Math
.
max
(...
regionDistribution
.
value
.
map
(
item
=>
item
.
count
),
0
);
regionDistributionChartData
.
value
=
res
.
data
||
[];
initMapChart
();
regionDistributionChart
.
interpret
({
type
:
"柱状图"
,
name
:
"进入本次实体清单的中国实体各省分布情况"
,
data
:
res
.
data
});
//
regionDistributionChart.interpret({ type: "柱状图", name: "进入本次实体清单的中国实体各省分布情况", data: res.data });
}
}
catch
(
error
)
{
console
.
log
(
error
);
...
...
@@ -289,11 +279,12 @@ const getCountryCount = async () => {
gradient
};
});
countryDistributionChart
.
interpret
({
type
:
"柱状图"
,
name
:
"进入本次实体清单的实体国家地区分布情况"
,
data
:
res
.
data
});
countryDistributionChartData
.
value
=
res
.
data
||
[];
// countryDistributionChart.interpret({
// type: "柱状图",
// name: "进入本次实体清单的实体国家地区分布情况",
// data: res.data
// });
}
}
catch
(
error
)
{
console
.
log
(
error
);
...
...
@@ -314,7 +305,7 @@ const getEntityTypeCount = async () => {
const
res
=
await
getSingleSanctionEntityTypeCount
(
params
);
if
(
res
.
code
===
200
)
{
entityTypeCount
.
value
=
res
.
data
||
[];
typeChart
.
interpret
({
type
:
"饼图"
,
name
:
"进入本次实体清单的中国实体类型分布情况"
,
data
:
entityTypeCount
.
value
});
//
typeChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体类型分布情况", data: entityTypeCount.value });
initTypeChart
();
}
}
catch
(
error
)
{
...
...
@@ -337,7 +328,7 @@ const getDomainCount = async () => {
if
(
res
.
code
===
200
)
{
domainCount
.
value
=
res
.
data
||
[];
initDomainChart
();
domainChart
.
interpret
({
type
:
"饼图"
,
name
:
"进入本次实体清单的中国实体领域分布情况"
,
data
:
domainCount
.
value
});
//
domainChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体领域分布情况", data: domainCount.value });
}
}
catch
(
error
)
{
console
.
log
(
error
);
...
...
@@ -351,7 +342,7 @@ const getTotalCount = async () => {
if
(
!
sanRecordId
.
value
)
return
;
try
{
const
res
=
await
getSingleSanctionTotalCount
(
route
.
query
.
sanTypeId
,
sanRecordId
.
value
);
console
.
log
(
'统计'
,
res
);
console
.
log
(
"统计"
,
res
);
if
(
res
.
code
===
200
)
{
totalCount
.
value
=
res
.
data
||
{};
...
...
@@ -389,14 +380,6 @@ for (let i = 2025; i >= 2000; i--) {
timeOptions
.
push
({
label
:
`
${
i
}
年`
,
value
:
`
${
i
}
`
});
}
// const countryDistribution = [
// { name: "中国", count: 24, width: "80%", gradient: "linear-gradient(90deg, rgba(205, 66, 70, 0) 0%, rgba(205, 66, 70, 1) 100%)" },
// { name: "沙特阿拉伯", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "伊朗", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "俄罗斯", count: 2, width: "55%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "中国香港", count: 1, width: "40%", gradient: "linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)" }
// ];
const
mapChartRef
=
ref
(
null
);
const
domainChartRef
=
ref
(
null
);
const
typeChartRef
=
ref
(
null
);
...
...
@@ -474,13 +457,13 @@ const initMapChart = () => {
chart
.
setOption
(
option
);
chart
.
on
(
'click'
,
function
(
params
)
{
chart
.
on
(
"click"
,
function
(
params
)
{
const
param
=
{
selectedProvince
:
params
.
name
,
selectedDate
:
JSON
.
stringify
([
route
.
query
.
date
,
route
.
query
.
date
])
}
}
;
const
curRoute
=
router
.
resolve
({
path
:
'/dataLibrary/dataEntityList'
,
path
:
"/dataLibrary/dataEntityList"
,
query
:
param
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
...
...
@@ -855,67 +838,67 @@ const initTypeChart = () => {
const
sanTypeId
=
ref
(
""
);
// 制裁实体领域分布情况
const
handleToDataLibrary3
=
(
val
)
=>
{
const
handleToDataLibrary3
=
val
=>
{
// console.log('val', val);
const
params
=
{
domains
:
val
.
name
,
isCnEntityOnly
:
true
,
selectedDate
:
JSON
.
stringify
([
route
.
query
.
date
,
route
.
query
.
date
])
}
}
;
const
curRoute
=
router
.
resolve
({
path
:
'/dataLibrary/dataEntityList'
,
path
:
"/dataLibrary/dataEntityList"
,
query
:
params
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
// 制裁实体类型分布情况
const
handleToDataLibrary4
=
(
val
)
=>
{
const
handleToDataLibrary4
=
val
=>
{
// console.log('val', val);
const
params
=
{
selectedEntityType
:
val
.
name
,
isCnEntityOnly
:
true
,
selectedDate
:
JSON
.
stringify
([
route
.
query
.
date
,
route
.
query
.
date
])
}
}
;
const
curRoute
=
router
.
resolve
({
path
:
'/dataLibrary/dataEntityList'
,
path
:
"/dataLibrary/dataEntityList"
,
query
:
params
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
// 制裁实体国家地区分布情况
const
handleToDataLibrary5
=
(
item
)
=>
{
const
handleToDataLibrary5
=
item
=>
{
const
params
=
{
selectedCountryId
:
item
.
id
,
isCnEntityOnly
:
true
,
selectedDate
:
JSON
.
stringify
([
route
.
query
.
date
,
route
.
query
.
date
])
}
}
;
const
curRoute
=
router
.
resolve
({
path
:
'/dataLibrary/dataEntityList'
,
path
:
"/dataLibrary/dataEntityList"
,
query
:
params
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
// 制裁实体各省分布情况
const
handleToDataLibrary6
=
(
item
)
=>
{
console
.
log
(
'item'
,
item
);
const
handleToDataLibrary6
=
item
=>
{
console
.
log
(
"item"
,
item
);
const
params
=
{
selectedProvince
:
item
.
name
,
isCnEntityOnly
:
true
,
selectedDate
:
JSON
.
stringify
([
route
.
query
.
date
,
route
.
query
.
date
])
}
}
;
const
curRoute
=
router
.
resolve
({
path
:
'/dataLibrary/dataEntityList'
,
path
:
"/dataLibrary/dataEntityList"
,
query
:
params
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
// 跳转到数据资源库
const
handleToDataLibrary
=
()
=>
{
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
''
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
""
;
const
curRoute
=
router
.
resolve
({
path
:
"/dataLibrary/dataEntityList"
,
query
:
{
...
...
@@ -924,10 +907,10 @@ const handleToDataLibrary = () => {
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
const
handleToDataLibrary1
=
()
=>
{
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
''
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
""
;
const
curRoute
=
router
.
resolve
({
path
:
"/dataLibrary/dataEntityList"
,
query
:
{
...
...
@@ -937,10 +920,10 @@ const handleToDataLibrary1 = () => {
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
}
;
const
handleToDataLibrary2
=
()
=>
{
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
''
const
dateStr
=
route
.
query
.
date
?
route
.
query
.
date
:
""
;
const
curRoute
=
router
.
resolve
({
path
:
"/dataLibrary/dataEntityList"
,
query
:
{
...
...
@@ -949,8 +932,129 @@ const handleToDataLibrary2 = () => {
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
}
};
const
requestAiPaneContent
=
async
key
=>
{
if
(
!
key
||
aiPaneLoading
.
value
[
key
]
||
aiPaneFetched
.
value
[
key
])
return
;
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
true
};
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成中..."
};
try
{
const
payload
=
buildAiChartPayload
(
key
);
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
onChunk
:
chunk
=>
{
const
current
=
overviewAiContent
.
value
[
key
];
const
base
=
current
===
"智能总结生成中..."
?
""
:
current
;
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
base
+
chunk
};
}
}
);
const
list
=
res
?.
data
;
const
first
=
Array
.
isArray
(
list
)
?
list
[
0
]
:
null
;
const
interpretation
=
first
?.
解读
||
first
?.[
"解读"
];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if
(
interpretation
)
{
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
interpretation
};
}
aiPaneFetched
.
value
=
{
...
aiPaneFetched
.
value
,
[
key
]:
true
};
}
catch
(
error
)
{
console
.
error
(
"获取图表解读失败"
,
error
);
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成失败"
};
}
finally
{
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
false
};
}
};
const
domainChartData
=
ref
([]);
const
typeChartData
=
ref
([]);
const
countryDistributionChartData
=
ref
([]);
const
regionDistributionChartData
=
ref
([]);
const
aiPaneVisible
=
ref
({
domainChart
:
false
,
typeChart
:
false
,
countryDistributionChart
:
false
,
regionDistributionChart
:
false
});
const
overviewAiContent
=
ref
({
domainChart
:
"智能总结生成中..."
,
typeChart
:
"智能总结生成中..."
,
countryDistributionChart
:
"智能总结生成中..."
,
regionDistributionChart
:
"智能总结生成中..."
});
const
aiPaneFetched
=
ref
({
domainChart
:
false
,
typeChart
:
false
,
countryDistributionChart
:
false
,
regionDistributionChart
:
false
});
const
aiPaneLoading
=
ref
({
domainChart
:
false
,
typeChart
:
false
,
countryDistributionChart
:
false
,
regionDistributionChart
:
false
});
const
chartLoading
=
ref
({
domainChart
:
false
,
typeChart
:
false
,
countryDistributionChart
:
false
,
regionDistributionChart
:
false
});
const
buildAiChartPayload
=
key
=>
{
if
(
key
===
"domainChart"
)
{
return
{
type
:
"饼图"
,
name
:
"进入本次实体清单的中国实体领域分布情况"
,
data
:
domainCount
.
value
};
}
if
(
key
===
"typeChart"
)
{
return
{
type
:
"饼图"
,
name
:
"进入本次实体清单的中国实体类型分布情况"
,
data
:
entityTypeCount
.
value
};
}
if
(
key
===
"countryDistributionChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"进入本次实体清单的实体国家地区分布情况"
,
data
:
countryDistributionChartData
.
value
};
}
if
(
key
===
"regionDistributionChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"进入本次实体清单的中国实体各省分布情况"
,
data
:
regionDistributionChartData
.
value
};
}
return
{
type
:
""
,
name
:
""
,
data
:
[]
};
};
const
handleShowAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
true
};
requestAiPaneContent
(
key
);
};
const
handleHideAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
false
};
};
onMounted
(()
=>
{
// 获取路由参数id
...
...
src/views/exportControl/v2.0SingleSanction/components/sanctionsOverview/index.vue
浏览文件 @
a37f484f
...
...
@@ -8,7 +8,7 @@
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
/>
<img
:src=
"
formattedData.postOrgLogoUrl ||
title"
alt=
""
class=
"icon"
/>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
...
...
@@ -45,15 +45,22 @@
<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"
@
click=
"handleToDataLibrary(item)"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<img
:src=
"item.imageUrl || 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>
...
...
@@ -96,13 +103,25 @@
</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-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-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon=
"Search"
/>
:suffix-icon=
"Search"
/>
</div>
</div>
<div
class=
"stats-row"
>
...
...
@@ -117,14 +136,21 @@
<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>
<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>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span
>
家)
</span
>
</div>
</div>
</div>
...
...
@@ -156,7 +182,11 @@
>
{{
item
}}
</span
>
-->
<div
class=
"domain-box"
>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
</div>
</
template
>
</el-table-column>
...
...
@@ -165,8 +195,11 @@
<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)"
>
<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>
...
...
@@ -184,8 +217,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</div>
</template>
...
...
@@ -310,8 +347,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText
:
org
.
ruleOrgList
&&
org
.
ruleOrgList
.
length
>
0
?
(
org
.
ruleOrgList
[
0
].
orgName
.
length
>
10
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
:
""
}))
}));
...
...
@@ -445,7 +482,8 @@ const formattedData = computed(() => {
administrativeOrderId
:
info
.
administrativeOrderId
?
`No.
${
info
.
administrativeOrderId
}
`
:
""
,
postPersonName
:
info
.
postPersonName
,
domains
:
info
.
domainNames
,
avartar
:
info
.
postPersonAvatarUrl
avartar
:
info
.
postPersonAvatarUrl
,
postOrgLogoUrl
:
info
.
postOrgLogoUrl
};
});
...
...
@@ -525,23 +563,22 @@ const entityDistribution = ref([
const
sanTypeId
=
ref
(
""
);
// 跳转到数据资源库
const
handleToDataLibrary
=
(
item
)
=>
{
console
.
log
(
'item'
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
'0'
)}
-
${
d
.
padStart
(
2
,
'0'
)}
`
const
handleToDataLibrary
=
item
=>
{
console
.
log
(
"item"
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
"0"
)}
-
${
d
.
padStart
(
2
,
"0"
)}
`
);
const
route
=
router
.
resolve
({
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/dataEntityList"
,
query
:{
selectedDate
:
JSON
.
stringify
([
dateStr
,
dateStr
]),
query
:
{
selectedDate
:
JSON
.
stringify
([
dateStr
,
dateStr
]),
selectedCountryId
:
item
.
id
}
});
window
.
open
(
route
.
href
,
"_blank"
);
}
};
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
...
...
src/views/exportControl/v2.0SingleSanction/originPage/index.vue
浏览文件 @
a37f484f
...
...
@@ -51,7 +51,7 @@
object-fit: contain;
"
/>
</div>
<div
class=
"translate-text"
>
{{
"显示
原
文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示
译
文"
}}
</div>
</div>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"icon"
>
...
...
@@ -62,13 +62,13 @@
</div>
</div>
<div
class=
"report-box"
>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`" ref="rightPdfRef" :pdfUrl="reportUrlWithPage"
class="pdf-pane-inner" />
</div>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
...
...
src/views/finance/components/title.vue
浏览文件 @
a37f484f
...
...
@@ -43,8 +43,8 @@ defineProps({
.title-text
{
color
:
rgba
(
10
,
18
,
30
,
1
);
font-size
:
32px
;
font-family
:
$base-font-family
;
font-weight
:
bold
;
font-family
:
"Microsoft YaHei"
;
font-weight
:
700
;
margin-left
:
20px
;
white-space
:
nowrap
;
}
...
...
src/views/finance/index.vue
浏览文件 @
a37f484f
...
...
@@ -234,13 +234,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布实体清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"entityListReleaseFreqChart.interpretation"
/>
<!-- <AiButton />
<AiPane :aiContent="entityListReleaseFreqChart.interpretation" /> -->
<AiButton
@
mouseenter=
"handleShowAiPane('entityListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.entityListReleaseFreqChart"
:aiContent=
"overviewAiContent.entityListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('entityListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
>
...
...
@@ -280,13 +284,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布商业管制清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"commerceControlListReleaseFreqChart.interpretation"
/>
<!-- <AiButton />
<AiPane :aiContent="commerceControlListReleaseFreqChart.interpretation" /> -->
<AiButton
@
mouseenter=
"handleShowAiPane('commerceControlListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.commerceControlListReleaseFreqChart"
:aiContent=
"overviewAiContent.commerceControlListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('commerceControlListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
style=
"display: none"
>
...
...
@@ -339,13 +347,13 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入SDN清单的中国实体领域分布情况,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"radarChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"radarChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('radarChart')"
/>
<AiPane
:aiContent=
"overviewAiContent.radarChart"
@
mouseleave=
"handleHideAiPane('radarChart')"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -371,13 +379,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入SDN清单的中国实体数量变化趋势,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"trendChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"trendChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('trendChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.trendChart"
:aiContent=
"overviewAiContent.trendChart"
@
mouseleave=
"handleHideAiPane('trendChart')"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -705,6 +717,7 @@ import RiskSignal from "@/components/base/riskSignal/index.vue";
import
RiskSignalOverviewDetailDialog
from
"@/components/base/RiskSignalOverviewDetailDialog/index.vue"
;
import
{
onMounted
,
ref
,
computed
,
reactive
,
shallowRef
,
watch
,
nextTick
}
from
"vue"
;
import
{
useContainerScroll
}
from
"@/hooks/useScrollShow"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
const
homeMainRef
=
ref
(
null
);
const
{
isShow
}
=
useContainerScroll
(
homeMainRef
);
import
*
as
echarts
from
"echarts"
;
...
...
@@ -1265,7 +1278,8 @@ const fetchRadarData = async checked => {
};
});
console
.
log
(
"图例 =>"
,
radarOption
.
value
);
radarChart
.
interpret
({
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
data
});
radarChartData
.
value
=
data
;
// radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
}
}
catch
(
error
)
{
console
.
error
(
"获取雷达图数据失败:"
,
error
);
...
...
@@ -1824,6 +1838,128 @@ const handleToDataLibrary = item => {
window
.
open
(
route
.
href
,
"_blank"
);
};
const
requestAiPaneContent
=
async
key
=>
{
if
(
!
key
||
aiPaneLoading
.
value
[
key
]
||
aiPaneFetched
.
value
[
key
])
return
;
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
true
};
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成中..."
};
try
{
const
payload
=
buildAiChartPayload
(
key
);
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
onChunk
:
chunk
=>
{
const
current
=
overviewAiContent
.
value
[
key
];
const
base
=
current
===
"智能总结生成中..."
?
""
:
current
;
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
base
+
chunk
};
}
}
);
const
list
=
res
?.
data
;
const
first
=
Array
.
isArray
(
list
)
?
list
[
0
]
:
null
;
const
interpretation
=
first
?.
解读
||
first
?.[
"解读"
];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if
(
interpretation
)
{
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
interpretation
};
}
aiPaneFetched
.
value
=
{
...
aiPaneFetched
.
value
,
[
key
]:
true
};
}
catch
(
error
)
{
console
.
error
(
"获取图表解读失败"
,
error
);
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成失败"
};
}
finally
{
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
false
};
}
};
const
trendChartData
=
ref
([]);
const
radarChartData
=
ref
([]);
const
entityListReleaseFreqChartData
=
ref
([]);
const
commerceControlListReleaseFreqChartData
=
ref
([]);
const
aiPaneVisible
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
overviewAiContent
=
ref
({
trendChart
:
"智能总结生成中..."
,
radarChart
:
"智能总结生成中..."
,
entityListReleaseFreqChart
:
"智能总结生成中..."
,
commerceControlListReleaseFreqChart
:
"智能总结生成中..."
});
const
aiPaneFetched
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
aiPaneLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
chartLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
buildAiChartPayload
=
key
=>
{
if
(
key
===
"trendChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"制裁清单数量增长趋势"
,
data
:
trendChartData
.
value
};
}
if
(
key
===
"radarChart"
)
{
return
{
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
radarChartData
.
value
};
}
if
(
key
===
"entityListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreqChartData
.
value
};
}
if
(
key
===
"commerceControlListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreqChartData
.
value
};
}
return
{
type
:
""
,
name
:
""
,
data
:
[]
};
};
const
handleShowAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
true
};
requestAiPaneContent
(
key
);
};
const
handleHideAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
false
};
};
onMounted
(
async
()
=>
{
console
.
log
(
"finance 页面 mounted"
);
try
{
...
...
@@ -1886,11 +2022,12 @@ onMounted(async () => {
await
fetchRadarData
(
domainChecked
.
value
);
// 获取出口管制制裁措施
await
fetchSanctionList
();
entityListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreq
.
value
});
entityListReleaseFreqChartData
.
value
=
entityListReleaseFreq
.
value
;
// entityListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布实体清单的频次",
// data: entityListReleaseFreq.value
// });
commerceControlListReleaseFreq
.
value
=
_
.
map
(
cclList1
,
item
=>
{
return
{
year
:
item
.
year
,
...
...
@@ -1899,11 +2036,12 @@ onMounted(async () => {
tags
:
item
.
domain
};
});
commerceControlListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreq
.
value
});
commerceControlListReleaseFreqChartData
.
value
=
commerceControlListReleaseFreq
.
value
;
// commerceControlListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布商业管制清单的频次",
// data: commerceControlListReleaseFreq.value
// });
}
catch
(
err
)
{
console
.
log
(
"此处报错?"
);
console
.
log
(
err
);
...
...
src/views/finance/singleSanction/components/sanctionsOverview/index.vue
浏览文件 @
a37f484f
...
...
@@ -8,7 +8,7 @@
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
/>
<img
:src=
"
formattedData.postOrgLogoUrl ||
title"
alt=
""
class=
"icon"
/>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
...
...
@@ -46,17 +46,24 @@
<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"
@
click=
"handleToDataLibrary(item)"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<img
:src=
"item.imageUrl || 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: i
ndex === 0
}">
{{
item
.
count
}}
家
</div>
<div
class=
"count"
:class=
"
{ highlight: i
tem.name === '中国'
}">
{{
item
.
count
}}
家
</div>
</div>
</div>
</div>
...
...
@@ -97,13 +104,25 @@
</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-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-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon=
"Search"
/>
:suffix-icon=
"Search"
/>
</div>
</div>
<div
class=
"stats-row"
>
...
...
@@ -118,14 +137,21 @@
<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>
<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>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span
>
家)
</span
>
</div>
</div>
</div>
...
...
@@ -157,7 +183,11 @@
>
{{
item
}}
</span
>
-->
<div
class=
"domain-box"
>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
</div>
</
template
>
</el-table-column>
...
...
@@ -166,20 +196,26 @@
<el-table-column
prop=
"entityTypeId"
label=
"类型"
width=
"120"
align=
"center"
>
<
template
#
default=
"scope"
>
<div
style=
"display: flex; gap: 4px; justify-content: center"
>
<AreaTag
:tagName=
"scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
"
/>
<AreaTag
:tagName=
"
scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
"
/>
</div>
</
template
>
</el-table-column>
<!-- <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)"
>
<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>
...
...
@@ -223,8 +259,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</div>
</template>
...
...
@@ -333,8 +373,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText
:
org
.
ruleOrgList
&&
org
.
ruleOrgList
.
length
>
0
?
(
org
.
ruleOrgList
[
0
].
orgName
.
length
>
10
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
:
""
}))
}));
...
...
@@ -481,7 +521,8 @@ const formattedData = computed(() => {
administrativeOrderId
:
info
.
administrativeOrderId
?
`No.
${
info
.
administrativeOrderId
}
`
:
""
,
postPersonName
:
info
.
postPersonName
,
domains
:
info
.
domainNames
,
avartar
:
info
.
postPersonAvatarUrl
avartar
:
info
.
postPersonAvatarUrl
,
postOrgLogoUrl
:
info
.
postOrgLogoUrl
};
});
...
...
@@ -590,10 +631,11 @@ const getReasonHistoryList = async () => {
const
sanTypeId
=
ref
(
""
);
// 跳转到数据资源库
const
handleToDataLibrary
=
(
item
)
=>
{
console
.
log
(
'item'
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
'0'
)}
-
${
d
.
padStart
(
2
,
'0'
)}
`
const
handleToDataLibrary
=
item
=>
{
console
.
log
(
"item"
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
"0"
)}
-
${
d
.
padStart
(
2
,
"0"
)}
`
);
const
route
=
router
.
resolve
({
...
...
@@ -604,7 +646,7 @@ const handleToDataLibrary = (item) => {
}
});
window
.
open
(
route
.
href
,
"_blank"
);
}
}
;
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
...
...
src/views/finance/singleSanction/index.vue
浏览文件 @
a37f484f
...
...
@@ -28,7 +28,7 @@
</div>
<div
class=
"original-text-btn"
@
click=
"handleClickOriginalText"
>
<img
:src=
"icon1"
alt=
""
/>
<span>
实体
清单原文
</span>
<span>
SDN
清单原文
</span>
</div>
<div
class=
"btn3"
@
click=
"handleAnalysisClick"
>
<div
class=
"icon"
>
...
...
src/views/finance/singleSanction/originPage/index.vue
浏览文件 @
a37f484f
...
...
@@ -68,7 +68,7 @@
"
/>
</div>
<div
class=
"translate-text"
>
{{
"显示
原
文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示
译
文"
}}
</div>
</div>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"icon"
>
...
...
@@ -79,9 +79,6 @@
</div>
</div>
<div
class=
"report-box"
>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`"
...
...
@@ -90,6 +87,9 @@
class="pdf-pane-inner"
/>
</div>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
...
...
src/views/thinkTank/ThinkTankDetail/index.vue
浏览文件 @
a37f484f
...
...
@@ -75,10 +75,8 @@ const switchTab = name => {
const
thinkTank
=
ref
({});
// 获取智库基本信息
const
handleGetThinkTankSummary
=
async
()
=>
{
const
id
=
getDecodedParams
()
try
{
const
parmas
=
{
id
:
id
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论