提交 95f8d886 authored 作者: coderBryanFu's avatar coderBryanFu

fix:概览页打压遏制时间线模块修改

...@@ -20,7 +20,7 @@ export function getBillInfo(params) { ...@@ -20,7 +20,7 @@ export function getBillInfo(params) {
export function getBillPerson(params) { export function getBillPerson(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/bill/billInfoBean/person/${params.billId}`, url: `/api/billInfoBean/person/${params.billId}`,
params, params,
}) })
} }
......
...@@ -111,7 +111,7 @@ export function getBillPostOrg(params) { ...@@ -111,7 +111,7 @@ export function getBillPostOrg(params) {
export function getBillProcess(params) { export function getBillProcess(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/bill/BillOverview/billsProcess/${params.year}`, url: `/api/BillOverview/billsProcess/${params.year}`,
}) })
} }
...@@ -119,7 +119,7 @@ export function getBillProcess(params) { ...@@ -119,7 +119,7 @@ export function getBillProcess(params) {
export function getBills(params, signal) { export function getBills(params, signal) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/bill/BillOverview/bills`, url: `/api/BillOverview/bills`,
params, params,
signal signal
}) })
...@@ -129,7 +129,7 @@ export function getBills(params, signal) { ...@@ -129,7 +129,7 @@ export function getBills(params, signal) {
export function getBillsPerson(params, signal) { export function getBillsPerson(params, signal) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/bill/BillOverview/billsPerson`, url: `/api/BillOverview/billsPerson`,
params, params,
signal signal
}) })
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div :class="{ <div :class="{
itemLeftStatus1: item[props.riskLevel] === '特别重大', itemLeftStatus1: item[props.riskLevel] === '特别重大',
itemLeftStatus2: item[props.riskLevel] === '重大风险', itemLeftStatus2: item[props.riskLevel] === '重大风险',
itemLeftStatus3: item[props.riskLevel] === '较大风险', itemLeftStatus3: item[props.riskLevel] === '较大风险' || item[props.riskLevel] === '中等风险',
itemLeftStatus4: item[props.riskLevel] === '一般风险' || !item[props.riskLevel], itemLeftStatus4: item[props.riskLevel] === '一般风险' || !item[props.riskLevel],
itemLeftStatus5: item[props.riskLevel] === '低风险', itemLeftStatus5: item[props.riskLevel] === '低风险',
}"> }">
......
<template> <template>
<div class="search-container" v-show="!isShow"> <div class="search-container" v-show="!isShow">
<div class="search-type-tabs" v-if="enableBillTypeSwitch"> <div class="search-type-tabs" v-if="enableBillTypeSwitch">
<div class="search-type-tab" :class="{ active: billSearchType === 'federal' }" <div
@click="handleChangeBillSearchType('federal')"> class="search-type-tab"
:class="{ active: billSearchType === 'federal' }"
@click="handleChangeBillSearchType('federal')"
>
联邦议会 联邦议会
</div> </div>
<div class="search-type-tab" :class="{ active: billSearchType === 'state' }" <div
@click="handleChangeBillSearchType('state')"> class="search-type-tab"
:class="{ active: billSearchType === 'state' }"
@click="handleChangeBillSearchType('state')"
>
州议会 州议会
</div> </div>
</div> </div>
<div class="search-main" :class="{ 'search-main-with-tabs': enableBillTypeSwitch }"> <div class="search-main" :class="{ 'search-main-with-tabs': enableBillTypeSwitch }">
<input v-model="store.searchBillText" :placeholder="placeholder" @keyup.enter="handleSearch" <input v-model="store.searchBillText" :placeholder="placeholder" @keyup.enter="handleSearch" class="search-input" />
class="search-input" />
<div class="search-btn" @click="handleSearch"> <div class="search-btn" @click="handleSearch">
<img src="@/assets/icons/search-icon.png" alt /> <img src="@/assets/icons/search-icon.png" alt />
搜索 搜索
...@@ -32,7 +37,7 @@ ...@@ -32,7 +37,7 @@
</div> </div>
</div> </div>
<div class="btn" @click="handleToPosi('position2')"> <div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div> <div class="btn-text">资讯要闻</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt /> <img src="@/assets/icons/arrow-right-icon.png" alt />
</div> </div>
...@@ -62,14 +67,7 @@ import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; ...@@ -62,14 +67,7 @@ import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
const store = useWrittingAsstaintStore(); const store = useWrittingAsstaintStore();
const router = useRouter(); const router = useRouter();
const { const { countInfo, containerRef, placeholder, areaName, enableBillTypeSwitch, defaultBillSearchType } = defineProps({
countInfo,
containerRef,
placeholder,
areaName,
enableBillTypeSwitch,
defaultBillSearchType
} = defineProps({
countInfo: { countInfo: {
type: Array, type: Array,
default: () => [] default: () => []
...@@ -138,11 +136,9 @@ watchEffect(() => { ...@@ -138,11 +136,9 @@ watchEffect(() => {
if (isShow.value) { if (isShow.value) {
homeMainRef.value.classList.add("scroll-main"); homeMainRef.value.classList.add("scroll-main");
homeMainRef.value.classList.add("scrollHomeMain"); homeMainRef.value.classList.add("scrollHomeMain");
} else { } else {
homeMainRef.value.classList.remove("scroll-main"); homeMainRef.value.classList.remove("scroll-main");
homeMainRef.value.classList.remove("scrollHomeMain"); homeMainRef.value.classList.remove("scrollHomeMain");
} }
store.changeIsShowSearchBar(isShow.value); store.changeIsShowSearchBar(isShow.value);
......
<script setup>
</script>
<template>
<div class="flex-display box">
<div class="img"></div>
<div class="flex-fill txt text-tip-1">
<slot></slot>
</div>
<div class="arrow"><span></span></div>
</div>
</template>
<style scoped lang="scss">
@use '@/styles/common.scss';
.box {
background-color: var(--color-primary-2);
flex-direction: row;
gap: 10px;
justify-content: center;
align-items: center;
padding: 6px 12px;
border-radius: 4px;
border: 1px solid var(--color-primary-10);
color: var(--color-primary-100);
}
.img {
width: 19px;
height: 20px;
background-image: url("@/assets/icons/model.png");
}
.txt {
flex: 1;
min-width: 0;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;
max-width: 100%;
}
.arrow {
border-radius: 50%;
min-width: 24px;
width: 24px;
height: 24px;
background: var(--color-primary-10);
display: flex;
align-items: center;
justify-content: center;
span {
font-size: 22px;
font-weight: bold;
position: relative;
top: -3px;
/* 向上偏移2px */
}
}
</style>
\ No newline at end of file
...@@ -7,9 +7,14 @@ ...@@ -7,9 +7,14 @@
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
} }
.flex-display{ .flex-display {
display: flex; display: flex;
} }
.flex-fill {
flex: 1;
}
// 文本超出指定行数省略号显示 // 文本超出指定行数省略号显示
@mixin text-ellipsis($line-clamp) { @mixin text-ellipsis($line-clamp) {
overflow: hidden; overflow: hidden;
...@@ -26,64 +31,67 @@ ...@@ -26,64 +31,67 @@
word-break: break-word; word-break: break-word;
/* 允许单词换行 */ /* 允许单词换行 */
} }
//禁止换行 //禁止换行
.text-nowrap{ .text-nowrap {
white-space: nowrap; white-space: nowrap;
} }
/***文本样式***/ /***文本样式***/
.text-base{ .text-base {
color: var(--color-primary-80); color: var(--color-primary-80);
} }
//0级标题 //0级标题
.text-title-0{ .text-title-0 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 32px; font-size: 32px;
} }
.text-title-0-bold{ .text-title-0-bold {
@extend .text-title-0; @extend .text-title-0;
font-weight: Bold; font-weight: Bold;
} }
.text-title-0-show{
.text-title-0-show {
@extend .text-title-0; @extend .text-title-0;
font-size: 48px; font-size: 48px;
font-family: "YouSheBiaoTiHei"; font-family: "YouSheBiaoTiHei";
} }
//1级标题 //1级标题
.text-title-1{ .text-title-1 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 24px; font-size: 24px;
} }
.text-title-1-bold{ .text-title-1-bold {
@extend .text-title-1; @extend .text-title-1;
font-weight: Bold; font-weight: Bold;
} }
.text-title-1-show{ .text-title-1-show {
@extend .text-title-1; @extend .text-title-1;
font-size: 30px; font-size: 30px;
font-family: "YouSheBiaoTiHei"; font-family: "YouSheBiaoTiHei";
} }
//2级标题 //2级标题
.text-title-2{ .text-title-2 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 20px; font-size: 20px;
line-height:26px; line-height: 26px;
} }
.text-title-2-bold{ .text-title-2-bold {
@extend .text-title-2; @extend .text-title-2;
font-weight: Bold; font-weight: Bold;
} }
.text-title-2-show{ .text-title-2-show {
@extend .text-title-2; @extend .text-title-2;
font-size: 24px; font-size: 24px;
font-family: "YouSheBiaoTiHei"; font-family: "YouSheBiaoTiHei";
...@@ -91,19 +99,19 @@ ...@@ -91,19 +99,19 @@
} }
//3级标题 //3级标题
.text-title-3{ .text-title-3 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 18px; font-size: 18px;
line-height:24px; line-height: 24px;
} }
.text-title-3-bold{ .text-title-3-bold {
@extend .text-title-3; @extend .text-title-3;
font-weight: Bold; font-weight: Bold;
} }
.text-title-3-show{ .text-title-3-show {
@extend .text-title-3; @extend .text-title-3;
font-size: 20px; font-size: 20px;
font-family: "YouSheBiaoTiHei"; font-family: "YouSheBiaoTiHei";
...@@ -111,58 +119,60 @@ ...@@ -111,58 +119,60 @@
} }
//正文 //正文
.text-regular{ .text-regular {
@extend .text-base; @extend .text-base;
font-size: 16px; font-size: 16px;
line-height:30px; line-height: 30px;
} }
//正文-加粗 //正文-加粗
.text-bold{ .text-bold {
@extend .text-base; @extend .text-base;
font-weight: Bold; font-weight: Bold;
} }
//正文-紧凑 //正文-紧凑
.text-compact{ .text-compact {
@extend .text-base; @extend .text-base;
font-size: 16px; font-size: 16px;
line-height:24px; line-height: 24px;
} }
.text-compact-bold{ .text-compact-bold {
@extend .text-base; @extend .text-base;
font-size: 16px; font-size: 16px;
line-height:24px; line-height: 24px;
font-weight: Bold; font-weight: Bold;
} }
//1级提示文字 //1级提示文字
.text-tip-1{ .text-tip-1 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 16px; font-size: 16px;
line-height:24px; line-height: 24px;
} }
.text-tip-1-bold{ .text-tip-1-bold {
@extend .text-tip-1; @extend .text-tip-1;
font-weight: Bold; font-weight: Bold;
} }
//2级提示文字 //2级提示文字
.text-tip-2{ .text-tip-2 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 14px; font-size: 14px;
line-height:22px; line-height: 22px;
} }
.text-tip-2-bold{ .text-tip-2-bold {
@extend .text-tip-2; @extend .text-tip-2;
font-weight: Bold; font-weight: Bold;
} }
//3级提示文字 //3级提示文字
.text-tip-3{ .text-tip-3 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--color-primary-90);
font-size: 12px; font-size: 12px;
......
@use "common"; @use "common";
/***通用页面***/ /***通用页面***/
.common-page { .common-page {
font-family: "Microsoft Yahei", "PingFang SC", sans-serif;
margin: 16px auto; margin: 16px auto;
width: 1600px; width: 1600px;
align-items: center; align-items: center;
......
@use "common"; @use "common";
/***没有nav下划线***/ /***通用描述组件***/
.common-descriptions .el-descriptions__label{ .common-descriptions {
.el-descriptions__label {
@extend .text-tip-1-bold; @extend .text-tip-1-bold;
color: var(--text-primary-80-color); color: var(--text-primary-80-color);
} }
.common-descriptions .el-descriptions__content{
.common-descriptions .el-descriptions__content {
@extend .text-tip-1; @extend .text-tip-1;
color: var(--text-primary-80-color); color: var(--text-primary-80-color);
}
} }
\ No newline at end of file
@use "common"; @use "common";
/***没有nav下划线***/ /***没有nav下划线***/
.tabs-nav-no-wrap .el-tabs__nav-wrap::after{ .tabs-nav-no-wrap {
.el-tabs__nav-wrap::after {
height: 0px !important; height: 0px !important;
width: 0px !important; width: 0px !important;
}
} }
/***nav as card***/ /***nav as card***/
.tabs-header-as-card .el-tabs__header:not(.disinheritance .el-tabs__header) { .tabs-header-as-card {
.el-tabs__header:not(.disinheritance .el-tabs__header) {
@extend .background-as-card; @extend .background-as-card;
padding: 2px; padding: 2px;
}
} }
/***作为按钮样式的tabs-bar***/ /***作为按钮样式的tabs-bar***/
/*选中无下划线*/ /*选中无下划线*/
.tabs-bar-as-btn .el-tabs__active-bar:not(.disinheritance .el-tabs__active-bar) { .tabs-bar-as-btn {
.el-tabs__active-bar:not(.disinheritance .el-tabs__active-bar) {
height: 0px; height: 0px;
} }
/*定义字体*/ /*定义字体*/
.tabs-bar-as-btn .el-tabs__item:not(.disinheritance .el-tabs__item) { .el-tabs__item:not(.disinheritance .el-tabs__item) {
font-size: 20px; font-size: 20px;
font-family: "Source Han Sans CN-Regular"; font-family: "Source Han Sans CN-Regular";
font-weight: Regular; font-weight: Regular;
line-height: 26px; line-height: 26px;
height: 50px; height: 50px;
} }
/*激活时按钮样式*/ /*激活时按钮样式*/
.tabs-bar-as-btn .el-tabs__item.is-active:not(.disinheritance .el-tabs__item){ .el-tabs__item.is-active:not(.disinheritance .el-tabs__item) {
font-size: 24px; font-size: 24px;
font-family: "Source Han Sans CN-Bold"; font-family: "Source Han Sans CN-Bold";
...@@ -40,12 +45,30 @@ ...@@ -40,12 +45,30 @@
border-color: var(--color-primary-35); border-color: var(--color-primary-35);
border-radius: 10px; border-radius: 10px;
background-color: var(--color-primary-2); background-color: var(--color-primary-2);
}
} }
/***tabs-bar左边悬浮***/ /***tabs-bar左边悬浮***/
.left-float-nav-tabs .el-tabs__item{ .left-float-nav-tabs {
position:relative;
} // .el-tabs__header {
.left-float-nav-tabs .el-tabs__item.is-active{
background-color: aquamarine; position: relative;
overflow: visible;
transform: translateZ(0);
/* 双重保障 */
/* 创建新的渲染层 */
/* 创建新的层叠上下文 */
.el-tabs__header {
// position: absolute;
left: 0;
top: 0;
z-index: 999;
background-color: red;
// margin-left: -20px;
}
// }
} }
\ No newline at end of file
...@@ -74,7 +74,12 @@ ...@@ -74,7 +74,12 @@
<span>美对华领域打压遏制最新动态</span> <span>美对华领域打压遏制最新动态</span>
</div> </div>
<div class="title-right-select"> <div class="title-right-select">
<el-select v-model="selectedFieldForLatest" placeholder="全部领域" class="field-select"> <el-select
v-model="selectedFieldForLatest"
@change="handleFieldChange"
placeholder="全部领域"
class="field-select"
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
...@@ -131,7 +136,7 @@ ...@@ -131,7 +136,7 @@
<simple-pagination <simple-pagination
v-model:current-page="newsCurrentPage" v-model:current-page="newsCurrentPage"
:page-size="pageSize" :page-size="pageSize"
:total="newsList.length" :total="allNewsList.length"
@page-change="handleNewsPageChange" @page-change="handleNewsPageChange"
/> />
</div> </div>
...@@ -215,7 +220,7 @@ ...@@ -215,7 +220,7 @@
<div class="bottom-item"> <div class="bottom-item">
<div class="bottom-item-title"> <div class="bottom-item-title">
<img :src="icon4" alt="" /> <img :src="icon4" alt="" />
<span>美对领域打压遏制时间线</span> <span>美对领域打压遏制时间线</span>
</div> </div>
<el-select <el-select
v-model="selectedFieldTimeline" v-model="selectedFieldTimeline"
...@@ -408,6 +413,12 @@ const newsList = ref([]); ...@@ -408,6 +413,12 @@ const newsList = ref([]);
const newsCurrentPage = ref(1); const newsCurrentPage = ref(1);
const pageSize = ref(5); // 每页显示 5 条 const pageSize = ref(5); // 每页显示 5 条
const handleFieldChange = domainId => {
console.log("领域改变", domainId);
console.log("领域值 =>", selectedFieldForLatest.value);
getUSGovernmentLatestDynamicData();
};
// 总页数 // 总页数
const totalPages = computed(() => { const totalPages = computed(() => {
return Math.ceil(newsList.value.length / pageSize.value) || 1; return Math.ceil(newsList.value.length / pageSize.value) || 1;
...@@ -445,10 +456,11 @@ const handleNextPage = () => { ...@@ -445,10 +456,11 @@ const handleNextPage = () => {
const getUSGovernmentLatestDynamicData = async () => { const getUSGovernmentLatestDynamicData = async () => {
try { try {
const params = { const params = {
orgId: !!deptValueForLatest.value ? deptValueForLatest.value : null, domainId: !!selectedFieldForLatest.value ? selectedFieldForLatest.value : null,
pageSize: 50, pageSize: 50,
currentPage: newsCurrentPage.value currentPage: newsCurrentPage.value
}; };
console.log("领域值 =>", selectedFieldForLatest.value);
const res = await getUSGovernmentLatestDynamic(params); const res = await getUSGovernmentLatestDynamic(params);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
// 将接口数据转换为 newsList 需要的格式 // 将接口数据转换为 newsList 需要的格式
......
...@@ -9,13 +9,18 @@ ...@@ -9,13 +9,18 @@
</div> </div>
<div class="cards-mask"> <div class="cards-mask">
<div class="item-box" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }"> <div class="item-box" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }">
<div class="header-item" :class="{ <div
class="header-item"
:class="{
headerItem1: index % 5 === 0, headerItem1: index % 5 === 0,
headerItem2: index % 5 === 1, headerItem2: index % 5 === 1,
headerItem3: index % 5 === 2, headerItem3: index % 5 === 2,
headerItem4: index % 5 === 3, headerItem4: index % 5 === 3,
headerItem5: index % 5 === 4 headerItem5: index % 5 === 4
}" v-for="(item, index) in headerList" :key="index"> }"
v-for="(item, index) in headerList"
:key="index"
>
<div class="name">{{ item.elementName }}</div> <div class="name">{{ item.elementName }}</div>
<div class="num">{{ item.num }}</div> <div class="num">{{ item.num }}</div>
</div> </div>
...@@ -32,8 +37,12 @@ ...@@ -32,8 +37,12 @@
<div class="title">{{ "最新动态" }}</div> <div class="title">{{ "最新动态" }}</div>
</div> </div>
<div class="box1-main"> <div class="box1-main">
<div class="box1-item" v-for="(item, index) in box1DataList" :key="index" <div
@click="handleToDecreeDetail(item)"> class="box1-item"
v-for="(item, index) in box1DataList"
:key="index"
@click="handleToDecreeDetail(item)"
>
<div class="box1-item-left">{{ index + 1 }}</div> <div class="box1-item-left">{{ index + 1 }}</div>
<div class="box1-item-right"> <div class="box1-item-right">
<div class="title">{{ item.name }}</div> <div class="title">{{ item.name }}</div>
...@@ -44,13 +53,18 @@ ...@@ -44,13 +53,18 @@
<div class="box1-item-right-footer"> <div class="box1-item-right-footer">
<div class="time">{{ item.postDate }}</div> <div class="time">{{ item.postDate }}</div>
<div class="area-box"> <div class="area-box">
<div class="area" :class="{ <div
class="area"
:class="{
area1: vall.status === '1', area1: vall.status === '1',
area2: vall.status === '2', area2: vall.status === '2',
area3: vall.status === '3', area3: vall.status === '3',
area4: vall.status === '4', area4: vall.status === '4',
area5: vall.status === '5' area5: vall.status === '5'
}" v-for="(vall, idxx) in item.areaList" :key="idxx"> }"
v-for="(vall, idxx) in item.areaList"
:key="idxx"
>
{{ vall.industryName }} {{ vall.industryName }}
</div> </div>
</div> </div>
...@@ -66,30 +80,49 @@ ...@@ -66,30 +80,49 @@
<div class="icon"> <div class="icon">
<img src="./assets/images/box-header-icon2.png" alt="" /> <img src="./assets/images/box-header-icon2.png" alt="" />
</div> </div>
<div class="title">{{ "美对要素打压情况" }}</div> <div class="title">{{ "美对要素打压情况" }}</div>
</div> </div>
<div class="box2-main"> <div class="box2-main">
<div class="inner-box1"> <div class="inner-box1">
<div class="left"> <div class="left">
<div class="left-main"> <div class="left-main">
<el-empty v-if="box2DataList.length === 0" style="padding-top: 80px" description="暂无数据" <el-empty
:image-size="100" /> v-if="box2DataList.length === 0"
<div class="left-item" :class="{ leftItemActive: box2LeftActiveIndex === index }" style="padding-top: 80px"
v-for="(item, index) in box2DataList" :key="index" @click="handleClickBox2Item(index)"> description="暂无数据1"
:image-size="100"
/>
<div
class="left-item"
:class="{ leftItemActive: box2LeftActiveIndex === index }"
v-for="(item, index) in box2DataList"
:key="index"
@click="handleClickBox2Item(index)"
>
<div class="id">{{ index + 1 }}</div> <div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.name }}</div> <div class="text">{{ item.name }}</div>
</div> </div>
</div> </div>
<div class="left-footer" v-if="box2DataList.length !== 0"> <div class="left-footer" v-if="box2DataList.length !== 0">
<el-pagination background layout="prev, pager, next" :total="box2Total" :page-size="box2PageSize" <el-pagination
v-model:current-page="box2CurrentPage" @current-change="handleGetBox2DataList" size="small" background
:pager-count="4" /> layout="prev, pager, next"
:total="box2Total"
:page-size="box2PageSize"
v-model:current-page="box2CurrentPage"
@current-change="handleGetBox2DataList"
size="small"
:pager-count="4"
/>
</div> </div>
</div> </div>
<div class="right" @click="handleToDecreeDetail(box2DetailInfo)"> <div class="right" @click="handleToDecreeDetail(box2DetailInfo)">
<el-empty v-if="box2DataList.length === 0 || !isShowBox2Info" style="padding-top: 80px" <el-empty
description="暂无数据" :image-size="100" /> v-if="box2DataList.length === 0 || !isShowBox2Info"
style="padding-top: 80px"
description="暂无数据"
:image-size="100"
/>
<div v-else> <div v-else>
<div class="title">{{ box2DetailInfo?.name }}</div> <div class="title">{{ box2DetailInfo?.name }}</div>
<div class="tag-box"> <div class="tag-box">
...@@ -99,13 +132,18 @@ ...@@ -99,13 +132,18 @@
</div> </div>
<div class="content">{{ box2DetailInfo?.describe }}</div> <div class="content">{{ box2DetailInfo?.describe }}</div>
<div class="area-box"> <div class="area-box">
<div class="area" :class="{ <div
class="area"
:class="{
area1: item.status === '1', area1: item.status === '1',
area2: item.status === '2', area2: item.status === '2',
area3: item.status === '3', area3: item.status === '3',
area4: item.status === '4', area4: item.status === '4',
area5: item.status === '5' area5: item.status === '5'
}" v-for="(item, index) in box2DetailInfo.areaList" :key="index"> }"
v-for="(item, index) in box2DetailInfo.areaList"
:key="index"
>
{{ item.industryName }} {{ item.industryName }}
</div> </div>
</div> </div>
...@@ -132,23 +170,43 @@ ...@@ -132,23 +170,43 @@
<div class="inner-box1"> <div class="inner-box1">
<div class="left"> <div class="left">
<div class="left-main"> <div class="left-main">
<el-empty v-if="box3DataList.length === 0" style="padding-top: 80px" description="暂无数据" <el-empty
:image-size="100" /> v-if="box3DataList.length === 0"
<div class="left-item" :class="{ leftItemActive: box3LeftActiveIndex === index }" style="padding-top: 80px"
v-for="(item, index) in box3DataList" :key="index" @click="handleClickBox3Item(index)"> description="暂无数据"
:image-size="100"
/>
<div
class="left-item"
:class="{ leftItemActive: box3LeftActiveIndex === index }"
v-for="(item, index) in box3DataList"
:key="index"
@click="handleClickBox3Item(index)"
>
<div class="id">{{ index + 1 }}</div> <div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.name }}</div> <div class="text">{{ item.name }}</div>
</div> </div>
</div> </div>
<div class="left-footer" v-if="box3DataList.length"> <div class="left-footer" v-if="box3DataList.length">
<el-pagination background layout="prev, pager, next" :total="box3Total" :page-size="box3PageSize" <el-pagination
v-model:current-page="box3CurrentPage" @current-change="handleGetBox3DataList" size="small" background
:pager-count="4" /> layout="prev, pager, next"
:total="box3Total"
:page-size="box3PageSize"
v-model:current-page="box3CurrentPage"
@current-change="handleGetBox3DataList"
size="small"
:pager-count="4"
/>
</div> </div>
</div> </div>
<div class="right" @click="handleToDecreeDetail(box3DetailInfo)"> <div class="right" @click="handleToDecreeDetail(box3DetailInfo)">
<el-empty v-if="box3DataList.length === 0 || !isShowBox3Info" style="padding-top: 80px" <el-empty
description="暂无数据" :image-size="100" /> v-if="box3DataList.length === 0 || !isShowBox3Info"
style="padding-top: 80px"
description="暂无数据"
:image-size="100"
/>
<div v-else> <div v-else>
<div class="title">{{ box3DetailInfo?.name }}</div> <div class="title">{{ box3DetailInfo?.name }}</div>
<div class="tag-box"> <div class="tag-box">
...@@ -158,13 +216,18 @@ ...@@ -158,13 +216,18 @@
</div> </div>
<div class="content">{{ box3DetailInfo?.describe }}</div> <div class="content">{{ box3DetailInfo?.describe }}</div>
<div class="area-box"> <div class="area-box">
<div class="area" :class="{ <div
class="area"
:class="{
area1: item.status === '1', area1: item.status === '1',
area2: item.status === '2', area2: item.status === '2',
area3: item.status === '3', area3: item.status === '3',
area4: item.status === '4', area4: item.status === '4',
area5: item.status === '5' area5: item.status === '5'
}" v-for="(item, index) in box3DetailInfo?.areaList" :key="index"> }"
v-for="(item, index) in box3DetailInfo?.areaList"
:key="index"
>
{{ item.industryName }} {{ item.industryName }}
</div> </div>
</div> </div>
...@@ -175,8 +238,12 @@ ...@@ -175,8 +238,12 @@
</div> </div>
</div> </div>
<div class="inner-box2"> <div class="inner-box2">
<el-empty v-if="box3ChartData.length === 0" style="padding-top: 80px" description="暂无数据" <el-empty
:image-size="100" /> v-if="box3ChartData.length === 0"
style="padding-top: 80px"
description="暂无数据"
:image-size="100"
/>
<div class="chart-header">{{ "关键词云" }}</div> <div class="chart-header">{{ "关键词云" }}</div>
<div class="box3Chart" id="box3Chart"></div> <div class="box3Chart" id="box3Chart"></div>
</div> </div>
...@@ -204,9 +271,8 @@ import { ...@@ -204,9 +271,8 @@ import {
const activeDate = inject("activeDate"); const activeDate = inject("activeDate");
const isShowBox2Info = ref(true) const isShowBox2Info = ref(true);
const isShowBox3Info = ref(true) const isShowBox3Info = ref(true);
const getCalculatedDate = type => { const getCalculatedDate = type => {
const now = new Date(); const now = new Date();
...@@ -256,7 +322,7 @@ const handleGetHeaderList = async () => { ...@@ -256,7 +322,7 @@ const handleGetHeaderList = async () => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
headerList.value = res.data; headerList.value = res.data;
} }
} catch (error) { } } catch (error) {}
}; };
// 最新动态 // 最新动态
...@@ -283,7 +349,7 @@ const handleGetBox1Data = async () => { ...@@ -283,7 +349,7 @@ const handleGetBox1Data = async () => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
box1DataList.value = res.data; box1DataList.value = res.data;
} }
} catch (error) { } } catch (error) {}
}; };
const box2DataList = ref([]); const box2DataList = ref([]);
...@@ -304,10 +370,10 @@ const handleGetOrderInfo = async id => { ...@@ -304,10 +370,10 @@ const handleGetOrderInfo = async id => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
box2DetailInfo.value = res.data; box2DetailInfo.value = res.data;
} else { } else {
isShowBox2Info.value = false isShowBox2Info.value = false;
} }
} catch (error) { } catch (error) {
isShowBox2Info.value = false isShowBox2Info.value = false;
} }
}; };
...@@ -326,7 +392,7 @@ const handleGetBox2DataList = async () => { ...@@ -326,7 +392,7 @@ const handleGetBox2DataList = async () => {
box2Total.value = res.data.totalElements; box2Total.value = res.data.totalElements;
handleGetOrderInfo(box2DataList.value[box2LeftActiveIndex.value].id); handleGetOrderInfo(box2DataList.value[box2LeftActiveIndex.value].id);
} }
} catch (error) { } } catch (error) {}
}; };
const handleClickBox2Item = index => { const handleClickBox2Item = index => {
...@@ -351,7 +417,7 @@ const handleGetBox2ChartData = async () => { ...@@ -351,7 +417,7 @@ const handleGetBox2ChartData = async () => {
}; };
}); });
} }
} catch (error) { } } catch (error) {}
}; };
const handleBox2Chart = async () => { const handleBox2Chart = async () => {
await handleGetBox2ChartData(); await handleGetBox2ChartData();
...@@ -378,10 +444,10 @@ const handleGetOrderInfo1 = async id => { ...@@ -378,10 +444,10 @@ const handleGetOrderInfo1 = async id => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
box3DetailInfo.value = res.data; box3DetailInfo.value = res.data;
} else { } else {
isShowBox3Info.value = false isShowBox3Info.value = false;
} }
} catch (error) { } catch (error) {
isShowBox3Info.value = false isShowBox3Info.value = false;
} }
}; };
...@@ -407,7 +473,7 @@ const handleGetBox3DataList = async () => { ...@@ -407,7 +473,7 @@ const handleGetBox3DataList = async () => {
} else { } else {
box3DetailInfo.value = {}; box3DetailInfo.value = {};
} }
} catch (error) { } } catch (error) {}
}; };
const handleClickBox3Item = index => { const handleClickBox3Item = index => {
...@@ -433,7 +499,7 @@ const handleGetBox3ChartData = async () => { ...@@ -433,7 +499,7 @@ const handleGetBox3ChartData = async () => {
}; };
}); });
} }
} catch (error) { } } catch (error) {}
}; };
const handleBox3Chart = async () => { const handleBox3Chart = async () => {
await handleGetBox3ChartData(); await handleGetBox3ChartData();
......
...@@ -185,8 +185,8 @@ ...@@ -185,8 +185,8 @@
<simple-pagination <simple-pagination
v-model:current-page="rankCurrentPage" v-model:current-page="rankCurrentPage"
:page-size="pageSize" :page-size="rankingPageSize"
:total="rankingList.length" :total="allRankingList.length"
@page-change="handleRankPageChange" @page-change="handleRankPageChange"
/> />
</div> </div>
...@@ -426,6 +426,7 @@ const getUSGovernmentSanctionHistoryData = async () => { ...@@ -426,6 +426,7 @@ const getUSGovernmentSanctionHistoryData = async () => {
const loadingJointRank = ref(false); const loadingJointRank = ref(false);
const getUSGovernmentJointSanctionRankData = async () => { const getUSGovernmentJointSanctionRankData = async () => {
loadingJointRank.value = true; loadingJointRank.value = true;
const params = {};
try { try {
const res = await getUSGovernmentJointSanctionRank(); const res = await getUSGovernmentJointSanctionRank();
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
...@@ -494,8 +495,8 @@ const updateDynamicListByPage = () => { ...@@ -494,8 +495,8 @@ const updateDynamicListByPage = () => {
}; };
const updateRankListByPage = () => { const updateRankListByPage = () => {
const start = (rankCurrentPage.value - 1) * pageSize.value; const start = (rankCurrentPage.value - 1) * rankingPageSize.value;
const end = start + pageSize.value; const end = start + rankingPageSize.value;
rankingList.value = allRankingList.value.slice(start, end); rankingList.value = allRankingList.value.slice(start, end);
}; };
// 点击科技要闻-跳转详情页 // 点击科技要闻-跳转详情页
...@@ -792,31 +793,7 @@ const fieldOptions = ref([ ...@@ -792,31 +793,7 @@ const fieldOptions = ref([
{ label: "其他", value: "99" } { label: "其他", value: "99" }
]); ]);
const methodOptions = ref([ const methodOptions = ref([]);
// { label: "全部制裁手段", value: "" },
// { label: "法案", value: "-1" },
// { label: "政令", value: "-2" },
// { label: "实体清单", value: "1" },
// { label: "特别国民指定清单", value: "2" },
// { label: "涉军企业", value: "3" },
// { label: "行业制裁识别清单", value: "4" },
// { label: "无法核实清单", value: "5" },
// { label: "军事最终用户清单", value: "6" },
// { label: "非SDN中国军工企业名单", value: "7" },
// { label: "拒绝往来人员清单", value: "8" },
// { label: "军事最终用途与最终用户规则", value: "9" },
// { label: "欧盟合并制裁清单", value: "10" },
// { label: "英国制裁清单", value: "11" },
// { label: "加拿大合并自主制裁清单", value: "12" },
// { label: "商业管制清单", value: "13" }
// { label: "232调查", value: "14" },
// { label: "Capta List (CAP) - Treasury Department", value: "15" },
// { label: "ITAR Debarred (DTC) - State Department", value: "16" },
// { label: "Nonproliferation Sanctions (ISN) - State Department", value: "17" },
// { label: "Non-SDN Menu-Based Sanctions List (NS-MBS List) - Treasury Department", value: "18" },
// { label: "Palestinian Legislative Council List (PLC) - Treasury Department", value: "19" },
// { label: "经验证最终用户清单", value: "20" }
]);
const handleGetSanList = async () => { const handleGetSanList = async () => {
const params = { const params = {
...@@ -832,16 +809,17 @@ const handleGetSanList = async () => { ...@@ -832,16 +809,17 @@ const handleGetSanList = async () => {
value: item.id value: item.id
}; };
}); });
methodValue.value = "";
} }
} catch (error) {} } catch (error) {}
}; };
watch( // watch(
() => methodValue.value, // () => methodValue.value,
val => { // val => {
getDepartmentListData(); // getDepartmentListData();
} // }
); // );
watch( watch(
() => deptValue.value, () => deptValue.value,
...@@ -854,6 +832,7 @@ const allDynamicData = ref([]); ...@@ -854,6 +832,7 @@ const allDynamicData = ref([]);
const dynamicList = ref([]); const dynamicList = ref([]);
const newsCurrentPage = ref(1); const newsCurrentPage = ref(1);
const pageSize = ref(5); // 每页显示 5 条 const pageSize = ref(5); // 每页显示 5 条
const rankingPageSize = ref(10);
const handleNewsPageChange = page => { const handleNewsPageChange = page => {
newsCurrentPage.value = page; newsCurrentPage.value = page;
...@@ -1669,7 +1648,7 @@ const prev = () => { ...@@ -1669,7 +1648,7 @@ const prev = () => {
// border-radius: 10px; // border-radius: 10px;
// border: 1px solid rgb(234, 236, 238); // border: 1px solid rgb(234, 236, 238);
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
padding: 12px 24px; padding: 10px 24px;
margin-bottom: 12px; margin-bottom: 12px;
box-sizing: border-box; box-sizing: border-box;
......
...@@ -6,10 +6,16 @@ ...@@ -6,10 +6,16 @@
<img src="./assets/leftbtn.png" alt class="left-btn" @click="prev" :class="{ disabled: startIndex === 0 }" /> <img src="./assets/leftbtn.png" alt class="left-btn" @click="prev" :class="{ disabled: startIndex === 0 }" />
<div class="content"> <div class="content">
<div class="carousel-container" :style="{ transform: `translateX(-${startIndex * (307 + 16)}px)` }"> <div class="carousel-container" :style="{ transform: `translateX(-${startIndex * (307 + 16)}px)` }">
<div class="carousel-item" v-for="item, index in carouselList" :key="index"> <div class="carousel-item" v-for="(item, index) in carouselList" :key="index">
<div class="item-top"> <div class="item-top">
<div class="top-img"> <div class="top-img">
<img :src="ele" :class="{ img1: index !== 0 }" alt v-for="(ele, idx) in item.imageList" :key="idx" /> <img
:src="ele"
:class="{ img1: index !== 0 }"
alt
v-for="(ele, idx) in item.imageList"
:key="idx"
/>
</div> </div>
<div class="top-num">{{ item.count }}</div> <div class="top-num">{{ item.count }}</div>
</div> </div>
...@@ -20,13 +26,18 @@ ...@@ -20,13 +26,18 @@
:key="idxx"> :key="idxx">
{{ ele.industryName }} {{ ele.industryName }}
</div> --> </div> -->
<AreaTag v-for="ele, idxx in item.industryList" :key="idxx" :tagName="ele.industryName"></AreaTag> <AreaTag v-for="(ele, idxx) in item.industryList" :key="idxx" :tagName="ele.industryName"></AreaTag>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<img src="./assets/rightbtn.png" alt class="right-btn" @click="next" <img
:class="{ disabled: startIndex >= carouselList.length - 5 }" /> src="./assets/rightbtn.png"
alt
class="right-btn"
@click="next"
:class="{ disabled: startIndex >= carouselList.length - 5 }"
/>
</div> </div>
<!-- 排华联盟分布 --> <!-- 排华联盟分布 -->
...@@ -48,8 +59,13 @@ ...@@ -48,8 +59,13 @@
<div class="item" v-for="(item, index) in countList" :key="index"> <div class="item" v-for="(item, index) in countList" :key="index">
<div class="item-left"> <div class="item-left">
<img :src="item.image" alt /> <img :src="item.image" alt />
<el-tooltip effect="dark" :content="item.zhName" popper-class="common-prompt-popper" placement="top" <el-tooltip
:show-after="500"> effect="dark"
:content="item.zhName"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<span>{{ item.zhName }}</span> <span>{{ item.zhName }}</span>
</el-tooltip> </el-tooltip>
</div> </div>
...@@ -91,13 +107,18 @@ ...@@ -91,13 +107,18 @@
<span>排华联盟最新动态</span> <span>排华联盟最新动态</span>
</div> </div>
<div class="news-content"> <div class="news-content">
<div class="item" v-for="item, index in newsList" :key="index"> <div class="item" v-for="(item, index) in newsList" :key="index">
<div class="item-title"> <div class="item-title">
<img :src="item.image || defaultImg" alt /> <img :src="item.image || defaultImg" alt />
<span @click="handleClick(item)">{{ item.title }}</span> <span @click="handleClick(item)">{{ item.title }}</span>
</div> </div>
<el-tooltip effect="dark" :content="item.content" popper-class="common-prompt-popper" placement="top" <el-tooltip
:show-after="500"> effect="dark"
:content="item.content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="item-content">{{ item.content }}</div> <div class="item-content">{{ item.content }}</div>
</el-tooltip> </el-tooltip>
<div class="item-bottom"> <div class="item-bottom">
...@@ -106,7 +127,7 @@ ...@@ -106,7 +127,7 @@
:key="idx"> :key="idx">
<span>{{ ele.industryName }}</span> <span>{{ ele.industryName }}</span>
</div> --> </div> -->
<AreaTag v-for="ele, idx in item.industryList" :key="idx" :tagName="ele.industryName"></AreaTag> <AreaTag v-for="(ele, idx) in item.industryList" :key="idx" :tagName="ele.industryName"></AreaTag>
</div> </div>
<div class="bottom-right">{{ getTime(item.time) }}</div> <div class="bottom-right">{{ getTime(item.time) }}</div>
</div> </div>
...@@ -123,16 +144,26 @@ ...@@ -123,16 +144,26 @@
<div class="item-title"> <div class="item-title">
<div class="title-left"> <div class="title-left">
<div class="img-list"> <div class="img-list">
<img :src="ele.image || defaultImg" :class="{ img1: index !== 0 }" <img
@error="e => (e.target.src = defaultImg)" alt v-for="(ele, index) in item.countryList" :src="ele.image || defaultImg"
:key="index" /> :class="{ img1: index !== 0 }"
@error="e => (e.target.src = defaultImg)"
alt
v-for="(ele, index) in item.countryList"
:key="index"
/>
</div> </div>
<div class="left-content">{{ getContent(item.countryList) }}</div> <div class="left-content">{{ getContent(item.countryList) }}</div>
</div> </div>
<div class="title-right">{{ item.statementList?.length }}次合作</div> <div class="title-right">{{ item.statementList?.length }}次合作</div>
</div> </div>
<div class="item-content"> <div class="item-content">
<div class="content-list" v-for="(ele, idx) in item.statementList" :key="idx" @click="handleClick(ele)"> <div
class="content-list"
v-for="(ele, idx) in item.statementList"
:key="idx"
@click="handleClick(ele)"
>
<div class="list-left"> <div class="list-left">
<!-- <span>{{ getName(ele.industryList) }}</span> --> <!-- <span>{{ getName(ele.industryList) }}</span> -->
<AreaTag v-if="getName(ele.industryList)" :tagName="getName(ele.industryList)"></AreaTag> <AreaTag v-if="getName(ele.industryList)" :tagName="getName(ele.industryList)"></AreaTag>
...@@ -945,7 +976,7 @@ const initRightDonut = async () => { ...@@ -945,7 +976,7 @@ const initRightDonut = async () => {
value: res.data[i].amount, value: res.data[i].amount,
x: Math.random() * 10, x: Math.random() * 10,
y: Math.random() * 10, y: Math.random() * 10,
symbolSize: res.data[i].amount * 9, symbolSize: res.data[i].amount * 9
}; };
// 先判断data中是否包含上述数据 // 先判断data中是否包含上述数据
...@@ -985,8 +1016,8 @@ const initRightDonut = async () => { ...@@ -985,8 +1016,8 @@ const initRightDonut = async () => {
// text: 'Basic Graph' // text: 'Basic Graph'
}, },
tooltip: { tooltip: {
trigger: 'item', // 针对Graph的节点/边触发(必配,否则弹框不生效) trigger: "item", // 针对Graph的节点/边触发(必配,否则弹框不生效)
triggerOn: 'click', triggerOn: "click",
formatter: function (params) { formatter: function (params) {
// params.data 是当前点击节点的完整数据对象 // params.data 是当前点击节点的完整数据对象
const { name, value } = params.data; const { name, value } = params.data;
...@@ -1018,7 +1049,7 @@ const initRightDonut = async () => { ...@@ -1018,7 +1049,7 @@ const initRightDonut = async () => {
show: true show: true
}, },
edgeSymbol: ["circle", ""], edgeSymbol: ["circle", ""],
edgeSymbolSize: [4, 40], edgeSymbolSize: [4, 80],
edgeLabel: { edgeLabel: {
fontSize: 20 fontSize: 20
}, },
......
...@@ -217,9 +217,9 @@ const props = defineProps({ ...@@ -217,9 +217,9 @@ const props = defineProps({
const tabList = ref([ const tabList = ref([
{ name: "国会法案", active: true }, { name: "国会法案", active: true },
{ name: "国会议员", active: true }, { name: "国会议员", active: false },
{ name: "议员合作关系", active: false }, { name: "议员合作关系", active: false },
{ name: "委员会", active: true } { name: "委员会", active: false }
]); ]);
const activeTabName = ref("国会法案"); const activeTabName = ref("国会法案");
const handleClickTab = tab => { const handleClickTab = tab => {
......
...@@ -329,7 +329,7 @@ const handleClickToDetailO = item => { ...@@ -329,7 +329,7 @@ const handleClickToDetailO = item => {
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/riskSignal"); const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
// 风险信号 // 风险信号
......
...@@ -283,7 +283,7 @@ onMounted(() => { ...@@ -283,7 +283,7 @@ onMounted(() => {
margin: 0 auto; margin: 0 auto;
padding-top: 10px; padding-top: 10px;
.layout-main-header-left-box { .layout-main-header-left-box {
width: 900px; width: 2600px;
.left-box-top { .left-box-top {
height: 64px; height: 64px;
display: flex; display: flex;
......
...@@ -16,16 +16,16 @@ ...@@ -16,16 +16,16 @@
> >
<div class="item-title">{{ item.actionTitle }}</div> <div class="item-title">{{ item.actionTitle }}</div>
</el-tooltip> </el-tooltip>
<div class="right"> <!-- <div class="right">
<div class="risk-tag" :class="item.riskClass">{{ item.riskText }}</div> <div class="risk-tag" :class="item.riskClass">{{ item.riskText }}</div>
<div class="arrow">></div> <div class="arrow">></div>
</div> </div> -->
</div> </div>
</div> </div>
<div class="empty" v-if="displayList.length === 0">暂无进展数据</div> <div class="empty" v-if="displayList.length === 0">暂无进展数据</div>
</div> </div>
<div class="more" v-if="hasMore">查看更多<img :src="arrowDown"/></div> <!-- <div class="more" v-if="hasMore">查看更多<img :src="arrowDown"/></div> -->
</div> </div>
</template> </template>
......
...@@ -145,14 +145,14 @@ ...@@ -145,14 +145,14 @@
</div> --> </div> -->
<AnalysisBox title="法案进展" :showAllBtn="false"> <AnalysisBox title="法案进展" :showAllBtn="false">
<template #header-btn> <template #header-btn>
<div class="progress-header-btns"> <!-- <div class="progress-header-btns">
<div class="btn" :class="{ btnActive: progressMode === 'latest' }" @click="handleSwitchProgressMode('latest')"> <div class="btn" :class="{ btnActive: progressMode === 'latest' }" @click="handleSwitchProgressMode('latest')">
最新进展 最新进展
</div> </div>
<div class="btn" :class="{ btnActive: progressMode === 'early' }" @click="handleSwitchProgressMode('early')"> <div class="btn" :class="{ btnActive: progressMode === 'early' }" @click="handleSwitchProgressMode('early')">
前期进程 前期进程
</div> </div>
</div> </div> -->
</template> </template>
<div class="box2-main"> <div class="box2-main">
<div class="box2-main-center"> <div class="box2-main-center">
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<!-- 数值区域 --> <!-- 数值区域 -->
<el-space :size="6"> <el-space :size="6">
<!-- 数值图标 --> <!-- 数值图标 -->
<div class="item-value-img" /> <div class="item-value-img"></div>
<!-- 数值 --> <!-- 数值 -->
<div class="item-value"> <div class="item-value">
{{ t.value }} {{ t.value }}
......
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ElSpace, ElRadioGroup, ElRadio, ElRadioButton } from 'element-plus';
import * as echarts from 'echarts';
import { getStudyList, getSanctionList } from '@/api/companyPages';
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
import AiTipPane from '@/components/base/panes/AiTipPane.vue'
// 定义组件属性
const props = defineProps({
enterpriseInfo: {
type: Object,
default: {}
}
});
const studyList = ref([])
const sanctionList = ref([])
const studyTypes = ref([])
const studyType = ref()
const chartDom = ref()
let myChart = null
onMounted(async () => {
await intData()
// await initChart()
})
onUnmounted(() => myChart?.dispose())
async function intData() {
const { data } = await getStudyList(props.enterpriseInfo.id)
studyList.value = data ?? []
data.forEach(t => t.time = new Date(t.year, 1, 1))
studyTypes.value = [...new Set(data.map(t => t.type))]
const { data: sanctionData } = await getSanctionList(props.enterpriseInfo.id)
sanctionList.value = sanctionData ?? []
sanctionList.value.forEach(t => t.time = new Date(t.sanctionDate))
if (studyTypes.value.length > 0) {
studyType.value = studyTypes.value[0]
}
updateCharts(studyType.value, studyList.value, sanctionList.value)
}
// 辅助函数:获取制裁年份对应的Y值
function getSanctionYValue(sanctionDate, filteredList, defaultYValue) {
const year = sanctionDate.getFullYear()
const yearData = filteredList.find(d => d.year === year)
return yearData ? yearData.currentValue : defaultYValue
}
// 辅助函数:格式化文本内容,实现智能换行(考虑中英文混合)
function formatContent(content, maxWidth = 26) {
if (!content) return ''
const lines = []
let currentLine = ''
let currentWidth = 0
// 中文字符宽度设为2,英文字符宽度设为1
for (let i = 0; i < content.length; i++) {
const char = content[i]
const charWidth = /[\u4e00-\u9fa5]/.test(char) ? 2 : 1
if (currentWidth + charWidth <= maxWidth) {
currentLine += char
currentWidth += charWidth
} else {
lines.push(currentLine)
currentLine = char
currentWidth = charWidth
}
}
if (currentLine) {
lines.push(currentLine)
}
return lines.join('\n')
}
function updateCharts(type, dataStudy, dataSanction) {
const filteredList = dataStudy.filter(t => t.type === type)
if (!filteredList.length) return
// 销毁现有图表实例
if (myChart) {
myChart.dispose()
}
myChart = echarts.init(chartDom.value)
// y轴单位
const unit = filteredList[0].unit
const yValue = Math.max(...filteredList.map(d => d.currentValue))
// 计算x轴范围,扩大活动空间
const allDates = [
...filteredList.map(t => t.time),
...dataSanction.map(t => t.time)
]
const minDate = new Date(Math.min(...allDates))
const maxDate = new Date(Math.max(...allDates))
// 向前扩展1年,向后扩展1年
minDate.setFullYear(minDate.getFullYear() - 1)
maxDate.setFullYear(maxDate.getFullYear() + 1)
myChart.setOption({
textStyle: {
fontSize: 14
},
grid: {
left: 60,
right: 110,
},
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%'];
},
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100
},
{
start: 0,
end: 100
}
],
xAxis: {
type: 'time',
boundaryGap: false,
min: minDate,
max: maxDate
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
// 显示y轴单位在顶部
name: unit,
nameLocation: 'end'
},
series: [
{
type: 'line',
//从上到下填充颜色
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(128, 181, 255, 0.8)'
},
{
offset: 1,
color: 'rgba(128, 181, 255, 0)'
}
])
},
data: filteredList.map(t => [t.time, t.currentValue])
},
{
type: 'scatter',
tooltip: { show: false },
data: dataSanction.map(t => {
const currentYValue = getSanctionYValue(t.time, filteredList, yValue) + yValue / 3
return [t.time, currentYValue, t]
}),
markLine: {
lineStyle: {
color: '#ff4d4f',
type: 'dashed',
width: 1
},
data: dataSanction.map(t => {
const currentYValue = getSanctionYValue(t.time, filteredList, yValue) + yValue / 3
return [{
coord: [t.time, 0],
symbol: 'none'
}, {
coord: [t.time, currentYValue],
symbol: 'none',
}]
})
},
coordinateSystem: 'cartesian2d',
symbolSize: 1,
label: {
show: true,
position: 'insideBottomLeft',
formatter: function (params) {
const title = params.data[2].sanctionDate;
const content = params.data[2].content;
const formattedContent = formatContent(content);
return `{title|${title}}\n{content|${formattedContent}}`;
},
rich: {
title: {
fontSize: 16,
lineHeight: 22,
color: '#CE4F51'
},
content: {
fontSize: 15,
fontWeight: 'bold',
width: 200, // 限制宽度
color: '#CE4F51',
lineHeight: 22,
overflow: 'break' // 超出宽度自动换行
}
},
backgroundColor: 'rgba(255, 241, 240, 1)',
borderColor: 'rgba(255, 204, 199, 1)',
borderRadius: 4,
padding: [8, 12],
distance: -1
}
}
],
})
}
function handleStudyTypesChange() {
updateCharts(studyType.value, studyList.value, sanctionList.value)
}
</script>
<template>
<analysis-box title="被制裁时间轴">
<template v-slot:header-btn>
<el-radio-group v-model="studyType" @change="handleStudyTypesChange">
<el-radio-button v-for="item in studyTypes" :key="item" :label="item">{{ item }}</el-radio-button>
</el-radio-group>
</template>
<div class="flex-display content-box">
<div ref="chartDom" class="chart-container"></div>
<ai-tip-pane>123</ai-tip-pane>
</div>
</analysis-box>
</template>
<style lang="scss" scoped>
.content-box {
padding: 10px;
gap: 10px;
flex-direction: column;
}
.chart-container {
height: 500px;
width: 100%;
}
</style>
\ No newline at end of file
<script setup lang="ts"> <script setup lang="ts">
import '@/styles/tabs.scss' import '@/styles/tabs.scss'
import { ElTabPane, ElTabs } from 'element-plus'; import { ElTabPane, ElTabs } from 'element-plus';
import SanctionsSituation from './SanctionsSituation.vue';
// 定义组件属性
const props = defineProps({
enterpriseInfo: {
type: Object,
default: {}
}
});
</script> </script>
<template> <template>
<div style="overflow: visible;">
<el-tabs tabPosition="left" class="disinheritance tabs-nav-no-wrap left-float-nav-tabs"> <el-tabs tabPosition="left" class="disinheritance tabs-nav-no-wrap left-float-nav-tabs">
<el-tab-pane label="企业规模"></el-tab-pane> <el-tab-pane label="企业规模">
<el-tab-pane label="市值变化"></el-tab-pane> <sanctions-situation :enterprise-info="enterpriseInfo"></sanctions-situation>
</el-tab-pane>
<!-- <el-tab-pane label="市值变化"></el-tab-pane>
<el-tab-pane label="研发投入"></el-tab-pane> <el-tab-pane label="研发投入"></el-tab-pane>
<el-tab-pane label="市场占比"></el-tab-pane> <el-tab-pane label="市场占比"></el-tab-pane> -->
</el-tabs> </el-tabs>
</div>
</template> </template>
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
</div> </div>
<el-scrollbar> <el-scrollbar>
<div class="common-page"> <div class="common-page">
<el-space wrap :size="16"> <el-space wrap :size="16" fill>
<title-pane :enterprise-info="enterpriseInfo"></title-pane> <title-pane :enterprise-info="enterpriseInfo"></title-pane>
<el-tabs stretch class="tabs-header-as-card tabs-nav-no-wrap tabs-bar-as-btn"> <el-tabs stretch class="tabs-header-as-card tabs-nav-no-wrap tabs-bar-as-btn">
<el-tab-pane label="企业详情"> <el-tab-pane label="企业详情">
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane lazy label="经营情况"> <el-tab-pane lazy label="经营情况">
<operating-pages /> <operating-pages :enterprise-info="enterpriseInfo" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane lazy label="供应链 / 股权"> <el-tab-pane lazy label="供应链 / 股权">
<div class="flex-display"> <div class="flex-display">
......
...@@ -207,7 +207,7 @@ const handleToRiskDetail = (item) => { ...@@ -207,7 +207,7 @@ const handleToRiskDetail = (item) => {
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/riskSignal"); const route = router.resolve("/vieRiskSignal");
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</div> </div>
<!-- 资讯要问 --> <!-- 资讯要问 -->
<div class="ask" id="position2"> <div class="ask" id="position2">
<com-title title="咨询要闻" /> <com-title title="资讯要闻" />
<div class="ask-main"> <div class="ask-main">
<askPage /> <askPage />
</div> </div>
......
...@@ -534,7 +534,7 @@ handleGetDepartmentList(); ...@@ -534,7 +534,7 @@ handleGetDepartmentList();
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/riskSignal"); const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
......
...@@ -50,12 +50,25 @@ ...@@ -50,12 +50,25 @@
<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="次" :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="次"
:color="infoListColor[index]"
@click="handleToEntityListNoId(item)"
/>
</div> </div>
</div> </div>
...@@ -81,8 +94,15 @@ ...@@ -81,8 +94,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">
...@@ -92,16 +112,21 @@ ...@@ -92,16 +112,21 @@
<div class="box1-top-content"> <div class="box1-top-content">
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 发布机构:</span> <span class="box1-top-content-item-title">· 发布机构:</span>
<span class="box1-top-content-item-content">{{ item.postOrgName }}</span> <span class="box1-top-content-item-content">{{ item.postOrgName
}}</span>
</div> </div>
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 生效日期:</span> <span class="box1-top-content-item-title">· 生效日期:</span>
<span class="box1-top-content-item-content">{{ item.postDate }}</span> <span class="box1-top-content-item-content">{{ item.postDate
}}</span>
</div> </div>
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 涉及领域:</span> <span class="box1-top-content-item-title">· 涉及领域:</span>
<div class="box1-top-content-item-tags" v-for="(domainItem, index) in item.domains" <div
:key="index"> class="box1-top-content-item-tags"
v-for="(domainItem, index) in item.domains"
:key="index"
>
<el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag> <el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag>
</div> </div>
</div> </div>
...@@ -110,10 +135,18 @@ ...@@ -110,10 +135,18 @@
<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.entityNameZh || ett.enName)?.match( (ett.entityNameZh || ett.enName)?.match(
...@@ -186,10 +219,14 @@ ...@@ -186,10 +219,14 @@
</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>
...@@ -212,12 +249,19 @@ ...@@ -212,12 +249,19 @@
</custom-container> </custom-container>
</el-col> --> </el-col> -->
<div class="center-center"> <div class="center-center">
<NewsList
<NewsList :newsList="newsList" @item-click="handleNewsInfoClick" @more-click="handleToMoreNews" :newsList="newsList"
content="newsContent" /> @item-click="handleNewsInfoClick"
@more-click="handleToMoreNews"
<MessageBubble :messageList="socialMediaList" @person-click="handlePerClick" imageUrl="avatar" content="newsContent"
@more-click="handleToSocialDetail" /> />
<MessageBubble
: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">
...@@ -227,12 +271,8 @@ ...@@ -227,12 +271,8 @@
</div> </div>
</template> </template>
</custom-container> --> </custom-container> -->
</div> </div>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px"> <el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 528px; margin-top: 64px">
<CustomTitle id="position3" title="数据总览" /> <CustomTitle id="position3" title="数据总览" />
<el-col :span="24"> <el-col :span="24">
...@@ -247,14 +287,19 @@ ...@@ -247,14 +287,19 @@
<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="280" align="center"> <el-table-column label="重点领域" width="280" 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>
...@@ -271,14 +316,19 @@ ...@@ -271,14 +316,19 @@
<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="280" align="center"> <el-table-column label="重点领域" width="280" 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>
...@@ -295,15 +345,19 @@ ...@@ -295,15 +345,19 @@
<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="180"> <el-table-column label="重点领域" width="180">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center; gap: 5px"> <div style="display: flex; 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>
</div> </div>
...@@ -334,7 +388,8 @@ ...@@ -334,7 +388,8 @@
<div style="display: flex; align-items: center; gap: 16px"> <div style="display: flex; align-items: center; gap: 16px">
<el-checkbox v-model="trendChecked" label="50%规则" size="large" /> <el-checkbox v-model="trendChecked" label="50%规则" size="large" />
<el-select v-model="selectedEntityId" placeholder="请选择清单类型" style="width: 160px"> <el-select v-model="selectedEntityId" placeholder="请选择清单类型" style="width: 160px">
<el-option v-for="item in infoList" :key="item.id" :label="item.nameZh" :value="item.id" /> <el-option v-for="item in infoList" :key="item.id" :label="item.nameZh"
:value="item.id" />
</el-select> </el-select>
</div> </div>
</template> </template>
...@@ -348,9 +403,13 @@ ...@@ -348,9 +403,13 @@
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px"> <el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px">
<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>
...@@ -360,18 +419,29 @@ ...@@ -360,18 +419,29 @@
<template #default> <template #default>
<div class="box4"> <div class="box4">
<div style="height: 90%; overflow-y: auto; padding-top: 10px"> <div style="height: 90%; overflow-y: auto; padding-top: 10px">
<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>
...@@ -379,8 +449,12 @@ ...@@ -379,8 +449,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>
...@@ -397,8 +471,15 @@ ...@@ -397,8 +471,15 @@
</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="index" label="序号" width="80" align="center"> <!-- <el-table-column prop="index" label="序号" width="80" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.$index + 1 + (currentPage - 1) * pageSize }} {{ scope.$index + 1 + (currentPage - 1) * pageSize }}
...@@ -408,8 +489,12 @@ ...@@ -408,8 +489,12 @@
<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(
...@@ -417,7 +502,8 @@ ...@@ -417,7 +502,8 @@
)?.[0] )?.[0]
}} }}
</div> </div>
<CommonPrompt :content="scope.row.name" style="flex: 1; overflow: hidden" /> <CommonPrompt :content="scope.row.name"
style="flex: 1; overflow: hidden" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
...@@ -425,7 +511,8 @@ ...@@ -425,7 +511,8 @@
<el-table-column prop="domains" label="涉及领域" min-width="150"> <el-table-column prop="domains" label="涉及领域" min-width="150">
<template #default="scope"> <template #default="scope">
<div class="domain-tags"> <div class="domain-tags">
<el-tag v-for="tag in scope.row.domains" :key="tag" :type="getTagType(tag)">{{ <el-tag v-for="tag in scope.row.domains" :key="tag"
:type="getTagType(tag)">{{
tag tag
}}</el-tag> }}</el-tag>
</div> </div>
...@@ -460,13 +547,19 @@ ...@@ -460,13 +547,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>
...@@ -478,8 +571,15 @@ ...@@ -478,8 +571,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>
...@@ -500,7 +600,8 @@ ...@@ -500,7 +600,8 @@
<div class="left-main"> <div class="left-main">
<el-checkbox-group v-model="checkedTech"> <el-checkbox-group v-model="checkedTech">
<div class="checkbox-grid"> <div class="checkbox-grid">
<el-checkbox v-for="item in techOptions" :key="item.value" :label="item.value">{{ <el-checkbox v-for="item in techOptions" :key="item.value"
:label="item.value">{{
item.label item.label
}}</el-checkbox> }}</el-checkbox>
</div> </div>
...@@ -533,10 +634,12 @@ ...@@ -533,10 +634,12 @@
</div> </div>
<img :src="item.orgLogoUrl || comTitle" alt="" /> <img :src="item.orgLogoUrl || comTitle" alt="" />
<div class="main"> <div class="main">
<div class="main-title" @click="handleTitleClick(item)">{{ item.title }}</div> <div class="main-title" @click="handleTitleClick(item)">{{ item.title }}
</div>
<div class="main-desc">{{ item.desc }}</div> <div class="main-desc">{{ item.desc }}</div>
<div class="tag-box"> <div class="tag-box">
<div v-for="tag in item.tags" :key="tag" class="tag-item">{{ tag }}</div> <div v-for="tag in item.tags" :key="tag" class="tag-item">{{ tag }}
</div>
</div> </div>
<div :class="{ 'count-tag': item.countTag }">{{ item.countTag }}</div> <div :class="{ 'count-tag': item.countTag }">{{ item.countTag }}</div>
...@@ -545,8 +648,14 @@ ...@@ -545,8 +648,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>
...@@ -581,8 +690,12 @@ ...@@ -581,8 +690,12 @@
</div> </div>
</template> </template>
</el-dialog> --> </el-dialog> -->
<RuleSubsidiaryDialog v-model="dialogVisible" :company-name="currentRuleCompany" :total-count="currentRuleCount" <RuleSubsidiaryDialog
:data-list="currentOrgList" /> v-model="dialogVisible"
:company-name="currentRuleCompany"
:total-count="currentRuleCount"
:data-list="currentOrgList"
/>
</div> </div>
<el-dialog v-model="mediaVisible" title="社交媒体信息" width="500" :before-close="handleMediaClose"> <el-dialog v-model="mediaVisible" title="社交媒体信息" width="500" :before-close="handleMediaClose">
<div class="dialog-content"> <div class="dialog-content">
...@@ -834,7 +947,7 @@ onMounted(async () => { ...@@ -834,7 +947,7 @@ onMounted(async () => {
]); ]);
// 交换第二个和第三个元素 // 交换第二个和第三个元素
[dataCount[1], dataCount[2]] = [dataCount[2], dataCount[1]]; [dataCount[1], dataCount[2]] = [dataCount[2], dataCount[1]];
infoList.value = dataCount; infoList.value = dataCount.slice(0, 2);
const entityList = _.map(entitiesDataInfo?.sanEntities ?? [], ({ entityNameZh, entityName }) => { const entityList = _.map(entitiesDataInfo?.sanEntities ?? [], ({ entityNameZh, entityName }) => {
return { name: entityNameZh, enName: entityName }; return { name: entityNameZh, enName: entityName };
...@@ -1254,7 +1367,7 @@ const fetchSanctionList = async () => { ...@@ -1254,7 +1367,7 @@ const fetchSanctionList = async () => {
}); });
totalAll.value = res.totalElements; totalAll.value = res.totalElements;
} }
} catch (error) { } } catch (error) {}
}; };
const handlePageChangeAll = val => { const handlePageChangeAll = val => {
...@@ -1497,9 +1610,9 @@ const handlePerClick = item => { ...@@ -1497,9 +1610,9 @@ const handlePerClick = item => {
// 添加格式化时间的方法 // 添加格式化时间的方法
const formatTime = timeStr => { const formatTime = timeStr => {
// 空值兜底,避免报错 // 空值兜底,避免报错
if (!timeStr) return '暂无时间'; if (!timeStr) return "暂无时间";
// 核心:替换T为空格 // 核心:替换T为空格
return timeStr.replace('T', ' '); return timeStr.replace("T", " ");
}; };
const warningList = ref([]); const warningList = ref([]);
...@@ -1526,7 +1639,7 @@ const handleGetHylyList = async () => { ...@@ -1526,7 +1639,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({
...@@ -1570,7 +1683,7 @@ const handleSanc = item => { ...@@ -1570,7 +1683,7 @@ const handleSanc = item => {
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/riskSignal"); const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
...@@ -2017,7 +2130,6 @@ const handleMediaClick = item => { ...@@ -2017,7 +2130,6 @@ const handleMediaClick = item => {
} }
.box3-content { .box3-content {
// flex: 1; // flex: 1;
.el-progress--line { .el-progress--line {
width: 82px; width: 82px;
...@@ -3413,7 +3525,6 @@ const handleMediaClick = item => { ...@@ -3413,7 +3525,6 @@ const handleMediaClick = item => {
.center-center-news { .center-center-news {
flex-shrink: 0; flex-shrink: 0;
} }
.boxs4 { .boxs4 {
......
<template>
<div class="relation-graph-wrapper">
<div class="graph-controls">
<div
v-for="item in controlBtns"
:key="item.type"
:class="['control-btn', { 'control-btn-active': currentLayoutType === item.type }]"
@click="handleClickControlBtn(item.type)"
>
<img :src="item.icon" alt="" />
</div>
</div>
<div ref="containerRef" class="graph-container"></div>
<div v-if="selectedNode" class="node-popup">
<div class="popup-header">
<img :src="selectedNode.image || defaultIcon" alt="" class="popup-icon" />
<div class="popup-title">{{ selectedNode.name }}</div>
<el-icon class="close-icon" @click="selectedNode = null">
<Close />
</el-icon>
</div>
<div class="popup-body">
<div v-if="selectedNode.isSanctioned" class="tag-row">
<span class="red-dot"></span>
<span class="red-text">被制裁实体</span>
</div>
<p class="desc">{{ selectedNode.description || '暂无描述' }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import G6 from '@antv/g6'
import { Close } from '@element-plus/icons-vue'
import echartsIcon01 from '../assets/echartsicon01.png'
import echartsIcon02 from '../assets/echartsicon02.png'
import echartsIcon03 from '../assets/echartsicon03.png'
import defaultIcon from '../assets/echartsicon03.png'
const props = defineProps({
graphData: {
type: Object,
default: () => ({ nodes: [], links: [] })
},
treeData: {
type: Object,
default: () => null
},
controlActive: {
type: Number,
default: 1
}
})
const emit = defineEmits(['nodeClick', 'layoutChange'])
const containerRef = ref(null)
const graphInstance = ref(null)
const currentLayoutType = ref(1)
const selectedNode = ref(null)
const controlBtns = [
{ type: 1, icon: echartsIcon01, name: '力导向布局' },
{ type: 2, icon: echartsIcon02, name: '树布局' },
{ type: 3, icon: echartsIcon03, name: '环状布局' }
]
const initGraph = (layoutType = 1) => {
if (!containerRef.value) return
destroyGraph()
nextTick(() => {
const container = containerRef.value
const width = container.offsetWidth || 800
const height = container.offsetHeight || 600
if (layoutType === 2) {
initTreeGraph(width, height)
} else if (layoutType === 3) {
initCircularGraph(width, height)
} else {
initNormalGraph(layoutType, width, height)
}
})
}
const initNormalGraph = (layoutType, width, height) => {
const data = processGraphData(props.graphData)
if (!data.nodes || data.nodes.length === 0) return
const layout = {
type: 'force',
center: [width / 2, height / 2],
preventOverlap: true,
nodeSpacing: 80,
linkDistance: 250,
nodeStrength: -800,
edgeStrength: 0.1,
collideStrength: 0.8,
alphaDecay: 0.01,
alphaMin: 0.001
}
graphInstance.value = new G6.Graph({
container: containerRef.value,
width,
height,
fitView: true,
fitViewPadding: 100,
fitCenter: true,
animate: true,
animateCfg: {
duration: 300,
easing: 'easeLinear'
},
minZoom: 0.1,
maxZoom: 10,
modes: {
default: [
'drag-canvas',
'zoom-canvas',
'drag-node',
{
type: 'activate-relations',
trigger: 'mouseenter',
resetSelected: true
}
]
},
layout,
defaultNode: {
type: 'image',
size: 40,
clipCfg: {
show: true,
type: 'circle',
r: 20
},
labelCfg: {
position: 'bottom',
offset: 10,
style: {
fill: '#333',
fontSize: 11,
fontFamily: 'Microsoft YaHei',
textAlign: 'center',
background: {
fill: 'rgba(255, 255, 255, 0.95)',
padding: [4, 6, 4, 6],
radius: 4
}
}
}
},
defaultEdge: {
type: 'quadratic',
style: {
stroke: '#5B8FF9',
lineWidth: 3,
opacity: 0.9,
endArrow: {
path: 'M 0,0 L 12,6 L 12,-6 Z',
fill: '#5B8FF9'
}
},
labelCfg: {
autoRotate: true,
style: {
fill: '#333',
fontSize: 10,
fontFamily: 'Microsoft YaHei',
background: {
fill: '#fff',
padding: [2, 4, 2, 4],
radius: 2
}
}
}
},
nodeStateStyles: {
active: {
shadowColor: '#1459BB',
shadowBlur: 15,
stroke: '#1459BB',
lineWidth: 3
},
inactive: {
opacity: 0.3
}
},
edgeStateStyles: {
active: {
stroke: '#1459BB',
lineWidth: 4
},
inactive: {
opacity: 0.15
}
}
})
graphInstance.value.data(data)
graphInstance.value.render()
bindGraphEvents()
}
const initCircularGraph = (width, height) => {
const data = processGraphData(props.graphData)
if (!data.nodes || data.nodes.length === 0) return
const centerX = width / 2
const centerY = height / 2
const radius = Math.min(width, height) / 2 - 120
const otherNodes = data.nodes.filter(n => !n.isCenter)
const nodeCount = otherNodes.length
otherNodes.forEach((node, index) => {
const angle = (2 * Math.PI * index) / nodeCount - Math.PI / 2
node.x = centerX + radius * Math.cos(angle)
node.y = centerY + radius * Math.sin(angle)
})
const centerNode = data.nodes.find(n => n.isCenter)
if (centerNode) {
centerNode.x = centerX
centerNode.y = centerY
centerNode.fx = centerX
centerNode.fy = centerY
}
graphInstance.value = new G6.Graph({
container: containerRef.value,
width,
height,
fitView: false,
fitCenter: false,
animate: true,
animateCfg: {
duration: 300,
easing: 'easeLinear'
},
minZoom: 0.1,
maxZoom: 10,
modes: {
default: [
'drag-canvas',
'zoom-canvas',
'drag-node',
{
type: 'activate-relations',
trigger: 'mouseenter',
resetSelected: true
}
]
},
defaultNode: {
type: 'image',
size: 40,
clipCfg: {
show: true,
type: 'circle',
r: 20
},
labelCfg: {
position: 'bottom',
offset: 10,
style: {
fill: '#333',
fontSize: 11,
fontFamily: 'Microsoft YaHei',
textAlign: 'center',
background: {
fill: 'rgba(255, 255, 255, 0.95)',
padding: [4, 6, 4, 6],
radius: 4
}
}
}
},
defaultEdge: {
type: 'quadratic',
style: {
stroke: '#5B8FF9',
lineWidth: 3,
opacity: 0.9,
endArrow: {
path: 'M 0,0 L 12,6 L 12,-6 Z',
fill: '#5B8FF9'
}
},
labelCfg: {
autoRotate: true,
style: {
fill: '#333',
fontSize: 10,
fontFamily: 'Microsoft YaHei',
background: {
fill: '#fff',
padding: [2, 4, 2, 4],
radius: 2
}
}
}
},
nodeStateStyles: {
active: {
shadowColor: '#1459BB',
shadowBlur: 15,
stroke: '#1459BB',
lineWidth: 3
},
inactive: {
opacity: 0.3
}
},
edgeStateStyles: {
active: {
stroke: '#1459BB',
lineWidth: 4
},
inactive: {
opacity: 0.15
}
}
})
graphInstance.value.data(data)
graphInstance.value.render()
bindGraphEvents()
}
const initTreeGraph = (width, height) => {
const treeDataSource = convertGraphToTree(props.graphData)
if (!treeDataSource) return
graphInstance.value = new G6.TreeGraph({
container: containerRef.value,
width,
height,
fitView: true,
fitViewPadding: 80,
animate: true,
animateCfg: {
duration: 300,
easing: 'easeLinear'
},
minZoom: 0.1,
maxZoom: 10,
modes: {
default: [
'drag-canvas',
'zoom-canvas',
'drag-node',
{
type: 'collapse-expand',
onChange: function onChange(item, collapsed) {
const data = item.getModel()
data.collapsed = collapsed
return true
}
}
]
},
layout: {
type: 'compactBox',
direction: 'LR',
getId: function getId(d) {
return d.id
},
getHeight: function getHeight() {
return 16
},
getWidth: function getWidth() {
return 16
},
getVGap: function getVGap() {
return 30
},
getHGap: function getHGap() {
return 120
}
},
defaultNode: {
type: 'image',
size: 40,
clipCfg: {
show: true,
type: 'circle',
r: 20
},
labelCfg: {
position: 'right',
offset: 10,
style: {
fill: '#333',
fontSize: 11,
fontFamily: 'Microsoft YaHei',
background: {
fill: 'rgba(255, 255, 255, 0.95)',
padding: [4, 6, 4, 6],
radius: 4
}
}
}
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
stroke: '#5B8FF9',
lineWidth: 3
}
},
nodeStateStyles: {
active: {
shadowColor: '#1459BB',
shadowBlur: 15,
stroke: '#1459BB',
lineWidth: 3
}
}
})
graphInstance.value.data(treeDataSource)
graphInstance.value.render()
graphInstance.value.fitView()
bindGraphEvents()
}
const convertGraphToTree = (graphData) => {
if (!graphData || !graphData.nodes || graphData.nodes.length === 0) {
return null
}
const nodes = graphData.nodes
const links = graphData.links || graphData.edges || []
const centerNode = nodes[0]
const centerId = String(centerNode.id || '0')
const childIdSet = new Set()
const childrenNodes = []
links.forEach((link) => {
const source = String(link.source)
const target = String(link.target)
if (source === centerId && !childIdSet.has(target)) {
const node = nodes.find(n => String(n.id) === target)
if (node) {
childIdSet.add(target)
childrenNodes.push({
id: target,
label: node.name || '',
img: node.image || defaultIcon,
size: node.symbolSize || 40,
name: node.name,
image: node.image,
isSanctioned: node.isSanctioned
})
}
} else if (target === centerId && !childIdSet.has(source)) {
const node = nodes.find(n => String(n.id) === source)
if (node) {
childIdSet.add(source)
childrenNodes.push({
id: source,
label: node.name || '',
img: node.image || defaultIcon,
size: node.symbolSize || 40,
name: node.name,
image: node.image,
isSanctioned: node.isSanctioned
})
}
}
})
if (childrenNodes.length === 0) {
nodes.slice(1).forEach((node) => {
const nodeId = String(node.id)
if (!childIdSet.has(nodeId)) {
childIdSet.add(nodeId)
childrenNodes.push({
id: nodeId,
label: node.name || '',
img: node.image || defaultIcon,
size: node.symbolSize || 40,
name: node.name,
image: node.image,
isSanctioned: node.isSanctioned
})
}
})
}
return {
id: centerId,
label: centerNode.name || '',
img: centerNode.image || defaultIcon,
size: centerNode.symbolSize || 60,
name: centerNode.name,
image: centerNode.image,
isSanctioned: centerNode.isSanctioned,
children: childrenNodes
}
}
const processGraphData = (rawData) => {
if (!rawData || !rawData.nodes || rawData.nodes.length === 0) {
return { nodes: [], edges: [] }
}
const nodeMap = new Map()
const nodes = []
rawData.nodes.forEach((node, index) => {
const nodeId = String(node.id || index)
if (nodeMap.has(nodeId)) {
return
}
nodeMap.set(nodeId, true)
const isCenter = index === 0
const size = node.symbolSize || (isCenter ? 60 : 40)
nodes.push({
id: nodeId,
label: node.name || '',
img: node.image || defaultIcon,
size,
isCenter,
clipCfg: {
show: true,
type: 'circle',
r: size / 2
},
style: {
cursor: 'pointer'
},
labelCfg: {
position: 'bottom',
offset: 12,
style: {
fill: isCenter ? '#1459BB' : '#333',
fontSize: isCenter ? 13 : 11,
fontWeight: isCenter ? 'bold' : 'normal',
fontFamily: 'Microsoft YaHei',
textAlign: 'center'
}
},
...node,
id: nodeId
})
})
const edgeMap = new Map()
const edges = []
const rawEdges = rawData.links || rawData.edges || []
rawEdges.forEach((edge, index) => {
const source = String(edge.source)
const target = String(edge.target)
const edgeKey = `${source}-${target}`
if (edgeMap.has(edgeKey)) {
return
}
if (!nodeMap.has(source) || !nodeMap.has(target)) {
return
}
edgeMap.set(edgeKey, true)
edges.push({
id: `edge-${index}`,
source,
target,
label: edge.name || ''
})
})
return { nodes, edges }
}
const bindGraphEvents = () => {
if (!graphInstance.value) return
graphInstance.value.on('node:click', (evt) => {
const node = evt.item
const model = node.getModel()
selectedNode.value = model
emit('nodeClick', model)
})
graphInstance.value.on('canvas:click', () => {
selectedNode.value = null
})
}
const handleClickControlBtn = (btn) => {
currentLayoutType.value = btn
emit('layoutChange', btn)
initGraph(btn)
}
const destroyGraph = () => {
if (graphInstance.value) {
graphInstance.value.destroy()
graphInstance.value = null
}
}
const handleResize = () => {
if (graphInstance.value && containerRef.value) {
const width = containerRef.value.offsetWidth
const height = containerRef.value.offsetHeight
graphInstance.value.changeSize(width, height)
graphInstance.value.fitView()
}
}
watch(
() => props.graphData,
() => {
initGraph(currentLayoutType.value)
},
{ deep: true }
)
watch(
() => props.treeData,
() => {
if (currentLayoutType.value === 2) {
initGraph(2)
}
},
{ deep: true }
)
watch(
() => props.controlActive,
(newVal) => {
if (newVal !== currentLayoutType.value) {
handleClickControlBtn(newVal)
}
}
)
onMounted(() => {
initGraph(1)
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
destroyGraph()
})
defineExpose({
refresh: () => initGraph(currentLayoutType.value),
changeLayout: (type) => handleClickControlBtn(type),
getGraph: () => graphInstance.value
})
</script>
<style lang="scss" scoped>
.relation-graph-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.graph-container {
width: 100%;
height: 100%;
}
.graph-controls {
position: absolute;
top: 16px;
right: 16px;
display: flex;
gap: 8px;
z-index: 10;
.control-btn {
width: 32px;
height: 32px;
border-radius: 4px;
border: 1px solid rgba(234, 236, 238, 1);
background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
img {
width: 16px;
height: 16px;
}
&:hover {
border-color: rgba(5, 95, 194, 0.5);
}
}
.control-btn-active {
border-color: rgba(5, 95, 194, 1);
background: rgba(231, 243, 255, 1);
}
}
.node-popup {
position: absolute;
bottom: 16px;
left: 16px;
width: 320px;
background: rgba(255, 255, 255, 1);
border-radius: 8px;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(234, 236, 238, 1);
z-index: 20;
.popup-header {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
.popup-icon {
width: 32px;
height: 32px;
margin-right: 8px;
border-radius: 50%;
object-fit: cover;
}
.popup-title {
flex: 1;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgba(59, 65, 75, 1);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.close-icon {
cursor: pointer;
color: rgba(132, 136, 142, 1);
font-size: 16px;
&:hover {
color: rgba(5, 95, 194, 1);
}
}
}
.popup-body {
padding: 12px 16px;
.tag-row {
display: flex;
align-items: center;
margin-bottom: 8px;
.red-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(245, 63, 63, 1);
margin-right: 8px;
}
.red-text {
font-size: 14px;
font-family: "Microsoft YaHei";
color: rgba(245, 63, 63, 1);
}
}
.desc {
font-size: 14px;
font-family: "Microsoft YaHei";
line-height: 22px;
color: rgba(95, 101, 108, 1);
margin: 0;
}
}
}
</style>
\ No newline at end of file
...@@ -9,63 +9,6 @@ ...@@ -9,63 +9,6 @@
</div> </div>
<div class="main" v-if="activeIndex === 0"> <div class="main" v-if="activeIndex === 0">
<div class="left"> <div class="left">
<!-- <div class="title-com">
<div class="box"></div>
<div class="text">本次制裁实体清单列表</div>
<div class="right-group">
<div class="btn">
<img src="../../assets/数据库按钮.png" alt="" />
<img src="../../assets/下载按钮.png" alt="" />
<img src="../../assets/收藏按钮.png" alt="" />
</div>
</div>
</div>
<div class="left-main">
<div class="filter-bar">
<el-select v-model="searchDomain" placeholder="全部领域" class="domain-select">
<el-option label="全部领域" value="" />
<el-option label="人工智能" value="1" />
<el-option label="生物科技" value="2" />
<el-option label="新一代信息技术" value="3" />
<el-option label="量子科技" value="4" />
<el-option label="新能源" value="5" />
<el-option label="集成电路" value="6" />
<el-option label="海洋" value="7" />
<el-option label="先进制造" value="8" />
<el-option label="新材料" value="9" />
<el-option label="航空航天" value="10" />
<el-option label="深海" value="11" />
<el-option label="极地" value="12" />
<el-option label="太空" value="13" />
<el-option label="核" value="14" />
</el-select>
<el-input v-model="searchText" placeholder="搜索实体" class="search-input">
<template #suffix>
<el-icon class="el-input__icon"><Search /></el-icon>
</template>
</el-input>
</div>
<div class="entity-tree custom-scrollbar">
<div class="tree-group" v-for="group in entityList" :key="group.id">
<div class="group-header" @click="toggleGroup(group)">
<el-icon class="arrow-icon" :class="{ expanded: group.expanded }">
<CaretRight />
</el-icon>
<span class="group-name">{{ group.name }}</span>
<span class="group-count">{{ group.count }}家</span>
</div>
<div class="group-children custom-scrollbar" v-show="group.expanded">
<div class="entity-item" v-for="item in group.children" :key="item.id"
:class="{ active: activeEntityId === item.id }" @click="selectEntity(item)">
<div class="item-icon">
<img :src="defaultTitle" alt="" class="item-img" />
</div>
<span class="item-name">{{ item.name }}</span>
</div>
</div>
</div>
</div>
</div> -->
<AnalysisBox title="本次制裁实体清单列表"> <AnalysisBox title="本次制裁实体清单列表">
<div class="left-main"> <div class="left-main">
<div class="filter-bar"> <div class="filter-bar">
...@@ -134,26 +77,6 @@ ...@@ -134,26 +77,6 @@
<div class="rule-checkbox" v-if="rightActiveTab === 'equity'"> <div class="rule-checkbox" v-if="rightActiveTab === 'equity'">
<el-checkbox v-model="is50PercentRule" size="large">50%规则涉及实体</el-checkbox> <el-checkbox v-model="is50PercentRule" size="large">50%规则涉及实体</el-checkbox>
</div> </div>
<!-- <el-select v-model="filterType" placeholder="全部类型" class="header-select">
<el-option label="全部类型" value="" />
</el-select>
<el-select v-model="filterDomain" placeholder="全部领域" class="header-select last-select">
<el-option label="全部领域" value="" />
<el-option label="人工智能" value="1" />
<el-option label="生物科技" value="2" />
<el-option label="新一代信息技术" value="3" />
<el-option label="量子科技" value="4" />
<el-option label="新能源" value="5" />
<el-option label="集成电路" value="6" />
<el-option label="海洋" value="7" />
<el-option label="先进制造" value="8" />
<el-option label="新材料" value="9" />
<el-option label="航空航天" value="10" />
<el-option label="深海" value="11" />
<el-option label="极地" value="12" />
<el-option label="太空" value="13" />
<el-option label="核" value="14" />
</el-select> -->
<div class="btn"> <div class="btn">
<img src="../../assets/数据库按钮.png" alt="" /> <img src="../../assets/数据库按钮.png" alt="" />
<img src="../../assets/下载按钮.png" alt="" /> <img src="../../assets/下载按钮.png" alt="" />
...@@ -162,50 +85,14 @@ ...@@ -162,50 +85,14 @@
</div> </div>
</div> </div>
<div class="right-echarts"> <div class="right-echarts">
<div class="chart-wrapper"> <RelationGraph
<div class="chart-controls"> ref="relationGraphRef"
<div class="control-btn" :class="{ controlBtnActive: controlActive === 1 }" :graph-data="graphData"
@click="handleClickControlBtn(1)"> :tree-data="treeData"
<img :src="echartsIcon01" /> :control-active="controlActive"
</div> @node-click="handleNodeClick"
<div class="control-btn" :class="{ controlBtnActive: controlActive === 2 }" @layout-change="handleLayoutChange"
@click="handleClickControlBtn(2)"> />
<img :src="echartsIcon02" />
</div>
<div class="control-btn" :class="{ controlBtnActive: controlActive === 3 }"
@click="handleClickControlBtn(3)">
<img :src="echartsIcon03" />
</div>
</div>
<div class="chart-legend">
<div class="legend-item"><span class="dot blue"></span>已被制裁实体</div>
<div class="legend-item"><span class="dot grey"></span>未被制裁实体</div>
</div>
<div ref="chartRef" id="chartbox" class="chart-container"></div>
<div class="node-popup" v-if="selectedNode">
<div class="popup-header">
<img :src="defaultTitle" class="popup-icon" />
<span class="popup-title">{{ selectedNode.name.replace(/\n/g, "") }}</span>
<el-icon class="close-icon" @click="selectedNode = null">
<Close />
</el-icon>
</div>
<div class="popup-body">
<div class="tag-row">
<span class="red-dot"></span>
<span class="red-text">
<!-- 2025年7月15日 《实体清单》 -->
暂无数据
</span>
</div>
<div class="desc">
<!-- 因获取和试图获取美国原产物品以支持中国军事和国防相关空间领域活动以及中国量子技术能力而被列入。 -->
暂无数据
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -215,16 +102,12 @@ ...@@ -215,16 +102,12 @@
<script setup> <script setup>
import { ref, onMounted, nextTick, watch, onUnmounted } from "vue"; import { ref, onMounted, nextTick, watch, onUnmounted } from "vue";
import { debounce } from "lodash"; import { debounce } from "lodash";
import * as echarts from "echarts";
import { Search, CaretRight, Close } from "@element-plus/icons-vue"; import { Search, CaretRight, Close } from "@element-plus/icons-vue";
import defaultTitle from "../../assets/default-icon2.png"; import defaultTitle from "../../assets/default-icon2.png";
import icon01 from "./assets/icon01.png"; import icon01 from "./assets/icon01.png";
import icon02 from "./assets/icon02.png"; import icon02 from "./assets/icon02.png";
import icon01Active from "./assets/icon01-active.png"; import icon01Active from "./assets/icon01-active.png";
import icon02Active from "./assets/icon02-active.png"; import icon02Active from "./assets/icon02-active.png";
import echartsIcon01 from "./assets/echartsIcon01.png";
import echartsIcon02 from "./assets/echartsIcon02.png";
import echartsIcon03 from "./assets/echartsIcon03.png";
import company from "./assets/company.png"; import company from "./assets/company.png";
import companyActive from "./assets/company-active.png"; import companyActive from "./assets/company-active.png";
import { import {
...@@ -232,137 +115,10 @@ import { ...@@ -232,137 +115,10 @@ import {
getSingleSanctionEntitySupplyChain, getSingleSanctionEntitySupplyChain,
getSingleSanctionEntityEquity getSingleSanctionEntityEquity
} from "@/api/exportControlV2.0"; } from "@/api/exportControlV2.0";
import getTreeChart from "./utils/treeChart"; import RelationGraph from './components/RelationGraph.vue';
import setChart from "@/utils/setChart"; import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
const controlActive = ref(1);
const treeData = ref([{}]);
const handleClickControlBtn = btn => {
controlActive.value = btn;
if (btn === 1 || btn === 3) {
isInChart.value = true;
initChart();
} else if (btn === 2) {
isInChart.value = false;
if (chartInstance.value) {
chartInstance.value.dispose();
}
console.log("treeData", treeData.value);
let treeChart = getTreeChart(treeData.value);
setChart(treeChart, "chartbox");
}
};
const isInChart = ref(false);
const handleMouseEnter = () => {
if (controlActive.value !== 2) {
isInChart.value = true;
} else {
isInChart.value = false;
}
};
const handleMouseLeave = () => {
isInChart.value = false;
};
// 单次制裁-深度挖掘-制裁实体股权信息-列表
const singleSanctionEntityEquityData = ref(null);
// 单次制裁-深度挖掘-制裁实体股权信息-请求
const getSingleSanctionEntityEquityRequest = async () => {
try {
const res = await getSingleSanctionEntityEquity({
orgId: activeEntityId.value,
rule: is50PercentRule.value
});
if (res.code === 200) {
singleSanctionEntityEquityData.value = res.data || null;
initChart();
}
} catch (error) {
console.log(error);
}
};
// 单次制裁-深度挖掘-制裁实体供应链信息-列表
const singleSanctionEntitySupplyChainData = ref(null);
// 单次制裁-深度挖掘-制裁实体供应链信息-请求
const getSingleSanctionEntitySupplyChainRequest = async () => {
try {
const res = await getSingleSanctionEntitySupplyChain({
orgId: activeEntityId.value
});
if (res.code === 200) {
singleSanctionEntitySupplyChainData.value = res.data || null;
initChart();
treeData.value[0].id = res.data.orgId;
treeData.value[0].name = res.data.orgName;
treeData.value[0].symbol = "image://" + companyActive;
treeData.value[0].symbolSize = 50;
treeData.value[0].value = 10;
treeData.value[0].children = res.data.parentOrgList.map(item => {
return {
id: item.id,
name: item.name,
symbolSize: 30,
value: 10,
symbol: `image://${company}`
};
});
console.log("treeData0", treeData.value);
}
} catch (error) {
console.log(error);
}
};
// 单次制裁-深度挖掘-本次制裁实体清单列表
const singleSanctionEntityList = ref([]);
// 单次制裁-深度挖掘-本次制裁实体清单列表-请求
const getSingleSanctionEntityListRequest = async () => {
try {
const res = await getSingleSanctionEntityList({
sanRecordId: sanRecordId.value,
isOnlyCn: false,
domainId: searchDomain.value || undefined,
searchText: searchText.value || undefined
});
if (res.code === 200) {
entityList.value = (res.data || []).map((group, index) => ({
id: `group-${index}`,
name: group.orgType,
count: group.orgInfoList ? group.orgInfoList.length : 0,
expanded: index === 0, // 默认展开第一个分组
children: (group.orgInfoList || []).map(org => ({
id: org.id,
name: org.orgNameZh
}))
}));
// 如果有数据,且当前没有选中的实体,默认选中第一个分组的第一个实体
if (entityList.value.length > 0 && entityList.value[0].children && entityList.value[0].children.length > 0) {
const firstEntity = entityList.value[0].children[0];
if (!activeEntityId.value) {
activeEntityId.value = firstEntity.id;
currentEntityName.value = firstEntity.name;
}
}
}
} catch (error) {
console.log(error);
}
};
const sanRecordId = ref(""); const sanRecordId = ref("");
const getUrlParams = () => {
const urlParams = new URLSearchParams(window.location.search);
sanRecordId.value = urlParams.get("id") || "";
};
// const activeTab = ref(["实体穿透分析", "重点实体识别"]);
const activeTab = ref(["实体穿透分析"]); const activeTab = ref(["实体穿透分析"]);
const activeIndex = ref(0); const activeIndex = ref(0);
const rightActiveTab = ref("supplyChain"); const rightActiveTab = ref("supplyChain");
...@@ -376,6 +132,22 @@ const currentEntityName = ref(""); ...@@ -376,6 +132,22 @@ const currentEntityName = ref("");
const is50PercentRule = ref(false); const is50PercentRule = ref(false);
const entityList = ref([]); const entityList = ref([]);
const controlActive = ref(1);
const isInChart = ref(false);
const relationGraphRef = ref(null);
const graphData = ref({ nodes: [], links: [] });
const treeData = ref(null);
const selectedNode = ref(null);
const singleSanctionEntityEquityData = ref(null);
const singleSanctionEntitySupplyChainData = ref(null);
const singleSanctionEntityList = ref([]);
const getUrlParams = () => {
const urlParams = new URLSearchParams(window.location.search);
sanRecordId.value = urlParams.get("id") || "";
};
const toggleGroup = group => { const toggleGroup = group => {
group.expanded = !group.expanded; group.expanded = !group.expanded;
...@@ -386,512 +158,194 @@ const selectEntity = item => { ...@@ -386,512 +158,194 @@ const selectEntity = item => {
currentEntityName.value = item.name; currentEntityName.value = item.name;
}; };
const chartRef = ref(null); const handleMouseEnter = () => {
const chartInstance = ref(null); if (controlActive.value !== 2) {
const selectedNode = ref(null); isInChart.value = true;
const initChart = () => {
if (!chartRef.value) return;
if (chartInstance.value) {
chartInstance.value.dispose();
}
chartInstance.value = echarts.init(chartRef.value);
let option = {};
if (rightActiveTab.value === "supplyChain") {
option = getSupplyChainOption();
} else { } else {
option = getEquityOption(); isInChart.value = false;
} }
};
chartInstance.value.setOption(option); const handleMouseLeave = () => {
isInChart.value = false;
};
chartInstance.value.on("click", params => { const handleNodeClick = (node) => {
if (params.dataType === "node") { selectedNode.value = node;
selectedNode.value = params.data; };
} else {
selectedNode.value = null;
}
});
chartInstance.value.getZr().on("click", params => { const handleLayoutChange = (type) => {
if (!params.target) { controlActive.value = type;
selectedNode.value = null; if (type !== 2) {
isInChart.value = true;
} else {
isInChart.value = false;
} }
});
}; };
const getSupplyChainOption = () => { const updateGraphData = () => {
if (!singleSanctionEntitySupplyChainData.value) return {}; const data = rightActiveTab.value === 'supplyChain'
const data = singleSanctionEntitySupplyChainData.value; ? singleSanctionEntitySupplyChainData.value
: singleSanctionEntityEquityData.value;
if (!data) return;
const nodes = []; const nodes = [];
const links = []; const links = [];
const centerX = 550;
const centerY = 400;
// 中心节点
nodes.push({ nodes.push({
id: "0", id: "0",
name: data.orgName, name: data.orgName,
category: 0, // 强制为制裁中 image: companyActive,
symbol: "image://" + companyActive, // 强制使用制裁中图标 symbolSize: 60,
x: centerX, isSanctioned: true
y: centerY,
symbolSize: 50,
label: {
fontSize: 16,
fontWeight: "bold",
color: "#055FC2", // 使用制裁蓝,在图标下方更清晰
position: "bottom",
distance: 10,
width: 150,
overflow: "break"
}
}); });
// 父级节点 (供应商)
const parentList = data.parentOrgList || []; const parentList = data.parentOrgList || [];
parentList.forEach((item, index) => { parentList.forEach((item, index) => {
// 使用 (index + 0.5) 使节点在半圆弧内居中分布
const angle = -Math.PI + ((index + 0.5) / parentList.length) * Math.PI;
// 交错半径:偶数索引使用 340,奇数索引使用 440,拉开垂直距离
const radius = index % 2 === 0 ? 340 : 440;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
// 动态计算标签位置:根据余弦值判断左右,根据正弦值判断上下
let position = "right";
let align = "left";
const cosA = Math.cos(angle);
const sinA = Math.sin(angle);
if (Math.abs(cosA) < 0.3) {
// 顶部区域
position = "top";
align = "center";
} else if (cosA < 0) {
// 左侧区域
position = "left";
align = "right";
}
nodes.push({ nodes.push({
id: `p-${item.id}`, id: `p-${item.id || index}`,
name: item.name, name: item.name,
category: item.isSanctioned ? 0 : 1, image: item.isSanctioned ? companyActive : company,
symbol: "image://" + (item.isSanctioned ? companyActive : company), symbolSize: 40,
x: x, isSanctioned: item.isSanctioned
y: y,
isSanctioned: item.isSanctioned,
label: {
position: position,
align: align,
distance: 8,
width: 110,
overflow: "break",
lineHeight: 14,
fontSize: 11
}
}); });
links.push({ links.push({
source: `p-${item.id}`, source: `p-${item.id || index}`,
target: "0", target: "0",
value: "供应商", name: rightActiveTab.value === 'supplyChain' ? "供应商" : (item.type || "持股")
isSanctioned: item.isSanctioned && data.isSanctioned
}); });
}); });
// 子级节点 (客户)
const childList = data.childrenOrgList || []; const childList = data.childrenOrgList || [];
childList.forEach((item, index) => { childList.forEach((item, index) => {
const angle = ((index + 0.5) / childList.length) * Math.PI;
// 交错半径
const radius = index % 2 === 0 ? 340 : 440;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
// 动态计算标签位置
let position = "right";
let align = "left";
const cosA = Math.cos(angle);
const sinA = Math.sin(angle);
if (Math.abs(cosA) < 0.3) {
// 底部区域
position = "bottom";
align = "center";
} else if (cosA < 0) {
// 左侧区域
position = "left";
align = "right";
}
nodes.push({ nodes.push({
id: `c-${item.id}`, id: `c-${item.id || index}`,
name: item.name, name: item.name,
category: item.isSanctioned ? 0 : 1, image: item.isSanctioned ? companyActive : company,
symbol: "image://" + (item.isSanctioned ? companyActive : company), symbolSize: 40,
x: x, isSanctioned: item.isSanctioned
y: y,
isSanctioned: item.isSanctioned,
label: {
position: position,
align: align,
distance: 8,
width: 110,
overflow: "break",
lineHeight: 14,
fontSize: 11
}
}); });
links.push({ links.push({
source: "0", source: "0",
target: `c-${item.id}`, target: `c-${item.id || index}`,
value: "客户", name: rightActiveTab.value === 'supplyChain' ? "客户" : (item.type || "投资")
isSanctioned: item.isSanctioned && data.isSanctioned
}); });
}); });
return { graphData.value = { nodes, links };
tooltip: {
show: true,
formatter: params => {
if (params.dataType === "node") {
return `<div style="padding: 8px; max-width: 300px; white-space: normal; word-break: break-all;">${params.data.name}</div>`;
}
return "";
}
},
series: [
{
type: "graph",
layout: "none",
symbolSize: 36,
roam: true,
label: {
show: true,
formatter: "{b}",
fontSize: 12,
hideOverlap: true
},
edgeSymbol: ["none", "arrow"],
edgeSymbolSize: [4, 8],
edgeLabel: {
position: "middle",
offset: [0, 13],
fontSize: 12,
fontWeight: 400,
fontFamily: "Microsoft YaHei",
lineHeight: 16,
show: true,
formatter: "{c}",
color: "rgba(170, 173, 177, 1)",
backgroundColor: "rgba(234, 236, 238, 1)",
padding: [4, 8],
borderRadius: 20
},
data: nodes.map(node => ({
...node,
label: {
color: node.category === 0 ? "#055FC2" : "#5F656C",
...node.label
}
})),
links: links.map(link => {
return {
...link,
lineStyle: {
color: link.isSanctioned ? "rgba(100, 180, 255, 1)" : "rgb(180, 181, 182)",
width: 1,
curveness: 0
},
label: link.isSanctioned
? {
show: true,
formatter: "{c}",
backgroundColor: "rgba(231, 243, 255, 1)",
color: "rgba(50, 150, 250, 1)",
borderRadius: 20,
padding: [4, 8],
fontSize: 12,
fontWeight: 400,
fontFamily: "Microsoft YaHei",
lineHeight: 16
}
: undefined
};
})
}
]
};
}; };
const getEquityOption = () => { const updateTreeData = (data) => {
if (!singleSanctionEntityEquityData.value) return {}; if (!data) return;
const data = singleSanctionEntityEquityData.value;
const nodes = []; treeData.value = {
const links = []; id: data.orgId,
const centerX = 550;
const centerY = 400;
// 中心节点
nodes.push({
id: "0",
name: data.orgName, name: data.orgName,
category: 0, // 强制为制裁中 image: companyActive,
symbol: "image://" + companyActive, // 强制使用制裁中图标
x: centerX,
y: centerY,
symbolSize: 50, symbolSize: 50,
label: { children: (data.parentOrgList || []).map((item, index) => ({
fontSize: 16, id: item.id || `p-${index}`,
fontWeight: "bold",
color: "#055FC2", // 使用制裁蓝,在图标下方更清晰
position: "bottom",
distance: 10,
width: 150,
overflow: "break"
}
});
// 父级节点 (股东)
const parentList = data.parentOrgList || [];
parentList.forEach((item, index) => {
// 在顶部水平排列
const total = parentList.length;
const gap = 200;
const startX = centerX - ((total - 1) * gap) / 2;
const x = startX + index * gap;
const y = centerY - 250; // 向上偏移
nodes.push({
id: `p-${index}`,
name: item.name, name: item.name,
category: item.isSanctioned ? 0 : 1, image: item.isSanctioned ? companyActive : company,
symbol: "image://" + (item.isSanctioned ? companyActive : company), symbolSize: 30
x: x, }))
y: y, };
isSanctioned: item.isSanctioned, };
label: {
position: "top",
distance: 8,
width: 110,
overflow: "break",
lineHeight: 14,
fontSize: 11
}
});
links.push({ const getSingleSanctionEntityEquityRequest = async () => {
source: `p-${index}`, try {
target: "0", const res = await getSingleSanctionEntityEquity({
value: item.type || "持股", // 使用 type 字段 orgId: activeEntityId.value,
isSanctioned: item.isSanctioned // 中心节点强制制裁,高亮取决于对方 rule: is50PercentRule.value
});
}); });
if (res.code === 200) {
// 子级节点 (对外投资) singleSanctionEntityEquityData.value = res.data || null;
const childList = data.childrenOrgList || []; updateGraphData();
childList.forEach((item, index) => {
// 在底部水平排列
const total = childList.length;
const gap = 240; // 稍微拉开一点间距
const startX = centerX - ((total - 1) * gap) / 2;
const x = startX + index * gap;
const y = centerY + 250; // 向下偏移
nodes.push({
id: `c-${index}`,
name: item.name,
category: item.isSanctioned ? 0 : 1,
symbol: "image://" + (item.isSanctioned ? companyActive : company),
x: x,
y: y,
isSanctioned: item.isSanctioned,
label: {
position: "bottom",
distance: 8,
width: 110,
overflow: "break",
lineHeight: 14,
fontSize: 11
} }
}); } catch (error) {
console.log(error);
}
};
links.push({ const getSingleSanctionEntitySupplyChainRequest = async () => {
source: "0", try {
target: `c-${index}`, const res = await getSingleSanctionEntitySupplyChain({
value: item.type || "持股", // 使用 type 字段 orgId: activeEntityId.value
isSanctioned: item.isSanctioned // 中心节点强制制裁,高亮取决于对方
});
}); });
if (res.code === 200) {
return { singleSanctionEntitySupplyChainData.value = res.data || null;
tooltip: { updateGraphData();
show: true, updateTreeData(res.data);
formatter: params => { }
if (params.dataType === "node") { } catch (error) {
return `<div style="padding: 8px; max-width: 300px; white-space: normal; word-break: break-all;">${params.data.name}</div>`; console.log(error);
}
return "";
}
},
series: [
{
type: "graph",
layout: "none",
symbolSize: 36,
roam: true,
label: {
show: true,
formatter: "{b}",
fontSize: 12,
hideOverlap: true
},
edgeSymbol: ["none", "arrow"],
edgeSymbolSize: [4, 8],
edgeLabel: {
position: "middle",
offset: [0, 13],
fontSize: 12,
fontWeight: 400,
fontFamily: "Microsoft YaHei",
lineHeight: 16,
show: true,
formatter: "{c}",
color: "rgba(170, 173, 177, 1)",
backgroundColor: "rgba(234, 236, 238, 1)",
padding: [4, 8],
borderRadius: 20
},
data: nodes.map(node => ({
...node,
label: {
color: node.category === 0 ? "#055FC2" : "#5F656C",
...node.label
}
})),
links: links.map(link => {
return {
...link,
lineStyle: {
color: link.isSanctioned ? "rgba(100, 180, 255, 1)" : "rgb(180, 181, 182)",
width: 1,
curveness: 0
},
label: link.isSanctioned
? {
show: true,
formatter: "{c}",
backgroundColor: "rgba(231, 243, 255, 1)",
color: "rgba(50, 150, 250, 1)",
borderRadius: 20,
padding: [4, 8],
fontSize: 12,
fontWeight: 400,
fontFamily: "Microsoft YaHei",
lineHeight: 16
}
: undefined
};
})
} }
]
};
}; };
const debouncedGetList = debounce(() => { const getSingleSanctionEntityListRequest = async () => {
getSingleSanctionEntityListRequest(); try {
}, 1000); const res = await getSingleSanctionEntityList({
sanRecordId: sanRecordId.value,
watch(searchText, () => { isOnlyCn: false,
debouncedGetList(); domainId: searchDomain.value || undefined,
}); searchText: searchText.value || undefined
});
watch(searchDomain, () => { if (res.code === 200) {
getSingleSanctionEntityListRequest(); entityList.value = (res.data || []).map((group, index) => ({
}); id: `group-${index}`,
name: group.orgType,
count: group.orgInfoList ? group.orgInfoList.length : 0,
expanded: index === 0,
children: (group.orgInfoList || []).map(org => ({
id: org.id,
name: org.orgNameZh
}))
}));
watch(activeIndex, val => { if (entityList.value.length > 0 && entityList.value[0].children && entityList.value[0].children.length > 0) {
if (val === 0) { const firstEntity = entityList.value[0].children[0];
nextTick(() => { if (!activeEntityId.value) {
if (activeEntityId.value) { activeEntityId.value = firstEntity.id;
if (rightActiveTab.value === "supplyChain") { currentEntityName.value = firstEntity.name;
getSingleSanctionEntitySupplyChainRequest();
} else {
getSingleSanctionEntityEquityRequest();
}
} }
initChart();
});
} }
});
watch(activeEntityId, val => {
if (val) {
if (rightActiveTab.value === "supplyChain") {
getSingleSanctionEntitySupplyChainRequest();
} else {
getSingleSanctionEntityEquityRequest();
} }
initChart(); } catch (error) {
console.log(error);
} }
}); };
watch(is50PercentRule, () => { watch(rightActiveTab, async (newTab) => {
if (rightActiveTab.value === "equity" && activeEntityId.value) { if (newTab === 'supplyChain') {
getSingleSanctionEntityEquityRequest(); await getSingleSanctionEntitySupplyChainRequest();
} else {
await getSingleSanctionEntityEquityRequest();
} }
}); });
watch( watch(activeEntityId, async (newId) => {
rightActiveTab, if (newId) {
val => { if (rightActiveTab.value === 'supplyChain') {
if (activeEntityId.value) { await getSingleSanctionEntitySupplyChainRequest();
if (val === "supplyChain") {
getSingleSanctionEntitySupplyChainRequest();
} else { } else {
getSingleSanctionEntityEquityRequest(); await getSingleSanctionEntityEquityRequest();
} }
} }
nextTick(() => {
initChart();
});
},
{ immediate: true }
);
onMounted(() => {
// 获取URL参数
getUrlParams();
// 单次制裁-深度挖掘-本次制裁实体清单列表-请求
getSingleSanctionEntityListRequest();
window.addEventListener("resize", handleResize);
}); });
onUnmounted(() => { watch(is50PercentRule, async () => {
if (chartInstance.value) { if (rightActiveTab.value === 'equity') {
chartInstance.value.dispose(); await getSingleSanctionEntityEquityRequest();
}
if (debouncedGetList && debouncedGetList.cancel) {
debouncedGetList.cancel();
} }
window.removeEventListener("resize", handleResize);
}); });
const handleResize = () => { onMounted(async () => {
if (chartInstance.value) { getUrlParams();
chartInstance.value.resize(); await getSingleSanctionEntityListRequest();
} });
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
......
...@@ -550,7 +550,7 @@ const messageList = ref([ ...@@ -550,7 +550,7 @@ const messageList = ref([
]); ]);
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/riskSignal"); const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
......
...@@ -3,8 +3,13 @@ ...@@ -3,8 +3,13 @@
<div class="main-content" ref="homeMainRef"> <div class="main-content" ref="homeMainRef">
<div class="home-top-bg"></div> <div class="home-top-bg"></div>
<!-- 搜索栏部分 --> <!-- 搜索栏部分 -->
<SearchContainer v-if="homeMainRef" :countInfo="statCountInfo" placeholder="搜索规则限制" :containerRef="homeMainRef" <SearchContainer
areaName="" /> v-if="homeMainRef"
:countInfo="statCountInfo"
placeholder="搜索规则限制"
:containerRef="homeMainRef"
areaName=""
/>
<!-- 最新动态 --> <!-- 最新动态 -->
<div class="newdata" id="position1"> <div class="newdata" id="position1">
<com-title title="最新动态" /> <com-title title="最新动态" />
...@@ -14,7 +19,7 @@ ...@@ -14,7 +19,7 @@
</div> </div>
<!-- 资讯要问 --> <!-- 资讯要问 -->
<div class="ask" id="position2"> <div class="ask" id="position2">
<com-title title="咨询要闻" /> <com-title title="资讯要闻" />
<div class="ask-main"> <div class="ask-main">
<askPage /> <askPage />
</div> </div>
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
</div> </div>
</div> </div>
<div class="btn" @click="handleToPosi('position2')"> <div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div> <div class="btn-text">资讯要闻</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt /> <img src="@/assets/icons/arrow-right-icon.png" alt />
</div> </div>
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt /> <img src="@/assets/icons/arrow-right-icon.png" alt />
</div> </div>
</div>, </div>
,
<div class="btn" @click="handleToPosi('position4')"> <div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">资源库</div> <div class="btn-text">资源库</div>
<div class="btn-icon"> <div class="btn-icon">
...@@ -85,7 +86,7 @@ ...@@ -85,7 +86,7 @@
</div> </div>
</div> </div>
<div class="btn" @click="handleToPosi('position2')"> <div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div> <div class="btn-text">资讯要闻</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt /> <img src="@/assets/icons/arrow-right-icon.png" alt />
</div> </div>
...@@ -113,7 +114,7 @@ ...@@ -113,7 +114,7 @@
</div> </div>
<!-- 资讯要问 --> <!-- 资讯要问 -->
<div class="ask" id="position2"> <div class="ask" id="position2">
<com-title title="咨询要闻" /> <com-title title="资讯要闻" />
<div class="ask-main"> <div class="ask-main">
<askPage /> <askPage />
</div> </div>
...@@ -145,7 +146,7 @@ import askPage from "./components/askPage/index.vue"; ...@@ -145,7 +146,7 @@ import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue"; import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue"; import resLib from "./components/resLib/index.vue";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
import { getStatCount } from '@/api/ruleRestriction/index.js' import { getStatCount } from "@/api/ruleRestriction/index.js";
// 搜索框 // 搜索框
const input = ref(""); const input = ref("");
...@@ -154,19 +155,19 @@ const { isShow } = useContainerScroll(homeMainRef); ...@@ -154,19 +155,19 @@ const { isShow } = useContainerScroll(homeMainRef);
const router = useRouter(); const router = useRouter();
const statCountInfo = ref({}) const statCountInfo = ref({});
const getStatCountInfo = async () => { const getStatCountInfo = async () => {
try { try {
const res = await getStatCount(); const res = await getStatCount();
if (res && res.code === 200) { if (res && res.code === 200) {
// console.log('----getStatCountInfo', res.data) // console.log('----getStatCountInfo', res.data)
statCountInfo.value = res.data statCountInfo.value = res.data;
} }
} catch (error) { } catch (error) {
console.error("获取首页统计接口失败:", error); console.error("获取首页统计接口失败:", error);
} }
} };
// 搜索功能 // 搜索功能
const handleSearch = () => { const handleSearch = () => {
...@@ -205,8 +206,8 @@ const handleBackHome = () => { ...@@ -205,8 +206,8 @@ const handleBackHome = () => {
}; };
onMounted(async () => { onMounted(async () => {
await getStatCountInfo() await getStatCountInfo();
}) });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
......
...@@ -14,8 +14,14 @@ ...@@ -14,8 +14,14 @@
<div class="main-content" ref="containerRef"> <div class="main-content" ref="containerRef">
<div class="home-top-bg"></div> <div class="home-top-bg"></div>
<!-- 搜索栏部分 --> <!-- 搜索栏部分 -->
<SearchContainer style="margin-bottom: 48px;height: fit-content;" v-if="containerRef" :countInfo="countInfo" <SearchContainer
placeholder="搜索科研资助实体、资助记录" :containerRef="containerRef" areaName="" /> style="margin-bottom: 48px; height: fit-content"
v-if="containerRef"
:countInfo="countInfo"
placeholder="搜索科研资助实体、资助记录"
:containerRef="containerRef"
areaName=""
/>
<!-- <div class="search"> --> <!-- <div class="search"> -->
<!-- <div class="search-main"> <!-- <div class="search-main">
...@@ -51,7 +57,7 @@ ...@@ -51,7 +57,7 @@
</div> </div>
</div> </div>
<div class="btn" @click="scrollToTop('position2')"> <div class="btn" @click="scrollToTop('position2')">
<div class="btn-text">咨询要闻</div> <div class="btn-text">资讯要闻</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
...@@ -80,7 +86,7 @@ ...@@ -80,7 +86,7 @@
<div class="data-item-name">{{ item.orgNameEn }}</div> <div class="data-item-name">{{ item.orgNameEn }}</div>
<div v-if="item.orgAbbEn" class="data-item-abb">{{ item.orgAbbEn }}</div> <div v-if="item.orgAbbEn" class="data-item-abb">{{ item.orgAbbEn }}</div>
</div> </div>
<div class="data-item-num" :style="{ color: color[index] }">{{ item.num + '项' }}</div> <div class="data-item-num" :style="{ color: color[index] }">{{ item.num + "项" }}</div>
</div> </div>
</div> </div>
<!-- 最新动态 --> <!-- 最新动态 -->
...@@ -92,7 +98,7 @@ ...@@ -92,7 +98,7 @@
</div> </div>
<!-- 资讯要问 --> <!-- 资讯要问 -->
<div class="ask" id="position2"> <div class="ask" id="position2">
<com-title title="咨询要闻" /> <com-title title="资讯要闻" />
<div class="ask-main"> <div class="ask-main">
<askPage /> <askPage />
</div> </div>
...@@ -125,10 +131,7 @@ import dataSub from "./components/dataSub/index.vue"; ...@@ -125,10 +131,7 @@ import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue"; import resLib from "./components/resLib/index.vue";
import scrollToTop from "@/utils/scrollToTop"; import scrollToTop from "@/utils/scrollToTop";
import { getFundSourceOrg } from "@/api/scientificFunding/overview";
import {
getFundSourceOrg
} from "@/api/scientificFunding/overview";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
...@@ -141,21 +144,21 @@ import img06 from "./assets/images/img06.png"; ...@@ -141,21 +144,21 @@ import img06 from "./assets/images/img06.png";
let containerRef = ref(null); let containerRef = ref(null);
let countInfo = ref([ let countInfo = ref([
{ {
name: '科研资助机构', name: "科研资助机构",
count: 18 count: 18
}, },
{ {
name: '科研资助动态', name: "科研资助动态",
count: 633 count: 633
}, },
{ {
name: '科研资助项目', name: "科研资助项目",
count: 312 count: 312
}, },
{ {
name: '经费总额(亿美元)', name: "经费总额(亿美元)",
count: '15,556' count: "15,556"
}, }
]); ]);
// 搜索框 // 搜索框
const input = ref(""); const input = ref("");
...@@ -231,14 +234,14 @@ const color = ref([ ...@@ -231,14 +234,14 @@ const color = ref([
"rgba(64, 150, 255, 1)", "rgba(64, 150, 255, 1)",
"rgb(33, 129, 57)", "rgb(33, 129, 57)",
"rgb(5, 95, 194)" "rgb(5, 95, 194)"
]) ]);
//// 来源机构列表 //// 来源机构列表
const handleGetFundSourceOrg = async () => { const handleGetFundSourceOrg = async () => {
try { try {
const res = await getFundSourceOrg(); const res = await getFundSourceOrg();
console.log("来源机构列表", res); console.log("来源机构列表", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
dataList.value = res.data dataList.value = res.data;
} }
} catch (error) { } catch (error) {
console.error("获取来源机构列表error", error); console.error("获取来源机构列表error", error);
...@@ -246,7 +249,7 @@ const handleGetFundSourceOrg = async () => { ...@@ -246,7 +249,7 @@ const handleGetFundSourceOrg = async () => {
}; };
onMounted(async () => { onMounted(async () => {
handleGetFundSourceOrg() handleGetFundSourceOrg();
}); });
</script> </script>
...@@ -461,7 +464,9 @@ onMounted(async () => { ...@@ -461,7 +464,9 @@ onMounted(async () => {
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease; transition:
transform 0.3s ease,
box-shadow 0.3s ease;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0px 24px; padding: 0px 24px;
......
...@@ -89,7 +89,8 @@ export default defineConfig({ ...@@ -89,7 +89,8 @@ export default defineConfig({
rewrite: (path) => path.replace(/^\/temporarySearch/, '') rewrite: (path) => path.replace(/^\/temporarySearch/, '')
}, },
'^/bill(?:/|$)': { '^/bill(?:/|$)': {
target: 'http://172.20.10.3:28080/', target: 'http://8.140.26.4:9085/',
// target: 'http://172.20.10.3:28080/',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/bill/, '') rewrite: (path) => path.replace(/^\/bill/, '')
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论