Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
bb55d9a0
提交
bb55d9a0
authored
4月 22, 2026
作者:
张伊明
浏览文件
操作
浏览文件
下载
差异文件
合并分支 'yp-dev' 到 'pre'
Yp dev 查看合并请求
!380
上级
c5e4a135
02ee47e1
流水线
#597
已通过 于阶段
in 5 分 38 秒
变更
12
流水线
1
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
1051 行增加
和
428 行删除
+1051
-428
index.js
src/router/index.js
+1
-1
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
没有找到文件。
src/router/index.js
浏览文件 @
bb55d9a0
...
...
@@ -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/views/exportControl/components/ChartAiAnalysis.vue
0 → 100644
浏览文件 @
bb55d9a0
<
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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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
浏览文件 @
bb55d9a0
...
...
@@ -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>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论