提交 0809f3bb authored 作者: 李顺's avatar 李顺

Merge branch 'master' of http://8.140.26.4:10003/caijian/risk-monitor into ls_dev

{
"name": "think-tank",
"name": "zm-system",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "think-tank",
"name": "zm-system",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
......
......@@ -10,12 +10,14 @@
<div class="title-box">
<div
class="title"
:aria-disabled="item.disabled"
v-for="(item, index) in homeTitleList"
:key="index"
@mouseenter="handleShowMenu(index, true)"
@mouseleave="handleShowMenu(index, false)"
>
<div class="text" :class="{ textActive: homeActiveTitleIndex === index }">{{ item.name }}</div>
<div class="text" :class="{ textActive: homeActiveTitleIndex === index }">
{{ item.name }}</div>
<div class="bottom-line" v-if="homeActiveTitleIndex === index"></div>
</div>
</div>
......@@ -28,7 +30,7 @@
<img src="@/assets/icons/overview/search.png" alt="" />
</div>
</div>
<div class="info-box">
<div class="info-box" aria-disabled="true">
<div class="mail">
<img src="@/assets/icons/overview/mail.png" alt="" />
</div>
......@@ -78,6 +80,21 @@
</div>
</div>
<div class="right-btn" aria-disabled="true">
<div class="item">
<div class="icon">
<img src="@/assets/icons/overview/domain.png" alt="" />
</div>
<div class="text">{{ "领域" }}</div>
</div>
<div class="item">
<div class="icon">
<img src="@/assets/icons/overview/element.png" alt="" />
</div>
<div class="text">{{ "要素" }}</div>
</div>
</div>
<div class="ai-btn" @click="openAiBox">
<div class="icon">
<img src="@/assets/icons/ai-icon.png" alt="" />
......@@ -116,7 +133,7 @@ import Menu11 from "@/assets/icons/overview/menu11.png";
import Menu12 from "@/assets/icons/overview/menu12.png";
const router = useRouter();
const route = useRoute()
const route = useRoute();
// const target = ref(null);
......@@ -166,21 +183,30 @@ const handleGetPersonType = async () => {
} catch (error) {}
};
const isCurrentOverview = ref(true);
const isCurrentOverview = computed(() => {
if (route.path === "/ZMOverView") {
return true;
} else {
return false;
}
});
// 概览页标题列表
const homeTitleList = ref([
{
name: "中美科技博弈",
path: "/ZMOverView"
path: "/ZMOverView",
disabled: false
},
{
name: "主要国家科技动向感知",
path: ""
path: "",
disabled: true
},
{
name: "主要国家竞争科技安全",
path: ""
path: "",
disabled: true
}
]);
......@@ -281,11 +307,6 @@ const handleSearch = () => {
onMounted(() => {
handleGetPersonType();
if (route.path === "/ZMOverView") {
isCurrentOverview.value = true;
} else {
isCurrentOverview.value = false;
}
});
</script>
......@@ -619,6 +640,47 @@ body {
}
}
.right-btn {
position: absolute;
top: 132px;
right: 0;
.item {
width: 108px;
height: 40px;
box-sizing: border-box;
border: 1px solid #fff;
border-radius: 50px 0px 0px 50px;
background: rgba(255, 255, 255, 0.65);
display: flex;
margin-bottom: 8px;
cursor: pointer;
.icon {
width: 36px;
height: 36px;
margin-top: 2px;
margin-left: 2px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 36px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Source Han Sans CN;
font-style: Regular;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
margin-top: 8px;
margin-left: 10px;
}
}
}
.ai-btn {
position: absolute;
bottom: 240px;
......@@ -656,7 +718,7 @@ body {
}
:deep(.el-input__wrapper) {
height: 34px;
height: 100%;
box-shadow: none;
border-radius: 10px;
background: transparent;
......@@ -668,4 +730,23 @@ body {
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
.title[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.info-box[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.right-btn[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
</style>
import request from "@/api/request.js";
// 全政府-获取美国政府部门制裁数据
/**
* @header token
* * @param {String} params.monthNum // 月份数
*/
export function getAllGovernmentList(params) {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentSanctionsData`,
params
})
}
// 全政府-获取美对华制裁措施数量趋势
/**
* @header token
* @param {Object} params
* @param {String} params.field // 领域
* @param {String} params.monthNum // 月份数
* @param {String} params.orgId // 机构ID
* @param {String} params.sanType // 制裁手段
*/
export function getUSChinaSanctionTrend(params) {
return request({
method: 'GET',
url: `/api/rivalryIndex/sanctionsQuantitativeTrend`,
params
})
}
// 全政府-美政府部门打压遏制最新动态
/**
* @header token
*/
export function getUSGovernmentLatestDynamic() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentSanctionsDynamics`
})
}
// 全政府-美政府部门联合制裁排行
/**
* @header token
*/
export function getUSGovernmentJointSanctionRank() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentJointSanctionsRanking`
})
}
// 全政府-美政府部门对我打压遏制时间线
/**
* @header token
* @param {Object} params
* @param {String} params.currentPage = 1 // 当前页
* @param {String} params.pageSize = 1000 // 每页数量
*/
export function getUSGovernmentSanctionHistory(params) {
return request({
method: 'GET',
url: `/api/rivalryIndex/getSanctionProcess`,
params
})
}
// 全政府-获取部门数据
/**
* @header token
*/
export function getDepartmentList() {
return request({
method: 'GET',
url: `/api/organization/summaryDepartList`
})
}
\ No newline at end of file
......@@ -3,11 +3,13 @@ import request from "@/api/request.js";
// 全联盟-获取联盟列表
/**
* @header token
* @param {Object} params
* @param {String} params.date - 日期
*/
export function getAllUnionList() {
export function getAllUnionList(params) {
return request({
method: 'GET',
url: `/api/union/union/unionList`
url: `/api/union/union/unionList/${params.date}`
})
}
......
......@@ -78,3 +78,11 @@ export function getDecreehylyList(params) {
params
})
}
// 发布机构
export function getDecreeTypeList() {
return request({
method: 'GET',
url: `/api/administrativeDict/type`,
})
}
\ No newline at end of file
import request from "@/api/request.js";
// 全领域
// 全领域统计
/**
* @header token
* @param {Object} params
* @param {String} params.startDate - 开始日期
* @param {String} params.endDate - 结束日期
*/
export function getAllDomainCount(params) {
return request({
method: "GET",
url: `/api/rivalryIndexV2/AllDomainCount`,
params
});
}
// 美对华制裁措施数量趋势
/**
* @header token
* @param {Object} params
* @param {String} params.byYOrM - 按月统计或按年统计
*/
export function getDomainContainmentTrend(params) {
return request({
method: "GET",
url: `/api/rivalryIndexV2/DomainContainmentTrend`,
params
});
}
// 中美科技博弈分析
export function getTechnologyGameAnalysis(domain = 1) {
return request({
method: "GET",
url: `/api/rivalryIndexV2/TechnologyGameAnalysis?domain=${domain}`
});
}
// 领域打压遏制排行
/**
* @param {currentPage, pageSize}
*/
export function getDomainContainmentRanking(ContainmentOrg = "对我打压机构", Domains = "") {
return request({
method: "GET",
url: !!Domains
? `/api/rivalryIndexV2/DomainContainmentRanking?ContainmentOrg=${ContainmentOrg}&Domains=${Domains}`
: `/api/rivalryIndexV2/DomainContainmentRanking?ContainmentOrg=${ContainmentOrg}`
});
}
// 领域打压遏制时间线
/**
* @param {currentPage, pageSize}
*/
export function getDomainContainmentTimeline(params) {
return request({
method: "GET",
url: `/api/rivalryIndexV2/DomainContainmentTimeline`,
params
});
}
import request from "@/api/request.js";
// 全要素统计
export function getElementCount(params) {
return request({
method: 'GET',
url: `/api/element/elementCount/${params.date}`,
})
}
// 最新动态
export function getNewDynamics() {
return request({
method: 'GET',
url: `/api/element/newDynamics`,
})
}
// 美对我要素打压情况
/**
* @param {currentPage, pageSize}
*/
export function getElementSuppress(params) {
return request({
method: 'GET',
url: `/api/element/elementSuppress/${params.date}`,
params
})
}
// 关键词云-上
/**
* @param {date}
*/
export function getKeyWordUp(params) {
return request({
method: 'GET',
url: `/api/element/getKeyWordUp/${params.date}`,
})
}
// 美自身要素发展情况
/**
* @param {currentPage, pageSize}
*/
export function getElementDevelop(params) {
return request({
method: 'GET',
url: `/api/element/elementDevelop/${params.date}`,
params
})
}
// 关键词云-下
/**
* @param {currentPage, pageSize}
*/
export function getKeyWordDown(params) {
return request({
method: 'GET',
url: `/api/element/getKeyWordDown/${params.date}`,
})
}
// 通过id获取政令详细信息
/**
* @param {id}
*/
export function getOrderInfo(params) {
return request({
method: 'GET',
url: `/api/element/getOrderInfo/${params.id}`,
params
})
}
\ No newline at end of file
import request from "@/api/request.js";
// 中美博弈概览V2:最新风险动态统计
export function getLatestRiskUpdates(params) {
return request({
method: 'GET',
url: `/api/rivalryIndexV2/LatestRiskUpdates`,
params: params
})
}
// 中美博弈概览V2:最新风险信号
export function getLatestRisks() {
return request({
method: 'GET',
url: `/api/rivalryIndexV2/LatestRisks`,
})
}
// 中美博弈概览V2:美对华制裁措施数量趋势
export function geDomainContainmentTrend(params) {
return request({
method: 'GET',
url: `/api/rivalryIndexV2/DomainContainmentTrend`,
params: params
})
}
// 中美博弈概况:获取榜单字典
export function getChartDict() {
return request({
method: 'GET',
url: `/api/union/summary/chartDict`,
})
}
// 中美博弈概况:中美科技实力对比
export function getCompare(id) {
return request({
method: 'GET',
url: `/api/union/summary/compare/${id}`,
})
}
// 中美博弈分析
export function getTechnologyGameAnalysis() {
return request({
method: 'GET',
url: `/api/rivalryIndexV2/TechnologyGameAnalysis`,
})
}
//中美博弈概览V7:美国政府部门对华制裁最新动态
export function getGovernmentSanctionsDynamics() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentSanctionsDynamics`,
})
}
\ No newline at end of file
......@@ -87,7 +87,8 @@ const handleInfoClick = () => {
border-radius: 12px;
padding: 10px 16px;
position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(231, 243, 255, 1);
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
}
.bubble-header {
......
......@@ -2,6 +2,10 @@
import ZMOverview from "@/views/ZMOverView/index.vue";
const ZMOverviewRoutes = [
{
path: "/",
redirect: "/ZMOverView"
},
{
path: "/ZMOverView",
name: "ZMOverView",
......
......@@ -7,6 +7,7 @@ import DecreeBackground from "@/views/decree/decreeLayout/overview/background/in
import DecreeDeepDig from "@/views/decree/decreeLayout/deepdig/index.vue";
import DecreeInfluence from "@/views/decree/decreeLayout/influence/index.vue";
import Institution from "@/views/decree/institution/index.vue"
import DecreeOriginal from "@/views/decree/decreeOriginal/index.vue"
const decreeRoutes = [
// 政令首页
......@@ -68,7 +69,7 @@ const decreeRoutes = [
// meta: {
// title: "影响分析"
// }
}
},
]
},
{
......@@ -79,7 +80,15 @@ const decreeRoutes = [
title: "行政机构主页",
dynamicTitle: true
}
}
},
{
path: "/decree/decreeOriginal",
name: "DecreeOriginal",
component: DecreeOriginal,
// meta: {
// title: "政令原文"
// }
},
]
......
//新闻速览
import newsBrief from "@/views/newsBrief/index.vue"
import newsBrief from "@/views/newsBrief/index.vue";
// 新闻事件分析
import NewsAnalysis from "@/views/newsAnalysis/index.vue";
......@@ -22,8 +22,7 @@ const newsRoutes = [
meta: {
title: "新闻事件分析"
}
},
]
}
];
export default newsRoutes
\ No newline at end of file
export default newsRoutes;
......@@ -5,10 +5,10 @@ import gjOverView from "@/views/gjOverView/index.vue";
const overViewRoutes = [
// 中美博弈概览页面路由
{
path: "/",
redirect: "/overview"
},
// {
// path: "/",
// redirect: "/overview"
// },
{
path: "/overview",
name: "overView",
......
......@@ -7,11 +7,14 @@
:style="sections[index].waveBall.length === 2 ? 'width: 350px' : 'width:503px'">
<img class="section-title" :src="`/public/icon/riskToday/btn-` + index + `.png`" />
<div class="stats">
<div v-for="value in sections[index].waveBall">
<WaveBall :percent="value.percent" :data="value" :color="section.waterColor" :size="128" />
<div v-for="value in sections[index].waveBall" @click="highLight(value)">
<div @click="highLight(value.title)" style="cursor: pointer;">
<WaveBall :percent="value.percent" :data="value" :color="section.waterColor" :size="128"
@click="highLight(value)" />
<div class="waveBall-text">{{ value.title }}</div>
</div>
</div>
</div>
<div class="bottm-box">
<img src="./icon/title-icon-1.png" />
<div style="width: 225px">
......@@ -27,13 +30,13 @@
<div class="content-title">
<img class="section-title" src="./icon/title-icon-2.png" />
<div>风险信号</div>
<div>风险信号3323</div>
<div class="num">12</div>
</div>
<div style="display: flex">
<div class="risk-signals">
<div class="risk-signals-item" v-for="(item, index) in warningList" :key="index"
@click="handleClickToDetailO(item)">
@click="handleClickToDetailO(item)" :class="{ 'highlighted': item.eventType === highlightedEventType }">
<div class="item-left" :class="{
itemLeftStatus1: item.signalLevel === '特别重大',
itemLeftStatus2: item.signalLevel === '重大风险'
......@@ -257,6 +260,26 @@ const handleSwithCurNews = name => {
carouselRef.value.next();
}
};
// 查看详情 传递参数
const handleClickToDetailO = item => {
window.sessionStorage.setItem("billId", item.billId);
window.sessionStorage.setItem("curTabName", item.name || item.signalTitle);
const route = router.resolve("/billLayout?billId=" + item.billId);
window.open(route.href, "_blank");
};
const highlightedEventType = ref('')
const highLight = (title) => {
console.log(title)
// 如果再次点击同一个,取消高亮
if (highlightedEventType.value === title) {
highlightedEventType.value = null;
} else {
highlightedEventType.value = title;
}
};
onMounted(() => {
// 这里可以添加从后端获取数据的代码
handlegetBillRiskSignal();
......
......@@ -2,12 +2,19 @@
<template>
<div class="content-wrapper">
<div class="btn-wrapper">
<div class="btn-box">
<div v-for="(value, index) in buttonsData" class="btn-item" :style="{ background: value.background }">
<div class="btn-wrapper" @mouseenter="stopAutoPlay" @mouseleave="startAutoPlay">
<div class="cards-mask">
<div class="btn-box" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }">
<div class="btn-item-outer" v-for="(item, indexx) in buttonsData" :key="indexx">
<div
v-for="(value, index) in item"
:key="index"
class="btn-item"
:style="{ background: value.background }"
>
<div
:style="{
backgroundImage: 'url(' + `/public/icon/ZM/btn-icon2-${index}.png` + ')'
backgroundImage: 'url(' + `/public/icon/ZM/btn-icon2-${value.originalIndex}.png` + ')'
}"
class="btn-left-text"
>
......@@ -18,8 +25,11 @@
</div>
</div>
</div>
<img :src="leftBtn" alt="" class="left-btn" />
<img :src="rightBtn" alt="" class="right-btn" />
</div>
</div>
<img :src="leftBtn" alt="" @click="prev" class="left-btn" />
<img :src="rightBtn" alt="" @click="next" class="right-btn" />
</div>
<div class="main-charts">
<div class="charts-title">
......@@ -28,8 +38,14 @@
<span>美对华领域打压遏制数量趋势</span>
</div>
<div class="title-right">
<el-select v-model="select1" placeholder="按月统计" class="custom-select">
<el-option label="按月统计" value="" />
<el-select
v-model="select1"
placeholder="按月统计"
class="custom-select"
@change="handleGetDomainContainmentTrend"
>
<el-option label="按月统计" value="按月统计" />
<el-option label="按年统计" value="按年统计" />
</el-select>
</div>
</div>
......@@ -58,7 +74,7 @@
{{ value.date + " · " + value.type }}
</div>
</div>
<div class="content-title">
<div class="content-title" @click="handleClickTitle(value)">
{{ value.title }}
</div>
<el-tooltip
......@@ -117,14 +133,18 @@
<img :src="icon4" alt="" />
<span>美对我领域打压遏制时间线</span>
</div>
<el-select v-model="selectedFieldTimeline" placeholder="全部领域" class="field-select">
<el-select
v-model="selectedFieldTimeline"
placeholder="全部领域"
class="field-select"
@change="handleGetDomainContainmentTimeline"
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="line-main">
<div class="nav-btn left"><img :src="leftBtn" alt="" /></div>
<div class="svg-container">
<svg :viewBox="`0 0 ${svgWidth} ${svgHeight}`" width="100%" height="100%">
<svg :viewBox="`0 0 ${svgWidth} ${svgHeight}`" width="100%">
<defs>
<marker
id="arrow"
......@@ -166,7 +186,9 @@
<div class="item-tags">
<span v-for="tag in node.tags" :key="tag" :class="getTagClass(tag)">{{ tag }}</span>
</div>
<div class="item-title">{{ node.title }}</div>
<div class="item-title">
<CommonPrompt :content="node.title" />
</div>
<el-tooltip
effect="dark"
:content="node.content"
......@@ -182,28 +204,79 @@
</g>
</svg>
</div>
<div class="nav-btn right"><img :src="rightBtn" alt="" /></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import { ref, computed, onMounted, watch, inject, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import setChart from "@/utils/setChart";
import getMultiLineChart from "./multiLineChart";
import CommonPrompt from "../../../../commonPrompt/index.vue";
import leftBtn from "../../assets/left-btn.png";
import rightBtn from "../../assets/right-btn.png";
import icon3 from "./icon/icon-3.png";
import icon4 from "./icon/icon-4.png";
import defaultImg from "../../../../assets/images/default-icon2.png";
const select1 = ref("");
import {
getAllDomainCount,
getDomainContainmentTrend,
getTechnologyGameAnalysis,
getDomainContainmentRanking,
getDomainContainmentTimeline
} from "@/api/zmOverview/allDomains";
import { getUSGovernmentLatestDynamic } from "@/api/allGovernment.js";
const router = useRouter();
const activeDate = inject("activeDate");
const select1 = ref("按月统计");
const rankType = ref("institution");
const selectedField = ref("");
const selectedFieldTimeline = ref("");
const timelineScrollX = ref(0);
const timelineContainerWidth = 1601;
const timelineContainerWidth = 1700;
// 美政府部门打压遏制最新动态
const newsList = ref([]);
const getUSGovernmentLatestDynamicData = async () => {
try {
const res = await getUSGovernmentLatestDynamic();
if (res.code === 200 && res.data) {
// 将接口数据转换为 newsList 需要的格式
newsList.value = res.data.map(item => {
const dateObj = new Date(item.time);
const formattedDate = `${dateObj.getFullYear()}${dateObj.getMonth() + 1}${dateObj.getDate()}日`;
return {
id: item.id,
tags: item.industrylist || [],
date: formattedDate,
type: item.orgName || "未知机构", // 使用 orgName 作为类型信息
title: item.title,
content: item.content || item.title // 如果 content 为空,使用 title 填充
};
});
}
} catch (error) {
console.error("获取美政府部门打压遏制最新动态失败:", error);
}
};
const handleClickTitle = item => {
if (!item || !item.id) return;
// 打开新标签页
const { href } = router.resolve({
path: "/newsAnalysis",
query: {
newsId: item.id
}
});
window.open(href, "_blank");
};
const timelineList = ref([
{
......@@ -271,10 +344,69 @@ const timelineList = ref([
}
]);
// 处理时间线数据的方法
const processTimelineData = rawData => {
if (!rawData || !Array.isArray(rawData) || rawData.length === 0) {
return [];
}
return rawData.map(item => {
// 将日期格式转换为 "YYYY年 M月" 格式
const eventDate = item.eventDate ? new Date(item.eventDate) : null;
const formattedDate = eventDate ? `${eventDate.getFullYear()}${eventDate.getMonth() + 1}月` : "";
// 提取领域标签
const tags = [];
if (item.eventDomainList && Array.isArray(item.eventDomainList)) {
item.eventDomainList.forEach(domain => {
if (domain.domainName) {
tags.push(domain.domainName);
}
});
}
return {
date: formattedDate,
tags: tags,
title: item.eventName || "",
content: item.eventDesc || "",
info: `${item.eventDate || ""} · ${item.eventType || ""}`,
// 保留原始数据字段
eventId: item.eventId,
eventOrg: item.eventOrg,
eventType: item.eventType,
eventCountryImg: item.eventCountryImg,
eventDomainList: item.eventDomainList
};
});
};
// 获取领域遏制时间线数据
const handleGetDomainContainmentTimeline = async () => {
try {
const params = {};
if (selectedFieldTimeline.value) {
params.domain = selectedFieldTimeline.value;
}
const res = await getDomainContainmentTimeline(params);
console.log("美对我领域打压遏制时间线", res);
if (res.code === 200 && res.data) {
// 处理返回的数据结构
const processedData = processTimelineData(res.data);
timelineList.value = processedData;
}
} catch (error) {
console.error("获取美对我领域打压遏制时间线失败:", error);
// 设置默认空数组
timelineList.value = [];
}
};
const maxPerRow = 3;
const nodeGapX = 450;
const rowHeight = 215;
const startX = 190;
const nodeGapX = 420;
const rowHeight = 230;
const startX = 250;
const startY = 45;
const axisDates = computed(() => {
......@@ -326,7 +458,7 @@ const timelineNodes = computed(() => {
contentX,
contentY,
contentWidth: 320,
contentHeight: 140
contentHeight: 180
};
});
});
......@@ -401,28 +533,31 @@ const svgWidth = computed(() => {
const svgHeight = computed(() => {
const rows = Math.ceil(timelineList.value.length / maxPerRow);
return startY + rows * rowHeight;
console.log("看下高度", rows);
return startY + rows * rowHeight + 50;
});
const scrollLeft = () => {
if (timelineScrollX.value < 0) {
timelineScrollX.value += nodeGapX;
}
};
const fieldOptions = [
{ value: "", label: "全部领域" },
{ value: "1", label: "人工智能" },
{ value: "2", label: "生物科技" },
{ value: "3", label: "新一代信息技术" },
{ value: "4", label: "量子科技" },
{ value: "5", label: "新能源" },
{ value: "6", label: "集成电路" },
{ value: "7", label: "海洋" },
{ value: "8", label: "先进制造" },
{ value: "9", label: "新材料" },
{ value: "10", label: "航空航天" },
{ value: "11", label: "深海" },
{ value: "12", label: "极地" },
{ value: "13", label: "太空" },
{ value: "14", label: "核" }
];
const scrollRight = () => {
const maxScroll = -(timelineNodes.value.length * nodeGapX - timelineContainerWidth + startX + 100);
if (timelineScrollX.value > maxScroll) {
timelineScrollX.value -= nodeGapX;
}
};
const fieldOptions = ref([
{ label: "全部领域", value: "" },
{ label: "集成电路", value: "集成电路" },
{ label: "人工智能", value: "人工智能" },
{ label: "量子科技", value: "量子科技" }
]);
const buttonsData = [
// 全领域统计
const buttonsData = ref([]);
const bgList = [
{
text: "集成电路",
count: 101,
......@@ -472,8 +607,145 @@ const buttonsData = [
text: "海洋",
count: 25,
background: "linear-gradient(180.00deg, rgba(153, 204, 255, 1),rgba(153, 204, 255, 0.5) 100%)"
},
{
text: "核",
count: 25,
background: "linear-gradient(180.00deg, rgba(255, 102, 102, 1),rgba(255, 102, 102, 0.5) 100%)"
},
{
text: "太空",
count: 25,
background: "linear-gradient(180.00deg, rgba(51, 153, 255, 1),rgba(51, 153, 255, 0.5) 100%)"
},
{
text: "其他",
count: 25,
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
},
{
text: "深海",
count: 25,
background: "linear-gradient(180.00deg, rgba(117, 73, 255, 1),rgba(117, 73, 255, 0.5) 100%)"
},
{
text: "极地",
count: 25,
background: "linear-gradient(180.00deg, rgba(102, 102, 102, 1),rgba(102, 102, 102, 0.5) 100%)"
},
{
text: "新一代通信网络",
count: 25,
background: "linear-gradient(180.00deg, rgba(153, 204, 255, 1),rgba(153, 204, 255, 0.5) 100%)"
}
];
const getCalculatedDate = type => {
const now = new Date();
const endDate = new Date();
const start = new Date();
switch (type) {
case "week": // 近一个月
start.setMonth(now.getMonth() - 1);
break;
case "three_month": // 近三个月
start.setMonth(now.getMonth() - 3);
break;
case "six_month": // 近半年
start.setMonth(now.getMonth() - 6);
break;
case "year": // 近一年
start.setFullYear(now.getFullYear() - 1);
break;
default:
start.setMonth(now.getMonth() - 1); // 默认近一个月
}
const formatDate = date => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
return {
startDate: formatDate(start),
endDate: formatDate(endDate)
};
};
const handleGetAllDomainCount = async () => {
try {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const res = await getAllDomainCount({
startDate: startDate,
endDate: endDate
});
console.log("全领域统计", res);
if (res.code === 200 && res.data) {
// 按照 countNum 从大到小排序
res.data.sort((a, b) => b.countNum - a.countNum);
buttonsData.value = res.data.map((item, index) => {
const matchedBg = bgList.find(bg => bg.text === item.countName);
const background = matchedBg ? matchedBg.background : bgList[index % 10].background;
// 映射图标索引
let iconIndex = 0; // 默认
switch (item.countName) {
case "集成电路":
iconIndex = 0;
break;
case "人工智能":
iconIndex = 1;
break;
case "新一代通信网络":
case "新一代信息技术":
iconIndex = 2;
break;
case "量子科技":
iconIndex = 3;
break;
case "先进制造":
iconIndex = 4;
break;
case "航空航天":
iconIndex = 5;
break;
case "生物科技":
iconIndex = 6;
break;
case "新能源":
iconIndex = 7;
break;
case "新材料":
iconIndex = 8;
break;
case "海洋":
iconIndex = 9;
break;
default:
iconIndex = 0;
}
return {
text: item.countName,
count: item.countNum,
background: background,
originalIndex: iconIndex
};
});
const chunkSize = 2;
buttonsData.value = Array.from({ length: Math.ceil(buttonsData.value.length / chunkSize) }, (_, i) =>
buttonsData.value.slice(i * chunkSize, i * chunkSize + chunkSize)
);
console.log("buttonsData", buttonsData.value);
startAutoPlay();
}
} catch (error) {}
};
const box5Data = ref({
title: [
"2024-12",
......@@ -563,43 +835,80 @@ const box5Data = ref({
]
});
const newsList = ref([
{
tags: ["航空航天", "能源"],
title: "联邦参议院:通过2026年国防授权法案",
date: "2025年12月18日",
type: "法案",
content: "2026年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普签署成为法律,法案..."
},
{
tags: ["新材料", "生物科技"],
title: "美国FDA:针对两家中国第三方检测机构的数据完整性问题采取行动",
date: "2025年12月18日",
type: "法案",
content: "FDA因数据造假或无效问题,向两家中国第三方检测公司(天津中联科技检测有限公司和苏...)"
},
{
tags: ["人工智能", "集成电路"],
title: "美国FCC:出台电信设备市场准入认证新规",
date: "2025年12月18日",
type: "法案",
content: "将国家安全部门审查全面嵌入设备授权程序,对实验室资质、测试标准以及供应链设置严格限制。"
},
{
tags: ["人工智能", "集成电路"],
title: "商务部工业与安全局:对实体清单的修订",
date: "2025年12月18日",
type: "法案",
content: "美国商务部工业与安全局公布对中国半导体出口管制措施新规则,将140家中国半导体公司列入..."
},
{
tags: ["集成电路"],
title: "国际贸易委员会:外国制造的半导体器件及其下游产品和组件",
date: "2025年12月18日",
type: "法案",
content: "美国国际贸易委员会(ITC)投票决定对特定半导体器件及其下游计算产品和组件(Certain ...)"
const handleGetDomainContainmentTrend = async () => {
try {
const res = await getDomainContainmentTrend({
byYOrM: select1.value
});
console.log("美对华领域打压遏制数量趋势", res);
if (res.code === 200 && res.data) {
// 处理返回的数据结构,按照 mockdata.json 中的格式
const processedData = processDomainTrendData(res.data);
box5Data.value = processedData;
// 更新图表
let Chart = getMultiLineChart(box5Data.value);
setChart(Chart, "chartRef");
}
]);
} catch (error) {
console.error("获取美对华领域打压遏制数量趋势失败:", error);
}
};
// 处理领域趋势数据的方法
const processDomainTrendData = rawData => {
// 提取所有的月份作为标题
const titles = rawData.map(item => item.yearOrMonth).reverse();
// 收集所有不重复的领域名称
const domainNamesSet = new Set();
rawData.forEach(item => {
item.domainList.forEach(domain => {
domainNamesSet.add(domain.domainName);
});
});
const domainNames = Array.from(domainNamesSet);
// 定义颜色映射
const colorMap = {
人工智能: "#E34D59",
新一代通信网络: "#FF9F1C",
: "#FFB3B3",
生物科技: "#00A79D",
量子科技: "#7B61FF",
先进制造: "#363B42",
新能源: "#2BA471",
太空: "#3762F0",
集成电路: "#0052D9",
新材料: "#FFD900",
航空航天: "#3762F0",
海洋: "#76D1FF",
深海: "#002060",
其他: "#A6A6A6"
};
// 生成数据系列
const dataSeries = domainNames.map(domainName => {
const values = rawData
.map(monthData => {
const domainItem = monthData.domainList.find(d => d.domainName === domainName);
return domainItem ? domainItem.domainNum : 0;
})
.reverse(); // 数据值也需要跟随标题反转顺序
return {
name: domainName,
color: colorMap[domainName] || `#${Math.floor(Math.random() * 16777215).toString(16)}`, // 如果没有预定义颜色,则随机生成
value: values
};
});
return {
title: titles,
data: dataSeries
};
};
const tagColors = [
{
......@@ -657,9 +966,113 @@ const getProgressWidth = count => {
return (count / maxCount) * 100 + "%";
};
// 处理排名数据的方法
const processRankingData = rawData => {
if (!rawData || !Array.isArray(rawData) || rawData.length === 0) {
return [];
}
return rawData.map(item => {
return {
name: item.orgName || "",
count: item.orgCount || 0,
orgPicture: item.orgPicture // 保留原始图片字段,以防后续需要使用
};
});
};
const rankTypeMap = {
institution: "对我打压机构",
enterprise: "受打压企业",
school: "受打压院校"
};
// 获取领域遏制排名数据
const handleGetDomainContainmentRanking = async () => {
try {
console.log("获取领域遏制排名数据", rankTypeMap[rankType.value], selectedField.value);
const res = await getDomainContainmentRanking(
rankTypeMap[rankType.value],
!!selectedField.value ? selectedField.value : ""
);
console.log("获取领域遏制排名数据", rankTypeMap[rankType.value], selectedField.value);
console.log("美对华领域打压遏制排行", res);
if (res.code === 200 && res.data) {
// 处理返回的数据结构
const processedData = processRankingData(res.data);
rankList.value = processedData;
}
} catch (error) {
console.error("获取美对华领域打压遏制排行失败:", error);
// 设置默认空数组
rankList.value = [];
}
};
// 监听rankType变化, 调用获取领域遏制排名数据方法
watch(rankType, handleGetDomainContainmentRanking);
// 监听selectedField变化, 调用获取领域遏制排名数据方法
watch(selectedField, handleGetDomainContainmentRanking);
const currentIndex = ref(0);
let autoTimer = null;
const startAutoPlay = () => {
stopAutoPlay();
if (buttonsData.value.length > 5) {
autoTimer = setInterval(() => {
next();
}, 3000);
}
};
const stopAutoPlay = () => {
if (autoTimer) {
clearInterval(autoTimer);
autoTimer = null;
}
};
const next = () => {
let arr = [...buttonsData.value];
if (currentIndex.value < buttonsData.value.length - 5) {
currentIndex.value++;
} else {
// currentIndex.value = 0;
buttonsData.value = [...buttonsData.value, ...arr];
currentIndex.value++;
}
};
const prev = () => {
if (currentIndex.value > 0) {
currentIndex.value--;
} else {
currentIndex.value = Math.max(0, buttonsData.value.length - 5);
}
};
onMounted(() => {
let Chart = getMultiLineChart(box5Data.value);
setChart(Chart, "chartRef");
// let Chart = getMultiLineChart(box5Data.value);
// setChart(Chart, "chartRef");
handleGetDomainContainmentTrend();
handleGetAllDomainCount();
handleGetDomainContainmentRanking();
handleGetDomainContainmentTimeline();
getUSGovernmentLatestDynamicData();
startAutoPlay();
});
onUnmounted(() => {
stopAutoPlay();
});
watch(activeDate, () => {
handleGetAllDomainCount();
});
</script>
......@@ -674,10 +1087,16 @@ onMounted(() => {
width: 1601px;
}
.cards-mask {
width: 100%;
overflow: hidden; // 仅在这里隐藏超出部分,不影响外层的按钮
}
.btn-box {
margin-top: 16px;
width: 100%;
width: max-content;
height: 176px;
overflow: hidden;
display: flex;
flex-direction: row;
flex-wrap: wrap;
......@@ -685,6 +1104,7 @@ onMounted(() => {
align-items: stretch;
gap: 16px;
padding: 0;
transition: transform 0.5s ease; // 平滑过渡动画
}
.left-btn {
......@@ -719,6 +1139,7 @@ onMounted(() => {
align-items: center;
justify-content: space-between;
padding: 0 16px;
margin-bottom: 10px;
.btn-left-text {
width: fit-content;
min-width: 60px;
......@@ -1136,7 +1557,7 @@ onMounted(() => {
}
.content-title {
/* 容器 1576 */
cursor: pointer;
width: 689px;
height: 30px;
/* 自动布局 */
......@@ -1263,10 +1684,11 @@ onMounted(() => {
.svg-container {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
overflow-x: hidden;
overflow-y: auto;
display: block;
padding: 0;
box-sizing: border-box;
}
.axis-date-label {
......@@ -1295,14 +1717,18 @@ onMounted(() => {
.timeline-content-item {
width: 100%;
height: 100%;
height: 160px;
display: flex;
flex-direction: column;
gap: 8px;
.item-tags {
width: 350px;
display: flex;
gap: 8px;
overflow: auto;
text-overflow: ellipsis;
white-space: nowrap;
.tag-item {
padding: 2px 8px;
border-radius: 4px;
......@@ -1349,6 +1775,7 @@ onMounted(() => {
color: rgb(59, 65, 75);
font-family: "Microsoft YaHei";
line-height: 24px;
width: 100%;
}
.item-desc {
......@@ -1356,11 +1783,18 @@ onMounted(() => {
font-weight: 400;
color: rgb(95, 101, 108);
line-height: 24px;
/* max-height: 48px; 可选:明确限制高度 */
font-family: "Microsoft YaHei";
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
white-space: normal;
}
.el-tooltip__trigger {
height: 48px;
}
.item-footer {
......@@ -1394,4 +1828,3 @@ onMounted(() => {
border-color: rgb(59, 65, 75) !important;
}
</style>
{
"code": 200,
"message": "操作成功",
"success": true,
"data": [
{
"eventType": "行政令",
"eventName": "为了美国和委内瑞拉人民的利益保障委内瑞拉的石油收入",
"eventDesc": null,
"eventId": "248914",
"eventDomainList": [
{
"domainId": "1",
"domainName": "人工智能"
},
{
"domainId": "3",
"domainName": "新一代通信网络"
}
],
"eventOrgName": null,
"eventDate": "2026-01-10"
},
{
"eventType": "行政令",
"eventName": null,
"eventDesc": null,
"eventId": "248863",
"eventDomainList": [
{
"domainId": "8",
"domainName": "先进制造"
},
{
"domainId": "9",
"domainName": "新材料"
}
],
"eventOrgName": null,
"eventDate": "2026-01-09"
},
{
"eventType": "行政令",
"eventName": "使美国退出与美国利益相悖的国际组织、公约、条约",
"eventDesc": null,
"eventId": "249004",
"eventDomainList": [
{
"domainId": "2",
"domainName": "生物科技"
},
{
"domainId": "4",
"domainName": "量子科技"
},
{
"domainId": "5",
"domainName": "新能源"
},
{
"domainId": "3",
"domainName": "新一代通信网络"
}
],
"eventOrgName": null,
"eventDate": "2026-01-08"
},
{
"eventType": "行政令",
"eventName": "在国防合同中优先考虑作战人员",
"eventDesc": null,
"eventId": "248915",
"eventDomainList": [
{
"domainId": "99",
"domainName": "其他"
},
{
"domainId": "8",
"domainName": "先进制造"
},
{
"domainId": "10",
"domainName": "航空航天"
}
],
"eventOrgName": null,
"eventDate": "2026-01-08"
},
{
"eventType": "行政令",
"eventName": "大西洋高度洄游物种;商业性大西洋黑鼻鲨和娱乐性大西洋鲨鱼渔业管理措施的修订",
"eventDesc": "NMFS提议对商业和休闲大西洋鲨鱼渔业进行几项改革。具体来说,NMFS正在考虑取消大西洋地区黑鼻鲨管理边界,修改商业保留限额...",
"eventId": "163447",
"eventDomainList": [
{
"domainId": "14",
"domainName": "核"
},
{
"domainId": "7",
"domainName": "海洋"
}
],
"eventOrgName": null,
"eventDate": "2026-01-05"
},
{
"eventType": "行政令",
"eventName": "关于Hiefo Corporation收购Emcore Corporation部分资产",
"eventDesc": null,
"eventId": "248916",
"eventDomainList": [
{
"domainId": "3",
"domainName": "新一代通信网络"
},
{
"domainId": "8",
"domainName": "先进制造"
},
{
"domainId": "6",
"domainName": "集成电路"
}
],
"eventOrgName": null,
"eventDate": "2026-01-03"
},
{
"eventType": "行政令",
"eventName": "汽车贷款利息扣除",
"eventDesc": "本文件包含关于某些纳税人扣除高达10,000美元的合格乘用车贷款利息的拟议法规。本文件还包含关于新信息报告的拟议法规...",
"eventId": "163443",
"eventDomainList": [
{
"domainId": "13",
"domainName": "太空"
},
{
"domainId": "14",
"domainName": "核"
}
],
"eventOrgName": null,
"eventDate": "2026-01-02"
},
{
"eventType": "行政令",
"eventName": "品牌处方药费用法规的法定更新",
"eventDesc": "本文件建议修订有关对从事制造或进口某些品牌处方药业务的相关实体征收年费的法规。为了应对覆盖缺口折扣的替代...",
"eventId": "163444",
"eventDomainList": [
{
"domainId": "2",
"domainName": "生物科技"
},
{
"domainId": "3",
"domainName": "新一代通信网络"
}
],
"eventOrgName": null,
"eventDate": "2026-01-02"
},
{
"eventType": "337",
"eventName": "外国制造的半导体器件及其下游产品和组件",
"eventDesc": "美国ITC发布对外国制造的半导体器件及其下游产品和组件的337部分终裁",
"eventId": "111",
"eventDomainList": [
{
"domainId": "1",
"domainName": "人工智能"
},
{
"domainId": "5",
"domainName": "新能源"
}
],
"eventOrgName": "美国商务部",
"eventDate": "2026-01-01"
},
{
"eventType": "行政令",
"eventName": "美国东北部的渔业;大西洋冲浪蛤和海洋圆蛤渔业管理计划修正案21",
"eventDesc": "NMFS宣布,中大西洋渔业管理委员会已提交大西洋冲浪蛤和海洋圆蛤渔业管理计划第21号修正案,供商务部长审查和批准。我们正在征求意见...",
"eventId": "163426",
"eventDomainList": [
{
"domainId": "7",
"domainName": "海洋"
},
{
"domainId": "3",
"domainName": "新一代通信网络"
}
],
"eventOrgName": null,
"eventDate": "2025-12-29"
}
]
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ const getMultiLineChart = (data) => {
const echartsSeries = series.map((item, index) => ({
name: item.name,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 8,
itemStyle: {
......@@ -88,8 +89,8 @@ const getMultiLineChart = (data) => {
{
type: 'value',
min: 0,
max: 100,
interval: 20,
// max: 100,
// interval: 20,
axisLine: {
show: false
},
......
......@@ -19,8 +19,8 @@
v-for="(item, index) in headerList"
:key="index"
>
<div class="name">{{ item.name }}</div>
<div class="num">{{ item.value }}</div>
<div class="name">{{ item.elementName }}</div>
<div class="num">{{ item.num }}</div>
</div>
</div>
<div class="main">
......@@ -36,15 +36,28 @@
<div class="box1-item" v-for="(item, index) in box1DataList" :key="index">
<div class="box1-item-left">{{ index + 1 }}</div>
<div class="box1-item-right">
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
<div class="title">{{ item.name }}</div>
<div class="content">{{ item.describe }}</div>
<div class="tag-box">
<div class="tag" v-for="(val, idx) in item.tagList" :key="idx">{{ val }}</div>
<div class="tag" v-for="(val, idx) in item.elemetList" :key="idx">{{ val }}</div>
</div>
<div class="box1-item-right-footer">
<div class="time">{{ item.time }}</div>
<div class="time">{{ item.postDate }}</div>
<div class="area-box">
<div class="area" v-for="(vall, idxx) in item.areaList" :key="idxx">{{ vall.name }}</div>
<div
class="area"
:class="{
area1: vall.status === '1',
area2: vall.status === '2',
area3: vall.status === '3',
area4: vall.status === '4',
area5: vall.status === '5'
}"
v-for="(vall, idxx) in item.areaList"
:key="idxx"
>
{{ vall.industryName }}
</div>
</div>
</div>
</div>
......@@ -72,7 +85,7 @@
@click="handleClickBox2Item(index)"
>
<div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.title }}</div>
<div class="text">{{ item.name }}</div>
</div>
</div>
<div class="left-footer">
......@@ -83,34 +96,37 @@
:page-size="box2PageSize"
v-model:current-page="box2CurrentPage"
@current-change="handleGetBox2DataList"
size="small"
:pager-count="6"
/>
</div>
</div>
<div class="right">
<div class="title">{{ box2DataList[box2LeftActiveIndex].title }}</div>
<div class="title">{{ box2DetailInfo.name }}</div>
<div class="tag-box">
<div
class="tag"
v-for="(item, index) in box2DataList[box2LeftActiveIndex].tagList"
:key="index"
>
<div class="tag" v-for="(item, index) in box2DetailInfo.elemetList" :key="index">
{{ item }}
</div>
</div>
<div class="content">{{ box2DataList[box2LeftActiveIndex].content }}</div>
<div class="content">{{ box2DetailInfo.describe }}</div>
<div class="area-box">
<div
class="area"
v-for="(item, index) in box2DataList[box2LeftActiveIndex].areaList"
:class="{
area1: item.status === '1',
area2: item.status === '2',
area3: item.status === '3',
area4: item.status === '4',
area5: item.status === '5'
}"
v-for="(item, index) in box2DetailInfo.areaList"
:key="index"
>
{{ item.name }}
{{ item.industryName }}
</div>
</div>
<div class="footer">
{{
`${box2DataList[box2LeftActiveIndex].time} · ${box2DataList[box2LeftActiveIndex].org} · ${box2DataList[box2LeftActiveIndex].type}`
}}
{{ `${box2DetailInfo?.postDate} · ${box2DetailInfo?.orgNameList?.toString()} · 行政令` }}
</div>
</div>
</div>
......@@ -139,7 +155,7 @@
@click="handleClickBox3Item(index)"
>
<div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.title }}</div>
<div class="text">{{ item.name }}</div>
</div>
</div>
<div class="left-footer">
......@@ -150,34 +166,37 @@
:page-size="box3PageSize"
v-model:current-page="box3CurrentPage"
@current-change="handleGetBox3DataList"
size="small"
:pager-count="6"
/>
</div>
</div>
<div class="right">
<div class="title">{{ box3DataList[box3LeftActiveIndex].title }}</div>
<div class="title">{{ box3DetailInfo.name }}</div>
<div class="tag-box">
<div
class="tag"
v-for="(item, index) in box3DataList[box3LeftActiveIndex].tagList"
:key="index"
>
<div class="tag" v-for="(item, index) in box3DetailInfo.elemetList" :key="index">
{{ item }}
</div>
</div>
<div class="content">{{ box3DataList[box3LeftActiveIndex].content }}</div>
<div class="content">{{ box3DetailInfo.describe }}</div>
<div class="area-box">
<div
class="area"
v-for="(item, index) in box3DataList[box3LeftActiveIndex].areaList"
:class="{
area1: item.status === '1',
area2: item.status === '2',
area3: item.status === '3',
area4: item.status === '4',
area5: item.status === '5'
}"
v-for="(item, index) in box3DetailInfo.areaList"
:key="index"
>
{{ item.name }}
{{ item.industryName }}
</div>
</div>
<div class="footer">
{{
`${box3DataList[box3LeftActiveIndex].time} · ${box3DataList[box3LeftActiveIndex].org} · ${box3DataList[box3LeftActiveIndex].type}`
}}
{{ `${box3DetailInfo.postDate} · ${box3DetailInfo.orgNameList?.toString()} · 行政令` }}
</div>
</div>
</div>
......@@ -193,372 +212,255 @@
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import { onMounted, ref, computed, inject, watch } from "vue";
import setChart from "@/utils/setChart";
import getWordCloudChart from "./uitls/worldCloudChart";
import {
getElementCount,
getNewDynamics,
getElementSuppress,
getKeyWordUp,
getElementDevelop,
getKeyWordDown,
getOrderInfo
} from "@/api/zmOverview/allElement";
const activeDate = inject("activeDate");
const getCalculatedDate = type => {
const now = new Date();
const endDate = new Date();
const start = new Date();
const headerList = ref([
{
name: "科研仪器",
value: 132
},
{
name: "科研机构",
value: 104
},
{
name: "科研人才",
value: 177
},
{
name: "科研数据",
value: 60
},
{
name: "科研经费",
value: 45
switch (type) {
case "week": // 近一个月
start.setMonth(now.getMonth() - 1);
break;
case "three_month": // 近三个月
start.setMonth(now.getMonth() - 3);
break;
case "six_month": // 近半年
start.setMonth(now.getMonth() - 6);
break;
case "year": // 近一年
start.setFullYear(now.getFullYear() - 1);
break;
default:
start.setMonth(now.getMonth() - 1); // 默认近一个月
}
]);
const formatDate = date => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
return {
startDate: formatDate(start),
endDate: formatDate(endDate)
};
};
// 全要素统计
const headerList = ref([]);
const handleGetHeaderList = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = {
date: startDate
};
try {
const res = await getElementCount(params);
console.log("全要素统计", res);
if (res.code === 200 && res.data) {
headerList.value = res.data;
}
} catch (error) {}
};
// 最新动态
const box1DataList = ref([
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
}
// {
// title: '更新"中国涉军企业清单"(NS-CMIC List)',
// content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
// tagList: ["科研机构", "科研经费"],
// time: "2025年12月18日",
// areaList: [
// {
// name: "量子科技"
// },
// {
// name: "量子科技"
// }
// ]
// }
]);
const handleGetBox1Data = async () => {
try {
const res = await getNewDynamics();
console.log("最新动态", res);
if (res.code === 200 && res.data) {
box1DataList.value = res.data;
}
} catch (error) {}
};
const box2DataList = ref([
{
title: "防止受关注国家访问美国敏感数据的规定",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定2",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定3",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定4",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定5",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
}
]);
const box2DataList = ref([]);
const box2Total = ref(0);
const box2CurrentPage = ref(1);
const box2PageSize = ref(5);
const box2LeftActiveIndex = ref(0);
const box2DetailInfo = ref({});
// 根据id 获取政令详细信息
const handleGetOrderInfo = async id => {
const params = {
id: id
};
try {
const res = await getOrderInfo(params);
console.log("根据id获取政令详细信息1", res);
if (res.code === 200 && res.data) {
box2DetailInfo.value = res.data;
}
} catch (error) {}
};
const handleGetBox2DataList = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = {
currentPage: box2CurrentPage.value,
pageSize: box2PageSize.value,
date: startDate
};
try {
const res = await getElementSuppress(params);
console.log("美对我要素打压情况", res);
if (res.code === 200 && res.data) {
box2DataList.value = res.data.content;
box2Total.value = res.data.totalElements;
handleGetOrderInfo(box2DataList.value[box2LeftActiveIndex.value].id);
}
} catch (error) {}
};
const handleClickBox2Item = index => {
box2LeftActiveIndex.value = index;
handleGetOrderInfo(box2DataList.value[index].id);
};
const box2Total = ref(25);
const box2CurrentPage = ref(1);
const box2PageSize = ref(5);
const handleGetBox2DataList = async () => {};
const box2ChartData = ref([]);
const handleGetBox2ChartData = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = {
date: startDate
};
try {
const res = await getKeyWordUp(params);
console.log("关键词云-上", res);
if (res.code === 200 && res.data) {
box2ChartData.value = res.data.map(item => {
return {
name: item.name,
value: item.count
};
});
}
} catch (error) {}
};
const handleBox2Chart = async () => {
await handleGetBox2ChartData();
let box2Chart = getWordCloudChart(box2ChartData.value);
setChart(box2Chart, "box2Chart");
};
const box2ChartData = ref([
{
name: "限制中国获取能源技术",
value: 35
},
{
name: "加强美国在核能领域得到领导力",
value: 12
},
{
name: "关注核聚变能源研究",
value: 15
},
{
name: "发展风能",
value: 21
},
{
name: "实施能源税收延期",
value: 18
},
{
name: "限制采购中国产电池",
value: 5
},
{
name: "评估中美现代化技术",
value: 11
},
{
name: "应对中国制造2025战略",
value: 9
},
{
name: "抵制外国人才争夺",
value: 16
}
]);
const box3DataList = ref([]);
const box3DataList = ref([
{
title: "防止受关注国家访问美国敏感数据的规定",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定2",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定3",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定4",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定5",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
}
]);
const box3Total = ref(0);
const box3CurrentPage = ref(1);
const box3PageSize = ref(5);
const box3LeftActiveIndex = ref(0);
const box3DetailInfo = ref({});
const handleClickBox3Item = index => {
box3LeftActiveIndex.value = index;
// 根据id 获取政令详细信息
const handleGetOrderInfo1 = async id => {
const params = {
id: id
};
try {
const res = await getOrderInfo(params);
console.log("根据id获取政令详细信息1", res);
if (res.code === 200 && res.data) {
box3DetailInfo.value = res.data;
}
} catch (error) {}
};
const box3Total = ref(25);
const box3CurrentPage = ref(1);
const box3PageSize = ref(5);
const handleGetBox3DataList = async () => {};
const handleGetBox3DataList = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const box3ChartData = ref([
{
name: "限制中国获取能源技术",
value: 35
},
{
name: "加强美国在核能领域得到领导力",
value: 12
},
{
name: "关注核聚变能源研究",
value: 15
},
{
name: "发展风能",
value: 21
},
{
name: "实施能源税收延期",
value: 18
},
{
name: "限制采购中国产电池",
value: 5
},
{
name: "评估中美现代化技术",
value: 11
},
{
name: "应对中国制造2025战略",
value: 9
},
{
name: "抵制外国人才争夺",
value: 16
}
]);
const params = {
currentPage: box3CurrentPage.value,
pageSize: box3PageSize.value,
date: startDate
};
try {
const res = await getElementDevelop(params);
console.log("美自身要素发展情况", res);
if (res.code === 200 && res.data) {
box3DataList.value = res.data.content;
box3Total.value = res.data.totalElements;
handleGetOrderInfo1(box3DataList.value[box3LeftActiveIndex.value].id);
}
} catch (error) {}
};
onMounted(() => {
let box2Chart = getWordCloudChart(box2ChartData.value);
setChart(box2Chart, "box2Chart");
const handleClickBox3Item = index => {
box3LeftActiveIndex.value = index;
handleGetOrderInfo1(box3DataList.value[index].id);
};
const box3ChartData = ref([]);
const handleGetBox3ChartData = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = {
date: startDate
};
try {
const res = await getKeyWordDown(params);
console.log("关键词云-下", res);
if (res.code === 200 && res.data) {
box3ChartData.value = res.data.map(item => {
return {
name: item.name,
value: item.count
};
});
}
} catch (error) {}
};
const handleBox3Chart = async () => {
await handleGetBox3ChartData();
let box3Chart = getWordCloudChart(box3ChartData.value);
setChart(box3Chart, "box3Chart");
};
watch(activeDate, () => {
handleGetHeaderList(); // 全要素统计
handleGetBox2DataList(); // 美对我要素打压情况
handleGetBox3DataList(); // 美自身要素发展情况
handleBox2Chart(); // 关键词云-上
handleBox3Chart(); // 关键词云-下
});
onMounted(() => {
handleGetHeaderList(); // 全要素统计
handleGetBox1Data(); // 最新动态
handleGetBox2DataList(); // 美对我要素打压情况
handleGetBox3DataList(); // 美自身要素发展情况
handleBox2Chart(); // 关键词云-上
handleBox3Chart(); // 关键词云-下
});
</script>
......@@ -733,7 +635,7 @@ onMounted(() => {
width: 388px;
margin-left: 12px;
.title {
height: 30px;
// height: 30px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
......@@ -755,8 +657,9 @@ onMounted(() => {
}
.tag-box {
margin-top: 4px;
height: 28px;
// height: 28px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.tag {
width: 80px;
......@@ -808,9 +711,13 @@ onMounted(() => {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
// border: 1px solid rgba(135, 232, 222, 1);
// background: rgba(230, 255, 251, 1);
// color: rgba(19, 168, 168, 1);
}
}
}
......@@ -924,7 +831,8 @@ onMounted(() => {
}
}
.left-footer {
height: 80px;
margin-top: 30px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
......@@ -932,10 +840,14 @@ onMounted(() => {
}
.right {
width: 320px;
height: 368px;
overflow: hidden;
overflow-y: auto;
.title {
width: 283px;
min-height: 24px;
max-height: 48px;
overflow: hidden;
margin: 16px auto 18px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -949,8 +861,9 @@ onMounted(() => {
.tag-box {
width: 283px;
margin: 0 auto;
height: 28px;
// height: 28px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.tag {
height: 28px;
......@@ -972,7 +885,7 @@ onMounted(() => {
margin: 0 auto;
margin-top: 8px;
width: 283px;
height: 150px;
max-height: 170px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
......@@ -981,13 +894,16 @@ onMounted(() => {
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
overflow: hidden;
overflow-y: auto;
}
.area-box {
width: 283px;
margin: 0 auto;
margin-top: 28px;
height: 24px;
margin-top: 8px;
// height: 24px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 8px;
.area {
......@@ -1002,9 +918,9 @@ onMounted(() => {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
}
.footer {
......@@ -1160,7 +1076,8 @@ onMounted(() => {
}
}
.left-footer {
height: 80px;
margin-top: 30px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
......@@ -1168,10 +1085,14 @@ onMounted(() => {
}
.right {
width: 320px;
height: 368px;
overflow: hidden;
overflow-y: auto;
.title {
width: 283px;
min-height: 24px;
max-height: 48px;
overflow: hidden;
margin: 16px auto 18px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -1185,8 +1106,9 @@ onMounted(() => {
.tag-box {
width: 283px;
margin: 0 auto;
height: 28px;
// height: 28px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.tag {
height: 28px;
......@@ -1208,7 +1130,7 @@ onMounted(() => {
margin: 0 auto;
margin-top: 8px;
width: 283px;
height: 150px;
max-height: 170px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
......@@ -1217,13 +1139,16 @@ onMounted(() => {
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
overflow: hidden;
overflow-y: auto;
}
.area-box {
width: 283px;
margin: 0 auto;
margin-top: 28px;
height: 24px;
margin-top: 8px;
// height: 24px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 8px;
.area {
......@@ -1238,9 +1163,9 @@ onMounted(() => {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
}
.footer {
......@@ -1295,4 +1220,29 @@ onMounted(() => {
}
}
}
.area1 {
border: 1px solid rgba(217, 247, 190, 1) !important;
background: rgba(246, 255, 237, 1) !important;
color: rgba(56, 158, 13, 1) !important;
}
.area2 {
border: 1px solid rgba(135, 232, 222, 1) !important;
background: rgba(230, 255, 251, 1) !important;
color: rgba(19, 168, 168, 1) !important;
}
.area3 {
border: 1px solid rgba(145, 202, 255, 1) !important;
background: rgba(230, 244, 255, 1) !important;
color: rgba(22, 119, 255, 1) !important;
}
.area4 {
border: 1px solid rgba(173, 198, 255, 1) !important;
background: rgba(240, 245, 255, 1) !important;
color: rgba(47, 84, 235, 1) !important;
}
.area5 {
border: 1px solid rgba(211, 173, 247, 1) !important;
background: rgba(249, 240, 255, 1) !important;
color: rgba(114, 46, 209, 1) !important;
}
</style>
<!--科技要闻-->
<template>
<div class="content-wrapper">
<div class="main-content">
<div class="main-content" @mouseenter="stopAutoPlay" @mouseleave="startAutoPlay">
<div class="cards-mask">
<div class="cards-container" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }">
<div v-for="(card, index) in cardList" :key="index" class="government-card">
<div v-for="(card, index) in cardList" :key="index" class="government-card" @click="handleCardClick(card)">
<div class="card-bg" :style="{ backgroundImage: `url(${card.icon || defaultIcon})` }"></div>
<div class="card-header">
<span class="card-title">{{ card.title }}</span>
......@@ -35,14 +35,35 @@
<span>美对华制裁措施数量趋势</span>
</div>
<div class="title-right">
<el-select v-model="fieldValue" placeholder="全部领域" class="custom-select">
<el-option label="全部领域" value="" />
<el-select
v-model="fieldValue"
placeholder="全部领域"
class="custom-select"
@change="getUSChinaSanctionTrendData"
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="deptValue" placeholder="全部部门" class="custom-select">
<el-select
v-model="deptValue"
placeholder="全部部门"
class="custom-select"
@change="getUSChinaSanctionTrendData"
>
<el-option label="全部部门" value="" />
<el-option
v-for="item in departmentList"
:key="item.departId"
:label="item.departName"
:value="item.departId"
/>
</el-select>
<el-select v-model="methodValue" placeholder="全部制裁手段" class="custom-select">
<el-option label="全部制裁手段" value="" />
<el-select
v-model="methodValue"
placeholder="全部制裁手段"
class="custom-select"
@change="getUSChinaSanctionTrendData"
>
<el-option v-for="item in methodOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
......@@ -61,8 +82,8 @@
<img :src="defaultImg" alt="" class="item-icon" />
<div class="item-right">
<div class="dynamic-item-header">
<span class="item-title">{{ item.title }}</span>
<span class="item-date">{{ item.date }} · {{ item.type }}</span>
<span class="item-title" @click="handleNewsClick(item)">{{ item.title }}</span>
<span class="item-date">{{ item.date }} · {{ item.orgName }}</span>
</div>
<el-tooltip
effect="dark"
......@@ -97,12 +118,12 @@
<img
v-for="(dept, dIndex) in rank.depts"
:key="dIndex"
:src="defaultImg"
:src="dept.icon || defaultImg"
alt=""
class="dept-icon"
/>
</div>
<span class="dept-names">{{ rank.depts.join(" / ") }}</span>
<span class="dept-names">{{ rank.depts.map(d => d.name).join(" / ") }}</span>
</div>
<div class="header-right">
<span class="joint-count">{{ rank.count }}次联合制裁</span>
......@@ -121,7 +142,12 @@
placement="top"
:show-after="500"
>
<span class="item-title">{{ item.title }}</span>
<span
class="item-title"
:class="{ clickable: item.type === '政令' }"
@click="handleJointRankItemClick(item)"
>{{ item.title }}</span
>
</el-tooltip>
</div>
<span class="item-date">{{ item.date }}</span>
......@@ -154,7 +180,7 @@
<div class="timeline-list">
<div v-for="(dept, index) in filteredTimelineList" :key="index" class="dept-row">
<div class="dept-info">
<img :src="defaultImg" alt="" class="dept-icon" />
<img :src="dept.icon || defaultImg" alt="" class="dept-icon" />
<div class="dept-text">
<div class="dept-name">{{ dept.name }}</div>
<div class="dept-count">{{ dept.count }}</div>
......@@ -181,7 +207,12 @@
<div class="event-content">{{ event.content }}</div>
</el-tooltip>
<div class="event-tags">
<span v-for="(tag, tIndex) in event.tags" :key="tIndex" class="tag" :class="getTagClass(tag)">
<span
v-for="(tag, tIndex) in event.tags"
:key="tIndex"
class="tag"
:class="getTagClass(tag)"
>
{{ tag }}
</span>
</div>
......@@ -190,7 +221,7 @@
</div>
</div>
<div class="timelineBar" :style="{ backgroundImage: `url(${timelineBg})` }">
<div ref="sliderChartRef" style="width: 100%; height: 100%; touch-action: none;"></div>
<div ref="sliderChartRef" style="width: 100%; height: 100%; touch-action: none"></div>
</div>
</div>
</div>
......@@ -199,7 +230,8 @@
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import { onMounted, ref, computed, inject, watch, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import * as echarts from "echarts";
import defaultIcon from "../../assets/defaultIcon.png";
import leftBtn from "../../assets/left-btn.png";
......@@ -210,6 +242,327 @@ import icon3 from "../../assets/icon3.png";
import icon4 from "../../assets/icon4.png";
import timelineBg from "../../assets/timeline.png";
import defaultImg from "../../../../assets/images/default-icon2.png";
// 传递接口
import {
getAllGovernmentList,
getUSChinaSanctionTrend,
getUSGovernmentLatestDynamic,
getUSGovernmentJointSanctionRank,
getUSGovernmentSanctionHistory,
getDepartmentList
} from "@/api/allGovernment.js";
const router = useRouter();
const activeDate = inject("activeDate");
// 根据时间范围映射月份数
const monthNum = computed(() => {
switch (activeDate.value) {
case "week":
return 1;
case "three_month":
return 3;
case "six_month":
return 6;
case "year":
return 12;
default:
return 12;
}
});
// 全政府-美政府部门对我打压遏制时间线
const loadingHistory = ref(false);
const getUSGovernmentSanctionHistoryData = async () => {
loadingHistory.value = true;
try {
const res = await getUSGovernmentSanctionHistory({
currentPage: 1,
pageSize: 1000
});
if (res.code === 200 && res.data && res.data.content) {
const rawList = res.data.content;
const orgMap = {};
if (governmentList.value && governmentList.value.length) {
governmentList.value.forEach(g => {
if (g.departId) {
orgMap[g.departId] = g.title;
}
});
}
const grouped = {};
rawList.forEach(item => {
// 尝试获取部门名称
let deptName = "未知部门";
if (item.orgName) {
deptName = item.orgName;
} else if (item.orgId && orgMap[item.orgId]) {
deptName = orgMap[item.orgId];
} else if (item.orgId) {
if (item.orgId === "241") deptName = "商务部工业与安全局";
else if (item.orgId === "203") deptName = "海外资产控制办公室";
else deptName = "部门 " + item.orgId;
}
if (!grouped[deptName]) {
grouped[deptName] = {
name: deptName,
icon: item.orgLogoUrl,
count: 0,
events: []
};
}
grouped[deptName].count++;
grouped[deptName].events.push({
date: item.postDate ? item.postDate.replace(/^(\d{4})-(\d{2})-(\d{2})$/, "$1年$2月$3日") : "",
content: item.name || item.summary,
tags: item.techDomainList ? item.techDomainList.slice(0, 2) : [],
level: getLevelByCount(item.cnEntityCount)
});
});
timelineList.value = Object.values(grouped);
initSlider();
}
} catch (error) {
console.error("获取制裁历程失败:", error);
} finally {
loadingHistory.value = false;
}
};
// 辅助函数:根据受影响实体数量生成level
const getLevelByCount = count => {
const c = count || 0;
if (c === 0) return "green"; // 0 或空 绿色
if (c <= 10) return "yellow"; // 1-10 黄色
return "red"; // >10 红色
};
// 全政府-美政府部门联合制裁排行
const loadingJointRank = ref(false);
const getUSGovernmentJointSanctionRankData = async () => {
loadingJointRank.value = true;
try {
const res = await getUSGovernmentJointSanctionRank();
if (res.code === 200 && res.data) {
rankingList.value = res.data.map(item => ({
depts: (item.organizations || item.org || []).map(o => ({
name: o.orgName,
icon: o.logoUrl ? o.logoUrl.trim() : ""
})),
count: item.jointCount,
items: (item.sanctions || []).map(s => ({
id: s.orderId,
type: s.typeName || "其他",
title: s.name,
date: s.postDate ? s.postDate.replace(/^(\d{4})-(\d{2})-(\d{2})$/, "$1年$2月$3日") : ""
}))
}));
}
} catch (error) {
console.error("获取美政府部门联合制裁排行失败:", error);
} finally {
loadingJointRank.value = false;
}
};
// 全政府-美政府部门打压遏制最新动态
const loadingLatestDynamic = ref(false);
const getUSGovernmentLatestDynamicData = async () => {
loadingLatestDynamic.value = true;
try {
const res = await getUSGovernmentLatestDynamic();
if (res.code === 200 && res.data) {
dynamicList.value = res.data.map(item => ({
title: item.orgName ? `${item.orgName}${item.title}` : item.title,
date: item.time ? item.time.replace(/^(\d{4})-(\d{2})-(\d{2})$/, "$1年$2月$3日") : "",
type: item.sanTypeName || "",
content: item.content || item.title,
tags: item.industrylist || [],
id: item.id,
orgName: item.orgName || ""
}));
}
} catch (error) {
console.error("获取美政府部门打压遏制最新动态失败:", error);
} finally {
loadingLatestDynamic.value = false;
}
};
// 点击科技要闻-跳转详情页
const handleNewsClick = item => {
if (!item || !item.id) return;
// 打开新标签页
const { href } = router.resolve({
path: "/newsAnalysis",
query: {
newsId: item.id
}
});
window.open(href, "_blank");
};
// 点击联合制裁项-如果是政令则跳转详情
const handleJointRankItemClick = item => {
if (item.type === "政令" && item.id) {
const { href } = router.resolve({
path: "/decreeLayout/overview/introduction",
query: {
id: item.id
}
});
window.open(href, "_blank");
}
};
// 全政府-获取美对华制裁措施数量趋势
const usChinaSanctionTrend = ref([]);
const loadingTrend = ref(false);
const getUSChinaSanctionTrendData = async () => {
loadingTrend.value = true;
try {
const params = {
monthNum: monthNum.value
};
if (fieldValue.value) {
params.field = fieldValue.value;
}
if (deptValue.value) {
params.orgId = deptValue.value;
}
if (methodValue.value) {
params.sanType = methodValue.value;
}
const res = await getUSChinaSanctionTrend(params);
if (res.code === 200 && res.data) {
usChinaSanctionTrend.value = res.data;
// 更新图表
let xAxisData = [];
let seriesData = [];
if (activeDate.value === "week") {
// 近一个月:按天展示
xAxisData = res.data.map(item => {
const month = item.month.toString().padStart(2, "0");
const day = item.day.toString().padStart(2, "0");
return `${item.year}-${month}-${day}`;
});
seriesData = res.data.map(item => item.sanctionCount);
} else {
// 近三个月、半年、一年:按月展示,需要聚合天数数据
const monthlyMap = {};
res.data.forEach(item => {
const key = `${item.year}-${item.month.toString().padStart(2, "0")}`;
if (!monthlyMap[key]) {
monthlyMap[key] = 0;
}
monthlyMap[key] += item.sanctionCount;
});
// 排序键值确保时间轴顺序正确
xAxisData = Object.keys(monthlyMap).sort();
seriesData = xAxisData.map(key => monthlyMap[key]);
}
initChart(xAxisData, seriesData);
}
} catch (error) {
console.error("获取美对华制裁措施数量趋势失败:", error);
} finally {
loadingTrend.value = false;
}
};
// 全政府-获取部门数据
const departmentList = ref([]);
const loadingDepartment = ref(false);
const getDepartmentListData = async () => {
loadingDepartment.value = true;
try {
const res = await getDepartmentList();
if (res.code === 200 && res.data) {
departmentList.value = res.data;
}
} catch (error) {
console.error("获取部门数据失败:", error);
} finally {
loadingDepartment.value = false;
}
};
// 全政府-获取美国政府部门制裁数据
const governmentList = ref([]);
const loadingGovernment = ref(false);
const getGovernmentList = async () => {
loadingGovernment.value = true;
try {
const res = await getAllGovernmentList({
monthNum: monthNum.value
});
if (res.code === 200 && res.data) {
governmentList.value = res.data;
// 如果后端返回了数据,则更新 cardList
if (Array.isArray(res.data) && res.data.length > 0) {
cardList.value = res.data.map(item => {
const stats = [];
if (item.parliament) {
// 议会类型
if (item.billCount) {
stats.push({
label: "法案(提出)",
value: (item.billCount.proposedBillCount || 0) + "项"
});
stats.push({
label: "法案(通过)",
value: (item.billCount.passedBillCount || 0) + "项"
});
}
} else {
// 非议会类型
// 1. 处理制裁清单
if (item.sanctionCountList && item.sanctionCountList.length > 0) {
item.sanctionCountList.forEach(sanction => {
stats.push({
label: sanction.nameZh || sanction.name,
value: (sanction.postCount || 0) + "次"
});
});
}
// 2. 处理政令
stats.push({
label: "政令",
value: (item.administrativeOrderCount || 0) + "项"
});
}
return {
title: item.departName || "未知部门",
icon: defaultIcon, // 暂时使用默认图标
stats: stats,
departId: item.departId
};
});
startAutoPlay();
}
}
} catch (error) {
console.error("获取美国政府部门制裁数据失败:", error);
} finally {
loadingGovernment.value = false;
}
};
// 跳转到机构页面
const handleCardClick = card => {
if (!card || !card.departId) return;
// 打开新标签页
const { href } = router.resolve({
path: "/institution",
query: {
id: card.departId
}
});
window.open(href, "_blank");
};
const cardList = ref([
{
......@@ -272,53 +625,56 @@ const cardList = ref([
]);
const currentIndex = ref(0);
let autoTimer = null;
const startAutoPlay = () => {
stopAutoPlay();
if (cardList.value.length > 5) {
autoTimer = setInterval(() => {
next();
}, 3000);
}
};
const stopAutoPlay = () => {
if (autoTimer) {
clearInterval(autoTimer);
autoTimer = null;
}
};
const fieldValue = ref("");
const deptValue = ref("");
const methodValue = ref("");
const measureType = ref("history");
const selectedField = ref("all");
const fieldOptions = ref([{ label: "全部领域", value: "all" }]);
const selectedField = ref("");
const fieldOptions = ref([
{ label: "全部领域", value: "" },
{ label: "人工智能", value: "1" },
{ label: "生物科技", value: "2" },
{ label: "新一代信息技术", value: "3" },
{ label: "量子科技", value: "4" },
{ label: "新能源", value: "5" },
{ label: "集成电路", value: "6" },
{ label: "海洋", value: "7" },
{ label: "先进制造", value: "8" },
{ label: "新材料", value: "9" },
{ label: "航空航天", value: "10" },
{ label: "深海", value: "11" },
{ label: "极地", value: "12" },
{ label: "太空", value: "13" },
{ label: "核", value: "14" }
]);
const dynamicList = ref([
{
title: "联邦参议院:通过2026财年国防授权法案",
date: "2025年12月18日",
type: "法案",
content: "2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普签署成为法律,法案...",
tags: ["航空航天", "能源"]
},
{
title: "美国FDA:针对两家中国第三方检测机构的数据完整性问题采取行动",
date: "2025年12月18日",
type: "法案",
content: "FDA因发现数据伪造或无效问题,向两家中国第三方检测公司(天津中联科技检测有限公司和苏...",
tags: ["新材料", "生物科技"]
},
{
title: "美国FCC:出台电信设备市场准入认证新规",
date: "2025年12月18日",
type: "法案",
content: "将国家安全审查全面嵌入设备授权程序,对实验室资质、测试标准以及供应链设置严格限制。",
tags: ["人工智能", "集成电路"]
},
{
title: "商务部工业与安全局:对实体清单的修订",
date: "2025年12月18日",
type: "出口管制",
content: "美国商务部工业与安全局公布对中国半导体出口管制措施新规则,将140家中国半导体公司列入...",
tags: ["人工智能", "集成电路"]
},
{
title: "国际贸易委员会:外国制造的半导体器件及其下游产品和组件",
date: "2025年12月18日",
type: "市场准入限制",
content: "美国国际贸易委员会(ITC)投票决定对特定半导体器件及其下游计算产品和组件(Certain ...",
tags: ["集成电路"]
}
const methodOptions = ref([
{ label: "全部制裁手段", value: "" },
{ label: "法案", value: "-1" },
{ label: "行政令", value: "-2" }
]);
const getColorName = (tag) => {
const dynamicList = ref([]);
const getColorName = tag => {
const tagColorMap = {
航空航天: "blue",
生物科技: "blue",
......@@ -342,7 +698,7 @@ const getTagClass = tag => {
return `tag-${getColorName(tag)}`;
};
const getLineColorClass = (level) => {
const getLineColorClass = level => {
if (level) {
return `line-${level}`;
}
......@@ -363,207 +719,9 @@ const filteredTimelineList = computed(() => {
});
});
const timelineList = ref([
{
name: "商务部",
count: 77,
events: [
{
date: "2025年1月24日",
content: "商务部发布《人工智能扩散框架》",
tags: ["人工智能"],
level: "red"
},
{
date: "2025年2月10日",
content: "工业与安全局出口管制指南,限制先进AI芯片及相关技术...",
tags: ["集成电路"]
},
{
date: "2025年2月15日",
content: "商务部宣布实施美国人工智能出口项目",
tags: ["人工智能"]
},
{
date: "2025年4月10日",
content: "针对中国产海运、钢、铝、铜矿产及制品加征关税",
tags: ["新材料"],
level: "yellow"
},
{
date: "2025年4月15日",
content: "工业与安全局新增实体清单,包含45家中国科技企业",
tags: ["人工智能"],
level: "red"
}
]
},
{
name: "国务院",
count: 49,
events: [
{
date: "2025年1月26日",
content: "联合声明—战略人工智能伙伴关系",
tags: ["人工智能"],
level: "green"
},
{
date: "2025年2月1日",
content: "商务部宣布实施美国人工智能出口项目商务部宣布实施美...",
tags: ["人工智能"]
},
{
date: "2025年2月13日",
content: "国务院宣布终止5项美中文化交流计划,包括政策制定者...",
tags: ["量子科技", "航空航天"],
level: "red"
},
{
date: "2025年3月8日",
content: "国务院宣布进一步调整对中国留学生的签证政策,主要针...",
tags: ["人工智能"]
},
{
date: "2025年4月19日",
content: "联合声明—战略人工智能伙伴关系",
tags: ["人工智能"],
level: "green"
}
]
},
{
name: "财政部",
count: 49,
events: [
{
date: "2025年1月28日",
content: "财政部执行税收抵免新规与供应链审查,终止电动汽车税...",
tags: ["能源"],
level: "red"
},
{
date: "2025年2月11日",
content: '将6名中国个人和10家中国实体加入"特别指定国民清单"',
tags: ["集成电路"]
},
{
date: "2025年2月27日",
content: '向EDA巨头发出"正告函",暂停处理对华出口3nm及以下...',
tags: ["集成电路"],
level: "red"
},
{
date: "2025年4月10日",
content: "财政部准备向美国9家人工智能企业投资210亿元",
tags: ["人工智能"],
level: "green"
}
]
},
{
name: "贸易代表办公室",
count: 49,
events: [
{
date: "2025年1月25日",
content: '对“中国相关船舶”进入美国港口征收“入港费”,并分...',
tags: ["能源"],
level: "yellow"
},
{
date: "2025年2月2日",
content: "对进口自中国的半导体产品加征关税",
tags: ["集成电路"]
},
{
date: "2025年2月20日",
content: '向EDA巨头发出"正告函",暂停处理对华出口3nm及以下...',
tags: ["集成电路"]
},
{
date: "2025年3月23日",
content: "财政部准备向美国9家人工智能企业投资210亿元",
tags: ["人工智能"],
level: "green"
},
{
date: "2025年4月21日",
content: "财政部拟议出台《境外投资规程》,限制对华高科技投...",
tags: ["人工智能"],
level: "yellow"
}
]
},
{
name: "商务部",
count: 77,
events: [
{
date: "2025年1月24日",
content: "商务部发布《人工智能扩散框架》",
tags: ["人工智能"],
level: "red"
},
{
date: "2025年2月10日",
content: "工业与安全局出口管制指南,限制先进AI芯片及相关技术...",
tags: ["集成电路"]
},
{
date: "2025年2月15日",
content: "商务部宣布实施美国人工智能出口项目",
tags: ["人工智能"]
},
{
date: "2025年4月10日",
content: "针对中国产海运、钢、铝、铜矿产及制品加征关税",
tags: ["新材料"],
level: "yellow"
},
{
date: "2025年4月15日",
content: "工业与安全局新增实体清单,包含45家中国科技企业",
tags: ["人工智能"],
level: "red"
}
]
}
]);
const timelineList = ref([]);
const rankingList = ref([
{
depts: ["商务部", "财政部", "国务院"],
count: 4,
items: [
{ type: "法案", title: "大而美法案", date: "2025年12月21日" },
{ type: "政令", title: "调整互惠关税范围及建立贸易与安全协议执行程序", date: "2025年12月12日" },
{ type: "政令", title: "综合对外投资国家安全法案", date: "2025年12月7日" },
{ type: "政令", title: "推动美国人工智能技术栈的出口", date: "2025年12月4日" }
]
},
{
depts: ["国务院", "食品药品监督管理局", "商务部"],
count: 2,
items: [
{ type: "法案", title: "生物安全法案", date: "2025年12月14日" },
{ type: "政令", title: "调整关税:针对中华人民共和国合成阿片类药物供应链", date: "2025年11月24日" }
]
},
{
depts: ["商务部", "联邦通信委员会"],
count: 2,
items: [
{ type: "政令", title: "保护抖音的同时保护国家安全", date: "2025年11月9日" },
{ type: "政令", title: "赢得 6G 竞赛", date: "2025年10月27日" }
]
},
{
depts: ["贸易代表办公室", "商务部"],
count: 2,
items: [{ type: "政令", title: "第301条关于中国针对海事、物流和造船业以争取主导地位的行动", date: "2025年10月24日" }]
}
]);
const rankingList = ref([]);
const chartRef = ref(null);
let myChart = null;
......@@ -571,7 +729,7 @@ let myChart = null;
const sliderChartRef = ref(null);
let sliderChart = null;
const parseDate = (str) => {
const parseDate = str => {
const match = str.match(/(\d+)(\d+)(\d+)日/);
if (match) return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])).getTime();
return new Date().getTime();
......@@ -614,7 +772,7 @@ const initSlider = () => {
right: 20
},
xAxis: {
type: 'time',
type: "time",
min: rangeMin,
max: rangeMax,
axisLabel: { show: false },
......@@ -625,10 +783,10 @@ const initSlider = () => {
yAxis: { show: false },
dataZoom: [
{
type: 'slider',
type: "slider",
xAxisIndex: 0,
filterMode: 'weakFilter',
height: '100%',
filterMode: "weakFilter",
height: "100%",
top: 0,
bottom: 0,
left: 20,
......@@ -636,26 +794,26 @@ const initSlider = () => {
// 默认选中范围
startValue: defaultStart,
endValue: defaultEnd,
backgroundColor: 'transparent',
borderColor: 'transparent',
fillerColor: 'rgba(246, 250, 255, 1)',
backgroundColor: "transparent",
borderColor: "transparent",
fillerColor: "rgba(246, 250, 255, 1)",
dataBackground: {
lineStyle: { color: 'transparent' },
areaStyle: { color: 'transparent' }
lineStyle: { color: "transparent" },
areaStyle: { color: "transparent" }
},
selectedDataBackground: {
lineStyle: { color: 'transparent' },
areaStyle: { color: 'transparent' }
lineStyle: { color: "transparent" },
areaStyle: { color: "transparent" }
},
labelFormatter: (value) => {
labelFormatter: value => {
const date = new Date(value);
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, '0');
const d = date.getDate().toString().padStart(2, '0');
const m = (date.getMonth() + 1).toString().padStart(2, "0");
const d = date.getDate().toString().padStart(2, "0");
return `${y}-${m}-${d}`;
},
textStyle: {
color: '#5F656C',
color: "#5F656C",
fontSize: 12
},
// 解决 passive event 警告的关键配置
......@@ -664,17 +822,17 @@ const initSlider = () => {
],
series: [
{
type: 'scatter',
type: "scatter",
data: dates.map(d => [d, 0]),
symbolSize: 5,
itemStyle: { color: 'transparent' }
itemStyle: { color: "transparent" }
}
]
};
sliderChart.setOption(option);
sliderChart.on('dataZoom', () => {
sliderChart.on("dataZoom", () => {
const opt = sliderChart.getOption();
if (opt.dataZoom && opt.dataZoom[0]) {
const start = opt.dataZoom[0].startValue;
......@@ -686,7 +844,7 @@ const initSlider = () => {
});
};
const initChart = () => {
const initChart = (xAxisData = [], seriesData = []) => {
if (!chartRef.value) return;
myChart = echarts.init(chartRef.value);
const option = {
......@@ -714,20 +872,7 @@ const initChart = () => {
xAxis: {
type: "category",
boundaryGap: false,
data: [
"2024-12",
"2025-1",
"2025-2",
"2025-3",
"2025-4",
"2025-5",
"2025-6",
"2025-7",
"2025-8",
"2025-9",
"2025-10",
"2025-11"
],
data: xAxisData,
axisLine: {
lineStyle: {
color: "#f0f0f0"
......@@ -744,8 +889,7 @@ const initChart = () => {
yAxis: {
type: "value",
min: 0,
max: 100,
interval: 20,
minInterval: 1, // 保证最小间隔为1,避免小数
axisLine: {
show: false
},
......@@ -762,8 +906,9 @@ const initChart = () => {
},
series: [
{
data: [45, 52, 62, 62, 62, 65, 52, 75, 75, 75, 82, 92],
data: seriesData,
type: "line",
smooth: false,
symbol: "circle",
symbolSize: 8,
itemStyle: {
......@@ -793,24 +938,51 @@ const initChart = () => {
myChart.setOption(option);
};
// 监听时间范围变化,重新获取趋势数据
watch(
() => activeDate.value,
() => {
getUSChinaSanctionTrendData();
getGovernmentList();
}
);
onMounted(() => {
initChart();
initSlider();
getGovernmentList();
getUSChinaSanctionTrendData();
getUSGovernmentLatestDynamicData();
getUSGovernmentJointSanctionRankData();
getUSGovernmentSanctionHistoryData();
getDepartmentListData();
startAutoPlay();
window.addEventListener("resize", () => {
myChart && myChart.resize();
sliderChart && sliderChart.resize();
});
});
onUnmounted(() => {
stopAutoPlay();
});
const next = () => {
let arr = [...cardList.value];
if (currentIndex.value < cardList.value.length - 5) {
currentIndex.value++;
} else {
cardList.value = [...cardList.value, ...arr];
currentIndex.value++;
// currentIndex.value = 0;
}
};
const prev = () => {
if (currentIndex.value > 0) {
currentIndex.value--;
} else {
currentIndex.value = Math.max(0, cardList.value.length - 5);
}
};
</script>
......@@ -819,10 +991,12 @@ const prev = () => {
.content-wrapper {
width: 1666px;
height: 2132px;
.main-content {
width: 100%;
position: relative;
margin-bottom: 16px;
.cards-mask {
width: 100%;
overflow: hidden; // 仅在这里隐藏超出部分,不影响外层的按钮
......@@ -848,6 +1022,7 @@ const prev = () => {
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
cursor: pointer;
.card-bg {
position: absolute;
top: 0;
......@@ -861,6 +1036,7 @@ const prev = () => {
z-index: 0;
opacity: 0.8;
background-color: rgba(5, 33, 77, 0.7); // 调整为更明显的深蓝色
&::after {
content: "";
position: absolute;
......@@ -931,11 +1107,13 @@ const prev = () => {
cursor: pointer;
z-index: 10;
transition: opacity 0.3s;
&.disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
.right-btn {
width: 24px;
height: 48px;
......@@ -946,12 +1124,14 @@ const prev = () => {
cursor: pointer;
z-index: 10;
transition: opacity 0.3s;
&.disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
}
.main-charts {
width: 1601px;
height: 500px;
......@@ -961,6 +1141,7 @@ const prev = () => {
display: flex;
flex-direction: column;
margin-bottom: 22px;
.charts-title {
width: 100%;
height: 48px;
......@@ -971,14 +1152,17 @@ const prev = () => {
padding-right: 35px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.title-left {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
......@@ -987,29 +1171,36 @@ const prev = () => {
color: rgb(5, 95, 194);
}
}
.title-right {
display: flex;
height: 48px;
align-items: end;
gap: 12px;
padding-right: 17px;
.custom-select {
width: 160px;
:deep(.el-input) {
height: 32px;
.el-input__wrapper {
background-color: #fff;
box-shadow: 0 0 0 1px #dcdfe6 inset;
border-radius: 4px;
padding: 0 12px;
height: 32px;
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #409eff inset;
}
}
.el-input__inner {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1017,6 +1208,7 @@ const prev = () => {
color: rgb(95, 101, 108);
height: 32px;
line-height: 24px;
&::placeholder {
color: rgb(95, 101, 108);
}
......@@ -1025,29 +1217,34 @@ const prev = () => {
}
}
}
.charts-content {
flex: 1;
width: 100%;
padding: 20px 40px 20px 40px;
box-sizing: border-box;
.chart-container {
width: 100%;
height: 100%;
}
}
}
.main-text {
width: 1601px;
height: 700px;
display: flex;
justify-content: space-between;
margin-bottom: 16px;
.text-item {
width: 792px;
height: 700px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
.text-item-title {
width: 100%;
height: 48px;
......@@ -1057,11 +1254,13 @@ const prev = () => {
padding-left: 17px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
......@@ -1070,32 +1269,42 @@ const prev = () => {
color: rgb(5, 95, 194);
}
}
.text-item-content {
width: 100%;
height: 652px;
padding: 6px 27px 22px 27px;
box-sizing: border-box;
overflow-y: auto;
.dynamic-item {
width: 100%;
display: flex;
padding: 16px 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:last-child {
border-bottom: none;
}
.item-icon {
width: 24px;
height: 24px;
margin-right: 12px;
margin-top: 3px;
}
.item-right {
flex: 1;
min-width: 0;
.dynamic-item-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
.item-title {
cursor: pointer;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 700;
......@@ -1104,16 +1313,22 @@ const prev = () => {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 500px;
flex: 1;
min-width: 0;
margin-right: 12px;
}
.item-date {
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 30px;
color: rgb(95, 101, 108);
flex-shrink: 0;
white-space: nowrap;
}
}
.dynamic-item-body {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1124,13 +1339,17 @@ const prev = () => {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
cursor: pointer;
}
.dynamic-item-tags {
display: flex;
gap: 8px;
margin-top: 5px;
.tag {
padding: 2px 8px;
border-radius: 4px;
......@@ -1138,32 +1357,55 @@ const prev = () => {
font-size: 14px;
font-weight: 400;
line-height: 20px;
&.tag-blue {
color: #2f79c4;
background: rgba(47, 121, 196, 0.1);
border: 1px solid rgba(47, 121, 196, 0.3);
}
&.tag-green {
color: #52c41a;
background: rgba(82, 196, 26, 0.1);
border: 1px solid rgba(82, 196, 26, 0.3);
}
&.tag-red {
color: #fa541c;
background: rgba(250, 84, 28, 0.1);
border: 1px solid rgba(250, 84, 28, 0.3);
}
&.tag-orange {
color: #fa8c16;
background: rgba(250, 140, 22, 0.1);
border: 1px solid rgba(250, 140, 22, 0.3);
}
&.tag-purple {
color: #722ed1;
background: rgba(114, 46, 209, 0.1);
border: 1px solid rgba(114, 46, 209, 0.3);
}
&.tag-cyan {
color: #13c2c2;
background: rgba(19, 194, 194, 0.1);
border: 1px solid rgba(19, 194, 194, 0.3);
}
}
}
}
}
}
.text-item-contentOther {
width: 100%;
height: 652px;
padding: 10px 30px 22px 27px;
box-sizing: border-box;
overflow-y: auto;
.ranking-card {
width: 100%;
border-radius: 10px;
......@@ -1171,29 +1413,36 @@ const prev = () => {
padding: 12px 24px;
margin-bottom: 12px;
box-sizing: border-box;
&:last-child {
margin-bottom: 0;
}
.ranking-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.header-left {
display: flex;
align-items: center;
.dept-icons {
display: flex;
margin-right: 12px;
.dept-icon {
width: 24px;
height: 24px;
margin-right: -8px; // 图标叠加效果
&:last-child {
margin-right: 0;
}
}
}
.dept-names {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1202,6 +1451,7 @@ const prev = () => {
color: rgb(59, 65, 75);
}
}
.header-right {
.joint-count {
font-family: "Microsoft YaHei";
......@@ -1212,19 +1462,23 @@ const prev = () => {
}
}
}
.ranking-body {
display: flex;
flex-direction: column;
gap: 8px;
.ranking-item {
display: flex;
justify-content: space-between;
align-items: center;
.item-left {
display: flex;
align-items: center;
flex: 1;
overflow: hidden;
.item-type {
padding: 2px 8px;
border-radius: 4px;
......@@ -1233,17 +1487,22 @@ const prev = () => {
font-weight: 400;
line-height: 20px;
margin-right: 13px;
white-space: nowrap;
flex-shrink: 0;
&.type-bill {
color: #2f79c4;
background: rgba(47, 121, 196, 0.1);
// border: 1px solid rgba(47, 121, 196, 0.3);
}
&.type-order {
color: #2f79c4;
background: rgba(47, 121, 196, 0.1);
// border: 1px solid rgba(47, 121, 196, 0.3);
}
}
.item-title {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1254,8 +1513,16 @@ const prev = () => {
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
&.clickable {
&:hover {
color: #055fc2;
text-decoration: underline;
}
}
}
}
.item-date {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1269,13 +1536,19 @@ const prev = () => {
}
}
}
.text-item:last-child {
padding-bottom: 22px;
}
}
.main-bottom {
width: 100%;
height: 700px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
.bottom-item {
width: 100%;
height: 48px;
......@@ -1286,14 +1559,17 @@ const prev = () => {
padding-right: 35px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.bottom-item-title {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
......@@ -1302,11 +1578,13 @@ const prev = () => {
color: rgb(5, 95, 194);
}
}
.bottom-item-select {
display: flex;
height: 48px;
align-items: end;
gap: 8px;
.select-btn {
padding: 4px 16px;
border-radius: 4px;
......@@ -1318,6 +1596,7 @@ const prev = () => {
color: rgb(95, 101, 108);
cursor: pointer;
background-color: #fff;
// transition: all 0.3s;
&.active {
color: rgb(5, 95, 194);
......@@ -1325,8 +1604,10 @@ const prev = () => {
background-color: rgba(231, 243, 255, 1);
}
}
.field-select {
width: 160px;
:deep(.el-input) {
.el-input__wrapper {
height: 32px;
......@@ -1334,6 +1615,7 @@ const prev = () => {
box-sizing: border-box;
background-color: transparent;
border-radius: 4px;
.el-input__inner {
height: 32px;
line-height: 32px;
......@@ -1341,6 +1623,7 @@ const prev = () => {
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
&::placeholder {
color: rgb(95, 101, 108);
}
......@@ -1350,11 +1633,13 @@ const prev = () => {
}
}
}
.bottom-content {
width: 100%;
height: calc(100% - 48px);
padding: 14px 23px 19px 23px;
box-sizing: border-box;
.timeline-container {
width: 100%;
height: 100%;
......@@ -1367,12 +1652,14 @@ const prev = () => {
.timeline-list {
flex: 1;
overflow-y: auto;
overflow: auto;
padding-right: 8px;
padding-bottom: 8px;
.dept-row {
display: flex;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
......@@ -1403,6 +1690,7 @@ const prev = () => {
color: rgb(59, 65, 75);
line-height: 24px;
}
.dept-count {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1419,11 +1707,27 @@ const prev = () => {
align-items: center;
gap: 8px;
overflow-x: auto;
// 隐藏滚动条
// 启用滚动条
&::-webkit-scrollbar {
display: none;
height: 6px;
/* 横向滚动条高度 */
display: block;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
padding-bottom: 4px; // 给滚动条留出空间
.event-card {
min-width: 240px;
max-width: 240px;
......@@ -1444,14 +1748,38 @@ const prev = () => {
left: 0;
width: 100%;
height: 3px;
&.line-blue { background-color: #2f79c4; }
&.line-green { background-color: rgb(33, 129, 57); }
&.line-red { background-color: rgb(206, 79, 81); }
&.line-orange { background-color: #fa8c16; }
&.line-yellow { background-color: rgb(232, 189, 11); }
&.line-purple { background-color: #722ed1; }
&.line-cyan { background-color: #13c2c2; }
&.line-default { background-color: rgb(234, 236, 238); }
&.line-blue {
background-color: #2f79c4;
}
&.line-green {
background-color: rgb(33, 129, 57);
}
&.line-red {
background-color: rgb(206, 79, 81);
}
&.line-orange {
background-color: #fa8c16;
}
&.line-yellow {
background-color: rgb(232, 189, 11);
}
&.line-purple {
background-color: #722ed1;
}
&.line-cyan {
background-color: #13c2c2;
}
&.line-default {
background-color: rgb(234, 236, 238);
}
}
.event-header {
......@@ -1459,7 +1787,6 @@ const prev = () => {
justify-content: space-between;
align-items: center;
.event-date {
font-family: "Microsoft YaHei";
font-size: 16px;
......@@ -1484,9 +1811,11 @@ const prev = () => {
&.green.active {
background-color: rgb(33, 129, 57);
}
&.yellow.active {
background-color: rgb(232, 189, 11);
}
&.red.active {
background-color: rgb(206, 79, 81);
}
......@@ -1500,6 +1829,8 @@ const prev = () => {
font-weight: 400;
color: rgb(59, 65, 75);
line-height: 24px;
min-height: 48px; // 强制最小高度为两行 (24px * 2)
height: 48px; // 固定高度为两行
// margin-bottom: auto;
display: -webkit-box;
-webkit-line-clamp: 2;
......@@ -1510,43 +1841,52 @@ const prev = () => {
.event-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 4px;
white-space: nowrap;
.tag {
padding: 2px 8px;
padding: 0 8px;
height: 22px;
line-height: 20px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
border: 1px solid;
flex-shrink: 0;
display: flex;
align-items: center;
&.tag-blue {
color: rgba(9, 88, 217, 1);
background: rgba(230, 244, 255, 1);
border-color: rgba(186, 224, 255, 1);
}
&.tag-green {
color: rgba(56, 158, 13, 1);
background: rgba(246, 255, 237, 1);
border-color: rgba(217, 247, 190, 1);
}
&.tag-red {
color: rgba(245, 34, 45, 1);
background: rgba(255, 241, 240, 1);
border-color: rgba(255, 163, 158, 1);
}
&.tag-orange {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
border-color: rgba(255, 213, 145, 1);
}
&.tag-purple {
color: rgba(114, 46, 209, 1);
background: rgba(249, 240, 255, 1);
border-color: rgba(211, 173, 247, 1);
}
&.tag-cyan {
color: rgba(19, 194, 194, 1);
background: rgba(230, 255, 251, 1);
......@@ -1563,10 +1903,12 @@ const prev = () => {
width: 100%;
height: 552px;
overflow-y: auto;
&::-webkit-scrollbar {
display: none;
}
}
.timelineBar {
width: 100%;
height: 51px;
......@@ -1592,6 +1934,7 @@ const prev = () => {
line-height: 30px !important;
color: #fff !important;
border: none !important;
.el-popper__arrow::before {
background-color: rgb(59, 65, 75) !important;
border-color: rgb(59, 65, 75) !important;
......
......@@ -2,28 +2,34 @@
<template>
<div class="content-wrapper">
<!-- 轮播栏目 -->
<div class="carousel-list">
<img src="./assets/leftbtn.png" alt class="left-btn" @click="handleSwitch('left')" />
<div class="carousel-list" @mouseenter="stopAutoPlay" @mouseleave="startAutoPlay">
<img src="./assets/leftbtn.png" alt class="left-btn" @click="prev" :class="{ disabled: startIndex === 0 }" />
<div class="content">
<div class="carousel-item" v-for="item in showCarouselList">
<div class="carousel-container" :style="{ transform: `translateX(-${startIndex * (307 + 16)}px)` }">
<div class="carousel-item" v-for="item in carouselList">
<div class="item-top">
<div class="top-img">
<img :src="ele" :class="{img1: index !== 0}" alt v-for="(ele, index) in item.imageList" />
<img :src="ele" :class="{ img1: index !== 0 }" alt v-for="(ele, index) in item.imageList" />
</div>
<div class="top-num">{{ item.count }}</div>
</div>
<div class="item-title">{{ item.name }}</div>
<div class="type">
<div
class="type-item"
:class="getClassName(ele.industryName)"
v-for="ele in item.industryList"
>{{ ele.industryName }}</div>
<div class="type-item" :class="getTagClass(ele.industryName)" v-for="ele in item.industryList">
{{ ele.industryName }}
</div>
</div>
</div>
<img src="./assets/rightbtn.png" alt class="right-btn" @click="handleSwitch('right')" />
</div>
</div>
<img
src="./assets/rightbtn.png"
alt
class="right-btn"
@click="next"
:class="{ disabled: startIndex >= carouselList.length - 5 }"
/>
</div>
<!-- 排华联盟分布 -->
......@@ -34,14 +40,14 @@
<div class="left-title">排华联盟分布情况</div>
</div>
<div class="title-right">
<div class="right-num" :class="{click: currentAntiType === 'num'}">排华联盟数量</div>
<div class="right-num" :class="{click: currentAntiType === 'active'}">排华联盟动态</div>
<!-- <div class="right-num" :class="{ click: currentAntiType === 'num' }">排华联盟数量</div> -->
<!-- <div class="right-num" :class="{ click: currentAntiType === 'active' }">排华联盟动态</div> -->
</div>
</div>
<div class="anti-content">
<div class="content-left">
<div class="item" v-for="item in antiChinaNum">
<div class="item" v-for="item in countList">
<div class="item-left">
<img :src="item.image" alt />
<el-tooltip
......@@ -72,8 +78,8 @@
<div class="news-content">
<div class="item" v-for="item in newsList">
<div class="item-title">
<img :src="item.image" alt />
<span>{{ item.title }}</span>
<img :src="item.image || defaultImg" alt />
<span @click="handleClick(item)">{{ item.title }}</span>
</div>
<el-tooltip
effect="dark"
......@@ -86,11 +92,7 @@
</el-tooltip>
<div class="item-bottom">
<div class="bottom-left">
<div
class="left-item"
:class="getClassName(ele.industryName)"
v-for="ele in item.industryList"
>
<div class="left-item" :class="getTagClass(ele.industryName)" v-for="ele in item.industryList">
<span>{{ ele.industryName }}</span>
</div>
</div>
......@@ -110,8 +112,9 @@
<div class="title-left">
<div class="img-list">
<img
:src="ele.image"
:class="{img1: index !== 0}"
:src="ele.image || defaultImg"
:class="{ img1: index !== 0 }"
@error="e => e.target.src = defaultImg"
alt
v-for="(ele, index) in item.countryList"
/>
......@@ -122,7 +125,7 @@
</div>
<div class="item-content">
<div class="content-list" v-for="ele in item.statementList">
<div class="list-left" :class="getClassName(getName(ele.industryList))">
<div class="list-left" :class="getTagClass(getName(ele.industryList))">
<span>{{ getName(ele.industryList) }}</span>
</div>
<div class="list-content">{{ ele.summary }}</div>
......@@ -155,293 +158,215 @@
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import { onMounted, ref, computed, inject, watch, onUnmounted } from "vue";
import router from "@/router";
import * as echarts from "echarts";
import { getAllUnionList, getDynamic, getPrediction, getUnionCount, getIndustry, getCountryRelation } from '@/api/allUnion.js'
const carouselList = ref([
{
name: '芯片四方联盟1',
num: 21,
area: ['人工智能'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
{
name: '芯片四方联盟2',
num: 21,
area: ['人工智能'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
{
name: '芯片四方联盟3',
num: 21,
area: ['人工智能'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
{
name: '芯片四方联盟4',
num: 21,
area: ['人工智能'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
{
name: '芯片四方联盟5',
num: 21,
area: ['人工智能', '集成电路'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
{
name: '芯片四方联盟6',
num: 21,
area: ['人工智能'],
imgList: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png'
},
]
},
])
import { getAllUnionList, getDynamic, getPrediction, getUnionCount, getIndustry, getCountryRelation } from "@/api/allUnion.js";
import defaultImg from "../../../../assets/images/default-icon2.png";
const carouselList = ref([]);
const countList = ref([]);
const startIndex = ref(0);
const activeDate = inject("activeDate", ref("week"));
const handleSwitch = flag => {
if (flag == "left") {
if (startIndex.value === 0) {
startIndex.value = carouselList.value.length - 5;
} else {
startIndex.value--;
}
} else {
if (startIndex.value === carouselList.value.length - 5) {
startIndex.value = 0;
} else {
startIndex.value++;
// 自动轮播定时器
let autoTimer = null;
const startAutoPlay = () => {
stopAutoPlay();
if (carouselList.value.length > 5) {
autoTimer = setInterval(() => {
next();
}, 3000); // 每3秒切换一次
}
};
const stopAutoPlay = () => {
if (autoTimer) {
clearInterval(autoTimer);
autoTimer = null;
}
};
const getAreaList = (areaList) => {
if (areaList && areaList.length > 2) {
const array = areaList.splice(0, 2)
console.log('---areaList', array)
return array
// 国家名称映射表,用于将接口返回的全称映射为地图支持的简称
const countryNameMap = {
"Commonwealth of Australia": "Australia",
Canada: "Canada",
"New Zealand": "New Zealand",
"United Kingdom of Great Britain and Northern Ireland": "United Kingdom",
"United States of America": "United States",
"Republic of India": "India",
"Brunei Darussalam": "Brunei",
"Republic of Fiji": "Fiji",
"Republic of Korea": "Korea",
"Federation of Malaysia": "Malaysia",
"Republic of the Philippines": "Philippines",
"Republic of Singapore": "Singapore",
Japan: "Japan",
"Russian Federation": "Russia",
"People's Republic of China": "China",
"Islamic Republic of Iran": "Iran",
"Syrian Arab Republic": "Syria",
"Lao People's Democratic Republic": "Laos",
"Republic of the Union of Myanmar": "Myanmar",
"Kingdom of Thailand": "Thailand",
"Republic of Indonesia": "Indonesia",
"Socialist Republic of Vietnam": "Vietnam",
"Kingdom of Cambodia": "Cambodia",
"Islamic Republic of Pakistan": "Pakistan",
"People's Republic of Bangladesh": "Bangladesh",
"Democratic Socialist Republic of Sri Lanka": "Sri Lanka",
"Federal Democratic Republic of Nepal": "Nepal",
"Kingdom of Bhutan": "Bhutan",
"French Republic": "France",
"Federal Republic of Germany": "Germany",
"Italian Republic": "Italy",
"Kingdom of Spain": "Spain",
"Kingdom of the Netherlands": "Netherlands",
"Kingdom of Belgium": "Belgium",
"Swiss Confederation": "Switzerland",
"Republic of Austria": "Austria",
"Kingdom of Sweden": "Sweden",
"Kingdom of Norway": "Norway",
"Kingdom of Denmark": "Denmark",
"Republic of Finland": "Finland",
"Hellenic Republic": "Greece",
"Republic of Turkey": "Turkey",
"State of Israel": "Israel",
"Kingdom of Saudi Arabia": "Saudi Arabia",
"Republic of South Africa": "South Africa",
"Federative Republic of Brazil": "Brazil",
"Republic of Argentina": "Argentina",
"Republic of Chile": "Chile",
"Republic of Colombia": "Colombia",
"Republic of Peru": "Peru",
"Bolivarian Republic of Venezuela": "Venezuela",
"United Mexican States": "Mexico",
"Republic of Cuba": "Cuba",
Ukraine: "Ukraine",
"Republic of Belarus": "Belarus",
"Republic of Poland": "Poland",
"Czech Republic": "Czech Republic",
Hungary: "Hungary",
Romania: "Romania",
"Republic of Bulgaria": "Bulgaria",
"Republic of Serbia": "Republic of Serbia",
"Republic of Croatia": "Croatia",
"Republic of Slovenia": "Slovenia",
"Slovak Republic": "Slovakia",
"Republic of Kazakhstan": "Kazakhstan",
"Republic of Uzbekistan": "Uzbekistan",
Turkmenistan: "Turkmenistan",
"Kyrgyz Republic": "Kyrgyzstan",
"Republic of Tajikistan": "Tajikistan",
"Republic of Azerbaijan": "Azerbaijan",
"Republic of Armenia": "Armenia",
Georgia: "Georgia",
Mongolia: "Mongolia",
"Democratic People's Republic of Korea": "North Korea",
"Republic of Iraq": "Iraq",
"State of Kuwait": "Kuwait",
"State of Qatar": "Qatar",
"Sultanate of Oman": "Oman",
"Republic of Yemen": "Yemen",
"Hashemite Kingdom of Jordan": "Jordan",
"Lebanese Republic": "Lebanon",
"Arab Republic of Egypt": "Egypt",
"State of Libya": "Libya",
"Republic of Tunisia": "Tunisia",
"People's Democratic Republic of Algeria": "Algeria",
"Kingdom of Morocco": "Morocco",
"Federal Republic of Nigeria": "Nigeria",
"Republic of Kenya": "Kenya",
"Federal Democratic Republic of Ethiopia": "Ethiopia",
"United Republic of Tanzania": "United Republic of Tanzania",
"Republic of Ghana": "Ghana",
"Republic of Côte d'Ivoire": "Ivory Coast",
"Democratic Republic of the Congo": "Democratic Republic of the Congo",
"Republic of the Congo": "Republic of the Congo",
"Republic of Zimbabwe": "Zimbabwe",
"Republic of Zambia": "Zambia",
"Republic of Angola": "Angola",
"Republic of Mozambique": "Mozambique",
"Republic of Botswana": "Botswana",
"Republic of Namibia": "Namibia",
"Republic of Ecuador": "Ecuador",
"Republic of Paraguay": "Paraguay",
"Oriental Republic of Uruguay": "Uruguay",
"Republic of Bolivia": "Bolivia",
"Republic of Panama": "Panama",
"Republic of Costa Rica": "Costa Rica",
"Republic of Nicaragua": "Nicaragua",
"Republic of Honduras": "Honduras",
"Republic of El Salvador": "El Salvador",
"Republic of Guatemala": "Guatemala",
"Dominican Republic": "Dominican Republic",
"Republic of Haiti": "Haiti",
Jamaica: "Jamaica"
};
const next = () => {
if (startIndex.value < carouselList.value.length - 5) {
startIndex.value++;
} else {
return areaList
startIndex.value = 0; // 循环播放
}
}
};
const getImageList = (imageList) => {
if (imageList && imageList.length > 5) {
const array = imageList.splice(0, 5)
return array
} else {
return imageList
const prev = () => {
if (startIndex.value > 0) {
startIndex.value--;
}
}
};
// const getAreaList = areaList => {
// if (areaList && areaList.length > 2) {
// const array = areaList.splice(0, 2);
// console.log("---areaList", array);
// return array;
// } else {
// return areaList;
// }
// };
// const getImageList = imageList => {
// if (imageList && imageList.length > 5) {
// const array = imageList.splice(0, 5);
// return array;
// } else {
// return imageList;
// }
// };
//打压遏制手段分布
const showCarouselList = computed(() => {
return carouselList.value.slice(startIndex.value, startIndex.value + 5);
});
const getClassName = (type) => {
let className = ''
const getColorName = (tag) => {
const tagColorMap = {
航空航天: "blue",
生物科技: "blue",
集成电路: "blue",
能源: "green",
新材料: "green",
人工智能: "red"
};
switch (type) {
case '人工智能':
className = 'ai'
break
case '新材料':
className = 'material'
break
case '量子科技':
className = 'technology'
break
case '生物科技':
className = 'organism'
break
case '航空航天':
className = 'aerospace'
break
case '能源':
className = 'energy'
break
case '集成电路':
className = 'integrated'
break
case '':
className = 'none-class'
break
default:
className = 'other'
}
if (tagColorMap[tag]) return tagColorMap[tag];
return className
}
const colors = ["blue", "green", "red", "orange", "purple", "cyan"];
let hash = 0;
if (tag) {
for (let i = 0; i < tag.length; i++) {
hash = tag.charCodeAt(i) + ((hash << 5) - hash);
}
}
return colors[Math.abs(hash) % colors.length];
};
const currentAntiType = ref('num')
const getTagClass = tag => {
if (!tag) return "";
return `tag-${getColorName(tag)}`;
};
const antiChinaNum = ref([
{
name: '美国1111111111111111',
value: 24,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '欧盟',
value: 18,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: 'Japan',
value: 13,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '韩国',
value: 13,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '英国',
value: 9,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '加拿大',
value: 6,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '澳大利亚',
value: 5,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '菲律宾',
value: 3,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '新西兰',
value: 3,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
},
{
name: '印度',
value: 2,
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/us.png'
}
])
const currentAntiType = ref("num");
// echarts地图的颜色区间
const startColor = [130, 175, 224]; // rgb(130, 175, 224)
......@@ -449,7 +374,7 @@ const endColor = [5, 95, 194]; // rgb(5, 95, 194)
// 计算颜色插值函数
const getColorByValue = (value, maxValue) => {
if (maxValue === 0) return `rgb(${startColor.join(',')})`;
if (maxValue === 0) return `rgb(${startColor.join(",")})`;
const ratio = value / maxValue;
const r = Math.round(startColor[0] + (endColor[0] - startColor[0]) * ratio);
......@@ -457,21 +382,22 @@ const getColorByValue = (value, maxValue) => {
const b = Math.round(startColor[2] + (endColor[2] - startColor[2]) * ratio);
return `rgb(${r}, ${g}, ${b})`;
}
};
const chartDom = ref()
const myChart = ref()
const chartDom = ref();
const myChart = ref();
function createChart() {
if (!countList.value) return;
// 找到最大值用于颜色计算
const maxValue = Math.max(...antiChinaNum.value.map(item => item.value));
const maxValue = Math.max(...countList.value.map(item => item.value));
// 为每个数据项计算颜色
const processedData = antiChinaNum.value.map(item => ({
const processedData = countList.value.map(item => ({
...item,
itemStyle: {
color: getColorByValue(item.value, maxValue)
}
}))
}));
const option = {
// geo: {
......@@ -487,18 +413,17 @@ function createChart() {
// }
// },
tooltip: {
trigger: 'item',
trigger: "item",
formatter: function (params) {
if (params.data) {
// 从数据中查找对应的中文名称
const item = antiChinaNum.value.find((item) => item.name === params.name)
console.log(item)
const item = countList.value.find(item => item.name === params.name);
console.log(item);
if (item) {
return `${item.zhName}: ${params.data.value || 0}`;
} else {
return `${params.name}: ${params.data.value || 0}`;
}
}
return params.name;
}
......@@ -506,41 +431,38 @@ function createChart() {
visualMap: {
min: 0,
max: maxValue,
left: 'left',
top: 'bottom',
text: ['高', '低'],
left: "left",
top: "bottom",
text: ["高", "低"],
calculable: true,
inRange: {
color: [
`rgb(${startColor.join(',')})`,
`rgb(${endColor.join(',')})`
]
color: [`rgb(${startColor.join(",")})`, `rgb(${endColor.join(",")})`]
},
textStyle: {
color: '#fff'
color: "#fff"
}
},
series: [
{
name: '数据值',
type: 'map',
name: "数据值",
type: "map",
roam: true, // 允许缩放和平移
zoom: 1.2, // 初始缩放级别
map: 'world',
map: "world",
emphasis: {
label: {
show: true,
color: '#fff'
color: "#fff"
},
itemStyle: {
areaColor: null,
borderWidth: 1,
borderColor: '#fff'
borderColor: "#fff"
}
},
itemStyle: {
areaColor: 'rgba(231, 243, 255, 1)', // 所有区域的默认底色
borderColor: 'rgb(130, 175, 224)' // 边界线颜色
areaColor: "rgba(231, 243, 255, 1)", // 所有区域的默认底色
borderColor: "rgb(130, 175, 224)" // 边界线颜色
},
data: processedData
}
......@@ -549,152 +471,42 @@ function createChart() {
myChart.value.setOption(option);
}
function fetchGeoJSON() {
chartDom.value = document.getElementById('echartsMap')
chartDom.value = document.getElementById("echartsMap");
if (!chartDom.value) return;
if (myChart.value) myChart.value.dispose();
myChart.value = echarts.init(chartDom.value)
myChart.value = echarts.init(chartDom.value);
myChart.value.showLoading();
fetch('/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/world.json').then((res) => res.json()).then((result) => {
echarts.registerMap('world', result);
fetch("/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/world.json")
.then(res => res.json())
.then(result => {
echarts.registerMap("world", result);
createChart();
myChart.value.hideLoading();
})
});
}
const newsList = ref([]);
const newsList = ref([
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
title: '芯片四方联盟: 发布新版管制清单',
content: '2026财年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普...',
area: ['新材料', '集成电路'],
time: '2025年12月18日'
}
])
const warningList = ref([
{
countries: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
}
],
area: [
{
type: '人工智能',
content: '推出“数字基础设施增长计划框架”(DiGi Framework)',
time: '2024年11月21日'
},
{
type: '人工智能',
content: '推出“数字基础设施增长计划框架”(DiGi Framework)',
time: '2024年11月21日'
},
{
type: '人工智能',
content: '推出“数字基础设施增长计划框架”(DiGi Framework)',
time: '2024年11月21日'
}
],
num: 3
},
{
countries: [
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
},
{
img: '/src/views/ZMOverView/components/fourSuppress/components/allUnion/assets/usa.png',
name: '美国'
}
],
area: [
{
type: '人工智能',
content: '推出“数字基础设施增长计划框架”(DiGi Framework)',
time: '2024年11月21日'
}
],
num: 1
}
])
const warningList = ref([]);
const getContent = (countries) => {
const getContent = countries => {
if (countries) {
let result = '' // 美国 / 日本 / 韩国 / 印度
let result = "";
if (countries.length > 1) {
for (let i = 0; i < countries.length; i++) {
result += countries[i].name
result += countries[i].name;
if (i !== countries.length - 1) {
result += ' / '
result += " / ";
}
}
} else if (countries.length === 1) {
result += countries[0].name
result += countries[0].name;
}
return result
return result;
}
}
};
const leftChartRef = ref(null);
let leftChart;
......@@ -706,43 +518,33 @@ const initLeftDonut = async () => {
if (!leftChartRef.value) return;
if (leftChart) leftChart.dispose();
leftChart = echarts.init(leftChartRef.value);
let data = []
let data = [];
try {
const res = await getIndustry();
if (res && res.code === 200) {
for (let i = 0; i < res.data.length; i++) {
let item = {
name: '',
name: "",
value: 0
}
};
item.name = res.data[i].industry
item.value = res.data[i].amount
item.name = res.data[i].industry;
item.value = res.data[i].amount;
item.itemStyle = {
color: getColor(colors, item.name)
}
data.push(item)
};
data.push(item);
}
}
} catch (error) {
console.error("获取限制领域分布情况接口失败:", error);
}
// console.log('----data', data)
// const data = [
// { name: "集成电路", value: 50 },
// { name: "人工智能", value: 46 },
// { name: "通信网络", value: 40 },
// { name: "能源", value: 32 },
// { name: "先进制造", value: 31 },
// { name: "生物科技", value: 31 },
// { name: "航空航天", value: 30 },
// { name: "新材料", value: 24 }
// ];
const option = {
tooltip: { trigger: "item", formatter: ({ name, value, percent }) => `${name}<br/>${value}${percent}%` },
series: [{
series: [
{
type: "pie",
radius: [96, 120],
center: ["50%", "50%"],
......@@ -751,15 +553,33 @@ const initLeftDonut = async () => {
label: {
show: true,
position: "outside",
alignTo: 'edge',
alignTo: "edge",
formatter: ({ name, value, percent }) => `{name|${name}} {value|${value}项} {percent|${percent}%}`,
minMargin: 5,
edgeDistance: 10,
lineHeight: 15,
rich: {
name: { fontSize: 16, fontFamily: "Microsoft YaHei", fontWeight: 700, lineHeight: 24, color: "rgb(59, 65, 75)" },
value: { fontSize: 14, fontFamily: "Microsoft YaHei", fontWeight: 400, lineHeight: 22, color: "rgb(95, 101, 108)" },
percent: { fontSize: 14, fontFamily: "Microsoft YaHei", fontWeight: 400, lineHeight: 22, color: "rgb(95, 101, 108)" }
name: {
fontSize: 16,
fontFamily: "Microsoft YaHei",
fontWeight: 700,
lineHeight: 24,
color: "rgb(59, 65, 75)"
},
value: {
fontSize: 14,
fontFamily: "Microsoft YaHei",
fontWeight: 400,
lineHeight: 22,
color: "rgb(95, 101, 108)"
},
percent: {
fontSize: 14,
fontFamily: "Microsoft YaHei",
fontWeight: 400,
lineHeight: 22,
color: "rgb(95, 101, 108)"
}
}
},
labelLine: {
......@@ -784,7 +604,8 @@ const initLeftDonut = async () => {
...d,
labelLine: { lineStyle: { color: getColor(colors, d.name), width: 1 } }
}))
}]
}
]
};
leftChart.setOption(option);
};
......@@ -793,8 +614,8 @@ const initRightDonut = async () => {
if (!rightChartRef.value) return;
if (rightChart) rightChart.dispose();
rightChart = echarts.init(rightChartRef.value);
let data = []
let links = []
let data = [];
let links = [];
try {
const res = await getCountryRelation();
......@@ -804,23 +625,23 @@ const initRightDonut = async () => {
name: res.data[i].country,
x: Math.random() * 10,
y: Math.random() * 10
}
};
let item1 = {
name: res.data[i].relationCountry,
x: Math.random() * 10,
y: Math.random() * 10
}
};
// 先判断data中是否包含上述数据
let index = data.findIndex((ele) => ele.name === item.name)
let index1 = data.findIndex((ele) => ele.name === item1.name)
let index = data.findIndex(ele => ele.name === item.name);
let index1 = data.findIndex(ele => ele.name === item1.name);
// 不存在则向data中添加
if (index === -1) {
data.push(item)
data.push(item);
}
if (index1 === -1) {
data.push(item1)
data.push(item1);
}
let link = {
......@@ -834,77 +655,14 @@ const initRightDonut = async () => {
width: res.data[i].amount,
curveness: 0.2
}
}
};
links.push(link)
links.push(link);
}
}
} catch (error) {
console.error("获取限制领域分布情况接口失败:", error);
}
// console.log('----data', data)
// const data = [
// {
// name: 'Node 1',
// x: 300,
// y: 300
// },
// {
// name: 'Node 2',
// x: 800,
// y: 300
// },
// {
// name: 'Node 3',
// x: 550,
// y: 100
// },
// {
// name: 'Node 4',
// x: 550,
// y: 500
// }
// ]
// const links = [
// {
// source: 0,
// target: 1,
// symbolSize: [5, 20],
// label: {
// show: false
// },
// lineStyle: {
// width: 5,
// curveness: 0.2
// }
// },
// {
// source: 'Node 2',
// target: 'Node 1',
// label: {
// show: false
// },
// lineStyle: {
// curveness: 0.2
// }
// },
// {
// source: 'Node 1',
// target: 'Node 3'
// },
// {
// source: 'Node 2',
// target: 'Node 3'
// },
// {
// source: 'Node 2',
// target: 'Node 4'
// },
// {
// source: 'Node 1',
// target: 'Node 4'
// }
// ]
let option = {
title: {
......@@ -912,24 +670,24 @@ const initRightDonut = async () => {
},
tooltip: {},
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
animationEasingUpdate: "quinticInOut",
dataZoom: [
{
type: 'slider',
type: "slider",
start: 10,
end: 10
}
],
series: [
{
type: 'graph',
layout: 'none',
type: "graph",
layout: "none",
symbolSize: 50,
roam: true,
label: {
show: true
},
edgeSymbol: ['circle', ''],
edgeSymbol: ["circle", ""],
edgeSymbolSize: [4, 10],
edgeLabel: {
fontSize: 20
......@@ -949,188 +707,240 @@ const initRightDonut = async () => {
};
const getColor = (colors, name) => {
const index = colors.findIndex((i) => i.name === name)
const index = colors.findIndex(i => i.name === name);
if (index !== -1) {
return colors[index].value
return colors[index].value;
} else {
return "rgba(105, 177, 255, 1)"
return "rgba(105, 177, 255, 1)";
}
}
};
const colors = [
{
name: '集成电路',
value: "rgba(105, 177, 255, 1)",
name: "集成电路",
value: "rgba(105, 177, 255, 1)"
},
{
name: '新一代通信网络',
value: "rgba(135, 232, 222, 1)",
name: "新一代通信网络",
value: "rgba(135, 232, 222, 1)"
},
{
name: '人工智能',
value: "rgba(255, 192, 105, 1)",
name: "人工智能",
value: "rgba(255, 192, 105, 1)"
},
{
name: '能源',
value: "rgba(89, 126, 247, 1)",
name: "能源",
value: "rgba(89, 126, 247, 1)"
},
{
name: '先进制造',
value: "rgba(214, 228, 255, 1)",
name: "先进制造",
value: "rgba(214, 228, 255, 1)"
},
{
name: '生物科技',
value: "rgba(255, 120, 117, 1)",
name: "生物科技",
value: "rgba(255, 120, 117, 1)"
},
{
name: '航空航天',
value: "rgba(179, 127, 235, 1)",
name: "航空航天",
value: "rgba(179, 127, 235, 1)"
},
{
name: '新材料',
value: "rgba(255, 163, 158, 1)",
name: "新材料",
value: "rgba(255, 163, 158, 1)"
}
];
// 获取计算后的日期
const getCalculatedDate = type => {
const now = new Date();
switch (type) {
case "week": // 近一个月
now.setMonth(now.getMonth() - 1);
break;
case "three_month": // 近三个月
now.setMonth(now.getMonth() - 3);
break;
case "six_month": // 近半年
now.setMonth(now.getMonth() - 6);
break;
case "year": // 近一年
now.setFullYear(now.getFullYear() - 1);
break;
default:
now.setMonth(now.getMonth() - 1); // 默认近一个月
}
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
// 获取联盟列表
const getUnionList = async () => {
try {
const res = await getAllUnionList();
const dateParam = getCalculatedDate(activeDate.value);
const res = await getAllUnionList({ date: dateParam });
if (res && res.code === 200) {
console.log('----getUnionList', res.data)
carouselList.value = res.data
console.log("----getUnionList", res.data);
carouselList.value = res.data;
}
} catch (error) {
console.error("获取联盟列表接口失败:", error);
}
}
};
// 获取联盟动态
const getDynamicList = async () => {
try {
const res = await getDynamic();
if (res && res.code === 200) {
console.log('----getDynamicList', res.data)
newsList.value = res.data
console.log("----getDynamicList", res.data);
newsList.value = res.data;
}
} catch (error) {
console.error("获取联盟动态接口失败:", error);
}
}
};
const handleClick = item => {
const { href } = router.resolve({
path: "/newsAnalysis",
query: {
newsId: item.id
}
});
window.open(href, "_blank");
};
// 获取联盟预警
const getPredictionList = async () => {
try {
const res = await getPrediction();
if (res && res.code === 200) {
console.log('----getPredictionList', res.data)
warningList.value = res.data
console.log("----getPredictionList", res.data);
warningList.value = res.data;
}
} catch (error) {
console.error("获取联盟动态接口失败:", error);
}
}
};
// 获取排华联盟数量
const getUnionCountList = async () => {
try {
const res = await getUnionCount();
if (res && res.code === 200) {
console.log('----getUnionCountList', res.data)
console.log("----getUnionCountList", res.data);
// 处理一下数据
antiChinaNum.value = res.data.map(item => {
item.value = item.count
item.zhName = item.name
item.name = item.ename
return item
})
countList.value = res.data
.sort((a, b) => b.count - a.count)
.map(item => {
item.value = item.count;
item.zhName = item.name;
// 1. 尝试直接从映射表获取
let mappedName = countryNameMap[item.ename];
// 2. 如果映射表没有,尝试简单的模糊匹配
if (!mappedName && item.ename) {
let tempName = item.ename
.replace(/Republic of /i, "")
.replace(/Kingdom of /i, "")
.replace(/Federal /i, "")
.replace(/Islamic /i, "")
.replace(/State of /i, "")
.replace(/Commonwealth of /i, "")
.trim();
mappedName = tempName;
}
item.name = mappedName || item.ename;
return item;
});
}
} catch (error) {
console.error("获取联盟动态接口失败:", error);
}
}
};
const getTime = (time) => {
const getTime = time => {
if (time) {
const array = time.split('-')
return `${array[0]}${array[1]}${array[2]}日`
const array = time.split("-");
return `${array[0]}${array[1]}${array[2]}日`;
}
}
};
const getName = (item) => {
if (item) {
return item[0].industryName
const getName = item => {
if (item && item.length > 0) {
return item[0].industryName;
} else {
return ''
return "";
}
}
};
onMounted(async () => {
await getUnionList()
await getDynamicList()
await getPredictionList()
await getUnionCountList()
await getUnionList();
await getDynamicList();
await getPredictionList();
await getUnionCountList();
fetchGeoJSON();
initLeftDonut()
initRightDonut()
})
initLeftDonut();
initRightDonut();
startAutoPlay();
});
onUnmounted(() => {
stopAutoPlay();
});
watch(activeDate, async () => {
await getUnionList();
// await getDynamicList();
// await getPredictionList();
// await getUnionCountList();
// fetchGeoJSON();
// initLeftDonut();
// initRightDonut();
});
</script>
<style lang="scss" scoped>
.content-wrapper {
width: 1666px;
height: 2132px;
user-select: none;
// 领域样式
// 人工智能
.ai {
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
// 新材料
.material {
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
// 量子科技
.technology {
border: 1px solid rgba(211, 173, 247, 1);
background: rgba(249, 240, 255, 1);
color: rgba(114, 46, 209, 1);
}
// 生物科技
.organism {
border: 1px solid rgba(145, 202, 255, 1);
.tag-blue {
color: rgba(9, 88, 217, 1);
background: rgba(230, 244, 255, 1);
color: rgba(22, 119, 255, 1);
}
// 航空航天
.aerospace {
border: 1px solid rgba(173, 198, 255, 1);
background: rgba(240, 245, 255, 1);
color: rgba(22, 119, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
}
// 能源
.energy {
border: 1px solid rgba(217, 247, 190, 1);
background: rgba(246, 255, 237, 1);
.tag-green {
color: rgba(56, 158, 13, 1);
background: rgba(246, 255, 237, 1);
border: 1px solid rgba(217, 247, 190, 1);
}
// 集成电路
.integrated {
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
color: rgba(9, 88, 217, 1);
.tag-red {
color: rgba(245, 34, 45, 1);
background: rgba(255, 241, 240, 1);
border: 1px solid rgba(255, 163, 158, 1);
}
// 其他
.other {
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
color: rgba(9, 88, 217, 1);
.tag-orange {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
border: 1px solid rgba(255, 213, 145, 1);
}
.tag-purple {
color: rgba(114, 46, 209, 1);
background: rgba(249, 240, 255, 1);
border: 1px solid rgba(211, 173, 247, 1);
}
.tag-cyan {
color: rgba(19, 194, 194, 1);
background: rgba(230, 255, 251, 1);
border: 1px solid rgba(135, 232, 222, 1);
}
// 无类型
......@@ -1140,16 +950,22 @@ onMounted(async () => {
}
.carousel-list {
width: 1666px;
width: 1601px;
height: 178px;
margin-top: 16px;
display: flex;
align-items: center;
position: relative;
.content {
width: calc(100% - 48px - 16px);
width: 1601px;
overflow: hidden;
.carousel-container {
display: flex;
align-items: center;
justify-content: space-around;
gap: 16px;
transition: transform 0.5s ease;
width: max-content;
}
.carousel-item {
width: 307px;
......@@ -1215,8 +1031,7 @@ onMounted(async () => {
overflow-x: auto;
overflow-y: hidden;
width: calc(100% - 22px);
height: 24px;
height: 25px;
.type-item {
display: inline-block;
border-radius: 4px;
......@@ -1226,6 +1041,7 @@ onMounted(async () => {
font-family: "Microsoft YaHei";
margin-left: 8px;
margin-bottom: 8px;
box-sizing: border-box;
}
}
}
......@@ -1233,22 +1049,38 @@ onMounted(async () => {
.left-btn {
width: 24px;
height: 48px;
margin-right: 8px;
position: absolute;
left: -33px;
z-index: 10;
cursor: pointer;
&.disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
.right-btn {
width: 24px;
height: 48px;
margin-left: 8px;
position: absolute;
right: -33px;
z-index: 10;
cursor: pointer;
&.disabled {
opacity: 0.3;
cursor: not-allowed;
}
}
}
.anti-china {
margin-top: 16px;
margin-left: 33px;
width: 1601px;
height: 700px;
background: rgba(255, 255, 255, 1);
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
.anti-title {
width: 100%;
height: 48px;
......@@ -1257,7 +1089,8 @@ onMounted(async () => {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.title-left {
display: flex;
align-items: center;
......@@ -1345,8 +1178,8 @@ onMounted(async () => {
img {
width: 16px;
height: 16px;
margin-right: 8px;
height: 12px;
margin-right: 12px;
}
span {
display: inline-block;
......@@ -1392,19 +1225,15 @@ onMounted(async () => {
.anti-news {
margin-top: 16px;
margin-left: 33px;
width: 1601px;
height: 700px;
background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
justify-content: space-between;
.news {
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.65);
width: 793px;
......@@ -1415,7 +1244,8 @@ onMounted(async () => {
height: 48px;
display: flex;
align-items: center;
// justify-content: center;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
img {
width: 22px;
......@@ -1439,7 +1269,7 @@ onMounted(async () => {
.news-content {
width: 100%;
height: calc(100% - 48px);
padding: 4px 28px 25px 27px;
padding: 4px 28px 10px 27px;
overflow-y: auto;
.item {
......@@ -1459,7 +1289,7 @@ onMounted(async () => {
height: 30px;
display: flex;
align-items: center;
cursor: pointer;
img {
width: 20px;
height: 20px;
......@@ -1516,7 +1346,7 @@ onMounted(async () => {
flex-direction: row;
justify-content: center;
align-items: center;
padding: 1px 8px 1px 8px;
padding: 2px 8px;
box-sizing: border-box;
border-radius: 4px;
margin-right: 8px;
......@@ -1550,9 +1380,7 @@ onMounted(async () => {
.warning {
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.65);
width: 793px;
......@@ -1563,7 +1391,8 @@ onMounted(async () => {
height: 48px;
display: flex;
align-items: center;
// justify-content: center;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
img {
width: 22px;
......@@ -1669,7 +1498,7 @@ onMounted(async () => {
flex-direction: row;
justify-content: center;
align-items: center;
padding: 1px 8px 1px 8px;
padding: 2px 8px;
box-sizing: border-box;
border-radius: 4px;
......@@ -1720,19 +1549,15 @@ onMounted(async () => {
.anti-area {
margin-top: 16px;
margin-left: 33px;
width: 1601px;
height: 500px;
// background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
justify-content: space-between;
.area-left {
width: 792px;
height: 500px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: var(---10, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
......@@ -1743,7 +1568,8 @@ onMounted(async () => {
height: 48px;
display: flex;
align-items: center;
// justify-content: center;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
img {
width: 22px;
......@@ -1775,7 +1601,6 @@ onMounted(async () => {
width: 792px;
height: 500px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: var(---10, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
......@@ -1786,7 +1611,8 @@ onMounted(async () => {
height: 48px;
display: flex;
align-items: center;
// justify-content: center;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
img {
width: 17px;
......
......@@ -5,7 +5,7 @@
<div class="timeline-box">
<div class="line" style="width:100vw ;" />
<!-- 一次性渲染全部节点 -->
<div v-for="(item, i) in data" :key="item[idKey]" class="node" :style="leftStyle(i)">
<div v-for="(item, i) in showList" :key="item[idKey]" class="node" :style="leftStyle(i)">
<!-- 圆环 -->
<div class="dot" :class="linePos(item)" :style="{ '--i': item.unit === '中国' ? ' #E29697' : '#69A0DA' }" />
......@@ -22,20 +22,21 @@
<!-- 卡片 -->
<div class="card" :class="[cardPos(item), 'right-side']" @click="$emit('click-card', item)">
<div style="justify-content: space-between;display: flex;width: 300px;">
<div class="tag">{{ item.tag }}</div>
<img :src="`/icon/${item.unit}.png`" class="icon"></img>
<div class="tag">{{ item.eventStrategy }}</div>
<img :src="item.eventCountryImg" class="icon"
style="border-radius: 14px;height: 28px;width: 28px;;"></img>
</div>
<div class="title" :style="{
}">{{ item.title }}</div>
<div class="time">{{ item.time }}</div>
}">{{ item.eventName }}</div>
<div class="time">{{ item.eventDate }}</div>
<!-- <div class="title" :style="{
color: item.unit === '中国' ? ' #CF4F51' : ''
}">{{ item.title }}</div>
<div class="time">{{ item.time }}</div> -->
<div class="content">{{ item.content }}</div>
<div class="content">{{ item.eventDesc }}</div>
</div>
</div>
</div>
......@@ -85,7 +86,7 @@ export default {
},
/* 卡片上下位置:unit=0 -> 下侧,其余 -> 上侧 */
cardPos(item) {
return item.unit === '中国' ? 'down' : 'up';
return item.eventType === '反制措施' ? 'down' : 'up';
},
/* 延伸线方向 = 卡片方向 */
linePos(item) {
......@@ -101,8 +102,8 @@ export default {
display: flex;
align-items: center;
width: 100%;
padding: 0 40px;
width: 1500px;
/* padding: 0 40px; */
width: 1600px;
overflow: auto;
}
......@@ -155,7 +156,7 @@ export default {
.line {
position: absolute;
left: 0;
left: 10px;
right: 0;
top: 50%;
height: 6px;
......@@ -278,7 +279,7 @@ export default {
width: 120px;
height: 28px;
/* 自动布局 */
display: flex;
/* display: flex; */
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
......@@ -303,7 +304,7 @@ export default {
width: 120px;
height: 28px;
/* 自动布局 */
display: flex;
/* display: flex; */
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
......@@ -346,5 +347,23 @@ export default {
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
width: 300px;
/* 设置一个固定的宽度 */
height: 100px;
/* 设置一个固定的高度,确保只显示 5 行 */
font-size: 16px;
/* 设置字体大小 */
line-height: 20px;
/* 设置行高 */
overflow: hidden;
/* 隐藏超出部分 */
display: -webkit-box;
/* 使用 Webkit 的 box 模型 */
-webkit-line-clamp: 3;
/* 限制为 5 行 */
-webkit-box-orient: vertical;
/* 垂直排列内容 */
text-overflow: ellipsis;
/* 超出部分显示省略号 */
}
</style>
\ No newline at end of file
......@@ -8,11 +8,18 @@
</div>
<div style="display: flex; height: 650px; width: 100%">
<div style="width: 50%">
<div style="display: flex; justify-content: space-between; margin-right: 50px; line-height: 32px;align-items: center;" >
<div style="
display: flex;
justify-content: space-between;
margin-right: 50px;
line-height: 32px;
align-items: center;
">
<div style="display: flex; margin-left: 50px">
数据来源:
<el-select class="select-item" size="default" style="margin-left: 15px; width: 240px; height: 32px">
<el-option label="国家创新指数报告" value="last_year" />
<el-select class="select-item" size="default" style="margin-left: 15px; width: 240px; height: 32px"
v-model="origin" @change="handleGetCompare()">
<el-option :value="value.id" :label="value.name" v-for="value in originList" />
</el-select>
</div>
<div style="display: flex">
......@@ -25,23 +32,40 @@
<div style="width: 672px; height: 486px; padding-top: 50px" id="char"></div>
</div>
<div style="width: 50%">
<el-table :data="tableData" style="width: 100%; margin-bottom: 20px" row-key="id" border default-expand-all>
<el-table-column prop="name" label="指标名称" sortable width="350" />
<el-table-column prop="percent" label="中国">
<div style="width: 778px; height: 620px;overflow: auto;">
<el-table :data="tableData" style="width: 100%; margin-bottom: 20px" row-key="targetCode" border
:tree-props="{ children: 'children' }" default-expand-all :header-cell-style="{ 'border-right': 'none' }"
:cell-style="{ 'border-right': 'none' }">
<!-- 自定义展开图标 -->
<el-table-column prop="targetName" label="指标名称" width="350" />
<el-table-column prop="usScore" label="美国">
<template #default="scope">
<div style="display: flex;">
<div class="progress-text">
{{ 'NO.' + scope.row.usRank + ' ' + scope.row.usScore }}
</div>
<div class="progress-wrapper left" :style="{ '--i': '40px', '--j': '-20px', marginLeft: '20px' }">
<el-progress :percentage="scope.row.percent[0]" :stroke-width="20" class="left-progress"
<el-progress :percentage="scope.row.chinaScore" :stroke-width="20" class="left-progress"
:show-text="false" />
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="percent" label="美国">
<el-table-column prop="chinaScore" label="中国">
<template #default="scope">
<div style="display: flex;">
<div class="progress-wrapper right" :style="{ '--i': '40px', marginRight: '20px' }">
<el-progress :percentage="scope.row.percent[0]" :stroke-width="20" class="right-progress"
<el-progress :percentage="scope.row.usScore" :stroke-width="20" class="right-progress"
:show-text="false" />
</div>
<div class="progress-text" style="color:rgba(206, 79, 81, 1);">
{{ 'NO.' + scope.row.chinaRank + ' ' + scope.row.chinaScore }}
</div>
</div>
</template>
</el-table-column>
</el-table>
......@@ -58,12 +82,17 @@
</div>
</div>
<div class="btn-box">
<div v-for="(value, index) in btnList" class="btn-item" :style="{
<div v-for="(value, index) in btnList" :key="index" class="btn-item disabled" :style="{
background: value.background
}">
<img :src="`/public/icon/ZM/btn-icon-${index}.png`" style="width: 22px; height: 19px; margin: 0 22px" />
<img :src="value.img" style="width: 22px; height: 19px; margin: 0 22px" />
{{ value.text }}
<img :src="`/public/icon/ZM/btn-icon-arrow.png`" style="margin-left: auto; margin-right: 22px; width: 13px; height: 12px" />
<img :src="`./icon/ZM/btn-icon-arrow.png`"
style="margin-left: auto; margin-right: 22px; width: 13px; height: 12px" />
<!-- <img src="./assets/images/btn-icon-arrow.png"
style="margin-left: auto; margin-right: 22px; width: 13px; height: 12px" /> -->
</div>
</div>
</div>
......@@ -72,9 +101,29 @@
<script setup>
import { onMounted, ref, computed } from "vue";
import * as echarts from "echarts";
import { ArrowRight, CaretBottom } from '@element-plus/icons-vue'
import Timeline from "./Timeline.vue";
import tableShow from "./tableShow.vue";
import radarChart from "./radarChart3.js";
import { getCompare, getChartDict, getTechnologyGameAnalysis } from '@/api/zmOverview/risk/index.js'
import icon1 from './icon/btn-icon-0.png'
import icon2 from './icon/btn-icon-1.png'
import icon3 from './icon/btn-icon-2.png'
import icon4 from './icon/btn-icon-3.png'
import icon5 from './icon/btn-icon-4.png'
import icon6 from './icon/btn-icon-5.png'
import icon7 from './icon/btn-icon-6.png'
import icon from './icon/btn-icon-7.png'
import Img1 from "./assets/images/btn-icon-0.png";
import Img2 from "./assets/images/btn-icon-1.png";
import Img3 from "./assets/images/btn-icon-2.png";
import Img4 from "./assets/images/btn-icon-3.png";
import Img5 from "./assets/images/btn-icon-4.png";
import Img6 from "./assets/images/btn-icon-5.png";
import Img7 from "./assets/images/btn-icon-6.png";
import Img8 from "./assets/images/btn-icon-7.png";
const course = ref([
{
time: "2025-01-15",
......@@ -82,28 +131,32 @@ const course = ref([
content:
"计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA的量子研究纳入,旨在加速技术应用与人才发展。",
unit: "美国",
tag: "科技发展战略"
tag: "科技发展战略",
icon: icon1,
},
{
time: "2025-01-21",
title: "将美国实体列入'不可靠实体清单'并采取商品进口限制",
content: "根据《反外国制裁法》等,将17家美国实体列入清单,禁止其从事与中国有关的进出口与新增投资。",
unit: "中国",
tag: "反制措施"
tag: "反制措施",
icon: icon2,
},
{
time: "2025-01-21",
title: "《国家创新驱动发展战略纲要》发布",
content: "纲领性文件,提出了建设世界科技强国的'三步走'战略目标,并系统部署了构建现代产业技术体系等八大战略任务。",
unit: "中国",
tag: "科技发展战略"
tag: "科技发展战略",
icon: icon3
},
{
time: "2025-01-21",
title: "中国网信办约谈英伟达",
content: "中国网信办约谈英伟达,要求就H20算力芯片漏洞后门安全风险问题进行说明。",
unit: "中国",
tag: "反制措施"
tag: "反制措施",
icon: icon4,
},
{
time: "2025-05-09",
......@@ -111,40 +164,100 @@ const course = ref([
content:
"年度国防预算法案,其中系统性地嵌入了大量对华竞争条款,首次将对外投资审查法律化,并在供应链、实体清单等方面加强限制。",
unit: "美国",
tag: "对华打压遏制"
tag: "对华打压遏制",
icon: icon5,
},
{
time: "2025-07-24",
title: "白宫'人工智能(AI)行动计划'",
content: "提出放宽监管、简化环境许可可以加快AI基础设施建设,并涉及促进AI出口、要求联邦采购的AI模型具备中立性等内容。",
unit: "美国",
tag: "科技发展战略"
tag: "科技发展战略",
icon: icon6,
},
{
time: "2025-07-24",
title: "2026财年《商务、司法、科学及相关支出法案》",
content: "法案为商务部工业与安全局(BIS)大幅增加预算,以加强出口管制执法,特别强调审查对华为、中芯国际的出口许可证。",
unit: "美国",
tag: "对华打压遏制"
tag: "对华打压遏制",
icon: icon7,
}
]);
onMounted(() => {
const originList = ref([]);
const origin = ref("");
//数据来源
const handleGetChartDict = async () => {
try {
const res = await getChartDict();
console.log("数据来源", res);
if (res.code === 200 && res.data) {
originList.value = res.data;
origin.value = res.data[0].id;
}
} catch (error) {
console.error("获取数据来源error", error);
}
};
//中美科技实力对比
const handleGetCompare = async () => {
try {
const res = await getCompare(origin.value);
console.log("中美科技实力对比", res);
if (res.code === 200 && res.data) {
tableData.value = res.data[0].children;
// tableData.value = [res.data];
const secondLevelData = res.data[0].children;
const indicatorNames = secondLevelData.map(item => item.targetName);
const chinaScores = secondLevelData.map(item => item.chinaScore);
const usScores = secondLevelData.map(item => item.usScore);
const dom = document.getElementById("char");
const myChart = echarts.init(dom);
const radarOption = {
indicatorNames: indicatorNames,
data: [chinaScores, usScores]
};
const option = radarChart(radarOption.indicatorNames, radarOption.data);
myChart.setOption(option);
console.log(radarOption);
}
} catch (error) {
console.error("获取中美科技实力对比error", error);
}
};
// 2. 传入容器,生成配置
const option = radarChart(
["创新资源", "知识创造", "企业创新", "创新绩效", "创新环境"],
[
[4200, 3000, 20000, 35000, 50000, 18000],
[1000, 42000, 32000, 35000, 50000, 18000]
]
);
//中美博弈概览V2:领域打压遏制时间线
const handlegetTechnologyGameAnalysis = async () => {
try {
const res = await getTechnologyGameAnalysis();
console.log("中美科技博弈分析", res);
if (res.code === 200 && res.data) {
course.value = res.data;
}
} catch (error) {
console.error("获取中美科技博弈分析error", error);
}
};
onMounted(async () => {
await handleGetChartDict();
await handleGetCompare();
await handlegetTechnologyGameAnalysis();
// const dom = document.getElementById("char");
// const myChart = echarts.init(dom);
myChart.setOption(option);
// // 2. 传入容器,生成配置
// const option = radarChart(
// ["创新资源", "知识创造", "企业创新", "创新绩效", "创新环境"],
// [
// [4200, 3000, 20000, 35000, 50000, 18000],
// [1000, 42000, 32000, 35000, 50000, 18000]
// ]
// );
// setChart(option, "char7");
// myChart.setOption(option);
// // setChart(option, "char7");
});
const tableData = ref([
......@@ -228,35 +341,43 @@ const tableData = ref([
const btnList = ref([
{
text: "制裁征候挖掘",
background: "linear-gradient(180.00deg, rgba(9, 88, 217, 1),rgba(9, 88, 217, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(9, 88, 217, 1),rgba(9, 88, 217, 0.5) 100%)",
img: Img1
},
{
text: "制裁内容快速摘要",
background: "linear-gradient(180.00deg, rgba(255, 97, 97, 1),rgba(255, 97, 97, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(255, 97, 97, 1),rgba(255, 97, 97, 0.5) 100%)",
img: Img2
},
{
text: "重点领域博弈历程分析",
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)",
img: Img3
},
{
text: "制裁手段关联分析",
background: "linear-gradient(180.00deg, rgba(48, 193, 195, 1),rgba(48, 193, 195, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(48, 193, 195, 1),rgba(48, 193, 195, 0.5) 100%)",
img: Img4
},
{
text: "政要人物画像",
background: "linear-gradient(180.00deg, rgba(123, 52, 184, 1),rgba(123, 52, 184, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(123, 52, 184, 1),rgba(123, 52, 184, 0.5) 100%)",
img: Img5
},
{
text: "人物关系挖掘",
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)",
img: Img6
},
{
text: "人物立场分析",
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)",
img: Img7
},
{
text: "制裁内容比对分析",
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)"
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)",
img: Img8
}
]);
</script>
......@@ -266,6 +387,7 @@ const btnList = ref([
width: 1600px;
height: 1600px;
margin: 0 auto;
.card-box {
width: 1600px;
height: 700px;
......@@ -298,6 +420,21 @@ const btnList = ref([
}
}
.progress-text {
/* No.1 92.5 */
width: 80px;
height: 22px;
color: rgba(5, 95, 194, 1);
font-family: Source Han Sans CN;
font-style: Bold;
font-size: 14px;
font-weight: 700;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
/* ========== 公共外壳 ========== */
.progress-wrapper {
position: relative;
......@@ -336,8 +473,9 @@ const btnList = ref([
background: rgba(231, 243, 255, 1);
overflow: hidden;
/* 读行内传进来的变量 */
height: var(--i) !important;
height: 10px !important;
margin-left: var(--j) !important;
margin-top: 6px;
}
.left-progress :deep(.el-progress-bar__inner) {
......@@ -347,7 +485,7 @@ const btnList = ref([
/* 设置为矩形,inset() 函数的四个参数都是0表示不裁剪 */
background: #055fc2;
/* 读行内传进来的变量 */
height: var(--i) !important;
height: 10px !important;
margin-left: var(--j) !important;
}
......@@ -357,14 +495,15 @@ const btnList = ref([
clip-path: inset(0 0 0 0);
background: #ffccc7;
overflow: hidden;
height: var(--i) !important;
height: 10px !important;
margin-top: 6px;
}
.right-progress :deep(.el-progress-bar__inner) {
border-radius: 0;
clip-path: inset(0 0 0 0);
background: #ce4f51;
height: var(--i) !important;
height: 10px !important;
}
:deep(.el-table .cell) {
......@@ -450,4 +589,69 @@ const btnList = ref([
letter-spacing: 0px;
text-align: center;
}
/* 隐藏原生箭头 */
// :deep(.el-table__expand-icon>.el-icon) {
// display: none;
// }
// /* 或者用背景图/伪元素替换 */
// :deep(.el-table__expand-icon>.el-icon::before) {
// content: "▶";
// color: #409eff;
// }
// :deep(.el-table .el-table__expand-icon--expanded::before) {
// content: "▼";
// }
/* 隐藏原生图标 */
:deep(.el-table__expand-icon > .el-icon) {
display: none;
}
/* 自定义未展开图标 */
:deep(.el-table__expand-icon) {
background-image: url('./icon/arrowDown.png');
background-repeat: no-repeat;
background-size: contain;
width: 24px;
height: 24px;
cursor: pointer;
transition: transform 0.3s;
}
/* 自定义展开图标 */
:deep(.el-table__expand-icon--expanded) {
background-image: url('./icon/arrowDown.png');
transform: rotate(270deg) !important;
}
/* 自定义表头背景颜色 */
:deep(.el-table th) {
background-color: #f0f9eb;
}
/* 自定义表头文字颜色 */
:deep(.el-table th .cell) {
color: rgba(59, 65, 75, 1);
font-family: Source Han Sans CN;
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
text-align: center;
}
/* 自定义表头边框 */
:deep(.el-table th) {
border-bottom: 2px solid #ebeef5;
}
/* 自定义表头高度 */
:deep(.el-table th) {
padding: 12px 0;
}
</style>
......@@ -34,7 +34,7 @@ let instance = null
/* 配置项 */
const makeOption = () => {
const p = Math.min(100, Math.max(0, props.percent)) / 100
console.log(props.color, 'colorcolorcolor')
// console.log(props, 'colorcolorcolor')
return {
series: [{
type: 'liquidFill',
......@@ -73,8 +73,8 @@ const makeOption = () => {
formatter: function () {
const change = props.data.change;
const unit = props.data.unit;
if (change === '无新增') {
return `{noChange|${change}}\n{b|共${props.data.count}${unit}}`
if (change === 0) {
return `{noChange|${'无新增'}}\n{b|共${props.data.count}${unit}}`
}
return `{num|${change}}{unit| ${unit}}\n{b|共${props.data.count}${unit}}`
},
......@@ -86,7 +86,7 @@ const makeOption = () => {
padding: [-4, 0, 8, 0]
},
noChange: {
fontSize: 24,
fontSize: 20,
fontWeight: 'bold',
color: `${props.color[5]}`,
padding: [0, 0, 8, 0]
......
......@@ -7,9 +7,9 @@
width: sections[index].waveBall.length === 2 ? '350px' : '503px',
background: section.background
}">
<img class="section-title" :src="`/public/icon/riskToday/title-` + (index + 1) + `.png`" />
<img class="section-title" :src="section.title" />
<div class="stats">
<div v-for="value in sections[index].waveBall">
<div v-for="value in sections[index].waveBall" @click="highLight(value.type)">
<WaveBall :percent="value.percent" :data="value" :color="section.waterColor" :size="128" />
<div class="waveBall-text">
{{ value.title }}
......@@ -23,7 +23,7 @@
{{ sections[index].title }}
</div>
<div style="width: 50px; color: #ffffff">
{{ sections[index].date }}
{{ sections[index].date.slice(-5) }}
</div>
</div>
</div>
......@@ -33,15 +33,17 @@
<img class="section-title" src="./icon/title-icon-2.png" />
<div>风险信号</div>
<div class="num">12</div>
<div class="num">{{ riskTotal }}</div>
<div class="manage-btn" @click="handleToRiskManage">
风险信号管理 >
</div>
</div>
<div style="display: flex">
<div class="risk-signals">
<!-- 风险信号列表 -->
<div class="risk-signals" ref="riskSignalsRef">
<div class="risk-signals-item" v-for="(item, index) in warningList" :key="index"
@click="handleClickToDetailO(item)">
@click="handleClickToDetailO(item)" @mouseenter="onMouseEnter(item.signalId)" @mouseleave="onMouseLeave"
:class="['risk-signals-item', { 'risk-signals-item-hightLight': item.eventType === highlightedEventType }]">
<div class="item-left" :class="{
itemLeftStatus1: item.signalLevel === '特别重大',
itemLeftStatus2: item.signalLevel === '重大风险'
......@@ -69,27 +71,27 @@
</div>
<el-carousel ref="carouselRef" style="height: 443px; width: 664px" :autoplay="true" :interval="30000"
arrow="never" indicator-position="none" @change="handleCarouselChange">
<el-carousel-item v-for="(News, NewsIndex) in hotNewsList" :key="NewsIndex">
<div class="carousel-item">
<el-carousel-item v-for="(News, NewsIndex) in filteredHotNewsList" :key="NewsIndex">
<div class="carousel-item" @click="toDetaile(News.hotspotID, News.hotspotType)">
<div class="carousel-title">
<div>
<div class="title-text">
{{ News.title }}
{{ News.hotspotTitle }}
</div>
<div class="title-tag">
{{ News.category }}
{{ News.hotspotTag }}
</div>
</div>
<img src="./icon/矩形 295.png" style="width: 96px; height: 96px" />
<img :src="News.hotspotPicture" style="width: 96px; height: 96px" />
</div>
<div style="/* 矩形 351 */ width: 664px; height: 1px; background: rgba(234, 236, 238, 1)"></div>
<div class="news-carousel-content">{{ News.content }}</div>
<div class="news-carousel-content">{{ News.hotspotDesc }}</div>
<div class="carousel-bottom">
<div class="left">{{ News.date + News.source }}</div>
<div class="left">{{ News.hotspotDate + News.hotspotOrgName }}</div>
<div class="right">
<div v-for="tag in News.tags" class="tag">
{{ tag }}
<div v-for="tag in News.domainList" class="tag">
{{ tag.name }}
</div>
</div>
</div>
......@@ -105,16 +107,21 @@
<script setup>
import { color } from "echarts";
import { onMounted, ref, computed } from "vue";
import { onMounted, ref, onUnmounted, computed } from "vue";
import WaveBall from "./WaveBall.vue";
import { getBillRiskSignal } from "@/api/bill/billHome";
import { getLatestRiskUpdates, getLatestRisks } from '@/api/zmOverview/risk/index.js'
import router from "@/router/index";
import icon1 from './icon/title-1.png'
import icon2 from './icon/title-2.png'
import icon3 from './icon/title-3.png'
import icon4 from './icon/title-4.png'
const sectionTab = [
{
textColor: "rgba(9, 88, 217, 1)",
background: "linear-gradient(180.00deg, rgba(9, 88, 217, 1),rgba(9, 88, 217, 0.5) 100%)",
borderColor: "rgba(186, 224, 255, 1)",
waterColor: ["#4E92F4", "#5993EE", "#8EB3ED", "#77A2EB", "#ffffff", "#ffffff"],
title: "政策法规"
title: icon1
},
{
textColor: "rgba(9, 88, 217, 1)",
......@@ -122,7 +129,7 @@ const sectionTab = [
borderColor: "rgba(186, 224, 255, 1)",
waterColor: ["#4AB9BA", "#5DBFC1", "#86CCCF", "#8DD0D2", "#ffffff", "#ffffff"],
title: "出口管制"
title: icon2
},
{
textColor: "rgba(9, 88, 217, 1)",
......@@ -130,7 +137,7 @@ const sectionTab = [
borderColor: "rgba(186, 224, 255, 1)",
waterColor: ["#EF797B", "#ED8C8E", "#EAB0B2", "#E7ABAC", "#ffffff", "#ffffff"],
title: "投融资限制"
title: icon3
},
{
textColor: "rgba(9, 88, 217, 1)",
......@@ -138,7 +145,7 @@ const sectionTab = [
borderColor: "rgba(186, 224, 255, 1)",
waterColor: ["#EFA04F", "#EDA969", "#EABA8C", "#E7AF7A", "#ffffff", "#ffffff"],
title: "市场准入"
title: icon4
}
];
// 模拟从后端获取的数据
......@@ -148,18 +155,20 @@ const sections = ref([
date: "12-18",
waveBall: [
{
percent: 30, // 估算的百分比
percent: 0, // 估算的百分比
count: 1626,
change: "+3",
unit: "项",
title: "法案(提出)"
title: "法案(提出)",
type: '法案'
},
{
percent: 20, // 估算的百分比
percent: 0, // 估算的百分比
count: 69,
change: "+2",
unit: "个",
title: "政令"
title: "政令",
type: '行政令'
}
]
},
......@@ -168,18 +177,20 @@ const sections = ref([
date: "12-19",
waveBall: [
{
percent: 10, // 估算的百分比
percent: 0, // 估算的百分比
count: 128,
change: "+1",
unit: "次",
title: "实体清单"
title: "实体清单",
type: '实体清单'
},
{
percent: 20, // 估算的百分比
percent: 0, // 估算的百分比
count: 69,
change: "+1",
unit: "次",
title: "CCL"
title: "CCL",
type: 'CCL'
}
]
},
......@@ -188,18 +199,20 @@ const sections = ref([
date: "12-15",
waveBall: [
{
percent: 15, // 估算的百分比
count: 35,
percent: 0, // 估算的百分比
count: 0,
change: "+1",
unit: "次",
title: "SDN"
title: "SDN",
type: 'SDN'
},
{
percent: 5, // 估算的百分比
count: 28,
percent: 0, // 估算的百分比
count: 0,
change: "+1",
unit: "家",
title: "涉军企业"
title: "涉军企业",
type: '涉军企业'
}
]
},
......@@ -208,44 +221,152 @@ const sections = ref([
date: "12-15",
waveBall: [
{
percent: 3, // 估算的百分比
count: 215,
percent: 0, // 估算的百分比
count: 0,
change: "+1",
unit: "次",
title: "337调查"
title: "337调查",
type: '337调查'
},
{
percent: 3, // 估算的百分比
count: 14,
percent: 0, // 估算的百分比
count: 0,
change: "无新增",
unit: "次",
title: "230调查"
title: "230调查",
type: '230调查'
},
{
percent: 3, // 估算的百分比
count: 9,
percent: 0, // 估算的百分比
count: 0,
change: "无新增",
unit: "次",
title: "301调查"
title: "301调查",
type: '301调查'
}
]
}
]);
// 风险信号
const warningList = ref([]);
// 获取法案风险信号
const handlegetBillRiskSignal = async () => {
const params = {
moduleId: "0100"
};
// 最新风险动态统计
const handleGetLatestRiskUpdates = async () => {
try {
const res = await getBillRiskSignal(params);
console.log("法案风险信号", res);
if (res.code === 200) {
warningList.value = res.data;
const params = {
currentDate: '本周'
}
const res = await getLatestRiskUpdates(params);
console.log("最新风险动态统计", res);
if (res.code === 200 && res.data) {
sections.value = [
{
title: res.data.policiesRegulations.hotspotTitle,
date: res.data.policiesRegulations.hotspotDate,
waveBall: [
{
percent: 30, // 估算的百分比
count: res.data.bill.total,
change: res.data.bill.dailyIncrement,
unit: "项",
title: "法案(提出)",
type: '法案'
},
{
percent: 20, // 估算的百分比
count: res.data.administrativeOrder.total,
change: res.data.administrativeOrder.dailyIncrement,
unit: "个",
title: "政令",
type: '行政令'
}
]
},
{
title: res.data.exportControl.hotspotTitle,
date: res.data.exportControl.hotspotDate,
waveBall: [
{
percent: 10, // 估算的百分比
count: res.data.Entities.total,
change: res.data.Entities.dailyIncrement,
unit: "次",
title: "实体清单",
type: '实体清单'
},
{
percent: 20, // 估算的百分比
count: res.data.CCL.total,
change: res.data.CCL.dailyIncrement,
unit: "次",
title: "CCL",
type: 'CCL'
}
]
},
{
title: res.data.investmentFinancingRestrictions.hotspotTitle,
date: res.data.investmentFinancingRestrictions.hotspotDate,
waveBall: [
{
percent: 15, // 估算的百分比
count: res.data.SDN.total,
change: res.data.SDN.dailyIncrement,
unit: "次",
title: "SDN",
type: 'SDN'
},
{
percent: 5, // 估算的百分比
count: res.data.militaryInvolvement.total,
change: res.data.militaryInvolvement.dailyIncrement,
unit: "家",
title: "涉军企业",
type: '涉军企业'
}
]
},
{
title: res.data.marketAccess.hotspotTitle,
date: res.data.marketAccess.hotspotDate,
waveBall: [
{
percent: 3, // 估算的百分比
count: res.data['337Survey'].total,
change: res.data['337Survey'].dailyIncrement,
unit: "次",
title: "337调查",
type: '337调查'
},
{
percent: 3, // 估算的百分比
count: res.data['232Survey'].total,
change: res.data['232Survey'].dailyIncrement,
unit: "次",
title: "230调查",
type: '230调查'
},
{
percent: 3, // 估算的百分比
count: res.data['301Survey'].total,
change: res.data['301Survey'].dailyIncrement,
unit: "次",
title: "301调查",
type: '301调查'
}
]
}
]
console.log(sections.value, 'sections.value')
}
} catch (error) {
console.error("获取最新风险动态统计error", error);
}
} catch (error) { }
};
// 风险信号
const warningList = ref([]);
const riskTotal = ref(0)
const hotNewsList = ref([
{
title: "美国白宫发布关于进一步延长TikTok执法宽限期的行政令",
......@@ -266,6 +387,23 @@ const hotNewsList = ref([
tags: ['人工智能', '通信网络']
}
]);
//最新风险信号
const handleGetLatestRisks = async () => {
try {
const res = await getLatestRisks();
console.log("最新风险信号", res);
if (res.code === 200 && res.data) {
warningList.value = res.data.riskVOS
hotNewsList.value = res.data.hotspotVOS
riskTotal.value = res.data.riskCount
console.log(hotNewsList.value, 'hotNewsList.value')
}
} catch (error) {
console.error("获取最新风险信号error", error);
}
};
const curNews = ref({});
const carouselRef = ref(null);
const curHotNewsListIndex = ref(0);
......@@ -276,12 +414,115 @@ const handleCarouselChange = index => {
}
};
// 查看详情
const handleClickToDetail = () => {
window.sessionStorage.setItem("billId", curBill.value.billId);
window.sessionStorage.setItem("curTabName", curBill.value.billName);
const route = router.resolve({
path: "/billLayout",
query: {
billId: curBill.value.billId
}
});
console.log(route);
window.open(route.href, "_blank");
};
// 查看详情 传递参数
const handleClickToDetailO = item => {
window.sessionStorage.setItem("billId", item.billId);
window.sessionStorage.setItem("curTabName", item.name || item.signalTitle);
const route = router.resolve("/billLayout?billId=" + item.billId);
window.open(route.href, "_blank");
};
const currentHoveredSignalId = ref(null);
const onMouseEnter = (signalId) => {
currentHoveredSignalId.value = signalId;
};
const onMouseLeave = () => {
currentHoveredSignalId.value = null;
};
// 计算属性或使用 watchEffect 来动态计算 filteredHotNewsList
const filteredHotNewsList = computed(() => {
if (!currentHoveredSignalId.value) return hotNewsList.value; // 如果没有悬停,则返回全部数据
return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value);
});
const handleToRiskManage = () => {
// 这里的路由路径请根据实际情况修改
// router.push('/riskSignalManage');
const route = router.resolve("/riskSignal");
window.open(route.href, "_blank");
console.log('跳转到风险信号管理');
};
const highlightedEventType = ref('')
const highLight = (title) => {
console.log(title)
// 如果再次点击同一个,取消高亮
if (highlightedEventType.value === title) {
highlightedEventType.value = null;
} else {
highlightedEventType.value = title;
}
};
const toDetaile = (id, type) => {
console.log(id, type)
if (type === '法案') {
let path = "/billLayout/bill/introduction";
let query = { billId: id };
const { href } = router.resolve({ path, query });
window.open(href, "_blank");
} else if (type === '政令') {
const route = router.resolve({
path: "/decreeLayout/overview/introduction",
query: {
id: id
}
});
window.open(route.href, "_blank");
} else if (type === '实体清单' || type === 'CCL') {
const route = router.resolve({
path: "/exportControl/singleSanction",
query: {
id: id
}
});
window.open(route.href, "_blank");
} else if (type === '337') {
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: "337",
searchId: id
}
});
window.open(curRoute.href, "_blank");
} else if (type === '203') {
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: "203",
searchId: id
}
});
window.open(curRoute.href, "_blank");
} else if (type === '301') {
const curRoute = router.resolve({
path: "/marketSingleCaseLayout/overview",
query: {
id: "301",
searchId: id
}
});
window.open(curRoute.href, "_blank");
}
//type=涉军企业和SDN未对接
}
const handleSwithCurNews = name => {
if (name === "left") {
carouselRef.value.prev();
......@@ -289,11 +530,41 @@ const handleSwithCurNews = name => {
carouselRef.value.next();
}
};
onMounted(() => {
// 这里可以添加从后端获取数据的代码
handlegetBillRiskSignal();
console.log("页面加载完成,可以获取数据了");
const riskSignalsRef = ref(null); // 引用risk-signals容器
let intervalId;
onMounted(async () => {
await handleGetLatestRiskUpdates()
await handleGetLatestRisks()
const scrollInterval = 2000; // 每隔2秒滚动一次
let currentScroll = 0;
intervalId = setInterval(() => {
if (riskSignalsRef.value) {
const scrollHeight = riskSignalsRef.value.scrollHeight; // 内容总高度
const clientHeight = riskSignalsRef.value.clientHeight; // 可视区域高度
console.log(scrollHeight, clientHeight)
// 如果滚动到底,则回到顶部
if (currentScroll + clientHeight >= scrollHeight) {
currentScroll = 0;
riskSignalsRef.value.scrollTop = currentScroll;
} else {
currentScroll += 10; // 调整这个值可以改变滚动速度
riskSignalsRef.value.scrollTop = currentScroll;
}
}
}, scrollInterval);
});
onUnmounted(() => {
clearInterval(intervalId); // 清除定时器
});
// onBeforeUnmount(() => {
// andleGetLatestRiskUpdates()
// });
</script>
<style lang="scss" scoped>
......@@ -344,6 +615,7 @@ onMounted(() => {
text-align: left;
display: flex;
align-items: center;
img {
/* 矢量 347 */
width: 22px;
......@@ -375,8 +647,10 @@ onMounted(() => {
}
.manage-btn {
margin-left: auto; /* 推到最右侧 */
margin-right: 16px; /* 适当的右边距 */
margin-left: auto;
/* 推到最右侧 */
margin-right: 16px;
/* 适当的右边距 */
padding: 4px 12px;
border-radius: 20px;
background-color: rgba(206, 79, 81, 0.1);
......@@ -430,7 +704,7 @@ onMounted(() => {
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 26px;
// line-height: 26px;
letter-spacing: 0px;
text-align: center;
display: flex;
......@@ -571,6 +845,80 @@ onMounted(() => {
}
}
}
.risk-signals-item-hightLight {
margin-left: 23px;
height: 47px;
width: 780px;
display: flex;
cursor: pointer;
background: var(--color-bg-hover);
&:hover {
background: var(--color-bg-hover);
}
.itemLeftStatus1 {
color: rgba(245, 34, 45, 1) !important;
background: rgba(255, 241, 240) !important;
}
.itemLeftStatus2 {
color: rgba(250, 140, 22, 1) !important;
background: rgba(255, 247, 230, 1) !important;
}
.item-left {
margin-top: 4px;
margin-left: 2px;
width: 40px;
height: 40px;
border-radius: 20px;
color: rgba(82, 196, 26, 1);
background: rgba(246, 255, 237, 1);
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: 400;
line-height: 14px;
box-sizing: border-box;
padding: 6px 4px;
text-align: center;
}
.item-right {
margin-left: 13px;
width: 722px;
height: 47px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
.text {
width: 598px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 47px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time {
width: 88px;
margin-left: 5px;
line-height: 47px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.news {
......@@ -662,32 +1010,29 @@ onMounted(() => {
.title-text {
/* 美国白宫发布关于进一步延长TikTok执法宽限期的行政令 */
width: 547px;
height: 26px;
// height: 26px;
}
.title-tag {
/* 容器 1563 */
width: 190px;
/* 移除固定 width */
height: 30px;
margin-top: 12px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
gap: 10;
padding: 0px 12px 0px 12px;
padding: 0 12px;
/* 左右内边距控制最小宽度 */
display: inline-flex;
/* ✅ 关键:inline + flex */
justify-content: center;
/* 水平居中 */
align-items: center;
/* 垂直居中 */
border-radius: 20px;
background: rgba(206, 79, 81, 0.1);
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
white-space: nowrap;
/* 防止文字换行 */
box-sizing: border-box;
}
}
......
......@@ -63,14 +63,16 @@
<div class="info-item-left">
<img :src="logo1" alt="" />
<div class="info-item-left-content">
<p>地址:北京市海淀区复兴路15号 邮编:100038</p>
<p>办公电话:010-58882033 办公传真:010-58882590</p>
<p>中国科学技术信息研究所 版权所有 京ICP备10027328</p>
<p>地址:北京市海淀区复兴路某某某某某某 邮编:100000</p>
<p>办公电话:000-88888888 办公传真:000-88888888</p>
<p>中国中国中国中国中国北京 版权所有 京ICP备00000000</p>
</div>
</div>
<div class="info-item-right">
<img :src="logo2" alt="" />
<img :src="logo3" alt="" />
<img class="img1" :src="logo2" alt="" />
<img class="img2" :src="logo3" alt="" />
<div class="text2">{{ "中国某某某某某某某某部" }}</div>
<div class="text3">{{ "中国某某某某某某某某所" }}</div>
</div>
</div>
</div>
......@@ -79,7 +81,7 @@
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import { onMounted, ref, computed, provide } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import background from "./assets/images/backgroundBT.png";
......@@ -110,6 +112,11 @@ const navList = ref([
]);
const handleScrollTo = id => {
if (id === "tech-news") {
const route = router.resolve("/newsBrief");
window.open(route.href, "_blank");
return;
}
const element = document.getElementById(id);
const container = document.querySelector(".content-box");
if (element && container) {
......@@ -142,11 +149,13 @@ const handleToSearch = () => {
};
const dateList = ref([
{ name: "本周", type: "week", icon: week, activeIcon: weekActive },
{ name: "本月", type: "month", icon: month, activeIcon: monthActive },
{ name: "今年", type: "year", icon: year, activeIcon: yearActive }
{ name: "近一个月", type: "week", icon: week, activeIcon: weekActive },
{ name: "近三个月", type: "three_month", icon: month, activeIcon: monthActive },
{ name: "近半年", type: "six_month", icon: month, activeIcon: monthActive },
{ name: "近一年", type: "year", icon: year, activeIcon: yearActive }
]);
const activeDate = ref("week");
provide("activeDate", activeDate);
const handleDateClick = type => {
activeDate.value = type;
};
......@@ -263,7 +272,7 @@ const handleDateClick = type => {
position: relative;
margin-top: 64px;
.data-select {
width: 120px;
width: 140px;
height: 144px;
position: absolute;
top: 80px;
......@@ -391,12 +400,45 @@ const handleDateClick = type => {
.info-item-right {
display: flex;
align-items: center;
img:first-child {
position: relative;
height: 60px;
.text2 {
left: 60px;
position: absolute;
width: 237px;
height: 37px;
background: #fff;
color: rgba(34, 41, 52, 1);
font-family: Source Han Sans CN;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 37px;
letter-spacing: 0px;
text-align: center;
}
.text3 {
left: 385px;
top: 12px;
position: absolute;
width: 237px;
height: 37px;
background: #fff;
color: rgba(34, 41, 52, 1);
font-family: Source Han Sans CN;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 37px;
letter-spacing: 0px;
text-align: center;
}
.img1 {
width: 300px;
height: 48.8px;
margin-right: 24px;
}
img:last-child {
.img2 {
width: 300px;
height: 43.5px;
}
......
......@@ -2071,13 +2071,13 @@ onUnmounted(() => {
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding: 0 40px 0 30px;
padding: 0 40px 0 21px;
.box1-header-left {
display: flex;
.icon {
width: 18px;
height: 18px;
margin-top: 19px;
margin-top: 15px;
img {
width: 100%;
height: 100%;
......@@ -2087,7 +2087,7 @@ onUnmounted(() => {
width: 112px;
height: 48px;
background: var(--color-main-active);
margin-left: 18px;
margin-left: 21px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 20px;
......@@ -2188,7 +2188,7 @@ onUnmounted(() => {
line-height: 30px;
}
.info1-box-right {
margin-left: 64px;
margin-left: 40px;
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
......@@ -2347,7 +2347,7 @@ onUnmounted(() => {
width: 24px;
height: 22px;
margin-left: 18px;
margin-top: 18px;
margin-top: 13px;
img {
width: 100%;
height: 100%;
......@@ -2574,11 +2574,12 @@ onUnmounted(() => {
width: 657px;
margin-left: 20px;
.right-top {
margin-top: 16px;
width: 657px;
height: 24px;
display: flex;
justify-content: space-between;
.title {
margin-top: 13px;
width: 440px;
height: 24px;
color: rgba(59, 65, 75, 1);
......@@ -2593,13 +2594,12 @@ onUnmounted(() => {
.time {
flex: 1;
text-align: right;
height: 22px;
margin-top: 19px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
line-height: 24px;
overflow: hidden;
}
}
......
......@@ -28,7 +28,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
legend: {
data: ['提出法案', '通过法案', '通过率'],
show: true,
top: 0,
top: 10,
icon: 'circle',
textStyle: {
color: 'rgba(95, 101, 108, 1)',
......
......@@ -86,7 +86,7 @@
</div>
</div> -->
</div>
<div class="main">
<div class="main" v-if="curArea !== '实体清单'">
<div class="item" v-for="(item, index) in searchResults" :key="index" @click="handleToPage(item)">
<div class="item-left" v-if="item.img">
<img :src="item?.img" alt="" />
......@@ -95,7 +95,7 @@
<div class="title" v-html="item?.originalTitle"></div>
<div class="content" v-html="item?.originalDescription"></div>
<div class="item-right-footer">
<div class="time">{{ item?.date }}</div>
<div class="time" v-if="item.date">{{ item?.date }}</div>
<div class="tag tag3">
{{ item?.typeStr }}
</div>
......@@ -103,6 +103,26 @@
</div>
</div>
</div>
<div class="main1" v-if="curArea === '实体清单'">
<div class="item" v-for="(item, index) in searchResults" :key="index" @click="handleToPage(item)">
<div class="main-header">
<div class="title" v-html="item?.originalTitle"></div>
<div class="tag tag3">
{{ item?.typeStr }}
</div>
</div>
<div class="entity-box">
<div class="entity" v-for="val,idx in item.sanctionList" :key="idx" @click.prevent="handleCompClick(val)">
<div class="icon">
<img src="./assets/images/warning.png" alt="">
</div>
<div class="name">{{ val.entityNameZh }}</div>
</div>
</div>
<div class="content" v-html="item?.originalDescription"></div>
<div class="time" v-if="item.date">{{ item?.date }}</div>
</div>
</div>
<div class="footer">
<el-pagination
@current-change="handleChangeCurrentPage"
......@@ -265,7 +285,7 @@ const handleClickDomains = domain => {
function highlightText(text, keyword) {
if (!text || !keyword) return text;
const regex = new RegExp(`(${keyword})`, 'gi');
const regex = new RegExp(`(${keyword})`, "gi");
return text.replace(regex, '<span style="color: red; font-weight: bold;">$1</span>');
}
......@@ -300,9 +320,9 @@ const handleSearch = async () => {
}
searchResults.value = res.data.records;
searchResults.value.forEach(item => {
item.originalTitle = highlightText(item.originalTitle, keyword.value)
item.originalDescription = highlightText(item.originalDescription, keyword.value)
})
item.originalTitle = highlightText(item.originalTitle, keyword.value);
item.originalDescription = highlightText(item.originalDescription, keyword.value);
});
totalNum.value = res.data.total;
if (totalNum.value) {
ElMessage.success(`共计搜索到${totalNum.value}条结果`);
......@@ -493,6 +513,19 @@ const handleToAi = () => {
});
window.open(route.href, "_blank");
};
// 跳转公司详情页
const handleCompClick = item => {
console.log("item", item);
window.sessionStorage.setItem('curTabName', item.entityNameZh)
const route = router.resolve({
name: "companyPages",
params: {
id: item.entityId
}
});
window.open(route.href, "_blank");
};
</script>
<style lang="scss" scoped>
......@@ -782,7 +815,7 @@ const handleToAi = () => {
.item-right {
// width: 731px;
flex: 1;
margin-left: 12px;
// margin-left: 12px;
.title {
height: 24px;
......@@ -820,9 +853,68 @@ const handleToAi = () => {
line-height: 24px;
letter-spacing: 0px;
text-align: left;
margin-right: 16px;
}
.tag {
height: 24px;
line-height: 24px;
box-sizing: border-box;
padding: 0 8px;
border-radius: 4px;
}
.tag1 {
border: 1px solid rgba(255, 204, 199, 1);
background: rgba(255, 241, 240, 1);
color: rgba(255, 77, 79, 1);
}
.tag2 {
color: rgba(250, 173, 20, 1);
border: 1px solid rgba(255, 241, 184, 1);
background: rgba(255, 251, 230, 1);
}
.tag3 {
color: rgba(22, 119, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
}
}
}
}
}
.main1 {
width: 913px;
margin-top: 36px;
margin-left: 240px;
.item {
width: 913px;
min-height: 108px;
// max-height: 148px;
padding-bottom: 24px;
padding-top: 16px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
cursor: pointer;
// &:hover {
// background: var(--color-bg-hover);
// }
.main-header {
min-height: 24px;
display: flex;
align-items: center;
justify-content: space-between;
.title {
width: 820px;
// height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.tag {
margin-left: 16px;
height: 24px;
line-height: 24px;
box-sizing: border-box;
......@@ -845,6 +937,73 @@ const handleToAi = () => {
background: rgba(230, 244, 255, 1);
}
}
.entity-box {
margin-top: 12px;
display: flex;
gap: 8px;
flex-wrap: wrap;
.entity {
display: flex;
justify-content: space-between;
gap: 6px;
height: 32px;
line-height: 32px;
padding: 0 12px;
border-radius: 20px;
background: rgba(255, 241, 240, 1);
cursor: pointer;
&:hover{
.name{
font-weight: bold;
}
}
.icon {
width: 12px;
height: 12px;
img {
width: 100%;
height: 100%;
}
}
.name {
margin-top: 4px;
height: 24px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: center;
}
}
}
.content {
margin-top: 10px;
min-height: 0;
// max-height: 48px;
font-family: Microsoft YaHei;
font-size: 16px;
color: rgba(59, 65, 75, 1);
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
}
.time {
margin-top: 10px;
height: 24px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
margin-right: 16px;
}
}
}
......
......@@ -430,6 +430,27 @@
</div>
<div class="home-main-footer-main">
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "政令类型" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox
v-for="type in decreeTypeList"
:key="type.id"
v-model="checkedDecreeType"
:label="type.typeId"
style="width: 180px"
class="filter-checkbox"
@change="handleChangeCheckedDecreeType"
>
{{ type.typeName }}
</el-checkbox>
</div>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
......@@ -573,7 +594,8 @@ import {
getKeyDecree,
getDecreeKeyInstruction,
getDecreeOrderList,
getDecreehylyList
getDecreehylyList,
getDecreeTypeList
} from "@/api/decree/home";
import { getPersonSummaryInfo } from "@/api/common/index";
import { getNews, getSocialMedia } from "@/api/general/index";
......@@ -948,6 +970,10 @@ const chart1Data = ref({
});
const box5YearList = ref([
{
label: "2026",
value: "2026"
},
{
label: "2025",
value: "2025"
......@@ -969,7 +995,7 @@ const box5YearList = ref([
value: "2021"
}
]);
const box5SelectedYear = ref("2025");
const box5SelectedYear = ref("2026");
const handleGetDecreeYearOrder = async () => {
const params = {
......@@ -1015,6 +1041,10 @@ const chart2Data = ref([
// const colorList = ["#69B1FF", "#FFC069", "#87E8DE", "#85A5FF", "#FF7875", "#B37FEB", "#4096FF"];
const box6YearList = ref([
{
label: "2026",
value: "2026"
},
{
label: "2025",
value: "2025"
......@@ -1036,7 +1066,7 @@ const box6YearList = ref([
value: "2021"
}
]);
const box6SelectedYear = ref("2025");
const box6SelectedYear = ref("2026");
const handleGetDecreeArea = async () => {
const params = {
......@@ -1068,18 +1098,7 @@ const handleBox6YearChange = () => {
};
// 关键行政令
const keyDecreeList = ref([
// {
// title: "关于“对等关税”的行政令",
// time: "2025年4月",
// content: `对所有贸易伙伴设立10%的“最低基准关税”,并对中国商品加征高达34%​​ 的额外关税,使针对中国的总关税水平大幅提升。`
// },
// {
// title: "《美国优先投资政策》备忘录",
// time: "2025年2月",
// content: `将中国列为“外国对手”,系统性收紧中美在半导体、人工智能、量子技术等前沿科技领域的双向投资,构建对华科技封锁体系。`
// },
]);
const keyDecreeList = ref([]);
const handleGetKeyDecree = async () => {
try {
......@@ -1185,7 +1204,26 @@ const handleGetAreaList = async () => {
} catch (error) {}
};
// 政令类型
const decreeTypeList = ref([]);
const checkedDecreeType = ref([]);
const handleGetDecreeTypeList = async () => {
try {
const res = await getDecreeTypeList();
console.log("政令类型", res);
if (res.code === 200 && res.data) {
decreeTypeList.value = res.data;
}
} catch (error) {}
};
const handleChangeCheckedDecreeType = () => {
handleGetDecreeOrderList()
}
const pubTime = ref([
{ id: "2026", name: "2026年" },
{ id: "2025", name: "2025年" },
{ id: "2024", name: "2024年" },
{ id: "2023", name: "2023年" },
......@@ -1194,30 +1232,11 @@ const pubTime = ref([
{ id: "2020", name: "2020年" }
// { id: "更早时间", name: "更早时间" }
]);
const activePubTime = ref(["2025"]);
const activePubTime = ref(["2026"]);
const totalDecreesNum = ref(0);
const decreeList = ref([
// {
// type: "总统政令",
// status: 1,
// title: "关于进一步延长TikTok执法宽限期的行政令",
// time: "2025年9月16日",
// img: 1,
// desc: "123",
// tagList: ["生物科技"]
// },
// {
// type: "总统政令",
// status: 1,
// title: "为国家安全部署先进核反应堆技术",
// time: "2025年9月16日",
// img: 1,
// desc: "123",
// tagList: ["生物科技"]
// }
]);
const decreeList = ref([]);
const handleGetDecreeOrderList = async () => {
const p0 = checkedGovIns.value.join(",");
......@@ -1229,7 +1248,8 @@ const handleGetDecreeOrderList = async () => {
proposeName: p0,
researchTypeIds: p1,
sortFun: isSort.value,
years: p2
years: p2,
typeIds: checkedDecreeType.value.toString()
};
try {
const res = await getDecreeOrderList(params);
......@@ -1315,6 +1335,7 @@ const handleSearch = () => {
};
onMounted(async () => {
handleGetDecreeTypeList();
handleGetAreaList();
handleGetDecreeOrderList();
handleBox1(); // 最新科技政令
......
......@@ -249,7 +249,13 @@ const handleCloseReport = () => {
isShowReport.value = false;
};
const handleShowReport = () => {
isShowReport.value = true;
const curRoute = router.resolve({
path: "/decree/decreeOriginal",
query: {
id: route.query.id
}
});
window.open(curRoute.href, "_blank");
};
onMounted(() => {
......@@ -562,7 +568,6 @@ onMounted(() => {
}
}
.layout-main-center {
}
}
.layout-report-box {
......
<template>
<div class="layout-container">
<!-- 导航菜单 -->
<div class="layout-main">
<div class="layout-main-header">
<div class="layout-main-header-left-box">
<div class="left-box-top">
<div class="icon">
<img :src="summaryInfo.imageUrl" alt="" />
</div>
<div class="info">
<div class="info-box1">{{ summaryInfo.name }}</div>
<div class="info-box2">
<div class="info-box2-item item1">{{ summaryInfo.order }}</div>
|
<div class="info-box2-item item2">{{ summaryInfo.type }}</div>
|
<div class="info-box2-item item3">{{ summaryInfo.ename }}</div>
</div>
</div>
</div>
</div>
<div class="layout-main-header-right-box">
<div class="right-box-top">
<div class="time">{{ summaryInfo.postDate }}</div>
<div class="name">{{ summaryInfo.orgName }}</div>
</div>
</div>
</div>
<div class="layout-main-center">
<div class="report-box">
<div class="report-header">
{{ "政令原文" }}
</div>
<div class="report-main">
<div class="left">
<div v-if="!reportUrl" class="noContent">{{ "中文原文暂无数据" }}</div>
<iframe v-else :src="reportUrl" style="border: none" width="100%" height="100%"> </iframe>
</div>
<div class="right">
<div v-if="!reportUrlEn" class="noContent">{{ "英文原文暂无数据" }}</div>
<iframe v-else :src="reportUrlEn" style="border: none" width="100%" height="100%"> </iframe>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getDecreeSummary } from "@/api/decree/introduction";
import { getDecreeReport } from "@/api/decree/introduction";
const route = useRoute();
const reportUrl = ref("");
const reportUrlEn = ref("");
const summaryInfo = ref({});
// 获取全局信息
const handleGetSummary = async () => {
const params = {
id: route.query.id
};
try {
const res = await getDecreeSummary(params);
console.log("全局信息", res);
if (res.code === 200 && res.data) {
summaryInfo.value = res.data;
}
} catch (error) {}
};
// 获取报告原文
const handleGetReport = async () => {
const params = {
id: route.query.id
};
try {
const res = await getDecreeReport(params);
console.log("报告原文", res);
if (res.code === 200 && res.data) {
reportUrl.value = res.data.content;
reportUrlEn.value = res.data.contentEn;
}
} catch (error) {}
};
onMounted(() => {
handleGetSummary();
handleGetReport();
});
</script>
<style lang="scss" scoped>
.layout-container {
width: 100%;
height: 100%;
overflow: hidden;
overflow-y: auto;
.layout-main {
width: 100%;
.layout-main-header {
height: 120px;
background: rgba(255, 255, 255, 1);
display: flex;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 99999999;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.layout-main-header-left-box {
width: 1100px;
margin-left: 160px;
margin-top: 13px;
.left-box-top {
height: 64px;
display: flex;
.icon {
width: 122px;
height: 64px;
border-radius: 4px;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.info {
width: 700px;
margin-left: 9px;
.info-box1 {
width: 700px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 600;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
margin-top: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.info-box2 {
margin-top: 5px;
height: 22px;
line-height: 22px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
display: flex;
margin-left: -10px;
.info-box2-item {
padding: 0 10px;
}
.item1 {
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item2 {
width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item3 {
width: 420px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.left-box-bottom {
display: flex;
height: 40px;
margin-top: 21px;
.left-box-bottom-item {
display: flex;
margin-right: 32px;
margin-top: 3px;
height: 35px;
cursor: pointer;
.icon {
margin-top: 4px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.name {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
margin-left: 3px;
}
.nameActive {
color: var(--color-main-active);
font-weight: 700;
}
}
.leftBoxBottomItemActive {
border-bottom: 3px solid var(--color-main-active);
}
}
}
.layout-main-header-right-box {
width: 300px;
margin-right: 150px;
margin-top: 19px;
.right-box-top {
.time {
height: 24px;
line-height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
.name {
height: 24px;
line-height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
}
.right-box-bottom {
margin-top: 24px;
text-align: right;
display: flex;
justify-content: flex-end;
gap: 8px;
.btn {
width: 120px;
height: 36px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 6px;
background: rgba(255, 255, 255, 1);
display: flex;
justify-content: center;
gap: 8px;
align-items: center;
cursor: pointer;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 64px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
.btn-active {
width: 120px;
height: 36px;
border-radius: 6px;
background: var(--color-main-active);
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
cursor: pointer;
.icon-active {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text-active {
width: 64px;
height: 24px;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: center;
}
}
}
}
}
.layout-main-center {
.report-box {
margin: 0 auto;
width: 1600px;
height: 926px;
background: rgba(248, 249, 250, 1);
.report-header {
height: 80px;
line-height: 80px;
padding-left: 20px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
letter-spacing: 0px;
text-align: left;
}
.report-main {
width: 1600px;
margin: 0 auto;
background: #fff;
box-sizing: border-box;
padding: 0 69px;
display: flex;
height: calc(100% - 100px);
justify-content: space-between;
.left {
width: 800px;
.noContent {
height: 100px;
line-height: 100px;
text-align: center;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
}
}
.right {
width: 800px;
.noContent {
height: 100px;
line-height: 100px;
text-align: center;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
}
}
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -9,7 +9,7 @@
</template>
<div class="tableWrap">
<el-table
:data="panel5MockData"
:data="panel5Data"
class="sanction-table"
stripe
empty-text="暂无数据"
......@@ -116,6 +116,7 @@ const selectedId = ref(selectOptions[0].value);
const indexMethod = index => {
return index + 1;
};
const panel5Data = ref([]);
const panel5MockData = [
{
name: "科大讯飞股份有限公司",
......@@ -283,6 +284,20 @@ const fetchOriginCountData = async () => {
}
};
const fetchScientificImpactEntityList = async () => {
try {
const data = await getScientificImpactEntityList(router.query.startTime);
if (data && Array.isArray(data)) {
panel5Data.value = data.map(item => ({
name: item.name,
number: item.count
}));
}
} catch (error) {
console.error("获取影响实体分析数据失败:", error);
}
};
onMounted(async () => {
// horizontalBarOptions.value = getHorizontalBarChart1(
// ["地球探测仪器", "计算机及其配套设备", "天文仪器", "分析仪器", "核仪器", "物理性能测试仪器", "医学科研仪器"],
......@@ -316,6 +331,8 @@ onMounted(async () => {
// { name: "核仪器", value: [48, 38, 28, 28, 28, 18, 18, 18, 28, 28] }
// ]
// });
// 科研机构
await fetchScientificImpactEntityList();
// 获取各类别仪器对美依赖情况数据
await fetchDependencyCountData();
// 获取仪器进口国可替代性分析数据
......
......@@ -88,13 +88,9 @@
<div class="hintWrap">
<div class="title">
共计
<span class="text1">{{ entityTotal }}</span>
<span class="text1">{{ entitiesCountBy50PercentRules?.totalCount }}</span>
家,其中50%规则涉及
<span class="text2">{{
panel5IsChecked
? entitiesCountBy50PercentRules?.ruleCount
: entitiesCountBy50PercentRules?.totalCount
}}</span>
<span class="text2">{{ entitiesCountBy50PercentRules?.ruleCount }}</span>
</div>
</div>
......
......@@ -126,7 +126,7 @@ onMounted(async () => {
overflow-y: auto;
.header {
width: 1920px;
width: 100%;
height: 188px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
......
......@@ -107,11 +107,11 @@ onMounted(async () => {
<style lang="scss" scoped>
.wrap {
width: 1920px;
width: 100%;
// height: 984px;
.header {
width: 1920px;
width: 100%;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
......@@ -315,7 +315,7 @@ onMounted(async () => {
}
.main {
margin-left: 160px;
margin: 0 auto;
background: #ffffff;
width: 1600px;
height: 1025px;
......
......@@ -72,7 +72,7 @@ export default defineConfig({
rewrite: (path) => path.replace(/^\/checklistChat/, '')
},
'/temporarySearch': {
target: 'http://106.12.150.59:18826/',
target: 'http://106.12.150.59:8001/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/temporarySearch/, '')
},
......
# 交互消息类
## ApiResult
```java
public class ApiResult<T> {
@ApiModelProperty("响应码")
private int code;
@ApiModelProperty("响应消息")
private String message;
@ApiModelProperty("是否成功")
private boolean success;
@ApiModelProperty("响应数据")
private T data;
}
```
## LatestRiskUpdatesVO
```java
public class LatestRiskUpdatesVO {
@Data
public static class ItemVO {
//统计总数
private long total;
//增加数
private long dailyIncrement;
}
@Data
public static class HotspotVO {
//最热事件id
private String HotspotID;
//最热事件类型
private String HotspotType;
//最热事件日期
private String HotspotDate;
//最热事件标题
private String HotspotTitle;
}
}
```
## LatestRisksVO
```java
public class LatestRisksVO {
//风险信号列表
private List<RiskVO> riskVOS;
//风险关联事件列表
private List<HotspotVO> hotspotVOS;
//所有风险数
private Long RiskCount;
@Data
@NoArgsConstructor
public static class HotspotVO {
@ApiModelProperty("id")
private String HotspotID;
@ApiModelProperty("标题")
private String HotspotTitle;
@ApiModelProperty("描述")
private String HotspotDesc;
@ApiModelProperty("时间")
private Date HotspotDate;
@ApiModelProperty("图片")
private String HotspotPicture;
@ApiModelProperty("发布机构名称")
private String HotspotOrgName;
@ApiModelProperty("类型")
private String HotspotType;
@ApiModelProperty("领域列表")
private List<BaseInfo> DomainList;
}
}
@Data
public static class RiskVO {
@ApiModelProperty("风险信号id")
private Long signalId;
@ApiModelProperty("风险信号标题")
private String signalTitle;
@ApiModelProperty("风险信号时间")
private Date signalTime;
@ApiModelProperty("风险信号等级")
private String signalLevel;
}
}
```
## AllDomainCountVO
```
public class AllDomainCountVO {
@ApiModelProperty(value = "统计名称")
private String countName;
@ApiModelProperty(value = "统计数量")
private Long countNum;
}
```
## DomainContainmentRankingVO
```
public class DomainContainmentRankingVO {
@ApiModelProperty(value = "机构名称")
private String orgName;
@ApiModelProperty(value = "机构图片")
private String orgPicture;
@ApiModelProperty(value = "机构打压次数")
private Long orgCount;
}
```
## DomainContainmentTrendVO
```
public class DomainContainmentTrendVO {
@ApiModelProperty(value = "年度/月份")
private String YearOrMonth;
@ApiModelProperty(value = "领域列表")
private List<DomainVO> DomainList;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class DomainVO {
@ApiModelProperty(value = "统计名称")
private String DomainName;
@ApiModelProperty(value = "统计数量")
private Long DomainNum;
}
}
```
## DomainContainmentTimelineVO
```
public class DomainContainmentTimelineVO {
@ApiModelProperty(value = "打压名称")
private String EventName;
@ApiModelProperty(value = "打压描述")
private String EventDesc;
@ApiModelProperty(value = "打压时间")
private Date EventDate;
@ApiModelProperty(value = "打压类别")
private String EventType;
@ApiModelProperty(value = "打压发起机构")
private String EventOrgName;
@ApiModelProperty(value = "打压事件id")
private String EventId;
@ApiModelProperty(value = "领域列表")
private List<DomainContainmentTimelineVO.DomainVO> EventDomainList;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class DomainVO {
@ApiModelProperty(value = "领域id")
private String DomainId;
@ApiModelProperty(value = "领域名称")
private String DomainName;
}
}
```
## TechnologyGameAnalysisVO
```
public class TechnologyGameAnalysisVO {
@ApiModelProperty(value = "事件id")
private String eventId;
@ApiModelProperty(value = "事件名称")
private String eventName;
@ApiModelProperty(value = "事件摘要")
private String eventDesc;
@ApiModelProperty(value = "事件发布机构")
private String eventOrg;
@ApiModelProperty(value = "事件时间")
private Date eventDate;
@ApiModelProperty(value = "事件策略")
private String eventStrategy;
@ApiModelProperty(value = "事件类型")
private String eventType;
@ApiModelProperty(value = "事件国旗图片")
private String eventCountryImg;
}
```
# 中美博弈概览
## **最新风险动态统计**
请求地址:/rivalryIndexV2/LatestRiskUpdates
请求类型:GET
输入参数:
​ 参数:无输入
```java
@ApiParam(value = "日期", defaultValue = "本周")
@RequestParam(value = "currentDate") String currentDate
```
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<LatestRiskUpdatesVO>
```
"data": {
"bill": { -法案
"total": 167258,
"dailyIncrement": 91
},
"administrativeOrder": {-政令
"total": 215763,
"dailyIncrement": 27
},
"Entities": {-实体清单
"total": 141,
"dailyIncrement": 0
},
"CCL": {-ccl
"total": 3,
"dailyIncrement": 0
},
"SDN": {-sdn
"total": 3,
"dailyIncrement": 0
},
"militaryInvolvement": {-涉军企业
"total": 0,
"dailyIncrement": 0
},
"337Survey": {-337调查
"total": 2881,
"dailyIncrement": 1
},
"232Survey": {-232调查
"total": 35,
"dailyIncrement": 0
},
"301Survey": {-301调查
"total": 5,
"dailyIncrement": 0
},
"policiesRegulations": {-最热政策法规
"hotspotID": "119_HR_7065",
"hotspotDate": "2026-01-14",
"hotspotType": "法案",
"hotspotTitle": "塞尼卡民族执法效率法案 (Seneca Nation Law Enforcement Efficiency Act)"
},
"exportControl": {-最热出口管制
"hotspotID": "138",
"hotspotDate": "2025-10-08",
"hotspotType": "实体清单",
"hotspotTitle": "实体清单的增补与修订"
},
"investmentFinancingRestrictions": {-最热投融资限制
"hotspotID": "147",
"hotspotDate": "2025-06-30",
"hotspotType": "SDN",
"hotspotTitle": "《出口管理条例》修订:商业管制清单新增条目"
},
"marketAccess": {-最热市场准入
"hotspotID": "111",
"hotspotDate": "2026-01-01",
"hotspotType": "337",
"hotspotTitle": "外国制造的半导体器件及其下游产品和组件"
}
}
```
## **最新风险信号**
请求地址:/rivalryIndexV2/LatestRisks
请求类型:GET
输入参数:
​ 参数:无输入
​ 请求头:携带token
输出结果:ApiResult<LatestRisksVO>
## **全领域统计**
请求地址:/rivalryIndexV2/AllDomainCount
请求类型:GET
输入参数:
​ 参数:无输入
​ 请求头:携带token
输出结果:ApiResult<List<AllDomainCountVO>>
## 领域打压遏制排行
请求地址:/rivalryIndexV2/DomainContainmentRanking
请求类型:GET
输入参数:
​ 参数:
```java
@ApiParam(value = "对我打压机构", defaultValue = "对我打压机构")
@RequestParam(value = "ContainmentOrg") String ContainmentOrg,
@ApiParam(value = "领域", defaultValue = "")//传空值为查询全部领域
@RequestParam(value = "Domains",required = false) String Domain
```
请求头:携带token
输出结果:ApiResult<List<DomainContainmentRankingVO>>
## 美对华制裁措施数量趋势
请求地址:/rivalryIndexV2/DomainContainmentTrend
请求类型:GET
输入参数:
参数:required = false为非必传
```java
@ApiParam(value = "日期", defaultValue = "按月统计")
@RequestParam(value = "byYOrM") String byYOrM
```
请求头:携带token
输出结果:ApiResult<List<DomainContainmentTrendVO>>
## 领域打压遏制时间线
请求地址:/rivalryIndexV2/DomainContainmentTimeline
请求类型:GET
输入参数:
参数:required = false为非必传
```JAVA
//传空值为查询全部领域
@ApiParam(value = "领域", defaultValue = "1")
@RequestParam(value = "domain",required = false) String domain
```
请求头:携带token
输出结果: ApiResult<List<DomainContainmentTimelineVO>>
## 中美科技博弈分析
请求地址:/rivalryIndexV2/TechnologyGameAnalysis
请求类型:GET
输入参数:
参数:required = false为非必传
```java
//传空值为查询全部领域
@ApiParam(value = "领域", defaultValue = "1")
@RequestParam(value = "domain",required = false) String domain
```
输出结果:ApiResult<List<TechnologyGameAnalysisVO>>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论