提交 f18fbd9e authored 作者: yanpeng's avatar yanpeng

style

上级 5c38f268
...@@ -160,3 +160,11 @@ export function getRecordRelation(sanRecordIds) { ...@@ -160,3 +160,11 @@ export function getRecordRelation(sanRecordIds) {
export function getVertexInfo(sanRecordId) { export function getVertexInfo(sanRecordId) {
return http.get(`/api/sanctionList/invFin/getVertexInfo?sanRecordId=${sanRecordId}`); return http.get(`/api/sanctionList/invFin/getVertexInfo?sanRecordId=${sanRecordId}`);
} }
/**
* 查询投融资限制关联-图谱-关系详情
* url:/sanctionList/invFin/getEdgeInfo
*/
export function getEdgeInfo(edgeId) {
return http.get(`/api/sanctionList/invFin/getEdgeInfo?edgeId=${edgeId}`);
}
...@@ -22,7 +22,7 @@ defineProps({ ...@@ -22,7 +22,7 @@ defineProps({
align-items: center; align-items: center;
width: 100%; width: 100%;
margin-bottom: 36px; margin-bottom: 36px;
padding: 0 15px; padding: 0px;
} }
.color-block { .color-block {
......
...@@ -3,18 +3,31 @@ ...@@ -3,18 +3,31 @@
<div class="home-main" ref="homeMainRef"> <div class="home-main" ref="homeMainRef">
<div class="home-top-bg"></div> <div class="home-top-bg"></div>
<div class="home-main-header"> <div class="home-main-header">
<SearchContainer style="margin-bottom: 0; margin-top: 48px; height: fit-content" v-if="homeMainRef" <SearchContainer
placeholder="搜索出口管制" :containerRef="homeMainRef" areaName="实体清单" /> style="margin-bottom: 0; margin-top: 48px; height: fit-content"
v-if="homeMainRef"
placeholder="搜索出口管制"
:containerRef="homeMainRef"
areaName="实体清单"
/>
<div class="home-main-header-footer-info"> <div class="home-main-header-footer-info">
<InfoCard v-for="(item, index) in infoList" :key="item.id" :title="item.nameZh" :subtitle="item.nameAbbr" <InfoCard
:description="item.description" :quantity="item.postCount" :unit="item.unit" :color="infoListColor[index]" v-for="(item, index) in infoList"
@click="handleToEntityListNoId(item)" /> :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>
</div> </div>
<el-row :gutter="15" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px">
<CustomTitle id="position1" title="最新动态" /> <CustomTitle id="position1" title="最新动态" />
<el-col :span="16"> <el-col :span="16" style="padding-left: 0px">
<custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px"> <custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px">
<template #header-right> <template #header-right>
<el-button type="primary" @click="handleToEntityList" link> <el-button type="primary" @click="handleToEntityList" link>
...@@ -34,8 +47,15 @@ ...@@ -34,8 +47,15 @@
<img src="./assets/images/box1-right.png" alt="" /> <img src="./assets/images/box1-right.png" alt="" />
</div> </div>
</div> </div>
<el-carousel ref="carouselRef" height="370px" :autoplay="true" :interval="3000" arrow="never" <el-carousel
indicator-position="none" @change="handleCarouselChange"> ref="carouselRef"
height="370px"
:autoplay="true"
:interval="3000"
arrow="never"
indicator-position="none"
@change="handleCarouselChange"
>
<el-carousel-item v-for="(item, index) in entitiesDataInfoList" :key="item.id + index"> <el-carousel-item v-for="(item, index) in entitiesDataInfoList" :key="item.id + index">
<div> <div>
<div class="box1-top"> <div class="box1-top">
...@@ -60,17 +80,29 @@ ...@@ -60,17 +80,29 @@
> >
<el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag> <el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag>
</div> --> </div> -->
<AreaTag v-for="(domainItem, index) in item.domains" :key="index" :tagName="domainItem" /> <AreaTag
v-for="(domainItem, index) in item.domains"
:key="index"
:tagName="domainItem"
/>
</div> </div>
</div> </div>
</div> </div>
<div class="box1-bottom"> <div class="box1-bottom">
<div class="box1-bottom-title">· 涉及主要实体:</div> <div class="box1-bottom-title">· 涉及主要实体:</div>
<div class="box1-bottom-content"> <div class="box1-bottom-content">
<div class="box1-bottom-content-item" v-for="(ett, index) in item.sanEntities" :key="index" <div
@click="handleEntityClick(ett)"> class="box1-bottom-content-item"
<el-image v-if="ett.img" class="box1-bottom-content-item-img" :src="ett.img" v-for="(ett, index) in item.sanEntities"
alt=""></el-image> :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"> <div v-else class="box1-bottom-content-item-imgUndefined">
{{ {{
(ett.orgName || ett.orgNameZh)?.match( (ett.orgName || ett.orgNameZh)?.match(
...@@ -100,7 +132,7 @@ ...@@ -100,7 +132,7 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8" style="padding-right: 0px">
<!-- <custom-container <!-- <custom-container
titleType="danger" titleType="danger"
title="风险信号" title="风险信号"
...@@ -143,12 +175,18 @@ ...@@ -143,12 +175,18 @@
</div> </div>
</template> </template>
</custom-container> --> </custom-container> -->
<RiskSignal :list="warningList" @item-click="handleToRiskSignalDetail" @more-click="handleToMoreRiskSignal" <RiskSignal
riskLevel="signalLevel" postDate="signalTime" name="signalTitle" /> :list="warningList"
@item-click="handleToRiskSignalDetail"
@more-click="handleToMoreRiskSignal"
riskLevel="signalLevel"
postDate="signalTime"
name="signalTitle"
/>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="15" style="width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px">
<CustomTitle id="position2" title="资讯要闻" /> <CustomTitle id="position2" title="资讯要闻" />
</el-row> </el-row>
<!-- <el-col :span="12"> <!-- <el-col :span="12">
...@@ -167,11 +205,19 @@ ...@@ -167,11 +205,19 @@
</custom-container> </custom-container>
</el-col> --> </el-col> -->
<div class="center-center"> <div class="center-center">
<NewsList :newsList="newsList" @item-click="handleNewsInfoClick" @more-click="handleToMoreNews" <NewsList
content="newsContent" /> :newsList="newsList"
@item-click="handleNewsInfoClick"
@more-click="handleToMoreNews"
content="newsContent"
/>
<MessageBubble :messageList="socialMediaList" @person-click="handlePerClick" imageUrl="avatar" <MessageBubble
@more-click="handleToSocialDetail" /> :messageList="socialMediaList"
@person-click="handlePerClick"
imageUrl="avatar"
@more-click="handleToSocialDetail"
/>
<!-- <custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px"> <!-- <custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px">
<template #default> <template #default>
<div class="dialog-list"> <div class="dialog-list">
...@@ -183,28 +229,38 @@ ...@@ -183,28 +229,38 @@
</custom-container> --> </custom-container> -->
</div> </div>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px">
<CustomTitle id="position3" title="数据总览" /> <CustomTitle id="position3" title="数据总览" />
<el-col :span="24"> <el-col :span="24" style="padding: 0">
<custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px"> <custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px">
<template #default> <template #default>
<div class="box3"> <div class="box3">
<div class="box3-content"> <div class="box3-content">
<div class="box3-content-title">实体清单发布频次统计</div> <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 prop="year" label="年份" width="200" />
<el-table-column label="发布次数" width="300"> <el-table-column label="发布次数" width="300">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span> <span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress :percentage="scope.row.percent * 100" :show-text="false" <el-progress
:status="getStatus(scope.row.percent)" /> :percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="重点领域" width="220" align="center"> <el-table-column label="重点领域" width="220" align="center">
<template #default="scope"> <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" /> <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)">{{ <!-- <el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{
tag tag
...@@ -226,21 +282,30 @@ ...@@ -226,21 +282,30 @@
</div> </div>
<div class="box3-content"> <div class="box3-content">
<div class="box3-content-title">商业管制清单发布频次统计</div> <div class="box3-content-title">商业管制清单发布频次统计</div>
<el-table :data="commerceControlListReleaseFreq" stripe style="width: 100%" <el-table
@row-click="handleCommercialRowClick"> :data="commerceControlListReleaseFreq"
stripe
style="width: 100%"
@row-click="handleCommercialRowClick"
>
<el-table-column prop="year" label="年份" width="200" /> <el-table-column prop="year" label="年份" width="200" />
<el-table-column label="发布次数" width="300"> <el-table-column label="发布次数" width="300">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span> <span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress :percentage="scope.row.percent * 100" :show-text="false" <el-progress
:status="getStatus(scope.row.percent)" /> :percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="重点领域" width="220" align="center"> <el-table-column label="重点领域" width="220" align="center">
<template #default="scope"> <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"
>
<el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{ <el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{
tag tag
}}</el-tag> }}</el-tag>
...@@ -269,8 +334,11 @@ ...@@ -269,8 +334,11 @@
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span> <span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress :percentage="scope.row.percent * 100" :show-text="false" <el-progress
:status="getStatus(scope.row.percent)" /> :percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
...@@ -291,15 +359,19 @@ ...@@ -291,15 +359,19 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8"> <el-col :span="8" style="padding-left: 0">
<custom-container title="实体清单领域分布情况" :titleIcon="radarIcon" height="540px"> <custom-container title="实体清单领域分布情况" :titleIcon="radarIcon" height="540px">
<template #header-right> <template #header-right>
<el-checkbox v-model="domainChecked" label="50%规则" size="large" /> <el-checkbox v-model="domainChecked" label="50%规则" size="large" />
</template> </template>
<template #default> <template #default>
<EChart :option="radarOption" autoresize :style="{ height: '420px' }" <EChart
@chart-click="handleRadarChartClick" /> :option="radarOption"
autoresize
:style="{ height: '420px' }"
@chart-click="handleRadarChartClick"
/>
<div class="data-origin-box"> <div class="data-origin-box">
<div class="data-origin-icon"> <div class="data-origin-icon">
<img :src="tipsIcon" alt="" /> <img :src="tipsIcon" alt="" />
...@@ -313,7 +385,7 @@ ...@@ -313,7 +385,7 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16" style="padding-right: 0">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px"> <custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px">
<template #header-right> <template #header-right>
<div style="display: flex; align-items: center; gap: 16px"> <div style="display: flex; align-items: center; gap: 16px">
...@@ -324,8 +396,12 @@ ...@@ -324,8 +396,12 @@
</div> </div>
</template> </template>
<template #default> <template #default>
<EChart :option="trendOption" autoresize :style="{ height: '420px' }" <EChart
@chart-click="handleMultiBarChartClick" /> :option="trendOption"
autoresize
:style="{ height: '420px' }"
@chart-click="handleMultiBarChartClick"
/>
<div class="data-origin-box"> <div class="data-origin-box">
<div class="data-origin-icon"> <div class="data-origin-icon">
<img :src="tipsIcon" alt="" /> <img :src="tipsIcon" alt="" />
...@@ -341,17 +417,21 @@ ...@@ -341,17 +417,21 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px">
<CustomTitle id="position4" title="资源库" style="margin-top: 0px" /> <CustomTitle id="position4" title="资源库" style="margin-top: 0px" />
<div class="resource-tabs"> <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 }" :class="{ active: activeResourceTab == tab.value, disabled: tab.disabled }"
@click="handleResourceTabClick(tab)"> @click="handleResourceTabClick(tab)"
>
{{ tab.label }} {{ tab.label }}
</div> </div>
</div> </div>
<template v-if="activeResourceTab === 'entity'"> <template v-if="activeResourceTab === 'entity'">
<el-col :span="8"> <el-col :span="8" style="padding-left: 0">
<custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px"> <custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px">
<template #default> <template #default>
<div class="box4"> <div class="box4">
...@@ -359,15 +439,25 @@ ...@@ -359,15 +439,25 @@
<div class="box4-item" v-for="(item, idx) in sanctionProcessList" :key="item.title"> <div class="box4-item" v-for="(item, idx) in sanctionProcessList" :key="item.title">
<div class="box4-item-left"> <div class="box4-item-left">
<el-image :src="dotIcon" alt="图片" class="box4-item-left-icon" /> <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>
<div class="box4-item-right"> <div class="box4-item-right">
<div class="box4-item-right-header" @click="handleSanc(item)"> <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> <span class="box4-item-right-header-desc">{{ item.desc }}</span>
</div> </div>
<el-tooltip effect="dark" :content="item.content" popper-class="common-prompt-popper" <el-tooltip
placement="top" :show-after="500"> effect="dark"
:content="item.content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="box4-item-right-content"> <div class="box4-item-right-content">
{{ item.content }} {{ item.content }}
</div> </div>
...@@ -375,8 +465,12 @@ ...@@ -375,8 +465,12 @@
</div> </div>
</div> </div>
</div> </div>
<div class="box4-footer" :style="{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }"> <div
<el-button type="primary" link @click="handleGetMore">查看更多 class="box4-footer"
:style="{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }"
>
<el-button type="primary" link @click="handleGetMore"
>查看更多
<el-icon> <el-icon>
<DArrowRight /> <DArrowRight />
</el-icon> </el-icon>
...@@ -386,20 +480,31 @@ ...@@ -386,20 +480,31 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16" style="padding-right: 0">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px"> <custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right> <template #header-right>
<div class="box5-header-right">{{ total }}家实体</div> <div class="box5-header-right">{{ total }}家实体</div>
</template> </template>
<template #default> <template #default>
<div class="box5"> <div class="box5">
<el-table :data="entitiesList" class="sanction-table" stripe empty-text="暂无数据" height="700px" <el-table
header-row-class-name="table-header" row-class-name="table-row"> :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"> <el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope"> <template #default="scope">
<div class="tableName" @click="handleCompClick(scope.row)"> <div class="tableName" @click="handleCompClick(scope.row)">
<el-image v-if="scope.row.img" class="box1-bottom-content-item-img" :src="scope.row.img" <el-image
alt=""></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"> <div v-else class="box1-bottom-content-item-imgUndefined">
{{ {{
(scope.row.name || scope.row.enName)?.match( (scope.row.name || scope.row.enName)?.match(
...@@ -453,13 +558,19 @@ ...@@ -453,13 +558,19 @@
<el-table-column prop="revenue" label="50%规则子企业" width="280" align="right"> <el-table-column prop="revenue" label="50%规则子企业" width="280" align="right">
<template #default="scope"> <template #default="scope">
<div class="num-item" v-if="scope.row.ruleOrgCount > 0"> <div class="num-item" v-if="scope.row.ruleOrgCount > 0">
<div class="name-item" :class="[ <div
class="name-item"
:class="[
'revenue-cell', 'revenue-cell',
scope.row.revenue === '无营收数据' ? 'no-revenue' : '' scope.row.revenue === '无营收数据' ? 'no-revenue' : ''
]"> ]"
>
{{ scope.row.ruleOrgList[0].orgName }}...等 {{ scope.row.ruleOrgList[0].orgName }}...等
</div> </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 }}家> {{ scope.row.ruleOrgCount }}家>
</div> </div>
</div> </div>
...@@ -471,8 +582,15 @@ ...@@ -471,8 +582,15 @@
<!-- <div class="pagination-info"> <!-- <div class="pagination-info">
第{{ currentPage }}页,共{{ totalPages }}页 第{{ currentPage }}页,共{{ totalPages }}页
</div> --> </div> -->
<el-pagination v-model:current-page="currentPage" :page-size="pageSize" :total="total" <el-pagination
:pager-count="5" layout="prev, pager, next" background @current-change="handlePageChange" /> v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
:pager-count="5"
layout="prev, pager, next"
background
@current-change="handlePageChange"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -480,7 +598,7 @@ ...@@ -480,7 +598,7 @@
</el-col> </el-col>
</template> </template>
<template v-if="activeResourceTab === 'all'"> <template v-if="activeResourceTab === 'all'">
<el-col :span="24"> <el-col :span="24" style="padding: 0">
<!-- <div style="min-height: 500px; display: flex; justify-content: center; align-items: center; background: #fff; border-radius: 4px;"> <!-- <div style="min-height: 500px; display: flex; justify-content: center; align-items: center; background: #fff; border-radius: 4px;">
暂无内容 暂无内容
</div> --> </div> -->
...@@ -538,8 +656,14 @@ ...@@ -538,8 +656,14 @@
</div> </div>
<div class="right-footer"> <div class="right-footer">
<div class="total-count">{{ totalAll }}</div> <div class="total-count">{{ totalAll }}</div>
<el-pagination v-model:current-page="currentPageAll" :page-size="pageSizeAll" :total="totalAll" <el-pagination
layout="prev, pager, next" background @current-change="handlePageChangeAll" /> v-model:current-page="currentPageAll"
:page-size="pageSizeAll"
:total="totalAll"
layout="prev, pager, next"
background
@current-change="handlePageChangeAll"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -1026,7 +1150,7 @@ const handleToEntityList = item => { ...@@ -1026,7 +1150,7 @@ const handleToEntityList = item => {
"curTabName", "curTabName",
entitiesDataInfoList.value[currentCarouselIndex.value].postDate + " 《实体清单新增条目》" entitiesDataInfoList.value[currentCarouselIndex.value].postDate + " 《实体清单新增条目》"
); );
let date = entitiesDataInfoList.value[currentCarouselIndex.value].postDate let date = entitiesDataInfoList.value[currentCarouselIndex.value].postDate;
const routeData = router.resolve({ const routeData = router.resolve({
path: "/exportControl/singleSanction", path: "/exportControl/singleSanction",
query: { query: {
...@@ -1082,10 +1206,10 @@ const radarOption = ref({ ...@@ -1082,10 +1206,10 @@ const radarOption = ref({
tooltip: { tooltip: {
// trigger: "item", // trigger: "item",
confine: true, confine: true,
trigger: 'axis', trigger: "axis",
formatter: function (params) { formatter: function (params) {
// params 包含所有系列的数据 // params 包含所有系列的数据
if (!params || params.length === 0) return ''; if (!params || params.length === 0) return "";
const radarData = params[0]; const radarData = params[0];
const indicator = radarData.axisValue; // 当前角度对应的指标名 const indicator = radarData.axisValue; // 当前角度对应的指标名
...@@ -1310,7 +1434,7 @@ const fetchSanctionList = async () => { ...@@ -1310,7 +1434,7 @@ const fetchSanctionList = async () => {
}); });
totalAll.value = res.totalElements; totalAll.value = res.totalElements;
} }
} catch (error) { } } catch (error) {}
}; };
const handlePageChangeAll = val => { const handlePageChangeAll = val => {
...@@ -1604,7 +1728,7 @@ const handleGetHylyList = async () => { ...@@ -1604,7 +1728,7 @@ const handleGetHylyList = async () => {
hylymc: "全部分类" hylymc: "全部分类"
}; };
categoryList.value = [obj, ...categoryList.value]; categoryList.value = [obj, ...categoryList.value];
} catch (error) { } } catch (error) {}
}; };
const chart1Data = ref({ const chart1Data = ref({
...@@ -1696,62 +1820,60 @@ const handleSearch = () => { ...@@ -1696,62 +1820,60 @@ const handleSearch = () => {
}; };
// 点击实体清单发布频次统计 // 点击实体清单发布频次统计
const handleEntityRowClick = (row) => { const handleEntityRowClick = row => {
console.log('row', row); console.log("row", row);
const params = { const params = {
domains: row.tags[0], domains: row.tags[0],
selectedDate: JSON.stringify([row.year + '-01-01', row.year + '-12-31']) selectedDate: JSON.stringify([row.year + "-01-01", row.year + "-12-31"])
};
}
const route = router.resolve({ const route = router.resolve({
path: '/dataLibrary/dataEntityListEvent', path: "/dataLibrary/dataEntityListEvent",
query: params query: params
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
} };
// 点击商业管制清单发布频次统计 // 点击商业管制清单发布频次统计
const handleCommercialRowClick = (row) => { const handleCommercialRowClick = row => {
console.log('row', row); console.log("row", row);
const params = { const params = {
domains: row.tags[0], domains: row.tags[0],
selectedDate: JSON.stringify([row.year + '-01-01', row.year + '-12-31']) selectedDate: JSON.stringify([row.year + "-01-01", row.year + "-12-31"])
} };
const route = router.resolve({ const route = router.resolve({
path: '/dataLibrary/dataCommerceControlListEvent', path: "/dataLibrary/dataCommerceControlListEvent",
query: params query: params
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
} };
// 点击实体清单领域分布情况 // 点击实体清单领域分布情况
const handleRadarChartClick = (value) => { const handleRadarChartClick = value => {
// console.log('value', value); // console.log('value', value);
// alert(domainChecked.value) // alert(domainChecked.value)
const params = { const params = {
isHalfRule: domainChecked.value isHalfRule: domainChecked.value
} };
const route = router.resolve({ const route = router.resolve({
path: '/dataLibrary/dataEntityList', path: "/dataLibrary/dataEntityList",
query: params query: params
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
} };
// 点击制裁清单数量增长趋势 // 点击制裁清单数量增长趋势
const handleMultiBarChartClick = (val) => { const handleMultiBarChartClick = val => {
// console.log('value', val); // console.log('value', val);
const params = { const params = {
domains: val.seriesName, domains: val.seriesName,
selectedDate: JSON.stringify([val.name + '-01-01', val.name + '-12-31']) selectedDate: JSON.stringify([val.name + "-01-01", val.name + "-12-31"])
};
}
const route = router.resolve({ const route = router.resolve({
path: '/dataLibrary/dataEntityListEvent', path: "/dataLibrary/dataEntityListEvent",
query: params query: params
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
} };
onMounted(async () => { onMounted(async () => {
handleGetHylyList(); handleGetHylyList();
...@@ -2162,7 +2284,6 @@ const handleMediaClick = item => { ...@@ -2162,7 +2284,6 @@ const handleMediaClick = item => {
} }
.box3-content { .box3-content {
// flex: 1; // flex: 1;
.el-progress--line { .el-progress--line {
width: 82px; width: 82px;
...@@ -3169,7 +3290,7 @@ const handleMediaClick = item => { ...@@ -3169,7 +3290,7 @@ const handleMediaClick = item => {
align-items: center; align-items: center;
margin-top: 6px; margin-top: 6px;
margin-bottom: 36px; margin-bottom: 36px;
padding-left: 10px; // padding-left: 10px;
.resource-tab-item { .resource-tab-item {
margin-right: 12px; margin-right: 12px;
...@@ -3556,7 +3677,7 @@ const handleMediaClick = item => { ...@@ -3556,7 +3677,7 @@ const handleMediaClick = item => {
margin-top: 21px; margin-top: 21px;
height: 450px; height: 450px;
display: flex; display: flex;
gap: 20px; gap: 16px;
.center-center-news { .center-center-news {
flex-shrink: 0; flex-shrink: 0;
......
...@@ -22,7 +22,7 @@ defineProps({ ...@@ -22,7 +22,7 @@ defineProps({
align-items: center; align-items: center;
width: 100%; width: 100%;
margin-bottom: 36px; margin-bottom: 36px;
padding: 0 15px; padding: 0;
} }
.color-block { .color-block {
......
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
<div class="list-item" v-for="item in sanctionList" :key="item.id" @click="handleSanctionSelect(item.id)"> <div class="list-item" v-for="item in sanctionList" :key="item.id" @click="handleSanctionSelect(item.id)">
<el-checkbox v-model="item.checked" @change="val => handleCheckOneChange(val, item)" @click.stop> <el-checkbox v-model="item.checked" @change="val => handleCheckOneChange(val, item)" @click.stop>
<div class="item-label"> <div class="item-label">
<div class="item-left">{{ item.date }}-{{ "SDN清单更新" }}</div> <div class="item-left">
{{ dayjs(item.date).format("YYYY年MM月DD日") }}-{{ "SDN清单更新" }}
</div>
<div class="item-right">{{ item.count }}{{ item.unit }}</div> <div class="item-right">{{ item.count }}{{ item.unit }}</div>
</div> </div>
</el-checkbox> </el-checkbox>
...@@ -51,50 +53,170 @@ ...@@ -51,50 +53,170 @@
</el-empty> </el-empty>
</div> </div>
<div class="relation-content" v-else> <div class="relation-content" v-else>
<!-- 修改点:绑定转换后的 graphNodes 和 graphLinks --> <!-- 绑定转换后的 graphNodes 和 graphLinks -->
<GraphChart :nodes="graphNodes" :links="graphLinks" /> <GraphChart :nodes="graphNodes" :links="graphLinks" @handleClickNode="handleClickNode" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
<el-dialog v-model="nodeVisible" :title="curNode?.data?.name || '制裁历程'" width="960">
<div class="dialog-content">
<div class="info-btn">
<el-button type="primary" :style="{ borderRadius: '8px', paddingTop: '10px' }" @click="handleInfoClick">
查看详情 >
</el-button>
</div>
<div class="content-item">
<div class="item-label">制裁标题:</div>
<div class="item-desc item-label">
{{ vertexInfo.name }}
</div>
</div>
<div class="content-item">
<div class="item-label">制裁领域:</div>
<div class="item-desc">
<AreaTag v-for="item in vertexInfo.domainList" :key="item" :tagName="item" />
</div>
</div>
<div class="content-item">
<div class="item-label">依托文件:</div>
<div class="item-desc">
<div class="item-file" v-for="item in vertexInfo.relyFileList" :key="item.id">
<div class="item-file-name">
{{ item.name }}
</div>
<img :src="openIcon" alt="文件" class="item-file-icon" />
</div>
</div>
</div>
<div class="content-item">
<div class="item-label">依托制裁:</div>
<div class="item-desc">
<div class="item-file" v-for="item in vertexInfo.relySanList" :key="item.id">
<div class="item-file-name">
{{ item.title }}
</div>
<img :src="openIcon" alt="文件" class="item-file-icon" />
</div>
</div>
</div>
<div class="content-item">
<div class="item-label">制裁原因:</div>
<div class="item-desc-table">
<div class="item-file" v-for="item in vertexInfo.sanReasonList" :key="item">
<div class="dot"></div>
<div class="item-file-name">{{ item }}</div>
</div>
</div>
</div>
<div class="content-item">
<div class="item-label">制裁对象:</div>
<div class="item-desc item-desc-table">
<span class="item-table-desc" v-if="vertexInfo.addObjectList?.length || vertexInfo.delObjectList?.length">
{{ formatChangeSummary(vertexInfo.addObjectList, vertexInfo.delObjectList) }}
</span>
<el-table :data="vertexInfo.sanList" stripe>
<el-table-column property="entityNameZh" label="制裁对象" width="350" />
<el-table-column property="domainNames" label="所属领域" width="400">
<template #default="scope">
<AreaTag v-for="item in scope.row.domainNames" :key="item" :tagName="item" />
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</el-dialog>
<el-dialog :title="curLink.data?.relationType + '关系' || '关联关系'" v-model="relationVisible" width="960">
<div class="dialog-content">
<div class="hintWrap">
<div class="icon1"></div>
<div class="title">
{{ tipsInfo }}
</div>
<div class="icon2Wrap">
<div class="icon2"></div>
</div>
</div>
<RelationChart :is-vertical-chart="true" :graph-data="graphData" />
</div>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, nextTick, onUnmounted, computed } from "vue"; import { ref, onMounted, computed } from "vue";
import defaultTitle from "../../../assets/default-icon2.png";
import GraphChart from "@/components/base/GraphChart/index.vue"; import GraphChart from "@/components/base/GraphChart/index.vue";
import RelationChart from "@/components/base/RelationChart/index.vue";
import emptyImg from "../assets/empty.png"; import emptyImg from "../assets/empty.png";
import markIcon from "../assets/icon-mark.png"; import markIcon from "../assets/icon-mark.png"; // 引入图标
import { getSanRecord, getRecordRelation } from "@/api/finance"; import openIcon from "../../../../assets/icons/icon-open.png";
import { useRoute } from "vue-router"; import { getSanRecord, getRecordRelation, getVertexInfo, getEdgeInfo } from "@/api/finance";
import { useRoute, useRouter } from "vue-router";
import dayjs from "dayjs";
const route = useRoute(); const route = useRoute();
const router = useRouter();
// 定义颜色映射
const colors = { const colors = {
// 相似 // 相似
similarity: { similarity: {
fontColor: "rgba(5, 95, 194)", fontColor: "rgba(5, 95, 194)",
color: "rgb(231, 243, 255)" color: "rgb(231, 243, 255)", // 连线颜色 & 标签背景
lineColor: "rgba(5, 95, 194)" // 专门用于连线的颜色,如果需要和背景色区分
}, },
// 继承 // 继承
inheritance: { inheritance: {
fontColor: "rgba(19, 168, 168)", fontColor: "rgba(19, 168, 168)",
color: "rgba(230, 255, 251, 1)" color: "rgba(230, 255, 251, 1)",
lineColor: "rgba(19, 168, 168)"
}, },
// 冲突 // 冲突
conflict: { conflict: {
fontColor: "rgb(206, 79, 81)", fontColor: "rgb(206, 79, 81)",
color: "rgba(5, 95, 194)" color: "rgba(255, 241, 240, 1)", // 修正:冲突背景通常偏红/浅红,这里沿用你提供的蓝色背景可能不太符合直觉,但我保留你的配置或微调
lineColor: "rgb(206, 79, 81)"
}
};
const handleInfoClick = () => {
window.sessionStorage.setItem("curTabName", `${curNode.value.data.originDate}-《${curNode.value.data.originName}》`);
const routeData = router.resolve({
path: "/finance/singleSanction",
query: {
id: curNode.value.data.id,
sanTypeId: sanTypeId.value
}
});
// 打开新页面
window.open(routeData.href, "_blank");
};
// 辅助函数:获取关系类型对应的颜色配置
const getRelationStyle = relationType => {
switch (relationType) {
case "相似":
return colors.similarity;
case "继承":
return colors.inheritance;
case "冲突":
return colors.conflict;
default:
return {
fontColor: "#666",
color: "#f0f2f5",
lineColor: "#AED6FF"
};
} }
}; };
// ... 其他原有变量保持不变 ...
const selectedSanctionIds = ref([]); const selectedSanctionIds = ref([]);
const isAllSelected = computed({ const isAllSelected = computed({
get() { get() {
return sanctionList.value.length > 0 && sanctionList.value.every(item => item.checked); return sanctionList.value.length > 0 && sanctionList.value.every(item => item.checked);
}, }
set(val) {}
}); });
const handleCheckAllChange = val => { const handleCheckAllChange = val => {
...@@ -123,27 +245,46 @@ const indeterminate = computed(() => { ...@@ -123,27 +245,46 @@ const indeterminate = computed(() => {
const recordRelation = ref({ noRelationVertices: [], relationVoList: [] }); const recordRelation = ref({ noRelationVertices: [], relationVoList: [] });
// 【新增】计算属性:处理图表数据 // 【修改】处理图表节点数据
const graphNodes = computed(() => { const graphNodes = computed(() => {
const nodesMap = new Map(); const nodesMap = new Map();
// 1. 处理无关联节点 (noRelationVertices) // 定义统一的标签样式
const labelStyle = {
show: true,
position: "bottom", // 标签位置,可根据需要调整为 'top', 'left', 'right', 'inside' 等
fontSize: 14,
fontWeight: 400,
fontFamily: "Source Han Sans CN",
lineHeight: 22,
color: "#333" // 默认字体颜色,可根据需要调整
// 如果标签背景需要透明或特定颜色,可以添加 backgroundColor
// backgroundColor: 'rgba(255, 255, 255, 0.8)',
// padding: [2, 4]
};
// 1. 处理无关联节点
if (recordRelation.value.noRelationVertices) { if (recordRelation.value.noRelationVertices) {
recordRelation.value.noRelationVertices.forEach(node => { recordRelation.value.noRelationVertices.forEach(node => {
if (!nodesMap.has(node.id)) { if (!nodesMap.has(node.id)) {
nodesMap.set(node.id, { nodesMap.set(node.id, {
id: node.id, id: node.id,
name: node.name, // name: node.name,
// 可以根据需要添加 category 或其他样式属性 originDate: node.date,
originName: node.name,
name: dayjs(node.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50], // 根据图标实际大小调整
itemStyle: { itemStyle: {
color: "#91cc75" // 例如:无关联节点用绿色区分 color: "#91cc75"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
}); });
} }
// 2. 处理关联节点 (relationVoList 中的 fromVertex 和 toVertex) // 2. 处理关联节点
if (recordRelation.value.relationVoList) { if (recordRelation.value.relationVoList) {
recordRelation.value.relationVoList.forEach(rel => { recordRelation.value.relationVoList.forEach(rel => {
const from = rel.fromVertex; const from = rel.fromVertex;
...@@ -152,20 +293,30 @@ const graphNodes = computed(() => { ...@@ -152,20 +293,30 @@ const graphNodes = computed(() => {
if (from && !nodesMap.has(from.id)) { if (from && !nodesMap.has(from.id)) {
nodesMap.set(from.id, { nodesMap.set(from.id, {
id: from.id, id: from.id,
name: from.name, // name: from.name,
originDate: from.date,
originName: from.name,
name: dayjs(from.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50],
itemStyle: { itemStyle: {
color: "#5470c6" // 例如:有关联节点用蓝色 color: "#5470c6"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
if (to && !nodesMap.has(to.id)) { if (to && !nodesMap.has(to.id)) {
nodesMap.set(to.id, { nodesMap.set(to.id, {
id: to.id, id: to.id,
name: to.name, // name: to.name,
name: dayjs(to.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50],
itemStyle: { itemStyle: {
color: "#5470c6" color: "#5470c6"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
}); });
...@@ -174,17 +325,38 @@ const graphNodes = computed(() => { ...@@ -174,17 +325,38 @@ const graphNodes = computed(() => {
return Array.from(nodesMap.values()); return Array.from(nodesMap.values());
}); });
// 【修改】处理图表连线数据
const graphLinks = computed(() => { const graphLinks = computed(() => {
if (!recordRelation.value.relationVoList) return []; if (!recordRelation.value.relationVoList) return [];
return recordRelation.value.relationVoList.map(rel => { return recordRelation.value.relationVoList.map(rel => {
const relationType = rel.edgeInfo ? rel.edgeInfo.value : "";
const style = getRelationStyle(relationType);
return { return {
source: rel.fromVertex.id, source: rel.fromVertex.id,
target: rel.toVertex.id, target: rel.toVertex.id,
// 将 edgeInfo 挂载到 data 上,以便在 formatter 中访问 // 将样式信息挂载到 label 或 data 上,供 formatter 和 lineStyle 使用
label: { label: {
formatter: rel.edgeInfo ? rel.edgeInfo.value : "" formatter: relationType,
} show: true,
color: style.fontColor, // 字体颜色
backgroundColor: style.color, // 标签背景色
borderColor: style.color, // 标签边框色
padding: [4, 8],
borderRadius: 4,
fontSize: 12
},
lineStyle: {
color: style.lineColor, // 连线颜色
width: 1,
type: "solid"
// curveness: 0.1 // 稍微有点弧度可能更好看
},
// 额外存储原始数据,以备后用
relationType: relationType,
edgeInfo: rel.edgeInfo,
originInfo: rel
}; };
}); });
}); });
...@@ -211,9 +383,119 @@ const fetchRecordRelation = async () => { ...@@ -211,9 +383,119 @@ const fetchRecordRelation = async () => {
recordRelation.value = { noRelationVertices: [], relationVoList: [] }; recordRelation.value = { noRelationVertices: [], relationVoList: [] };
} }
}; };
const vertexInfo = ref({});
const curNode = ref({});
const curLink = ref({});
const nodeVisible = ref(false);
const relationVisible = ref(false);
const tipsInfo = ref("");
const graphData = ref({});
const getTipsInfo = (relationType, reason) => {
switch (relationType) {
case "继承":
return `${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新依托于${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新,两次制裁存在继承关系。`;
case "冲突":
return `${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新中制裁的实体在${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新中被移除,存在冲突关系。`;
default:
return `${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新与${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新存在相同${reason},属于相似关系。`;
}
};
const handleClickNode = node => {
console.log("节点点击", node);
if (node.dataType == "node") {
nodeVisible.value = true;
curNode.value = node;
getVertexInfo(node.data.id).then(res => {
console.log("getVertexInfo", res);
if (!!res) {
vertexInfo.value = res;
nodeVisible.value = true;
} else {
vertexInfo.value = {};
}
});
} else {
relationVisible.value = true;
curLink.value = node;
const relationType = node.data.relationType;
// 继承 - 2025年10月1日-SDN清单更新依托于2024年2月08日-SDN清单更新,两次制裁存在继承关系。
// 冲突 - 2025年2月19日-SDN清单更新中制裁的实体在2024年2月08日-SDN清单更新中被移除,属于冲突关系。
// 相似 - 2025年2月19日-SDN清单更新与2024年2月08日-SDN清单更新存在相同制裁原因,属于相似关系。
// 相似 - 2025年2月19日-SDN清单更新与2024年2月08日-SDN清单更新存在同领域制裁实体,属于相似关系。
// 相似 - 2025年2月19日-SDN清单更新与2024年2月08日-SDN清单更新存在相同依托文件,属于相似关系。
getEdgeInfo(node.data.edgeInfo.key).then(res => {
if (!!res) {
// recordRelation.value = res;
console.log("制裁之间的关系 =>", res);
let reason = "";
if (relationType == "相似") {
reason = res[0].edgeReasonList[0].reason;
}
tipsInfo.value = getTipsInfo(relationType, reason);
} else {
// recordRelation.value = { noRelationVertices: [], relationVoList: [] };
}
});
}
};
// 【新增/修改】格式化变动 summary 的函数
const formatChangeSummary = (addList, delList) => {
const parts = [];
// 处理新增列表
if (addList && addList.length > 0) {
// 将每个对象转换为 "value个实体" 或 "value名个人" 的形式
const addItems = addList.map(item => {
let unit = "个";
let noun = "实体";
if (item.key === "人物") {
unit = "名";
noun = "个人";
} else if (item.key === "机构") {
// 默认机构对应实体,也可以根据需求调整
unit = "个";
noun = "实体";
}
return `${item.value}${unit}${noun}`;
});
// 拼接:新增 + item1 + , + item2 ...
parts.push(`新增${addItems.join(",")}`);
}
// 处理移除列表
if (delList && delList.length > 0) {
// 将每个对象转换为 "value个实体" 或 "value名个人" 的形式
const delItems = delList.map(item => {
let unit = "个";
let noun = "实体";
if (item.key === "人物") {
unit = "名";
noun = "个人";
} else if (item.key === "机构") {
unit = "个";
noun = "实体";
}
return `${item.value}${unit}${noun}`;
});
// 拼接:移除 + item1 + , + item2 ...
// 注意:题目要求“删除”,但之前代码用的是“移除”,这里统一使用“移除”或“删除”。
// 根据题目描述“展示样本为:新增12个实体,3名个人,移除1个实体”,这里使用“移除”更贴切上下文,
// 如果必须用“删除”,请将下面的 '移除' 改为 '删除'。
parts.push(`移除${delItems.join(",")}`);
}
return parts.length > 0 ? parts.join(",") : "无变动";
};
const loading = ref(false); const loading = ref(false);
const currentPage = ref(1); const sanctionList = ref([]);
const currentSanctionId = ref(5);
const dateRange = ref(["2025-01-01", "2025-12-31"]);
const sanTypeId = ref("");
const fetchSanRecord = async () => { const fetchSanRecord = async () => {
loading.value = true; loading.value = true;
...@@ -225,15 +507,13 @@ const fetchSanRecord = async () => { ...@@ -225,15 +507,13 @@ const fetchSanRecord = async () => {
try { try {
const res = await getSanRecord(params); const res = await getSanRecord(params);
if (res && res.length > 0) { if (res && res.length > 0) {
sanctionList.value = res sanctionList.value = res.map(item => ({
.map(item => ({
id: item.sanRecordId, id: item.sanRecordId,
date: item.sanRecordDate, date: item.sanRecordDate,
title: item.sanRecordName, title: item.sanRecordName,
count: item.cnEntitiesNum, count: item.cnEntitiesNum,
unit: "家中国实体" unit: "家中国实体"
})) }));
.reverse();
if (sanctionList.value.length > 0) { if (sanctionList.value.length > 0) {
currentSanctionId.value = sanctionList.value[0].id; currentSanctionId.value = sanctionList.value[0].id;
...@@ -257,11 +537,6 @@ const handleSanctionSelect = id => { ...@@ -257,11 +537,6 @@ const handleSanctionSelect = id => {
currentSanctionId.value = id; currentSanctionId.value = id;
}; };
const dateRange = ref(["2025-01-01", "2025-12-31"]);
const sanctionList = ref([]);
const currentSanctionId = ref(5);
const sanTypeId = ref("");
onMounted(() => { onMounted(() => {
sanTypeId.value = route.query.sanTypeId || ""; sanTypeId.value = route.query.sanTypeId || "";
fetchSanRecord(); fetchSanRecord();
...@@ -338,7 +613,6 @@ onMounted(() => { ...@@ -338,7 +613,6 @@ onMounted(() => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 100%;
} }
.item-left { .item-left {
...@@ -423,7 +697,135 @@ onMounted(() => { ...@@ -423,7 +697,135 @@ onMounted(() => {
} }
} }
.main-association { .main-association {
padding-top: 12px !important;
justify-content: flex-start !important; justify-content: flex-start !important;
gap: 16px; gap: 16px;
} }
.dialog-content {
position: relative;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
border-top: 1px solid rgb(238, 238, 238);
.hintWrap {
display: flex;
align-items: center;
padding: 7px 12px;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
margin-bottom: 9px;
.icon1 {
width: 19px;
height: 20px;
background-image: url("../assets/ai.png");
background-size: 100% 100%;
flex-shrink: 0;
}
.title {
color: rgb(5, 95, 194);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-left: 13px;
flex: 1;
}
.icon2Wrap {
width: 24px;
height: 24px;
background-color: rgba(231, 243, 255, 1);
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
margin-left: 20px;
flex-shrink: 0;
.icon2 {
width: 24px;
height: 24px;
background-image: url("../assets/right.png");
background-size: 100% 100%;
}
}
}
.info-btn {
position: absolute;
top: 20px;
right: 20px;
}
.content-item {
display: flex;
justify-content: flex-start;
align-items: flex-start;
gap: 8px;
.item-label {
min-width: 75px;
font-size: 14px;
font-weight: 700;
font-family: Source Han Sans CN;
color: rgba(59, 65, 75, 1);
line-height: 24px;
}
.item-desc-table {
display: flex;
flex-direction: column;
padding-top: 3px;
.dot {
width: 4px;
height: 4px;
border-radius: 50%;
background-color: rgb(59, 65, 75);
}
.item-file {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 8px;
// background: rgb(231, 243, 255);
// border-radius: 20px;
// border: 1px solid rgb(231, 243, 255);
padding: 4px 0px;
font-family: Source Han Sans CN;
font-size: 16px;
font-weight: 400;
}
}
.item-desc {
font-size: 14px;
font-family: Source Han Sans CN;
margin-top: 1px;
display: flex;
gap: 15px;
.item-file {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 8px;
background: rgb(231, 243, 255);
border-radius: 20px;
border: 1px solid rgb(231, 243, 255);
padding: 5px 15px;
cursor: pointer;
.item-file-name {
font-size: 16px;
font-weight: 400;
font-family: Source Han Sans CN;
color: rgb(5, 95, 194);
line-height: 24px;
}
.item-file-icon {
width: 16px;
height: 16px;
margin-top: 1px;
}
}
}
}
}
</style> </style>
...@@ -59,8 +59,13 @@ ...@@ -59,8 +59,13 @@
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
<el-dialog v-model="visible" :title="curNode.name" width="960"> <el-dialog v-model="nodeVisible" :title="curNode?.data?.name || '制裁历程'" width="960">
<div class="dialog-content"> <div class="dialog-content">
<div class="info-btn">
<el-button type="primary" :style="{ borderRadius: '8px', paddingTop: '10px' }" @click="handleInfoClick">
查看详情 >
</el-button>
</div>
<div class="content-item"> <div class="content-item">
<div class="item-label">制裁标题:</div> <div class="item-label">制裁标题:</div>
<div class="item-desc item-label"> <div class="item-desc item-label">
...@@ -76,24 +81,37 @@ ...@@ -76,24 +81,37 @@
<div class="content-item"> <div class="content-item">
<div class="item-label">依托文件:</div> <div class="item-label">依托文件:</div>
<div class="item-desc"> <div class="item-desc">
<div class="item-file" v-for="item in vertexInfo.relyFileList" :key="item.id">{{ item.name }}</div> <div class="item-file" v-for="item in vertexInfo.relyFileList" :key="item.id">
<div class="item-file-name">
{{ item.name }}
</div>
<img :src="openIcon" alt="文件" class="item-file-icon" />
</div>
</div> </div>
</div> </div>
<div class="content-item"> <div class="content-item">
<div class="item-label">依托制裁:</div> <div class="item-label">依托制裁:</div>
<div class="item-desc"> <div class="item-desc">
<div class="item-file" v-for="item in vertexInfo.relySanList" :key="item.id">{{ item.title }}</div> <div class="item-file" v-for="item in vertexInfo.relySanList" :key="item.id">
<div class="item-file-name">
{{ item.title }}
</div>
<img :src="openIcon" alt="文件" class="item-file-icon" />
</div>
</div> </div>
</div> </div>
<div class="content-item"> <div class="content-item">
<div class="item-label">制裁原因:</div> <div class="item-label">制裁原因:</div>
<div class="item-desc"> <div class="item-desc-table">
<div class="item-file" v-for="item in vertexInfo.sanReasonList" :key="item">{{ item }}</div> <div class="item-file" v-for="item in vertexInfo.sanReasonList" :key="item">
<div class="dot"></div>
<div class="item-file-name">{{ item }}</div>
</div>
</div> </div>
</div> </div>
<div class="content-item"> <div class="content-item">
<div class="item-label">制裁对象:</div> <div class="item-label">制裁对象:</div>
<div class="item-desc"> <div class="item-desc item-desc-table">
<span class="item-table-desc" v-if="vertexInfo.addObjectList?.length || vertexInfo.delObjectList?.length"> <span class="item-table-desc" v-if="vertexInfo.addObjectList?.length || vertexInfo.delObjectList?.length">
{{ formatChangeSummary(vertexInfo.addObjectList, vertexInfo.delObjectList) }} {{ formatChangeSummary(vertexInfo.addObjectList, vertexInfo.delObjectList) }}
</span> </span>
...@@ -109,19 +127,54 @@ ...@@ -109,19 +127,54 @@
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog :title="curLink.data?.relationType + '关系' || '关联关系'" v-model="relationVisible" width="960">
<div class="dialog-content">
<div class="hintWrap">
<div class="icon1"></div>
<div class="title">
{{ tipsInfo }}
</div>
<div class="icon2Wrap">
<div class="icon2"></div>
</div>
</div>
<!-- <div class="relation-content">
<RelationChart :is-vertical-chart="true" :graph-data="graphData" />
</div> -->
<div class="relation-charts-container">
<div v-for="(graphData, index) in graphDataList" :key="index" class="single-relation-chart-wrapper">
<!-- 可选:显示当前小图的标题,例如制裁名称 -->
<div class="chart-title" v-if="graphData.originalItem?.vertex?.name">
{{ graphData.originalItem.vertex.name }}
</div>
<div class="relation-content-item">
<RelationChart :is-vertical-chart="true" :graph-data="graphData" />
</div>
</div>
<!-- 空状态提示 -->
<div v-if="graphDataList.length === 0" class="empty-chart-tip">暂无关联详情数据</div>
</div>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, computed } from "vue";
import GraphChart from "@/components/base/GraphChart/index.vue"; import GraphChart from "@/components/base/GraphChart/index.vue";
import RelationChart from "@/components/base/RelationChart/index.vue";
import emptyImg from "../assets/empty.png"; import emptyImg from "../assets/empty.png";
import markIcon from "../assets/icon-mark.png"; // 引入图标 import markIcon from "../assets/icon-mark.png"; // 引入图标
import { getSanRecord, getRecordRelation, getVertexInfo } from "@/api/finance"; import openIcon from "../../../../assets/icons/icon-open.png";
import { useRoute } from "vue-router"; import { getSanRecord, getRecordRelation, getVertexInfo, getEdgeInfo } from "@/api/finance";
import { useRoute, useRouter } from "vue-router";
import dayjs from "dayjs"; import dayjs from "dayjs";
const route = useRoute(); const route = useRoute();
const router = useRouter();
// 定义颜色映射 // 定义颜色映射
const colors = { const colors = {
...@@ -145,6 +198,19 @@ const colors = { ...@@ -145,6 +198,19 @@ const colors = {
} }
}; };
const handleInfoClick = () => {
window.sessionStorage.setItem("curTabName", `${curNode.value.data.originDate}-《${curNode.value.data.originName}》`);
const routeData = router.resolve({
path: "/finance/singleSanction",
query: {
id: curNode.value.data.id,
sanTypeId: sanTypeId.value
}
});
// 打开新页面
window.open(routeData.href, "_blank");
};
// 辅助函数:获取关系类型对应的颜色配置 // 辅助函数:获取关系类型对应的颜色配置
const getRelationStyle = relationType => { const getRelationStyle = relationType => {
switch (relationType) { switch (relationType) {
...@@ -200,6 +266,20 @@ const recordRelation = ref({ noRelationVertices: [], relationVoList: [] }); ...@@ -200,6 +266,20 @@ const recordRelation = ref({ noRelationVertices: [], relationVoList: [] });
const graphNodes = computed(() => { const graphNodes = computed(() => {
const nodesMap = new Map(); const nodesMap = new Map();
// 定义统一的标签样式
const labelStyle = {
show: true,
position: "bottom", // 标签位置,可根据需要调整为 'top', 'left', 'right', 'inside' 等
fontSize: 14,
fontWeight: 400,
fontFamily: "Source Han Sans CN",
lineHeight: 22,
color: "#333" // 默认字体颜色,可根据需要调整
// 如果标签背景需要透明或特定颜色,可以添加 backgroundColor
// backgroundColor: 'rgba(255, 255, 255, 0.8)',
// padding: [2, 4]
};
// 1. 处理无关联节点 // 1. 处理无关联节点
if (recordRelation.value.noRelationVertices) { if (recordRelation.value.noRelationVertices) {
recordRelation.value.noRelationVertices.forEach(node => { recordRelation.value.noRelationVertices.forEach(node => {
...@@ -207,12 +287,15 @@ const graphNodes = computed(() => { ...@@ -207,12 +287,15 @@ const graphNodes = computed(() => {
nodesMap.set(node.id, { nodesMap.set(node.id, {
id: node.id, id: node.id,
// name: node.name, // name: node.name,
originDate: node.date,
originName: node.name,
name: dayjs(node.date).format("YYYY年MM月DD日") + " " + "SDN清单更新", name: dayjs(node.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标 symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50], // 根据图标实际大小调整 symbolSize: [50, 50], // 根据图标实际大小调整
itemStyle: { itemStyle: {
color: "#91cc75" color: "#91cc75"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
}); });
...@@ -228,12 +311,15 @@ const graphNodes = computed(() => { ...@@ -228,12 +311,15 @@ const graphNodes = computed(() => {
nodesMap.set(from.id, { nodesMap.set(from.id, {
id: from.id, id: from.id,
// name: from.name, // name: from.name,
originDate: from.date,
originName: from.name,
name: dayjs(from.date).format("YYYY年MM月DD日") + " " + "SDN清单更新", name: dayjs(from.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标 symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50], symbolSize: [50, 50],
itemStyle: { itemStyle: {
color: "#5470c6" color: "#5470c6"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
...@@ -246,7 +332,8 @@ const graphNodes = computed(() => { ...@@ -246,7 +332,8 @@ const graphNodes = computed(() => {
symbolSize: [50, 50], symbolSize: [50, 50],
itemStyle: { itemStyle: {
color: "#5470c6" color: "#5470c6"
} },
label: { ...labelStyle } // 应用标签样式
}); });
} }
}); });
...@@ -279,11 +366,14 @@ const graphLinks = computed(() => { ...@@ -279,11 +366,14 @@ const graphLinks = computed(() => {
}, },
lineStyle: { lineStyle: {
color: style.lineColor, // 连线颜色 color: style.lineColor, // 连线颜色
width: 2 width: 1,
type: "solid"
// curveness: 0.1 // 稍微有点弧度可能更好看 // curveness: 0.1 // 稍微有点弧度可能更好看
}, },
// 额外存储原始数据,以备后用 // 额外存储原始数据,以备后用
relationType: relationType relationType: relationType,
edgeInfo: rel.edgeInfo,
originInfo: rel
}; };
}); });
}); });
...@@ -312,19 +402,136 @@ const fetchRecordRelation = async () => { ...@@ -312,19 +402,136 @@ const fetchRecordRelation = async () => {
}; };
const vertexInfo = ref({}); const vertexInfo = ref({});
const curNode = ref({}); const curNode = ref({});
const visible = ref(false); const curLink = ref({});
const nodeVisible = ref(false);
const relationVisible = ref(false);
const tipsInfo = ref("");
const graphDataList = ref([]);
const getTipsInfo = (relationType, reason) => {
switch (relationType) {
case "继承":
return `${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新依托于${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新,两次制裁存在继承关系。`;
case "冲突":
return `${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新中制裁的实体在${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新中被移除,存在冲突关系。`;
default:
return `${dayjs(curLink.value.data.originInfo.fromVertex.date).format("YYYY年MM月DD日")}-SDN清单更新与${dayjs(curLink.value.data.originInfo.toVertex.date).format("YYYY年MM月DD日")}-SDN清单更新存在相同${reason},属于相似关系。`;
}
};
// 在 constrainedAssociation.vue 的 script setup 中
const handleClickNode = node => { const handleClickNode = node => {
console.log("节点点击", node); console.log("节点点击", node);
curNode.value = node.data; if (node.dataType == "node") {
nodeVisible.value = true;
curNode.value = node;
getVertexInfo(node.data.id).then(res => { getVertexInfo(node.data.id).then(res => {
console.log("getVertexInfo", res); console.log("getVertexInfo", res);
if (!!res) { if (!!res) {
vertexInfo.value = res; vertexInfo.value = res;
visible.value = true; nodeVisible.value = true;
} else { } else {
vertexInfo.value = {}; vertexInfo.value = {};
} }
}); });
} else {
relationVisible.value = true;
curLink.value = node;
const relationType = node.data.relationType;
// 获取边详情数据
getEdgeInfo(node.data.edgeInfo.key)
.then(res => {
if (!!res && Array.isArray(res)) {
console.log("制裁之间的关系 =>", res);
// 【核心修改】遍历 res,为每一项生成独立的图表数据
const list = [];
res.forEach((item, index) => {
const vertex = item.vertex;
if (!vertex || !vertex.id) return;
const nodes = [];
const lines = [];
const nodeMap = new Map();
// 辅助函数:添加节点
const addNode = (id, text, type = "vertex") => {
if (nodeMap.has(id)) return;
const newNode = {
id: id,
text: text,
// 样式:顶点用主题色,细节用白色
color: type === "vertex" ? "var(--color-primary-50)" : "#ffffff",
fontColor: type === "vertex" ? "var(--text-primary-90-color)" : "#333333",
customFontSize: type === "vertex" ? "14px" : "12px"
};
nodes.push(newNode);
nodeMap.set(id, newNode);
};
// 辅助函数:添加连线
const addLine = (fromId, toId, relationText) => {
lines.push({
from: fromId,
to: toId,
text: relationText,
color: "var(--color-primary-50)",
fontColor: "#666"
});
};
// 1. 添加出发点 (Vertex)
addNode(vertex.id, vertex.name, "vertex");
// 2. 处理 edgeReasonList -> reasonDetail
if (item.edgeReasonList && item.edgeReasonList.length > 0) {
item.edgeReasonList.forEach(reasonItem => {
const relationName = reasonItem.reason; // 例如: "依托文件"
if (reasonItem.reasonDetail && reasonItem.reasonDetail.length > 0) {
reasonItem.reasonDetail.forEach(detailItem => {
// 使用 detailItem.name 作为唯一 ID
// 注意:在这个独立的小图中,ID 只要不重复即可。
// 如果不同项之间有相同的 detailItem.name,它们在不同图中是隔离的,所以没问题。
const detailId = detailItem.name;
addNode(detailId, detailItem.name, "detail");
addLine(vertex.id, detailId, relationName);
});
}
});
}
// 只有当有连线时才加入列表,或者即使只有顶点也加入(视需求而定)
if (nodes.length > 0) {
list.push({
rootId: vertex.id,
nodes: nodes,
lines: lines,
// 可以保留原始数据用于调试或额外展示
originalItem: item
});
}
});
graphDataList.value = list;
// 处理提示文案 (取第一个或根据业务逻辑组合)
let reason = "";
if (relationType == "相似" && res[0]?.edgeReasonList?.[0]?.reason) {
reason = res[0].edgeReasonList[0].reason;
}
tipsInfo.value = getTipsInfo(relationType, reason);
} else {
graphDataList.value = [];
}
})
.catch(err => {
console.error("获取边信息失败", err);
graphDataList.value = [];
});
}
}; };
// 【新增/修改】格式化变动 summary 的函数 // 【新增/修改】格式化变动 summary 的函数
...@@ -394,15 +601,13 @@ const fetchSanRecord = async () => { ...@@ -394,15 +601,13 @@ const fetchSanRecord = async () => {
try { try {
const res = await getSanRecord(params); const res = await getSanRecord(params);
if (res && res.length > 0) { if (res && res.length > 0) {
sanctionList.value = res sanctionList.value = res.map(item => ({
.map(item => ({
id: item.sanRecordId, id: item.sanRecordId,
date: item.sanRecordDate, date: item.sanRecordDate,
title: item.sanRecordName, title: item.sanRecordName,
count: item.cnEntitiesNum, count: item.cnEntitiesNum,
unit: "家中国实体" unit: "家中国实体"
})) }));
.reverse();
if (sanctionList.value.length > 0) { if (sanctionList.value.length > 0) {
currentSanctionId.value = sanctionList.value[0].id; currentSanctionId.value = sanctionList.value[0].id;
...@@ -502,7 +707,6 @@ onMounted(() => { ...@@ -502,7 +707,6 @@ onMounted(() => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 100%;
} }
.item-left { .item-left {
...@@ -592,27 +796,184 @@ onMounted(() => { ...@@ -592,27 +796,184 @@ onMounted(() => {
gap: 16px; gap: 16px;
} }
.dialog-content { .dialog-content {
position: relative;
padding: 20px; padding: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 16px;
border-top: 1px solid rgb(238, 238, 238); border-top: 1px solid rgb(238, 238, 238);
// 【新增】关系图容器样式
.relation-charts-container {
display: flex;
flex-direction: column;
gap: 20px;
max-height: 60vh; // 限制最大高度,超出滚动
overflow-y: auto;
padding-right: 10px; // 给滚动条留空间
// 自定义滚动条样式
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
}
.single-relation-chart-wrapper {
border: 1px solid #eee;
border-radius: 8px;
padding: 10px;
background-color: #fafafa;
.chart-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
padding-left: 5px;
border-left: 3px solid var(--color-primary-50);
}
.relation-content-item {
height: 200px; // 每个小图的高度,可根据需要调整
width: 100%;
// 确保 RelationChart 内部能正确填充
:deep(.relation-graph) {
width: 100%;
height: 100%;
}
}
}
.empty-chart-tip {
text-align: center;
color: #999;
padding: 20px;
}
.hintWrap {
display: flex;
align-items: center;
padding: 7px 12px;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
margin-bottom: 9px;
.icon1 {
width: 19px;
height: 20px;
background-image: url("../assets/ai.png");
background-size: 100% 100%;
flex-shrink: 0;
}
.title {
color: rgb(5, 95, 194);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-left: 13px;
flex: 1;
}
.icon2Wrap {
width: 24px;
height: 24px;
background-color: rgba(231, 243, 255, 1);
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
margin-left: 20px;
flex-shrink: 0;
.icon2 {
width: 24px;
height: 24px;
background-image: url("../assets/right.png");
background-size: 100% 100%;
}
}
}
.relation-content {
height: 400px;
width: 100%;
}
.info-btn {
position: absolute;
top: 20px;
right: 20px;
}
.content-item { .content-item {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: flex-start;
gap: 8px; gap: 8px;
.item-label { .item-label {
font-size: 146x; min-width: 75px;
font-size: 14px;
font-weight: 700; font-weight: 700;
font-family: Source Han Sans CN; font-family: Source Han Sans CN;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
line-height: 24px; line-height: 24px;
} }
.item-desc-table {
display: flex;
flex-direction: column;
padding-top: 3px;
.dot {
width: 4px;
height: 4px;
border-radius: 50%;
background-color: rgb(59, 65, 75);
}
.item-file {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 8px;
// background: rgb(231, 243, 255);
// border-radius: 20px;
// border: 1px solid rgb(231, 243, 255);
padding: 4px 0px;
font-family: Source Han Sans CN;
font-size: 16px;
font-weight: 400;
}
}
.item-desc { .item-desc {
font-size: 14px; font-size: 14px;
font-family: Source Han Sans CN; font-family: Source Han Sans CN;
margin-top: 3px; margin-top: 1px;
display: flex;
gap: 15px;
.item-file {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 8px;
background: rgb(231, 243, 255);
border-radius: 20px;
border: 1px solid rgb(231, 243, 255);
padding: 5px 15px;
cursor: pointer;
.item-file-name {
font-size: 16px;
font-weight: 400;
font-family: Source Han Sans CN;
color: rgb(5, 95, 194);
line-height: 24px;
}
.item-file-icon {
width: 16px;
height: 16px;
margin-top: 1px;
}
}
} }
} }
} }
......
{ {
"id": 2140, "code": 200,
"name": "美国以伊朗石油非法贸易为由实施制裁,多家中国企业被列入制裁名单", "message": "操作成功",
"domainList": [ "success": true,
"海洋" "data": [
], {
"relyFileList": [ "vertex": {
{ "id": "2145",
"id": null, "name": "OFAC将38个实体及4名个人列入SDN清单,涉及中国关联主体",
"name": "第13382号行政命令" "highlight": true
}, },
"edgeReasonList": [
{ {
"id": null, "reason": "依托文件",
"name": "第14530号行政命令" "reasonDetail": [
}
],
"relySanList": [
{ {
"sanTypeId": 2, "name": "第13382号行政命令",
"id": 2145, "highlight": false
"title": "OFAC将38个实体及4名个人列入SDN清单,涉及中国关联主体", },
"postDate": "2025-10-14"
}
],
"sanReasonList": [
"参与了从伊朗购买、收购、销售、运输或营销石油化工产品",
"参与了与采购、获取、销售、运输或销售伊朗石油及石油制品相关的重大交易"
],
"addObjectList": [
{ {
"key": "机构", "name": "第13388号行政命令",
"value": 3 "highlight": true
} }
], ]
"delObjectList": [
{
"key": "人物",
"value": 1
} }
],
"sanList": [
{
"entityId": "91310115MA1HBB8PXH",
"entityName": "SHANGHAI QIZHANG SHIP MANAGEMENT CO., LTD.",
"entityNameZh": "上海启章船舶管理有限公司",
"entityTypeId": 2,
"entityTypeName": "机构",
"domainNames": [
"海洋"
] ]
}, },
{ {
"entityId": "71180883", "vertex": {
"entityName": "ALL WIN SHIPPING MANAGEMENT LIMITED", "id": "2146",
"entityNameZh": "誠安船舶管理有限公司", "name": "OFAC将15个中国实体及3名中国籍个人列入SDN清单",
"entityTypeId": 2, "highlight": true
"entityTypeName": "机构",
"domainNames": [
"海洋"
]
}, },
"edgeReasonList": [
{
"reason": "依托文件",
"reasonDetail": [
{ {
"entityId": "91370211MAEBUA7E2Q", "name": "第13388号行政命令",
"entityName": "QINGDAO OCEAN KIMO SHIP MANAGEMENT CO LTD", "highlight": true
"entityNameZh": "青岛明洋凯茂船舶管理有限公司", }
"entityTypeId": 2, ]
"entityTypeName": "机构", }
"domainNames": [
"海洋"
] ]
} }
] ]
......
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
</div> </div>
</div> </div>
<el-row :gutter="15" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px">
<CustomTitle id="position1" title="最新动态" /> <CustomTitle id="position1" title="最新动态" />
<el-col :span="16"> <el-col :span="16" style="padding: 0">
<custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px"> <custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px">
<template #header-right> <template #header-right>
<el-button type="primary" @click="handleToEntityList" link> <el-button type="primary" @click="handleToEntityList" link>
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8" style="padding: 0">
<RiskSignal <RiskSignal
:list="warningList" :list="warningList"
@item-click="handleToRiskSignalDetail" @item-click="handleToRiskSignalDetail"
...@@ -144,7 +144,7 @@ ...@@ -144,7 +144,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="15" style="width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px">
<CustomTitle id="position2" title="资讯要闻" /> <CustomTitle id="position2" title="资讯要闻" />
</el-row> </el-row>
<!-- <el-col :span="12"> <!-- <el-col :span="12">
...@@ -187,9 +187,9 @@ ...@@ -187,9 +187,9 @@
</custom-container> --> </custom-container> -->
</div> </div>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px">
<CustomTitle id="position3" title="数据总览" /> <CustomTitle id="position3" title="数据总览" />
<el-col :span="24"> <el-col :span="24" style="padding: 0">
<custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px"> <custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px">
<template #default> <template #default>
<div class="box3"> <div class="box3">
...@@ -235,7 +235,11 @@ ...@@ -235,7 +235,11 @@
</div> </div>
<div class="box3-content"> <div class="box3-content">
<div class="box3-content-title">中国军事工业复合体企业清单(CMIC)更新频度</div> <div class="box3-content-title">中国军事工业复合体企业清单(CMIC)更新频度</div>
<el-table :data="commerceControlListReleaseFreq" stripe style="width: 100%"> <el-table
:data="commerceControlListReleaseFreq"
stripe
style="width: 100%; margin-bottom: auto"
>
<el-table-column prop="year" label="年份" width="200" /> <el-table-column prop="year" label="年份" width="200" />
<el-table-column label="发布次数" width="300"> <el-table-column label="发布次数" width="300">
<template #default="scope"> <template #default="scope">
...@@ -307,8 +311,8 @@ ...@@ -307,8 +311,8 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8"> <el-col :span="8" style="padding-left: 0">
<custom-container title="制裁领域分布" :titleIcon="radarIcon" height="540px"> <custom-container title="制裁领域分布" :titleIcon="radarIcon" height="540px">
<template #header-right> <template #header-right>
<el-checkbox v-model="domainChecked" label="50%规则" size="large" /> <el-checkbox v-model="domainChecked" label="50%规则" size="large" />
...@@ -328,7 +332,7 @@ ...@@ -328,7 +332,7 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16" style="padding-right: 0">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px"> <custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px">
<template #header-right> <template #header-right>
<div style="display: flex; align-items: center; gap: 16px"> <div style="display: flex; align-items: center; gap: 16px">
...@@ -355,7 +359,7 @@ ...@@ -355,7 +359,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px"> <el-row :gutter="16" style="width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px">
<CustomTitle id="position4" title="资源库" style="margin-top: 0px" /> <CustomTitle id="position4" title="资源库" style="margin-top: 0px" />
<div class="resource-tabs"> <div class="resource-tabs">
<div <div
...@@ -369,7 +373,7 @@ ...@@ -369,7 +373,7 @@
</div> </div>
</div> </div>
<template v-if="activeResourceTab === 'entity'"> <template v-if="activeResourceTab === 'entity'">
<el-col :span="8"> <el-col :span="8" style="padding: 0">
<custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px"> <custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px">
<template #default> <template #default>
<div class="box4"> <div class="box4">
...@@ -418,7 +422,7 @@ ...@@ -418,7 +422,7 @@
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16" style="padding: 0">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px"> <custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right> <template #header-right>
<div class="box5-header-right">{{ total }}家实体</div> <div class="box5-header-right">{{ total }}家实体</div>
...@@ -542,7 +546,7 @@ ...@@ -542,7 +546,7 @@
</el-col> </el-col>
</template> </template>
<template v-if="activeResourceTab === 'all'"> <template v-if="activeResourceTab === 'all'">
<el-col :span="24"> <el-col :span="24" style="padding: 0">
<!-- <div style="min-height: 500px; display: flex; justify-content: center; align-items: center; background: #fff; border-radius: 4px;"> <!-- <div style="min-height: 500px; display: flex; justify-content: center; align-items: center; background: #fff; border-radius: 4px;">
暂无内容 暂无内容
</div> --> </div> -->
...@@ -781,7 +785,8 @@ const handleToRiskSignalDetail = item => { ...@@ -781,7 +785,8 @@ const handleToRiskSignalDetail = item => {
const routeData = router.resolve({ const routeData = router.resolve({
path: "/finance/singleSanction", path: "/finance/singleSanction",
query: { query: {
id: item.sanId id: item.sanId,
sanTypeId: item.sanTypeId
} }
}); });
// 打开新页面 // 打开新页面
...@@ -1514,6 +1519,7 @@ const fetchSocialMediaInfo = async () => { ...@@ -1514,6 +1519,7 @@ const fetchSocialMediaInfo = async () => {
if (data && Array.isArray(data)) { if (data && Array.isArray(data)) {
// console.log(data); // console.log(data);
socialMediaList.value = data.map(item => ({ socialMediaList.value = data.map(item => ({
...item,
avatar: item.personImage, avatar: item.personImage,
name: item.personName, name: item.personName,
time: formatTime(item.time), time: formatTime(item.time),
...@@ -1546,12 +1552,12 @@ const fetchNewsInfo = async () => { ...@@ -1546,12 +1552,12 @@ const fetchNewsInfo = async () => {
}; };
const handlePerClick = item => { const handlePerClick = item => {
// console.log("点击了社交媒体消息:", item); console.log("点击了社交媒体消息:", item);
window.sessionStorage.setItem("curTabName", item.name); window.sessionStorage.setItem("curTabName", item.name);
const route = router.resolve({ const route = router.resolve({
path: "/characterPage", path: "/characterPage",
query: { query: {
type: item.personType || [1, 2, 3][Math.floor(Math.random() * 3)], type: item.personType,
personId: item.personId personId: item.personId
} }
}); });
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论