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

style

上级 5c38f268
......@@ -160,3 +160,11 @@ export function getRecordRelation(sanRecordIds) {
export function getVertexInfo(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({
align-items: center;
width: 100%;
margin-bottom: 36px;
padding: 0 15px;
padding: 0px;
}
.color-block {
......
......@@ -3,18 +3,31 @@
<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>
<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="最新动态" />
<el-col :span="16">
<el-col :span="16" style="padding-left: 0px">
<custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px">
<template #header-right>
<el-button type="primary" @click="handleToEntityList" link>
......@@ -34,8 +47,15 @@
<img src="./assets/images/box1-right.png" alt="" />
</div>
</div>
<el-carousel ref="carouselRef" height="370px" :autoplay="true" :interval="3000" arrow="never"
indicator-position="none" @change="handleCarouselChange">
<el-carousel
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">
<div>
<div class="box1-top">
......@@ -60,17 +80,29 @@
>
<el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag>
</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 class="box1-bottom">
<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(
......@@ -100,7 +132,7 @@
</template>
</custom-container>
</el-col>
<el-col :span="8">
<el-col :span="8" style="padding-right: 0px">
<!-- <custom-container
titleType="danger"
title="风险信号"
......@@ -143,12 +175,18 @@
</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="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="资讯要闻" />
</el-row>
<!-- <el-col :span="12">
......@@ -167,11 +205,19 @@
</custom-container>
</el-col> -->
<div class="center-center">
<NewsList :newsList="newsList" @item-click="handleNewsInfoClick" @more-click="handleToMoreNews"
content="newsContent" />
<NewsList
:newsList="newsList"
@item-click="handleNewsInfoClick"
@more-click="handleToMoreNews"
content="newsContent"
/>
<MessageBubble :messageList="socialMediaList" @person-click="handlePerClick" imageUrl="avatar"
@more-click="handleToSocialDetail" />
<MessageBubble
:messageList="socialMediaList"
@person-click="handlePerClick"
imageUrl="avatar"
@more-click="handleToSocialDetail"
/>
<!-- <custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px">
<template #default>
<div class="dialog-list">
......@@ -183,28 +229,38 @@
</custom-container> -->
</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="数据总览" />
<el-col :span="24">
<el-col :span="24" style="padding: 0">
<custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px">
<template #default>
<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
......@@ -226,21 +282,30 @@
</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"
>
<el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{
tag
}}</el-tag>
......@@ -269,8 +334,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>
......@@ -291,15 +359,19 @@
</el-col>
</el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8">
<el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8" style="padding-left: 0">
<custom-container title="实体清单领域分布情况" :titleIcon="radarIcon" height="540px">
<template #header-right>
<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="" />
......@@ -313,7 +385,7 @@
</template>
</custom-container>
</el-col>
<el-col :span="16">
<el-col :span="16" style="padding-right: 0">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px">
<template #header-right>
<div style="display: flex; align-items: center; gap: 16px">
......@@ -324,8 +396,12 @@
</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="" />
......@@ -341,17 +417,21 @@
</el-col>
</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" />
<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>
<template v-if="activeResourceTab === 'entity'">
<el-col :span="8">
<el-col :span="8" style="padding-left: 0">
<custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px">
<template #default>
<div class="box4">
......@@ -359,15 +439,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>
......@@ -375,8 +465,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>
......@@ -386,20 +480,31 @@
</template>
</custom-container>
</el-col>
<el-col :span="16">
<el-col :span="16" style="padding-right: 0">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right>
<div class="box5-header-right">{{ total }}家实体</div>
</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(
......@@ -453,13 +558,19 @@
<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="[
<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>
......@@ -471,8 +582,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>
......@@ -480,7 +598,7 @@
</el-col>
</template>
<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> -->
......@@ -538,8 +656,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>
......@@ -1026,7 +1150,7 @@ const handleToEntityList = item => {
"curTabName",
entitiesDataInfoList.value[currentCarouselIndex.value].postDate + " 《实体清单新增条目》"
);
let date = entitiesDataInfoList.value[currentCarouselIndex.value].postDate
let date = entitiesDataInfoList.value[currentCarouselIndex.value].postDate;
const routeData = router.resolve({
path: "/exportControl/singleSanction",
query: {
......@@ -1082,10 +1206,10 @@ const radarOption = ref({
tooltip: {
// trigger: "item",
confine: true,
trigger: 'axis',
trigger: "axis",
formatter: function (params) {
// params 包含所有系列的数据
if (!params || params.length === 0) return '';
if (!params || params.length === 0) return "";
const radarData = params[0];
const indicator = radarData.axisValue; // 当前角度对应的指标名
......@@ -1310,7 +1434,7 @@ const fetchSanctionList = async () => {
});
totalAll.value = res.totalElements;
}
} catch (error) { }
} catch (error) {}
};
const handlePageChangeAll = val => {
......@@ -1604,7 +1728,7 @@ const handleGetHylyList = async () => {
hylymc: "全部分类"
};
categoryList.value = [obj, ...categoryList.value];
} catch (error) { }
} catch (error) {}
};
const chart1Data = ref({
......@@ -1696,62 +1820,60 @@ const handleSearch = () => {
};
// 点击实体清单发布频次统计
const handleEntityRowClick = (row) => {
console.log('row', row);
const handleEntityRowClick = row => {
console.log("row", row);
const params = {
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({
path: '/dataLibrary/dataEntityListEvent',
path: "/dataLibrary/dataEntityListEvent",
query: params
});
window.open(route.href, "_blank");
}
};
// 点击商业管制清单发布频次统计
const handleCommercialRowClick = (row) => {
console.log('row', row);
const handleCommercialRowClick = row => {
console.log("row", row);
const params = {
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({
path: '/dataLibrary/dataCommerceControlListEvent',
path: "/dataLibrary/dataCommerceControlListEvent",
query: params
});
window.open(route.href, "_blank");
}
};
// 点击实体清单领域分布情况
const handleRadarChartClick = (value) => {
const handleRadarChartClick = value => {
// console.log('value', value);
// alert(domainChecked.value)
const params = {
isHalfRule: domainChecked.value
}
};
const route = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: params
});
window.open(route.href, "_blank");
}
};
// 点击制裁清单数量增长趋势
const handleMultiBarChartClick = (val) => {
const handleMultiBarChartClick = val => {
// console.log('value', val);
const params = {
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({
path: '/dataLibrary/dataEntityListEvent',
path: "/dataLibrary/dataEntityListEvent",
query: params
});
window.open(route.href, "_blank");
}
};
onMounted(async () => {
handleGetHylyList();
......@@ -2162,7 +2284,6 @@ const handleMediaClick = item => {
}
.box3-content {
// flex: 1;
.el-progress--line {
width: 82px;
......@@ -3169,7 +3290,7 @@ const handleMediaClick = item => {
align-items: center;
margin-top: 6px;
margin-bottom: 36px;
padding-left: 10px;
// padding-left: 10px;
.resource-tab-item {
margin-right: 12px;
......@@ -3556,7 +3677,7 @@ const handleMediaClick = item => {
margin-top: 21px;
height: 450px;
display: flex;
gap: 20px;
gap: 16px;
.center-center-news {
flex-shrink: 0;
......
......@@ -22,7 +22,7 @@ defineProps({
align-items: center;
width: 100%;
margin-bottom: 36px;
padding: 0 15px;
padding: 0;
}
.color-block {
......
......@@ -27,7 +27,9 @@
<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>
<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>
</el-checkbox>
......@@ -51,50 +53,170 @@
</el-empty>
</div>
<div class="relation-content" v-else>
<!-- 修改点:绑定转换后的 graphNodes 和 graphLinks -->
<GraphChart :nodes="graphNodes" :links="graphLinks" />
<!-- 绑定转换后的 graphNodes 和 graphLinks -->
<GraphChart :nodes="graphNodes" :links="graphLinks" @handleClickNode="handleClickNode" />
</div>
</div>
</AnalysisBox>
</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>
</template>
<script setup>
import { ref, onMounted, nextTick, onUnmounted, computed } from "vue";
import defaultTitle from "../../../assets/default-icon2.png";
import { ref, onMounted, computed } from "vue";
import GraphChart from "@/components/base/GraphChart/index.vue";
import RelationChart from "@/components/base/RelationChart/index.vue";
import emptyImg from "../assets/empty.png";
import markIcon from "../assets/icon-mark.png";
import { getSanRecord, getRecordRelation } from "@/api/finance";
import { useRoute } from "vue-router";
import markIcon from "../assets/icon-mark.png"; // 引入图标
import openIcon from "../../../../assets/icons/icon-open.png";
import { getSanRecord, getRecordRelation, getVertexInfo, getEdgeInfo } from "@/api/finance";
import { useRoute, useRouter } from "vue-router";
import dayjs from "dayjs";
const route = useRoute();
const router = useRouter();
// 定义颜色映射
const colors = {
// 相似
similarity: {
fontColor: "rgba(5, 95, 194)",
color: "rgb(231, 243, 255)"
color: "rgb(231, 243, 255)", // 连线颜色 & 标签背景
lineColor: "rgba(5, 95, 194)" // 专门用于连线的颜色,如果需要和背景色区分
},
// 继承
inheritance: {
fontColor: "rgba(19, 168, 168)",
color: "rgba(230, 255, 251, 1)"
color: "rgba(230, 255, 251, 1)",
lineColor: "rgba(19, 168, 168)"
},
// 冲突
conflict: {
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 isAllSelected = computed({
get() {
return sanctionList.value.length > 0 && sanctionList.value.every(item => item.checked);
},
set(val) {}
}
});
const handleCheckAllChange = val => {
......@@ -123,27 +245,46 @@ const indeterminate = computed(() => {
const recordRelation = ref({ noRelationVertices: [], relationVoList: [] });
// 【新增】计算属性:处理图表数据
// 【修改】处理图表节点数据
const graphNodes = computed(() => {
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) {
recordRelation.value.noRelationVertices.forEach(node => {
if (!nodesMap.has(node.id)) {
nodesMap.set(node.id, {
id: node.id,
name: node.name,
// 可以根据需要添加 category 或其他样式属性
// name: node.name,
originDate: node.date,
originName: node.name,
name: dayjs(node.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50], // 根据图标实际大小调整
itemStyle: {
color: "#91cc75" // 例如:无关联节点用绿色区分
}
color: "#91cc75"
},
label: { ...labelStyle } // 应用标签样式
});
}
});
}
// 2. 处理关联节点 (relationVoList 中的 fromVertex 和 toVertex)
// 2. 处理关联节点
if (recordRelation.value.relationVoList) {
recordRelation.value.relationVoList.forEach(rel => {
const from = rel.fromVertex;
......@@ -152,20 +293,30 @@ const graphNodes = computed(() => {
if (from && !nodesMap.has(from.id)) {
nodesMap.set(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: {
color: "#5470c6" // 例如:有关联节点用蓝色
}
color: "#5470c6"
},
label: { ...labelStyle } // 应用标签样式
});
}
if (to && !nodesMap.has(to.id)) {
nodesMap.set(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: {
color: "#5470c6"
}
},
label: { ...labelStyle } // 应用标签样式
});
}
});
......@@ -174,17 +325,38 @@ const graphNodes = computed(() => {
return Array.from(nodesMap.values());
});
// 【修改】处理图表连线数据
const graphLinks = computed(() => {
if (!recordRelation.value.relationVoList) return [];
return recordRelation.value.relationVoList.map(rel => {
const relationType = rel.edgeInfo ? rel.edgeInfo.value : "";
const style = getRelationStyle(relationType);
return {
source: rel.fromVertex.id,
target: rel.toVertex.id,
// 将 edgeInfo 挂载到 data 上,以便在 formatter 中访问
// 将样式信息挂载到 label 或 data 上,供 formatter 和 lineStyle 使用
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 () => {
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 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 () => {
loading.value = true;
......@@ -225,15 +507,13 @@ const fetchSanRecord = async () => {
try {
const res = await getSanRecord(params);
if (res && res.length > 0) {
sanctionList.value = res
.map(item => ({
sanctionList.value = res.map(item => ({
id: item.sanRecordId,
date: item.sanRecordDate,
title: item.sanRecordName,
count: item.cnEntitiesNum,
unit: "家中国实体"
}))
.reverse();
}));
if (sanctionList.value.length > 0) {
currentSanctionId.value = sanctionList.value[0].id;
......@@ -257,11 +537,6 @@ const handleSanctionSelect = id => {
currentSanctionId.value = id;
};
const dateRange = ref(["2025-01-01", "2025-12-31"]);
const sanctionList = ref([]);
const currentSanctionId = ref(5);
const sanTypeId = ref("");
onMounted(() => {
sanTypeId.value = route.query.sanTypeId || "";
fetchSanRecord();
......@@ -338,7 +613,6 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.item-left {
......@@ -423,7 +697,135 @@ onMounted(() => {
}
}
.main-association {
padding-top: 12px !important;
justify-content: flex-start !important;
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>
......@@ -59,8 +59,13 @@
</div>
</AnalysisBox>
</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="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">
......@@ -76,24 +81,37 @@
<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">{{ 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 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">{{ 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 class="content-item">
<div class="item-label">制裁原因:</div>
<div class="item-desc">
<div class="item-file" v-for="item in vertexInfo.sanReasonList" :key="item">{{ item }}</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">
<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>
......@@ -109,19 +127,54 @@
</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>
<!-- <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>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import GraphChart from "@/components/base/GraphChart/index.vue";
import RelationChart from "@/components/base/RelationChart/index.vue";
import emptyImg from "../assets/empty.png";
import markIcon from "../assets/icon-mark.png"; // 引入图标
import { getSanRecord, getRecordRelation, getVertexInfo } from "@/api/finance";
import { useRoute } from "vue-router";
import openIcon from "../../../../assets/icons/icon-open.png";
import { getSanRecord, getRecordRelation, getVertexInfo, getEdgeInfo } from "@/api/finance";
import { useRoute, useRouter } from "vue-router";
import dayjs from "dayjs";
const route = useRoute();
const router = useRouter();
// 定义颜色映射
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 => {
switch (relationType) {
......@@ -200,6 +266,20 @@ const recordRelation = ref({ noRelationVertices: [], relationVoList: [] });
const graphNodes = computed(() => {
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. 处理无关联节点
if (recordRelation.value.noRelationVertices) {
recordRelation.value.noRelationVertices.forEach(node => {
......@@ -207,12 +287,15 @@ const graphNodes = computed(() => {
nodesMap.set(node.id, {
id: node.id,
// name: node.name,
originDate: node.date,
originName: node.name,
name: dayjs(node.date).format("YYYY年MM月DD日") + " " + "SDN清单更新",
symbol: `image://${markIcon}`, // 设置自定义图标
symbolSize: [50, 50], // 根据图标实际大小调整
itemStyle: {
color: "#91cc75"
}
},
label: { ...labelStyle } // 应用标签样式
});
}
});
......@@ -228,12 +311,15 @@ const graphNodes = computed(() => {
nodesMap.set(from.id, {
id: from.id,
// 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: {
color: "#5470c6"
}
},
label: { ...labelStyle } // 应用标签样式
});
}
......@@ -246,7 +332,8 @@ const graphNodes = computed(() => {
symbolSize: [50, 50],
itemStyle: {
color: "#5470c6"
}
},
label: { ...labelStyle } // 应用标签样式
});
}
});
......@@ -279,11 +366,14 @@ const graphLinks = computed(() => {
},
lineStyle: {
color: style.lineColor, // 连线颜色
width: 2
width: 1,
type: "solid"
// curveness: 0.1 // 稍微有点弧度可能更好看
},
// 额外存储原始数据,以备后用
relationType: relationType
relationType: relationType,
edgeInfo: rel.edgeInfo,
originInfo: rel
};
});
});
......@@ -312,19 +402,136 @@ const fetchRecordRelation = async () => {
};
const vertexInfo = 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 => {
console.log("节点点击", node);
curNode.value = node.data;
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;
visible.value = true;
nodeVisible.value = true;
} else {
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 的函数
......@@ -394,15 +601,13 @@ const fetchSanRecord = async () => {
try {
const res = await getSanRecord(params);
if (res && res.length > 0) {
sanctionList.value = res
.map(item => ({
sanctionList.value = res.map(item => ({
id: item.sanRecordId,
date: item.sanRecordDate,
title: item.sanRecordName,
count: item.cnEntitiesNum,
unit: "家中国实体"
}))
.reverse();
}));
if (sanctionList.value.length > 0) {
currentSanctionId.value = sanctionList.value[0].id;
......@@ -502,7 +707,6 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.item-left {
......@@ -592,27 +796,184 @@ onMounted(() => {
gap: 16px;
}
.dialog-content {
position: relative;
padding: 20px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 16px;
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 {
display: flex;
justify-content: flex-start;
align-items: center;
align-items: flex-start;
gap: 8px;
.item-label {
font-size: 146x;
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: 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,
"name": "美国以伊朗石油非法贸易为由实施制裁,多家中国企业被列入制裁名单",
"domainList": [
"海洋"
],
"relyFileList": [
{
"id": null,
"name": "第13382号行政命令"
"code": 200,
"message": "操作成功",
"success": true,
"data": [
{
"vertex": {
"id": "2145",
"name": "OFAC将38个实体及4名个人列入SDN清单,涉及中国关联主体",
"highlight": true
},
"edgeReasonList": [
{
"id": null,
"name": "第14530号行政命令"
}
],
"relySanList": [
"reason": "依托文件",
"reasonDetail": [
{
"sanTypeId": 2,
"id": 2145,
"title": "OFAC将38个实体及4名个人列入SDN清单,涉及中国关联主体",
"postDate": "2025-10-14"
}
],
"sanReasonList": [
"参与了从伊朗购买、收购、销售、运输或营销石油化工产品",
"参与了与采购、获取、销售、运输或销售伊朗石油及石油制品相关的重大交易"
],
"addObjectList": [
"name": "第13382号行政命令",
"highlight": false
},
{
"key": "机构",
"value": 3
"name": "第13388号行政命令",
"highlight": true
}
],
"delObjectList": [
{
"key": "人物",
"value": 1
]
}
],
"sanList": [
{
"entityId": "91310115MA1HBB8PXH",
"entityName": "SHANGHAI QIZHANG SHIP MANAGEMENT CO., LTD.",
"entityNameZh": "上海启章船舶管理有限公司",
"entityTypeId": 2,
"entityTypeName": "机构",
"domainNames": [
"海洋"
]
},
{
"entityId": "71180883",
"entityName": "ALL WIN SHIPPING MANAGEMENT LIMITED",
"entityNameZh": "誠安船舶管理有限公司",
"entityTypeId": 2,
"entityTypeName": "机构",
"domainNames": [
"海洋"
]
"vertex": {
"id": "2146",
"name": "OFAC将15个中国实体及3名中国籍个人列入SDN清单",
"highlight": true
},
"edgeReasonList": [
{
"reason": "依托文件",
"reasonDetail": [
{
"entityId": "91370211MAEBUA7E2Q",
"entityName": "QINGDAO OCEAN KIMO SHIP MANAGEMENT CO LTD",
"entityNameZh": "青岛明洋凯茂船舶管理有限公司",
"entityTypeId": 2,
"entityTypeName": "机构",
"domainNames": [
"海洋"
"name": "第13388号行政命令",
"highlight": true
}
]
}
]
}
]
......
......@@ -25,9 +25,9 @@
</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="最新动态" />
<el-col :span="16">
<el-col :span="16" style="padding: 0">
<custom-container titleType="primary" title="最新出口管制政策" :titleIcon="houseIcon" height="450px">
<template #header-right>
<el-button type="primary" @click="handleToEntityList" link>
......@@ -132,7 +132,7 @@
</template>
</custom-container>
</el-col>
<el-col :span="8">
<el-col :span="8" style="padding: 0">
<RiskSignal
:list="warningList"
@item-click="handleToRiskSignalDetail"
......@@ -144,7 +144,7 @@
</el-col>
</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="资讯要闻" />
</el-row>
<!-- <el-col :span="12">
......@@ -187,9 +187,9 @@
</custom-container> -->
</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="数据总览" />
<el-col :span="24">
<el-col :span="24" style="padding: 0">
<custom-container title="发布频次统计" :titleIcon="box3Icon" height="420px">
<template #default>
<div class="box3">
......@@ -235,7 +235,11 @@
</div>
<div class="box3-content">
<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 label="发布次数" width="300">
<template #default="scope">
......@@ -307,8 +311,8 @@
</el-col>
</el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8">
<el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8" style="padding-left: 0">
<custom-container title="制裁领域分布" :titleIcon="radarIcon" height="540px">
<template #header-right>
<el-checkbox v-model="domainChecked" label="50%规则" size="large" />
......@@ -328,7 +332,7 @@
</template>
</custom-container>
</el-col>
<el-col :span="16">
<el-col :span="16" style="padding-right: 0">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="540px">
<template #header-right>
<div style="display: flex; align-items: center; gap: 16px">
......@@ -355,7 +359,7 @@
</el-col>
</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" />
<div class="resource-tabs">
<div
......@@ -369,7 +373,7 @@
</div>
</div>
<template v-if="activeResourceTab === 'entity'">
<el-col :span="8">
<el-col :span="8" style="padding: 0">
<custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px">
<template #default>
<div class="box4">
......@@ -418,7 +422,7 @@
</template>
</custom-container>
</el-col>
<el-col :span="16">
<el-col :span="16" style="padding: 0">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right>
<div class="box5-header-right">{{ total }}家实体</div>
......@@ -542,7 +546,7 @@
</el-col>
</template>
<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> -->
......@@ -781,7 +785,8 @@ const handleToRiskSignalDetail = item => {
const routeData = router.resolve({
path: "/finance/singleSanction",
query: {
id: item.sanId
id: item.sanId,
sanTypeId: item.sanTypeId
}
});
// 打开新页面
......@@ -1514,6 +1519,7 @@ const fetchSocialMediaInfo = async () => {
if (data && Array.isArray(data)) {
// console.log(data);
socialMediaList.value = data.map(item => ({
...item,
avatar: item.personImage,
name: item.personName,
time: formatTime(item.time),
......@@ -1546,12 +1552,12 @@ const fetchNewsInfo = async () => {
};
const handlePerClick = item => {
// console.log("点击了社交媒体消息:", item);
console.log("点击了社交媒体消息:", item);
window.sessionStorage.setItem("curTabName", item.name);
const route = router.resolve({
path: "/characterPage",
query: {
type: item.personType || [1, 2, 3][Math.floor(Math.random() * 3)],
type: item.personType,
personId: item.personId
}
});
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论