提交 273f345b authored 作者: coderBryanFu's avatar coderBryanFu

update

...@@ -14,13 +14,13 @@ export function getBillInfo(params) { ...@@ -14,13 +14,13 @@ export function getBillInfo(params) {
// 提出人-根据动议ID获取对应的提出人信息 // 提出人-根据动议ID获取对应的提出人信息
/** /**
* @param {id} * @param {billId}
* @header token * @header token
*/ */
export function getBillPerson(params) { export function getBillPerson(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/billInfoBean/person/${params.id}`, url: `/api/billInfoBean/person/${params.billId}`,
params, params,
}) })
} }
...@@ -63,6 +63,18 @@ export function getBillBackground(params) { ...@@ -63,6 +63,18 @@ export function getBillBackground(params) {
params, params,
}) })
} }
// 相关事件-根据法案ID获取相关事件信息
/**
* @param {id}
* @header token
*/
export function getBillInfoEvent(params) {
return request({
method: 'GET',
url: `/api/billInfoBean/event/${params.id}`,
params,
})
}
// 议员相关性-根据法案ID获取议员分析信息(现在只包括名称 支持 反对,没有标签和事件动态) // 议员相关性-根据法案ID获取议员分析信息(现在只包括名称 支持 反对,没有标签和事件动态)
/** /**
...@@ -92,20 +104,20 @@ export function getBillContentId(params) { ...@@ -92,20 +104,20 @@ export function getBillContentId(params) {
// 主要条款-根据原文ID获取条款内容 // 主要条款-根据原文ID获取条款内容
/** /**
* @param {id,cRelated,currentPage,pageSize} * @param {billid,id,cRelated,currentPage,pageSize}
* @header token * @header token
*/ */
export function getBillContentTk(params) { export function getBillContentTk(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/billInfoBean/content/tk/${params.id}`, url: `/api/billInfoBean/content/tk/${params.billid}/${params.id}`,
params, params,
}) })
} }
// 限制方式-根据法案原文ID获取限制方式列表 // 限制方式-根据法案原文ID获取限制方式列表
/** /**
* @param {id} * @param {billId}
* @header token * @header token
*/ */
export function getBillContentXzfs(params) { export function getBillContentXzfs(params) {
......
...@@ -13,11 +13,11 @@ export function getBillIndustry(params) { ...@@ -13,11 +13,11 @@ export function getBillIndustry(params) {
} }
// 涉华法案统计 // 涉华法案统计
export function getBillCount() { export function getBillCount(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/BillOverview/billCount`, url: `/api/BillOverview/billCount`,
params
}) })
} }
...@@ -65,3 +65,34 @@ export function getHylyList() { ...@@ -65,3 +65,34 @@ export function getHylyList() {
url: `/api/billImpactAnalysis/industry/hylyList`, url: `/api/billImpactAnalysis/industry/hylyList`,
}) })
} }
// 获取法案提出部门
/**
* @param {year}
*/
export function getBillPostOrg(params) {
return request({
method: 'GET',
url: `/api/BillOverview/billPostOrg/${params.year}`,
})
}
// 获取关键议员提案
/**
* @param {year}
*/
export function getMemberProposal(params) {
return request({
method: 'GET',
url: `/api/BillOverview/memberProposal/${params.year}`,
})
}
// 获取资源库
export function getBills(params) {
return request({
method: 'GET',
url: `/api/BillOverview/bills`,
params
})
}
\ No newline at end of file
...@@ -12,6 +12,18 @@ export function getBillTimeAnalyze(params) { ...@@ -12,6 +12,18 @@ export function getBillTimeAnalyze(params) {
}) })
} }
// 修正案次数分析
/**
* @param {id}
*/
export function getBillAmeAnalyzeCount(params) {
return request({
method: 'GET',
url: `/api/billDeepDive/ameAnalyze/count/${params.id}`,
params,
})
}
// 根据法案ID获取党派政治献金 // 根据法案ID获取党派政治献金
/** /**
* @param {id, personCongress} * @param {id, personCongress}
......
...@@ -23,7 +23,8 @@ export function getEntitiesDataCount() { ...@@ -23,7 +23,8 @@ export function getEntitiesDataCount() {
return request200( return request200(
request({ request({
method: "GET", method: "GET",
url: "/api/entitiesDataCount/countData" // url: "/api/entitiesDataCount/countData",
url: "/api/sanctionList/export/getTotalInfo"
}) })
); );
} }
...@@ -62,12 +63,15 @@ export function getEntitiesDataInfo() { ...@@ -62,12 +63,15 @@ export function getEntitiesDataInfo() {
* maxCount: number * maxCount: number
* }[]>} * }[]>}
*/ */
export function getIndustryCountByYear() { export function getIndustryCountByYear(sanTypeId) {
return request200( return request200(
request({ request({
method: "GET", method: "GET",
// url: "/api/entitiesDataCount/industryCountByYear" // url: "/api/entitiesDataCount/industryCountByYear"
url: "/api/entitiesDataCount/getAnnualCount" url: "/api/entitiesDataCount/getAnnualCount",
params: {
sanTypeId
}
}) })
); );
} }
...@@ -84,11 +88,16 @@ export function getIndustryCountByYear() { ...@@ -84,11 +88,16 @@ export function getIndustryCountByYear() {
* domains: string[] * domains: string[]
* }>} * }>}
*/ */
export function getCountDomainByYear() { export function getCountDomainByYear(isRule, startYear = "2020", endYear = new Date().getFullYear()) {
return request200( return request200(
request({ request({
method: "GET", method: "POST",
url: "/api/entitiesDataCount/countDomainByYear" url: "/api/entitiesDataCount/getAnnualSanDomain",
data: {
isRule,
startYear,
endYear
}
}) })
); );
} }
...@@ -187,6 +196,21 @@ export function getKeyEntityList(date, keyword = "") { ...@@ -187,6 +196,21 @@ export function getKeyEntityList(date, keyword = "") {
); );
} }
/**
* 区域分布查询
*/
export function getAreaDistribution(date) {
return request200(
request({
method: "GET",
url: "/api/entitiesDataInfo/getRegionDistribution",
params: {
sanctionDate: date || "2025-11-11"
}
})
);
}
/** /**
* 不同领域实体统计 * 不同领域实体统计
* @param {string} startTime - 统计开始时间,格式为 'YYYY-MM-DD' * @param {string} startTime - 统计开始时间,格式为 'YYYY-MM-DD'
...@@ -292,7 +316,7 @@ export function getDomainDistribution(sanctionDate = "2025-11-11") { ...@@ -292,7 +316,7 @@ export function getDomainDistribution(sanctionDate = "2025-11-11") {
* startTime: string * startTime: string
* }[]>} * }[]>}
*/ */
export function getEntitiesList(typeName = "实体清单", pageNum = 1, pageSize = 10) { export function getEntitiesList(typeName = "实体清单", pageNum = 1, pageSize = 10, sanctionDate = "", rule = false) {
return request200( return request200(
request({ request({
method: "POST", method: "POST",
...@@ -300,7 +324,9 @@ export function getEntitiesList(typeName = "实体清单", pageNum = 1, pageSize ...@@ -300,7 +324,9 @@ export function getEntitiesList(typeName = "实体清单", pageNum = 1, pageSize
data: { data: {
typeName, typeName,
pageNum, pageNum,
pageSize pageSize,
sanctionDate,
rule
} }
}) })
); );
...@@ -340,15 +366,16 @@ export function getCompareCountSan(startTime) { ...@@ -340,15 +366,16 @@ export function getCompareCountSan(startTime) {
* count:number * count:number
* }[]>} * }[]>}
*/ */
export function getEntitiesChangeCount(domain, type) { export function getEntitiesChangeCount(domianId, typeId) {
return request200( return request200(
request({ request({
method: "GET", method: "GET",
// url: '/api/entitiesDataCount/entitiesChangeCount', // url: '/api/entitiesDataCount/entitiesChangeCount',
url: "/api/entitiesDataCount/sanCountByYear", // url: "/api/entitiesDataCount/sanCountByYear",
url: "/api/entitiesDataInfo/getCountByDomianAndType",
params: { params: {
domain, domianId,
type typeId
} }
}) })
); );
...@@ -377,11 +404,14 @@ export function getEntitiesGrowthTrend() { ...@@ -377,11 +404,14 @@ export function getEntitiesGrowthTrend() {
* xAxis: string[] * xAxis: string[]
* }>} * }>}
*/ */
export function getEntitiesUpdateCount() { export function getEntitiesUpdateCount(sanTypeId = 1) {
return request200( return request200(
request({ request({
method: "GET", method: "GET",
url: "/api/entitiesDataCount/entitiesUpdateCount" url: "/api/entitiesDataCount/getAnnualCount",
params: {
sanTypeId
}
}) })
); );
} }
...@@ -389,11 +419,14 @@ export function getEntitiesUpdateCount() { ...@@ -389,11 +419,14 @@ export function getEntitiesUpdateCount() {
/** /**
* 制裁领域分析 * 制裁领域分析
*/ */
export function getSanDomainCount() { export function getSanDomainCount(rule) {
return request200( return request200(
request({ request({
method: "GET", method: "GET",
url: "/api/entitiesDataCount/getSanDomainCount" url: "/api/entitiesDataCount/getSanDomainCount",
params: {
rule
}
}) })
); );
} }
......
...@@ -32,6 +32,7 @@ const emit = defineEmits(["click"]); ...@@ -32,6 +32,7 @@ const emit = defineEmits(["click"]);
cursor: pointer; cursor: pointer;
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
flex-shrink: 0;
} }
.activeButton { .activeButton {
border: 1px solid rgb(10, 87, 166); border: 1px solid rgb(10, 87, 166);
......
...@@ -61,5 +61,6 @@ function setActiveIndex(item) { ...@@ -61,5 +61,6 @@ function setActiveIndex(item) {
.buttonList { .buttonList {
display: flex; display: flex;
gap: 8px; gap: 8px;
overflow-x: auto;
} }
</style> </style>
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
v-for="(item, index) in backgroundList" v-for="(item, index) in backgroundList"
:key="item.id" :key="item.id"
> >
<div class="id">{{ index + 1 }}</div> <div class="id">{{ (currentPage - 1) * 10 + index + 1 }}</div>
<div class="title">{{ item.bjnr }}</div> <div class="title">{{ item.backgroundTitle }}</div>
<div class="share"> <div class="share">
<img src="./assets/icons/open.png" alt="打开" /> <img src="./assets/icons/open.png" alt="打开" />
</div> </div>
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
<div class="box1-main-footer"> <div class="box1-main-footer">
<div class="info"> <div class="info">
{{ {{
`共有${backgroundList.length}条${ `共有${total}条${
box1BtnActive === 1 ? "涉华" : "全部" box1BtnActive === 1 ? "涉华" : "全部"
}背景` }背景`
}} }}
...@@ -54,7 +54,9 @@ ...@@ -54,7 +54,9 @@
<el-pagination <el-pagination
background background
layout="prev, pager, next" layout="prev, pager, next"
:total="backgroundList.length" :total="total"
v-model:current-page="currentPage"
@current-change="handleGetBillBackground"
/> />
</div> </div>
</div> </div>
...@@ -80,7 +82,7 @@ ...@@ -80,7 +82,7 @@
:key="index" :key="index"
> >
<div class="left"> <div class="left">
<img :src="item.image" alt="" /> <img :src="item.imageUrl || item.image" alt="" />
</div> </div>
<div class="center"> <div class="center">
<div class="title">{{ item.sjbt }}</div> <div class="title">{{ item.sjbt }}</div>
...@@ -157,7 +159,7 @@ ...@@ -157,7 +159,7 @@
/></el-icon> /></el-icon>
</div> </div>
<div class="right-box1-main-bottom"> <div class="right-box1-main-bottom">
<WordCloudMap :data="wordCloudData" :shape="circle" /> <WordCloudMap :data="wordCloudData" shape="circle" />
</div> </div>
</div> </div>
</div> </div>
...@@ -214,25 +216,26 @@ import { ...@@ -214,25 +216,26 @@ import {
getBillBackground, getBillBackground,
getBillEvent, getBillEvent,
getBillPersonAnalyze, getBillPersonAnalyze,
getBillInfoEvent
} from "@/api/bill"; } from "@/api/bill";
const box1BtnActive = ref(1); const box1BtnActive = ref(1);
const currentPage = ref(1);
const total = ref(0);
const handleClickBox1Btn = (index) => { const handleClickBox1Btn = (index) => {
box1BtnActive.value = index; box1BtnActive.value = index;
if (index === 2) { currentPage.value = 1;
handleGetBillBackground(false); handleGetBillBackground();
} else {
handleGetBillBackground(true);
}
}; };
const box2BtnActive = ref(1); const box2BtnActive = ref(1);
const handleClickBox2Btn = (index) => { const handleClickBox2Btn = (index) => {
box2BtnActive.value = index; box2BtnActive.value = index;
if (index === 1) { if (index === 1) {
handleGetBillPersonAnalyze(true);
} else {
handleGetBillPersonAnalyze(false); handleGetBillPersonAnalyze(false);
} else {
handleGetBillPersonAnalyze(true);
} }
}; };
...@@ -439,27 +442,29 @@ const wordCloudData = [ ...@@ -439,27 +442,29 @@ const wordCloudData = [
]; ];
// 获取立法背景内容 // 获取立法背景内容
const handleGetBillBackground = async (cRelated) => { const handleGetBillBackground = async () => {
const cRelated = box1BtnActive.value === 1 ? 'Y' : 'N';
const params = { const params = {
cRelated: cRelated, cRelated: cRelated,
id: window.sessionStorage.getItem("billId"), id: window.sessionStorage.getItem("billId"),
currentPage: 0, currentPage: currentPage.value - 1,
pageSize: 10, pageSize: 10,
}; };
try { try {
const res = await getBillBackground(params); const res = await getBillBackground(params);
console.log("立法背景", res); console.log("立法背景", res);
backgroundList.value = res.data.content; backgroundList.value = res.data.content;
total.value = res.data.totalElements; // 假设API返回totalElements
} catch (error) {} } catch (error) {}
}; };
// 获取相关事件 // 获取相关事件
const handleGetRelatedEvent = async () => { const handleGetRelatedEvent = async () => {
const params = { const params = {
id: 1, id: window.sessionStorage.getItem("billId"),
}; };
try { try {
const res = await getBillEvent(params); const res = await getBillInfoEvent(params);
console.log("相关事件", res); console.log("相关事件", res);
eventList.value = res.data; eventList.value = res.data;
eventList.value.forEach((item, index) => { eventList.value.forEach((item, index) => {
...@@ -488,8 +493,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => { ...@@ -488,8 +493,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => {
const res = await getBillPersonAnalyze(params); const res = await getBillPersonAnalyze(params);
console.log("议员相关性分析", res); console.log("议员相关性分析", res);
personList.value = res.data; personList.value = res.data;
personList.value.forEach((item) => { personList.value.forEach((item, index) => {
item.image = user1; const imgList = [user1, user2, user3, user4, user5];
item.image = imgList[index % imgList.length];
item.icon = userIcon; item.icon = userIcon;
item.icon1 = userIcon1; item.icon1 = userIcon1;
}); });
...@@ -497,9 +503,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => { ...@@ -497,9 +503,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => {
}; };
onMounted(() => { onMounted(() => {
handleGetBillBackground(true); handleGetBillBackground();
handleGetRelatedEvent(); handleGetRelatedEvent();
handleGetBillPersonAnalyze(true); handleGetBillPersonAnalyze(false);
}); });
</script> </script>
...@@ -572,7 +578,7 @@ onMounted(() => { ...@@ -572,7 +578,7 @@ onMounted(() => {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
align-content: flex-start;
.box1-main-item { .box1-main-item {
width: 544px; width: 544px;
height: 48px; height: 48px;
...@@ -598,11 +604,12 @@ onMounted(() => { ...@@ -598,11 +604,12 @@ onMounted(() => {
width: 440px; width: 440px;
height: 48px; height: 48px;
line-height: 48px; line-height: 48px;
color: rgba(95, 101, 108, 1); color: rgb(59, 65, 75);
font-family: Microsoft YaHei; font-family: "Microsoft YaHei";
font-size: 14px; font-size: 16px;
font-weight: 400; font-weight: 400;
text-align: left; text-align: left;
overflow: hidden;
} }
.share { .share {
margin-left: 13px; margin-left: 13px;
...@@ -626,8 +633,8 @@ onMounted(() => { ...@@ -626,8 +633,8 @@ onMounted(() => {
.info { .info {
height: 22px; height: 22px;
line-height: 22px; line-height: 22px;
color: rgba(132, 136, 142, 1); color: rgb(132, 136, 142);
font-family: Microsoft YaHei; font-family: "Microsoft YaHei";
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
text-align: left; text-align: left;
...@@ -645,6 +652,7 @@ onMounted(() => { ...@@ -645,6 +652,7 @@ onMounted(() => {
margin-top: 9px; margin-top: 9px;
margin-left: 23px; margin-left: 23px;
height: 300px; height: 300px;
overflow: auto;
.box2-main-item { .box2-main-item {
width: 1103px; width: 1103px;
height: 60px; height: 60px;
...@@ -659,9 +667,9 @@ onMounted(() => { ...@@ -659,9 +667,9 @@ onMounted(() => {
width: 64px; width: 64px;
height: 48px; height: 48px;
border-radius: 2px; border-radius: 2px;
image { img {
width: 100px; width: 64px;
height: 100%; height: 48px;
} }
} }
.center { .center {
......
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
</div> </div>
<div class="btn" @click="handleToPosi('position2')"> <div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">{{ "资讯要闻" }}</div> <div class="btn-text">{{ "资讯要闻" }}</div>
...@@ -140,15 +141,15 @@ ...@@ -140,15 +141,15 @@
<div <div
class="info-box" class="info-box"
:class="{ :class="{
info1: item.status === 1, info1: index === 0,
info2: item.status === 2, info2: index === 1,
info3: item.status === 3, info3: index === 2,
info4: item.status === 4 info4: index === 3
}" }"
v-for="(item, index) in curBill.hylyList" v-for="(item, index) in curBill.hylyList"
:key="index" :key="index"
> >
{{ item }} {{ item.industryName }}
</div> </div>
</div> </div>
<div class="box1-main-left-info1"> <div class="box1-main-left-info1">
...@@ -307,6 +308,12 @@ ...@@ -307,6 +308,12 @@
<div class="header-right-text">{{ "数据来源:美国国会官方网站" }}</div> <div class="header-right-text">{{ "数据来源:美国国会官方网站" }}</div>
</div> </div>
</div> </div>
<div class="box5-select">
<el-select v-model="box5Select" placeholder="选择领域" @change="handleBox5Change" style="width: 150px">
<el-option label="全部领域" value="全部领域" />
<el-option v-for="item in categoryList.slice(1)" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
<div class="box5-main" id="box5Chart"></div> <div class="box5-main" id="box5Chart"></div>
</div> </div>
<div class="box6"> <div class="box6">
...@@ -386,7 +393,7 @@ ...@@ -386,7 +393,7 @@
@click="handleClcikToCharacter(0)" @click="handleClcikToCharacter(0)"
> >
<div class="box8-main-item-left"> <div class="box8-main-item-left">
<img :src="item.img" alt="" /> <img :src="item.img" alt="" referrerpolicy="no-referrer" class="left-img" />
<div class="left-icon1"> <div class="left-icon1">
<img :src="item.dangpai" alt="" /> <img :src="item.dangpai" alt="" />
</div> </div>
...@@ -448,16 +455,7 @@ ...@@ -448,16 +455,7 @@
{{ cate.name }} {{ cate.name }}
</div> </div>
</div> </div>
<div class="select-box">
<el-select v-model="releaseTime" placeholder="选择发布时间" style="width: 120px">
<el-option
v-for="item in releaseTimeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div> </div>
<div class="home-main-footer-main"> <div class="home-main-footer-main">
<div class="left"> <div class="left">
...@@ -467,17 +465,16 @@ ...@@ -467,17 +465,16 @@
<div class="title">{{ "科技领域" }}</div> <div class="title">{{ "科技领域" }}</div>
</div> </div>
<div class="select-main"> <div class="select-main">
<div class="checkbox-group"> <el-checkbox-group class="checkbox-group" v-model="activeAreaList">
<el-checkbox <el-checkbox
v-for="area in areaList" v-for="(area, index) in areaList"
:key="area.id" :key="area.id"
v-model="activeAreaList"
:label="area.id" :label="area.id"
class="filter-checkbox" class="filter-checkbox"
> >
{{ area.name }} {{ area.name }}
</el-checkbox> </el-checkbox>
</div> </el-checkbox-group>
</div> </div>
</div> </div>
<div class="select-box"> <div class="select-box">
...@@ -485,18 +482,17 @@ ...@@ -485,18 +482,17 @@
<div class="icon"></div> <div class="icon"></div>
<div class="title">{{ "党派" }}</div> <div class="title">{{ "党派" }}</div>
</div> </div>
<div class="select-main select-main1"> <div class="select-main">
<div class="checkbox-group"> <el-checkbox-group class="checkbox-group" v-model="activeDpList">
<el-checkbox <el-checkbox
v-for="dp in dpList" v-for="(dp, index) in dpList"
:key="dp.id" :key="dp.id"
v-model="activeDpList"
:label="dp.id" :label="dp.id"
class="filter-checkbox" class="filter-checkbox"
> >
{{ dp.name }} {{ dp.name }}
</el-checkbox> </el-checkbox>
</div> </el-checkbox-group>
</div> </div>
</div> </div>
<div class="select-box"> <div class="select-box">
...@@ -505,17 +501,16 @@ ...@@ -505,17 +501,16 @@
<div class="title">{{ "议院" }}</div> <div class="title">{{ "议院" }}</div>
</div> </div>
<div class="select-main"> <div class="select-main">
<div class="checkbox-group"> <el-checkbox-group class="checkbox-group" v-model="activeYyList">
<el-checkbox <el-checkbox
v-for="yy in yyList" v-for="(yy, index) in yyList"
:key="yy.id" :key="yy.id"
v-model="activeYyList"
:label="yy.id" :label="yy.id"
class="filter-checkbox" class="filter-checkbox"
> >
{{ yy.name }} {{ yy.name }}
</el-checkbox> </el-checkbox>
</div> </el-checkbox-group>
</div> </div>
</div> </div>
...@@ -525,17 +520,16 @@ ...@@ -525,17 +520,16 @@
<div class="title">{{ "发布时间" }}</div> <div class="title">{{ "发布时间" }}</div>
</div> </div>
<div class="select-main"> <div class="select-main">
<div class="checkbox-group"> <el-checkbox-group class="checkbox-group" v-model="activePubTime">
<el-checkbox <el-checkbox
v-for="time in pubTime" v-for="(time, index) in pubTime"
:key="time.id" :key="time.id"
v-model="activePubTime"
:label="time.id" :label="time.id"
class="filter-checkbox" class="filter-checkbox"
> >
{{ time.name }} {{ time.name }}
</el-checkbox> </el-checkbox>
</div> </el-checkbox-group>
</div> </div>
</div> </div>
</div> </div>
...@@ -561,6 +555,16 @@ ...@@ -561,6 +555,16 @@
/> />
</el-select> </el-select>
</div> </div>
<div class="right-header-box" style="margin-left: auto;">
<el-select v-model="releaseTime" placeholder="选择发布时间" style="width: 120px">
<el-option
v-for="item in releaseTimeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div> </div>
<div class="right-main"> <div class="right-main">
<div class="right-main-box" v-for="(item, index) in footerBillList" :key="index"> <div class="right-main-box" v-for="(item, index) in footerBillList" :key="index">
...@@ -620,7 +624,7 @@ ...@@ -620,7 +624,7 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref, computed, onUnmounted, nextTick } from "vue"; import { onMounted, ref, computed, onUnmounted, nextTick, watch } from "vue";
import router from "@/router/index"; import router from "@/router/index";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
...@@ -631,7 +635,9 @@ import { ...@@ -631,7 +635,9 @@ import {
getBillsByType, getBillsByType,
getHylyList, getHylyList,
getBillOverviewKeyTK, getBillOverviewKeyTK,
getBillCount getBillCount,
getBillPostOrg,
getMemberProposal
} from "@/api/bill/billHome"; } from "@/api/bill/billHome";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
...@@ -1138,6 +1144,7 @@ const handleGetBillsByType = async () => { ...@@ -1138,6 +1144,7 @@ const handleGetBillsByType = async () => {
}; };
// 涉华法案数量 // 涉华法案数量
const box5Select = ref("全部领域");
const box5Data = ref({ const box5Data = ref({
title: [ title: [
"2024-09", "2024-09",
...@@ -1166,11 +1173,31 @@ const box5Data = ref({ ...@@ -1166,11 +1173,31 @@ const box5Data = ref({
}); });
const handleGetBillCount = async () => { const handleGetBillCount = async () => {
try { try {
const res = await getBillCount(); let params = {};
if (box5Select.value !== "全部领域") {
params.industryId = box5Select.value;
}
const res = await getBillCount(params);
console.log("涉华法案统计", res); console.log("涉华法案统计", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const sortedData = res.data.sort((a, b) => a.month.localeCompare(b.month));
box5Data.value = {
title: sortedData.map(item => item.month),
data: [
{
name: "提出法案",
value: sortedData.map(item => item.totalCount)
},
{
name: "通过法案",
value: sortedData.map(item => item.passCount)
}
],
percent: sortedData.map(item => item.percent)
};
} else { } else {
box5Data.value = {}; // 保持默认数据或清空
// box5Data.value = {};
} }
} catch (error) { } catch (error) {
console.error("获取涉华法案统计error", error); console.error("获取涉华法案统计error", error);
...@@ -1179,10 +1206,60 @@ const handleGetBillCount = async () => { ...@@ -1179,10 +1206,60 @@ const handleGetBillCount = async () => {
const handleBox5 = async () => { const handleBox5 = async () => {
await handleGetBillCount(); await handleGetBillCount();
let box5Chart = getMultiLineChart(box5Data.value.title, box5Data.value.data[0].value, box5Data.value.data[1].value); const proposed = box5Data.value.data[0].value;
const passed = box5Data.value.data[1].value;
const rate = box5Data.value.percent || proposed.map((p, i) => {
const pass = passed[i] || 0;
return p ? ((pass / p) * 100).toFixed(2) : 0;
});
let box5Chart = getMultiLineChart(box5Data.value.title, proposed, passed, rate);
setChart(box5Chart, "box5Chart"); setChart(box5Chart, "box5Chart");
}; };
const handleBox5Change = () => {
handleBox5();
};
// 法案提出部门
const handleBox7Data = async () => {
try {
const res = await getBillPostOrg({ year: box7selectetedTime.value });
console.log("法案提出部门", res);
if (res.code === 200 && res.data) {
const apiData = res.data;
const houseItems = apiData.filter(i => i.congressName === "House");
const senateItems = apiData.filter(i => i.congressName === "Senate");
const houseTotal = houseItems.reduce((sum, i) => sum + i.countBill, 0);
const senateTotal = senateItems.reduce((sum, i) => sum + i.countBill, 0);
const data1 = [];
if (houseItems.length > 0) {
data1.push({ name: "众议院", value: houseTotal });
}
if (senateItems.length > 0) {
data1.push({ name: "参议院", value: senateTotal });
}
const data2 = [...houseItems, ...senateItems].map(item => ({
name: item.originDepart,
value: item.countBill,
type: item.congressName === "House" ? "众议院" : "参议院"
}));
const box7Chart = getDoublePieChart(data1, data2);
setChart(box7Chart, "box7Chart");
}
} catch (error) {
console.error("获取法案提出部门数据失败", error);
}
};
watch(box7selectetedTime, () => {
handleBox7Data();
});
// 关键条款 // 关键条款
const wordCloudData = ref([ const wordCloudData = ref([
{ name: "限制中国获取能源技术", value: 100 }, { name: "限制中国获取能源技术", value: 100 },
...@@ -1379,48 +1456,30 @@ const box7Data = ref([ ...@@ -1379,48 +1456,30 @@ const box7Data = ref([
] ]
]); ]);
const box8Data = ref([ const box8Data = ref([]);
{
name: "汤姆·科顿", const handleBox8Data = async () => {
zhiwei: "参议院情报委员会主席", try {
img: Message3, const res = await getMemberProposal({ year: box8selectetedTime.value });
dangpai: Cyy, console.log("关键议员提案", res);
yuan: Ghd, if (res.code === 200 && res.data) {
num: 8 box8Data.value = res.data.map(item => ({
}, name: item.memberName,
{ zhiwei: item.position,
name: "吉姆·里施", img: item.imageUrl,
zhiwei: "参议院外交关系委员会主席", num: item.countProposal,
img: Message3,
dangpai: Cyy,
yuan: Ghd,
num: 4
},
{
name: "特德·克鲁兹",
zhiwei: "参议院商务、科学和交通委员会主席",
img: Message3,
dangpai: Cyy,
yuan: Ghd,
num: 2
},
{
name: "里克·克劳福德",
zhiwei: "众议院美中战略竞争特设委员会主席",
img: Message3,
dangpai: Cyy,
yuan: Ghd,
num: 2
},
{
name: "布莱恩·马斯特",
zhiwei: "众议院情报委员会主席",
img: Message3,
dangpai: Cyy, dangpai: Cyy,
yuan: Ghd, yuan: item.position === "Democratic" ? Mzd : Ghd
num: 2 }));
} }
]); } catch (error) {
console.error("获取关键议员提案失败", error);
}
};
watch(box8selectetedTime, () => {
handleBox8Data();
});
const handleToPosi = id => { const handleToPosi = id => {
// 0 618 1240 2350 // 0 618 1240 2350
...@@ -1464,26 +1523,36 @@ const handleClickTab = tab => { ...@@ -1464,26 +1523,36 @@ const handleClickTab = tab => {
}; };
const areaList = [ const areaList = [
{ id: "全部领域", name: "全部领域" },
{ id: "人工智能", name: "人工智能" }, { id: "人工智能", name: "人工智能" },
{ id: "集成电路", name: "集成电路" }, { id: "集成电路", name: "集成电路" },
{ id: "通信网络", name: "通信网络" }, { id: "通信网络", name: "通信网络" },
{ id: "量子科技", name: "量子科技" } { id: "先进制造", name: "先进制造" },
{ id: "量子科技", name: "量子科技" },
{ id: "生物科技", name: "生物科技" },
{ id: "能源", name: "能源" },
{ id: "航空航天", name: "航空航天" },
{ id: "新材料", name: "新材料" },
{ id: "海洋", name: "海洋" }
]; ];
const activeAreaList = ["人工智能"]; const activeAreaList = ref(["全部领域"]);
const dpList = ref([ const dpList = ref([
{ id: "全部党派", name: "全部党派" },
{ id: "民主党", name: "民主党" }, { id: "民主党", name: "民主党" },
{ id: "共和党", name: "共和党" } { id: "共和党", name: "共和党" }
]); ]);
const activeDpList = ["民主党"]; const activeDpList = ref(["全部党派"]);
const yyList = ref([ const yyList = ref([
{ id: "全部议院", name: "全部议院" },
{ id: "参议院", name: "参议院" }, { id: "参议院", name: "参议院" },
{ id: "众议院", name: "众议院" } { id: "众议院", name: "众议院" }
]); ]);
const activeYyList = ["参议院"]; const activeYyList = ref(["全部议院"]);
const pubTime = ref([ const pubTime = ref([
{ id: "全部时间", name: "全部时间" },
{ id: "2025年", name: "2025年" }, { id: "2025年", name: "2025年" },
{ id: "2024年", name: "2024年" }, { id: "2024年", name: "2024年" },
{ id: "2023年", name: "2023年" }, { id: "2023年", name: "2023年" },
...@@ -1491,7 +1560,7 @@ const pubTime = ref([ ...@@ -1491,7 +1560,7 @@ const pubTime = ref([
{ id: "2021年", name: "2021年" }, { id: "2021年", name: "2021年" },
{ id: "更早时间", name: "更早时间" } { id: "更早时间", name: "更早时间" }
]); ]);
const activePubTime = ref(["2025年"]); const activePubTime = ref(["全部时间"]);
const footerSelectList1 = ref([ const footerSelectList1 = ref([
{ {
...@@ -1549,8 +1618,9 @@ onMounted(async () => { ...@@ -1549,8 +1618,9 @@ onMounted(async () => {
handleBox5(); //涉华法案统计 handleBox5(); //涉华法案统计
handleBox6(); // 关键条款 handleBox6(); // 关键条款
const box7Chart = getDoublePieChart(box7Data.value[0], box7Data.value[1]); handleBox7Data();
setChart(box7Chart, "box7Chart");
handleBox8Data();
await handleGetHotBills(); await handleGetHotBills();
curBill.value = hotBillList.value[0]; curBill.value = hotBillList.value[0];
...@@ -1929,15 +1999,14 @@ onUnmounted(() => {}); ...@@ -1929,15 +1999,14 @@ onUnmounted(() => {});
padding-left: 31px; padding-left: 31px;
.box1-main-left { .box1-main-left {
margin-left: 39px; margin-left: 39px;
// flex: 1;
width: 484px; width: 484px;
.box1-main-left-title { .box1-main-left-title {
height: 22px;
color: rgba(20, 89, 187, 1); color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei; font-family: "Microsoft YaHei";
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
line-height: 22px; line-height: 22px;
margin-bottom: 14px;
} }
.box1-main-left-info { .box1-main-left-info {
margin-top: 17px; margin-top: 17px;
...@@ -2549,6 +2618,7 @@ onUnmounted(() => {}); ...@@ -2549,6 +2618,7 @@ onUnmounted(() => {});
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
position: relative;
.box5-header { .box5-header {
height: 53px; height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
...@@ -2605,6 +2675,12 @@ onUnmounted(() => {}); ...@@ -2605,6 +2675,12 @@ onUnmounted(() => {});
.box5-main { .box5-main {
height: 397px; height: 397px;
} }
.box5-select {
position: absolute;
top: 50px;
left: 100px;
z-index: 100;
}
} }
.box6 { .box6 {
margin-left: 20px; margin-left: 20px;
...@@ -2615,7 +2691,7 @@ onUnmounted(() => {}); ...@@ -2615,7 +2691,7 @@ onUnmounted(() => {});
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
.box6-header { .box6-header {
width: 521px; width: 521px;
height: 48px; height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
...@@ -2665,8 +2741,8 @@ onUnmounted(() => {}); ...@@ -2665,8 +2741,8 @@ onUnmounted(() => {});
} }
} }
.box6-main { .box6-main {
width: 452px; width: 100%;
height: 402px; height: calc(100% - 53px);
} }
} }
} }
...@@ -2816,6 +2892,8 @@ onUnmounted(() => {}); ...@@ -2816,6 +2892,8 @@ onUnmounted(() => {});
} }
.box8-main { .box8-main {
height: 340px; height: 340px;
overflow-y: auto;
overflow-x: hidden;
.box8-main-item { .box8-main-item {
margin: 0 auto; margin: 0 auto;
width: 478px; width: 478px;
...@@ -2833,9 +2911,10 @@ onUnmounted(() => {}); ...@@ -2833,9 +2911,10 @@ onUnmounted(() => {});
position: relative; position: relative;
width: 42px; width: 42px;
height: 42px; height: 42px;
img { .left-img {
width: 100%; width: 42px;
height: 100%; height: 42px;
border-radius: 50%;
} }
.left-icon1 { .left-icon1 {
position: absolute; position: absolute;
...@@ -2892,11 +2971,10 @@ onUnmounted(() => {}); ...@@ -2892,11 +2971,10 @@ onUnmounted(() => {});
position: absolute; position: absolute;
top: 0; top: 0;
right: 10px; right: 10px;
width: 92px;
height: 51px; height: 51px;
text-align: right; text-align: right;
color: rgba(95, 101, 108, 1); color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei; font-family: "Microsoft YaHei";
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 51px; line-height: 51px;
...@@ -3051,7 +3129,7 @@ onUnmounted(() => {}); ...@@ -3051,7 +3129,7 @@ onUnmounted(() => {});
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
.select-box { .select-box {
margin-top: 21px; margin-top: 20px;
.select-box-header { .select-box-header {
display: flex; display: flex;
gap: 17px; gap: 17px;
...@@ -3063,18 +3141,35 @@ onUnmounted(() => {}); ...@@ -3063,18 +3141,35 @@ onUnmounted(() => {});
border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0;
} }
.title { .title {
height: 24px; // height: 24px;
color: var(--color-main-active); color: var(--color-main-active);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 16px; font-size: 20px;
font-weight: 700; font-weight: 700;
line-height: 24px; line-height: 26px;
letter-spacing: 1px; letter-spacing: 1px;
text-align: left; text-align: left;
} }
} }
.select-main { .select-main {
margin-left: 25px; margin-left: 25px;
margin-top: 16px;
.checkbox-group {
display: flex;
flex-wrap: wrap;
.filter-checkbox {
width: 50%;
margin-right: 0;
margin-bottom: 4px;
:deep(.el-checkbox__label) {
color: rgb(95, 101, 108);
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
}
}
}
} }
.select-main1 { .select-main1 {
width: 100px; width: 100px;
......
import * as echarts from 'echarts' import * as echarts from 'echarts'
const getMultiLineChart = (dataX, dataY1, dataY2) => { const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
return { return {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
...@@ -9,25 +9,34 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => { ...@@ -9,25 +9,34 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => {
label: { label: {
backgroundColor: '#6a7985' backgroundColor: '#6a7985'
} }
},
formatter: function (params) {
let res = params[0].name + '<br/>';
params.forEach(item => {
res += item.marker + item.seriesName + ': ' + item.value + (item.seriesName === '通过率' ? '%' : '') + '<br/>';
});
return res;
} }
}, },
grid: { grid: {
top: '8%', top: '15%',
right: '5%', right: '5%',
bottom: '5%', bottom: '5%',
left: '5%', left: '5%',
containLabel: true containLabel: true
}, },
legend: { legend: {
data: ['提出法案', '通过法案'], data: ['提出法案', '通过法案', '通过率'],
show: true, show: true,
top: 0,
icon: 'circle',
textStyle: { textStyle: {
color: 'rgba(95, 101, 108, 1)', color: 'rgba(95, 101, 108, 1)',
fontFamily: 'Microsoft YaHei', fontFamily: 'Microsoft YaHei',
fontSize: '16px', fontSize: '14px',
} }
}, },
color: ['#1459bb', '#fa8c16'], color: ['#1677FF', '#FA8C16', '#D9001B'],
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
...@@ -37,44 +46,80 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => { ...@@ -37,44 +46,80 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => {
], ],
yAxis: [ yAxis: [
{ {
type: 'value' type: 'value',
position: 'left',
axisLabel: {
color: '#666'
}
},
{
type: 'value',
position: 'right',
min: 0,
max: 100,
interval: 20,
axisLabel: {
formatter: '{value}%',
color: '#666'
},
splitLine: {
show: false
}
} }
], ],
series: [ series: [
{ {
name: '提出法案', name: '提出法案',
type: 'line', type: 'line',
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0, offset: 0,
color: 'rgba(22, 119, 255, 1)' // 起始颜色 color: 'rgba(22, 119, 255, 0.4)' // 起始颜色
}, { }, {
offset: 1, offset: 1,
color: 'rgba(22, 119, 255, 0)' // 结束颜色 color: 'rgba(22, 119, 255, 0)' // 结束颜色
}]) }])
}, },
emphasis: { itemStyle: {
focus: 'series' color: '#1677FF'
}, },
data: dataY1 data: dataY1
}, },
{ {
name: '通过法案', name: '通过法案',
type: 'line', type: 'line',
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0, offset: 0,
color: 'rgba(255, 172, 77, 1)' // 起始颜色 color: 'rgba(250, 140, 22, 0.4)' // 起始颜色
}, { }, {
offset: 1, offset: 1,
color: 'rgba(255, 172, 77, 0)' // 结束颜色 color: 'rgba(250, 140, 22, 0)' // 结束颜色
}]) }])
}, },
emphasis: { itemStyle: {
focus: 'series' color: '#FA8C16'
}, },
data: dataY2 data: dataY2
},
{
name: '通过率',
type: 'line',
yAxisIndex: 1,
symbol: 'emptyCircle',
symbolSize: 4,
lineStyle: {
type: 'dashed',
width: 2
},
itemStyle: {
color: '#D9001B'
},
data: dataY3
} }
] ]
} }
......
...@@ -11,6 +11,8 @@ const getWordCloudChart = (data) => { ...@@ -11,6 +11,8 @@ const getWordCloudChart = (data) => {
series: [ series: [
{ {
type: "wordCloud", type: "wordCloud",
width: '80%',
height: '80%',
shape: "rect", // shape: "rect", //
// 其他形状你可以使用形状路径 // 其他形状你可以使用形状路径
// 或者自定义路径 // 或者自定义路径
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
<div class="box2"> <div class="box2">
<div class="box-header"> <div class="box-header">
<div class="header-left"></div> <div class="header-left"></div>
<div class="title">辩论投票时长</div> <div class="title">修正案次数分析</div>
<div class="header-right"> <div class="header-right">
<div class="icon"> <div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" /> <img src="@/assets/icons/box-header-icon1.png" alt="" />
...@@ -320,7 +320,7 @@ ...@@ -320,7 +320,7 @@
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { getBillTimeAnalyze, getBillXj, getBillTotalXj, getBillTp } from "@/api/deepdig"; import { getBillTimeAnalyze, getBillAmeAnalyzeCount, getBillTp } from "@/api/deepdig";
import getBoxPlotChcart from "./utils/boxplot"; import getBoxPlotChcart from "./utils/boxplot";
import * as echarts from "echarts"; import * as echarts from "echarts";
...@@ -563,20 +563,69 @@ const handleGetBillTimeAnalyze = async () => { ...@@ -563,20 +563,69 @@ const handleGetBillTimeAnalyze = async () => {
return item.lcmc; return item.lcmc;
}); });
chartData1.value.dataY = res.data.map(item => { chartData1.value.dataY = res.data.map(item => {
return [item.zdhs, item.zshs, item.pjs, item.pjx, item.zws, item.dysc]; return [item.zshs, item.pjx, item.zws, item.pjs, item.zdhs, item.dysc];
}); });
} catch (error) {} } catch (error) {}
}; };
// 辩论投票时长 // 修正案次数分析
const handleGetBillAmeAnalyzeCount = async () => {
const params = {
id: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillAmeAnalyzeCount(params);
console.log("修正案次数分析", res);
chartData2.value.dataX = res.data.map(item => {
return item.lcmc;
});
chartData2.value.dataY = res.data.map(item => {
// 兼容多种字段名,防止字段不存在导致图表消失
// 顺序:最小(min), Q1(小), 中位数(median), Q3(大), 最大(max), 该法案(current)
return [
item.zsCount || item.zscs || item.zshs || 0,
item.jsx || item.pjx || 0,
item.zws || item.zwcs || 0,
item.pjs || item.pjcs || 0,
item.zdCount || item.zdcs || item.zdhs || 0,
item.dysc || item.dycs || 0
];
});
} catch (error) {
console.error("修正案次数分析 error", error);
}
};
// 获取投票分析
const handleGetBillVoteAnalyze = async () => {
const params = {
id: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillTp(params);
console.log("投票分析", res);
} catch (error) {
console.error("投票分析 error", error);
}
};
onMounted(async () => { onMounted(async () => {
await handleGetBillTimeAnalyze(); await handleGetBillTimeAnalyze();
await handleGetBillAmeAnalyzeCount();
await handleGetBillVoteAnalyze();
let chart1 = getBoxPlotChcart(chartData1.value, "天"); let chart1 = getBoxPlotChcart(chartData1.value, "天");
setChart(chart1, "chart1"); setChart(chart1, "chart1");
let chart2 = getBoxPlotChcart(chartData2.value, "小时"); const countLabels = {
max: '最大次数',
q3: '平均次数大',
median: '次数中位数',
q1: '平均次数小',
min: '最小次数',
current: '该法案修正案数量'
};
let chart2 = getBoxPlotChcart(chartData2.value, "次", countLabels);
setChart(chart2, "chart2"); setChart(chart2, "chart2");
}); });
</script> </script>
......
const getBoxPlotChcart = (data,unit) => { const getBoxPlotChcart = (data, unit, labelConfig = {}) => {
const labels = {
max: labelConfig.max || '最大耗时',
q3: labelConfig.q3 || '平均耗时大',
median: labelConfig.median || '耗时中位数',
q1: labelConfig.q1 || '平均耗时小',
min: labelConfig.min || '最小耗时',
current: labelConfig.current || '该法案耗时'
};
let option = { let option = {
// title: [ // title: [
// { // {
...@@ -11,6 +20,22 @@ const getBoxPlotChcart = (data,unit) => { ...@@ -11,6 +20,22 @@ const getBoxPlotChcart = (data,unit) => {
trigger: 'item', trigger: 'item',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow'
},
formatter: function (params) {
if (params.seriesType === 'scatter') {
return `${params.name}<br/>${labels.current}: ${params.data[1]} ${unit}`;
}
const { name, data } = params;
let tip = `${name}<br/>`;
tip += `${labels.max}: ${data[5]} ${unit}<br/>`;
tip += `${labels.q3}: ${data[4]} ${unit}<br/>`;
tip += `${labels.median}: ${data[3]} ${unit}<br/>`;
tip += `${labels.q1}: ${data[2]} ${unit}<br/>`;
tip += `${labels.min}: ${data[1]} ${unit}<br/>`;
if (data[6] !== undefined && data[6] !== null) {
tip += `${labels.current}: ${data[6]} ${unit}`;
}
return tip;
} }
}, },
grid: { grid: {
...@@ -48,13 +73,20 @@ const getBoxPlotChcart = (data,unit) => { ...@@ -48,13 +73,20 @@ const getBoxPlotChcart = (data,unit) => {
{ {
name: 'boxplot', name: 'boxplot',
type: 'boxplot', type: 'boxplot',
datasetIndex: 1,
data: data.dataY, data: data.dataY,
}, },
{ {
name: 'outlier', name: labels.current,
type: 'scatter', type: 'scatter',
datasetIndex: 2 data: data.dataY.map((item, index) => [index, item[5]]),
itemStyle: {
color: '#ff5722'
},
tooltip: {
formatter: function (params) {
return `${params.name}<br/>${labels.current}: ${params.data[1]} ${unit}`;
}
}
} }
] ]
} }
......
<template> <template>
<div class="container"> <div class="container">
<div class="svg-timeline"> <div class="svg-timeline">
<svg :width="svgWidth" :height="svgHeight"> <svg :viewBox="`0 0 ${svgWidth} ${svgHeight}`" width="100%" height="100%">
<!-- <line <!-- <line
:x1="lines[0].x1 - 100" :x1="lines[0].x1 - 100"
:y1="lines[0].y1" :y1="lines[0].y1"
...@@ -31,10 +31,11 @@ ...@@ -31,10 +31,11 @@
stroke-width="2" stroke-width="2"
/> --> /> -->
<line <line
:x1="lines[0].x1 - 100" v-if="nodes.length > 0"
:y1="lines[0].y1" :x1="nodes[0].x - 100"
:x2="lines[0].x1" :y1="nodes[0].y"
:y2="lines[0].y1" :x2="nodes[0].x"
:y2="nodes[0].y"
stroke="#e8f2ff" stroke="#e8f2ff"
stroke-width="2" stroke-width="2"
marker-end="url(#arrow)" marker-end="url(#arrow)"
...@@ -68,51 +69,60 @@ ...@@ -68,51 +69,60 @@
v-for="(node, idx) in nodes.slice(0, nodes.length)" v-for="(node, idx) in nodes.slice(0, nodes.length)"
:key="'line' + idx" :key="'line' + idx"
:x1="node.x" :x1="node.x"
:y1="node.y + 2" :y1="node.y + 4"
:x2="node.x" :x2="node.x"
:y2="node.dyms ? node.y + 80 : node.y + 50" :y2="node.y + verticalLineLength"
stroke="#1677ff" stroke="#1677ff"
stroke-width="2" :stroke-width="verticalLineWidth"
/> />
<text <foreignObject
v-for="(node, idx) in nodes" v-for="(node, idx) in nodes"
:key="'actionDate' + idx" :key="'fo-' + idx"
:x="node.x + 10" :x="node.x + 15"
:y="node.y + 30" :y="node.y + 5"
text-anchor="start" :width="nodeGapX - 30"
fill="#1677ff" height="100"
font-size="14" style="overflow: visible;"
font-weight="600"
> >
{{ node.actionDate }} <div class="node-content" xmlns="http://www.w3.org/1999/xhtml">
</text> <div class="date">{{ node.formattedDate }}</div>
<div class="title">{{ node.actionTitle }}</div>
<div class="votes" v-if="node.voteString">{{ node.voteString }}</div>
</div>
</foreignObject>
<text <text
v-for="(node, idx) in nodes" v-if="startMonth && nodes.length > 0"
:key="'actionTitle' + idx" :x="nodes[0].x - 110"
:x="node.x + 10" :y="nodes[0].y + 5"
:y="node.y + 50" text-anchor="end"
text-anchor="start" fill="rgb(5, 95, 194)"
fill="#3b414b" font-size="16"
font-size="14" font-weight="700"
textLength="170"
lengthAdjust="spacing"
font-weight="bold"
> >
{{ node.actionTitle.slice(0,24) }} {{ startMonth }}
</text> </text>
<line
v-if="nodes.length > 0"
:x1="nodes[nodes.length - 1].x"
:y1="nodes[nodes.length - 1].y"
:x2="nodes[nodes.length - 1].row % 2 === 0 ? nodes[nodes.length - 1].x + 100 : nodes[nodes.length - 1].x - 100"
:y2="nodes[nodes.length - 1].y"
stroke="#e8f2ff"
stroke-width="2"
marker-end="url(#arrow)"
/>
<text <text
v-for="(node, idx) in nodes" v-if="endMonth && nodes.length > 0"
:key="'actionTitle' + idx" :x="nodes[nodes.length - 1].row % 2 === 0 ? nodes[nodes.length - 1].x + 110 : nodes[nodes.length - 1].x - 110"
:x="node.x + 10" :y="nodes[nodes.length - 1].y + 5"
:y="node.y + 70" :text-anchor="nodes[nodes.length - 1].row % 2 === 0 ? 'start' : 'end'"
text-anchor="start" fill="rgb(5, 95, 194)"
fill="#84888e" font-size="16"
font-size="14" font-weight="700"
> >
{{ node.dyms ? node.dyms : "" }} {{ endMonth }}
</text> </text>
</svg> </svg>
</div> </div>
...@@ -134,26 +144,55 @@ export default { ...@@ -134,26 +144,55 @@ export default {
// ], // ],
maxPerRow: 5, maxPerRow: 5,
nodeGapX: 200, nodeGapX: 200,
nodeGapY: 100 nodeGapY: 180,
leftMargin: 150,
verticalLineLength: 80,
verticalLineWidth: 1
}; };
}, },
computed: { computed: {
startMonth() {
if (this.sortedDataList.length === 0) return '';
const date = new Date(this.sortedDataList[0].actionDate);
return `${date.getFullYear()}${date.getMonth() + 1}月`;
},
endMonth() {
if (this.sortedDataList.length === 0) return '';
const date = new Date(this.sortedDataList[this.sortedDataList.length - 1].actionDate);
return `${date.getFullYear()}${date.getMonth() + 1}月`;
},
sortedDataList() {
if (!this.dataList) return [];
// Clone and sort by date ascending (Old -> New)
return [...this.dataList].sort((a, b) => new Date(a.actionDate) - new Date(b.actionDate));
},
nodes() { nodes() {
// 计算每个节点的坐标(蛇形) // 计算每个节点的坐标(蛇形)
return this.dataList.map((item, idx) => { return this.sortedDataList.map((item, idx) => {
const row = Math.floor(idx / this.maxPerRow); const row = Math.floor(idx / this.maxPerRow);
const col = idx % this.maxPerRow; const col = idx % this.maxPerRow;
let x, y; let x, y;
const leftMargin = 10; // 你可以自定义这个值 // const leftMargin = 150; // 你可以自定义这个值
if (row % 2 === 0) { if (row % 2 === 0) {
x = leftMargin + col * this.nodeGapX + 50; x = this.leftMargin + col * this.nodeGapX + 50;
} else { } else {
x = leftMargin + (this.maxPerRow - 1 - col) * this.nodeGapX + 50; x = this.leftMargin + (this.maxPerRow - 1 - col) * this.nodeGapX + 50;
} }
// 节点纵坐标起始值 // 节点纵坐标起始值
y = 60 + row * this.nodeGapY; y = 60 + row * this.nodeGapY;
return { ...item, x, y, row };
// Format Date: 2025-07-04 -> 7月4日
const dateObj = new Date(item.actionDate);
const formattedDate = `${dateObj.getMonth() + 1}${dateObj.getDate()}日`;
// Format Votes
let voteString = '';
if (item.agreeVote !== null && item.disagreeVote !== null) {
voteString = `${item.agreeVote}票赞成 : ${item.disagreeVote}票反对`;
}
return { ...item, x, y, row, formattedDate, voteString };
}); });
}, },
lines() { lines() {
...@@ -194,7 +233,7 @@ export default { ...@@ -194,7 +233,7 @@ export default {
console.log("prev", prev); console.log("prev", prev);
// 判断是否是行尾转折点 // 判断是否是行尾转折点
const isTurnPoint = i % 5 === 0; const isTurnPoint = i % this.maxPerRow === 0;
if (isTurnPoint) { if (isTurnPoint) {
// 计算半圆路径 // 计算半圆路径
...@@ -221,7 +260,7 @@ export default { ...@@ -221,7 +260,7 @@ export default {
return path; return path;
}, },
svgWidth() { svgWidth() {
return this.maxPerRow * this.nodeGapX + 100; return this.leftMargin + this.maxPerRow * this.nodeGapX + 50;
}, },
svgHeight() { svgHeight() {
// SVG高度 // SVG高度
...@@ -240,7 +279,7 @@ export default { ...@@ -240,7 +279,7 @@ export default {
align-items: center; align-items: center;
} }
.svg-timeline { .svg-timeline {
width: 1000px; width: 100%;
// background-size: 100% 100%; // background-size: 100% 100%;
// position: relative; // position: relative;
// .title { // .title {
...@@ -264,5 +303,40 @@ export default { ...@@ -264,5 +303,40 @@ export default {
// height: 24px; // height: 24px;
// } // }
// } // }
.node-content {
font-family: Microsoft YaHei, sans-serif;
text-align: left;
padding-left: 4px;
.date {
color: rgb(5, 95, 194);
font-weight: 700;
font-size: 14px;
line-height: 22px;
margin-bottom: 0px;
margin-top: 6px;
}
.title {
color: rgb(59, 65, 75);
font-weight: 700;
font-size: 14px;
line-height: 22px;
margin-bottom: 0px;
white-space: nowrap; /* Keep text on one line */
overflow: hidden; /* Hide overflow */
text-overflow: ellipsis; /* Show ... for overflow */
width: 100%; /* Ensure it takes full width of container */
display: block; /* Block level for ellipsis to work */
}
.votes {
color: rgb(95, 101, 108);
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
} }
</style> </style>
...@@ -31,9 +31,10 @@ ...@@ -31,9 +31,10 @@
<div class="box1-right-item"> <div class="box1-right-item">
<div class="item-left">相关领域:</div> <div class="item-left">相关领域:</div>
<div class="item-right1"> <div class="item-right1">
<div class="right1-item">跨境电商</div> <!-- <div class="right1-item">跨境电商</div>
<div class="right1-item">新能源产业</div> <div class="right1-item">新能源产业</div>
<div class="right1-item">半导体产业</div> <div class="right1-item">半导体产业</div> -->
<div class="right1-item" v-for="item in basicInfo.hylyList" :key="item">{{ item }}</div>
</div> </div>
</div> </div>
<div class="box1-right-item"> <div class="box1-right-item">
...@@ -81,7 +82,7 @@ ...@@ -81,7 +82,7 @@
<div class="box-header"> <div class="box-header">
<div class="header-left"></div> <div class="header-left"></div>
<div class="title">法案进展</div> <div class="title">法案进展</div>
<div class="header-btn-box"> <!-- <div class="header-btn-box">
<div class="btn" @click="handleClcikBox2Btn(1)"> <div class="btn" @click="handleClcikBox2Btn(1)">
<el-badge :value="warningNum"> <el-badge :value="warningNum">
<el-button type="primary" plain v-if="box2BtnActive === 1">最新进展</el-button> <el-button type="primary" plain v-if="box2BtnActive === 1">最新进展</el-button>
...@@ -92,7 +93,7 @@ ...@@ -92,7 +93,7 @@
<el-button type="primary" plain v-if="box2BtnActive === 2">前期进展</el-button> <el-button type="primary" plain v-if="box2BtnActive === 2">前期进展</el-button>
<el-button type="info" plain v-else>前期进展</el-button> <el-button type="info" plain v-else>前期进展</el-button>
</div> </div>
</div> </div> -->
<div class="header-right"> <div class="header-right">
<div class="icon"> <div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" /> <img src="@/assets/icons/box-header-icon2.png" alt="" />
...@@ -104,8 +105,8 @@ ...@@ -104,8 +105,8 @@
</div> </div>
<div class="box2-main"> <div class="box2-main">
<div class="box2-main-center"> <div class="box2-main-center">
<STimeline v-if="box2BtnActive == 2" :dataList="timelineData" /> <STimeline :dataList="timelineData" />
<div class="box2-center-item-box" v-if="box2BtnActive == 1"> <!-- <div class="box2-center-item-box" v-if="box2BtnActive == 1">
<div class="box2-center-item" v-for="(item, index) in progressList" :key="index"> <div class="box2-center-item" v-for="(item, index) in progressList" :key="index">
<div class="tip" :class="{ tipActive: item.fxdj }"></div> <div class="tip" :class="{ tipActive: item.fxdj }"></div>
<div class="date">{{ item.actionDate }}</div> <div class="date">{{ item.actionDate }}</div>
...@@ -128,7 +129,7 @@ ...@@ -128,7 +129,7 @@
<el-icon size="22" color="#777"><ArrowRightBold /></el-icon> <el-icon size="22" color="#777"><ArrowRightBold /></el-icon>
</div> </div>
</div> </div>
</div> </div> -->
</div> </div>
</div> </div>
<div class="box2-footer"> <div class="box2-footer">
...@@ -210,15 +211,15 @@ ...@@ -210,15 +211,15 @@
<div <div
class="tag-box" class="tag-box"
:class="{ :class="{
status0: tag.status === 0, status0: index === 0 || index === 4,
status1: tag.status === 1, status1: index === 1 || index === 5,
status2: tag.status === 2, status2: index === 2 || index === 6,
status3: tag.status === 3 status3: index === 3 || index === 7
}" }"
v-for="(tag, index) in tagList" v-for="(tag, index) in curPerson.tagList"
:key="index" :key="index"
> >
{{ tag.title }} {{ tag }}
</div> </div>
</div> </div>
<div class="right-main-box3"> <div class="right-main-box3">
...@@ -231,13 +232,13 @@ ...@@ -231,13 +232,13 @@
<div class="right-main-box3-main"> <div class="right-main-box3-main">
<el-timeline style="max-width: 500px"> <el-timeline style="max-width: 500px">
<el-timeline-item <el-timeline-item
:timestamp="item.sjsj" :timestamp="item.newsDate"
placement="top" placement="top"
v-for="(item, index) in personEventList" v-for="(item, index) in curPerson.newsList"
:key="index" :key="index"
> >
<div class="timeline-content"> <div class="timeline-content">
{{ item.sjnr }} {{ item.newsContent }}
</div> </div>
</el-timeline-item> </el-timeline-item>
<!-- <el-timeline-item timestamp="2018/4/3" placement="top"> <!-- <el-timeline-item timestamp="2018/4/3" placement="top">
...@@ -324,14 +325,14 @@ ...@@ -324,14 +325,14 @@
<div class="inner-right-main"> <div class="inner-right-main">
<el-timeline style="max-width: 840px"> <el-timeline style="max-width: 840px">
<el-timeline-item <el-timeline-item
:timestamp="item.sjsj" :timestamp="item.newsDate"
placement="top" placement="top"
v-for="(item, index) in personEventList" v-for="(item, index) in curPerson.newsList"
:key="index" :key="index"
> >
<div class="timeline-content1"> <div class="timeline-content1">
<div class="text"> <div class="text">
{{ item.sjnr }} {{ item.newsContent }}
</div> </div>
<div class="pic"> <div class="pic">
<img src="./assets/imgs/img1.png" alt="" /> <img src="./assets/imgs/img1.png" alt="" />
...@@ -544,6 +545,7 @@ const handleGetBasicInfo = async () => { ...@@ -544,6 +545,7 @@ const handleGetBasicInfo = async () => {
const res = await getBillInfo(params); const res = await getBillInfo(params);
console.log("基本信息", res); console.log("基本信息", res);
basicInfo.value = res.data basicInfo.value = res.data
basicInfo.value.stageList.reverse()
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
...@@ -552,24 +554,24 @@ const handleGetBasicInfo = async () => { ...@@ -552,24 +554,24 @@ const handleGetBasicInfo = async () => {
const warningNum = ref(0); const warningNum = ref(0);
// 法案进展 获取最新进展 // 法案进展 获取最新进展
const handleGetBillEvent = async () => { // const handleGetBillEvent = async () => {
warningNum.value = 0; // warningNum.value = 0;
const params = { // const params = {
id: window.sessionStorage.getItem("billId") // id: window.sessionStorage.getItem("billId")
}; // };
try { // try {
const res = await getBillEvent(params); // const res = await getBillEvent(params);
console.log("最新进展", res); // console.log("最新进展", res);
progressList.value = res.data; // progressList.value = res.data;
progressList.value.forEach(item => { // progressList.value.forEach(item => {
if (item.fxdj) { // if (item.fxdj) {
warningNum.value++; // warningNum.value++;
} // }
}); // });
} catch (error) { // } catch (error) {
console.error(error); // console.error(error);
} // }
}; // };
// 法案进展 获取前期进展 --也是提出人左上角列表 // 法案进展 获取前期进展 --也是提出人左上角列表
const handleGetBillDyqk = async () => { const handleGetBillDyqk = async () => {
...@@ -601,9 +603,26 @@ const curPerson = ref({}); ...@@ -601,9 +603,26 @@ const curPerson = ref({});
const personEventList = ref([]); const personEventList = ref([]);
// 提出人 --动议id // 提出人 --动议id
const handleGetBillPerson = async id => { // const handleGetBillPerson = async id => {
// const params = {
// id: id
// };
// try {
// const res = await getBillPerson(params);
// console.log("提出人", res);
// personList.value = res.data;
// box3BtnActive.value = res.data.length ? res.data[0].name : "";
// curPerson.value = res.data.length ? res.data[0] : {};
// personEventList.value = res.data.length ? res.data[0].eventList : [];
// } catch (error) {
// console.error(error);
// }
// };
// 法案提出人
const handleGetBillPerson = async () => {
const params = { const params = {
id: id billId: window.sessionStorage.getItem("billId")
}; };
try { try {
const res = await getBillPerson(params); const res = await getBillPerson(params);
...@@ -619,8 +638,9 @@ const handleGetBillPerson = async id => { ...@@ -619,8 +638,9 @@ const handleGetBillPerson = async id => {
onMounted(() => { onMounted(() => {
handleGetBasicInfo(); handleGetBasicInfo();
handleGetBillEvent(); // handleGetBillEvent();
handleGetBillDyqk(); handleGetBillDyqk();
handleGetBillPerson();
}); });
</script> </script>
...@@ -695,11 +715,11 @@ onMounted(() => { ...@@ -695,11 +715,11 @@ onMounted(() => {
.box1-right { .box1-right {
margin-left: 31px; margin-left: 31px;
margin-top: 5px; margin-top: 5px;
width: 623px; // width: 623px;
height: 350px; height: 350px;
.box1-right-item { .box1-right-item {
display: flex; display: flex;
margin-bottom: 24px; margin-bottom: 21px;
.item-left { .item-left {
width: 100px; width: 100px;
height: 14px; height: 14px;
...@@ -722,7 +742,23 @@ onMounted(() => { ...@@ -722,7 +742,23 @@ onMounted(() => {
} }
.item-right1 { .item-right1 {
display: flex; display: flex;
align-items: center;
width: 700px;
height: 40px;
overflow-x: auto;
overflow-y: hidden;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #e1e1e1;
}
&::-webkit-scrollbar-track {
background: transparent;
}
.right1-item { .right1-item {
flex-shrink: 0;
margin-right: 10px; margin-right: 10px;
padding: 1px 8px; padding: 1px 8px;
box-sizing: border-box; box-sizing: border-box;
...@@ -774,10 +810,14 @@ onMounted(() => { ...@@ -774,10 +810,14 @@ onMounted(() => {
text-align: center; text-align: center;
position: relative; position: relative;
.step-box { .step-box {
padding: 0 10px 0 20px; padding: 4px 10px;
color: #333; color: #333;
position: relative; position: relative;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 14px;
margin-left: 10px;
.right-arrow { .right-arrow {
position: absolute; position: absolute;
right: -21px; right: -21px;
...@@ -792,7 +832,7 @@ onMounted(() => { ...@@ -792,7 +832,7 @@ onMounted(() => {
} }
} }
.step-box-active { .step-box-active {
padding: 0 12px; padding: 4 10px;
color: #fff; color: #fff;
background: #ce4f51; background: #ce4f51;
position: relative; position: relative;
...@@ -851,11 +891,13 @@ onMounted(() => { ...@@ -851,11 +891,13 @@ onMounted(() => {
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
.box2-main { .box2-main {
margin-top: 10px; margin-top: 10px;
height: calc(100% - 70px); // Subtract header height
width: 100%;
.box2-main-center { .box2-main-center {
margin-left: 23px; margin-left: 23px;
border-top: 1px solid rgba(243, 243, 244, 1); border-top: 1px solid rgba(243, 243, 244, 1);
// width: 100%; width: calc(100% - 46px); // Subtract margin
height: 300px; height: 100%;
// background: orange; // background: orange;
.box2-center-item { .box2-center-item {
display: flex; display: flex;
...@@ -1082,10 +1124,10 @@ onMounted(() => { ...@@ -1082,10 +1124,10 @@ onMounted(() => {
} }
} }
.right-main-box2 { .right-main-box2 {
width: 576px; // width: 576px;
height: 150px; // height: 150px;
box-sizing: border-box; box-sizing: border-box;
padding: 10px 26px; padding: 22px 26px;
// border-bottom: 1px solid rgb(243, 243, 244); // border-bottom: 1px solid rgb(243, 243, 244);
// display: flex; // display: flex;
// flex-wrap: wrap; // flex-wrap: wrap;
...@@ -1140,7 +1182,7 @@ onMounted(() => { ...@@ -1140,7 +1182,7 @@ onMounted(() => {
} }
.right-main-box3-main { .right-main-box3-main {
margin-top: 18px; margin-top: 18px;
height: 299px; height: 412px;
overflow-y: auto; overflow-y: auto;
} }
.right-main-box3-footer { .right-main-box3-footer {
...@@ -1330,6 +1372,10 @@ onMounted(() => { ...@@ -1330,6 +1372,10 @@ onMounted(() => {
color: var(--btn-active-text-color); color: var(--btn-active-text-color);
} }
} }
.inner-right-main {
height: 860px;
overflow: auto;
}
} }
} }
} }
......
...@@ -16,9 +16,15 @@ ...@@ -16,9 +16,15 @@
</div> </div>
<div class="left-top"> <div class="left-top">
<el-select v-model="curBill" placeholder="请选择" style="width: 240px" @change="handleChangeBill"> <el-select v-model="curBill" placeholder="请选择" style="width: 240px" @change="handleChangeBill">
<el-option v-for="item in billList" :key="item.value" :label="item.label" :value="item.id" /> <el-option v-for="item in billList" :key="item.id" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-checkbox style="margin-left: 30px" v-model="checkedValue" label="只看涉华条款" size="large" /> <el-checkbox
style="margin-left: 30px"
v-model="checkedValue"
label="只看涉华条款"
size="large"
@change="handleChangeCheckbox"
/>
<div class="search" style="width: 240px; margin-left: 475px"> <div class="search" style="width: 240px; margin-left: 475px">
<el-input v-model="searchValue" style="width: 240px" placeholder="搜索条款" /> <el-input v-model="searchValue" style="width: 240px" placeholder="搜索条款" />
<div class="icon"> <div class="icon">
...@@ -28,32 +34,32 @@ ...@@ -28,32 +34,32 @@
</div> </div>
<div class="left-main"> <div class="left-main">
<div class="left-main-item" v-for="(term, index) in mainTermsList" :key="index"> <div class="left-main-item" v-for="(term, index) in mainTermsList" :key="index">
<div class="id">{{ index + 1 }}</div> <div class="id">{{ (currentPage - 1) * pageSize + index + 1 }}</div>
<div class="info"> <div class="info">
<div class="title"> <div class="title">
<span class="title-active">{{ term.header }}</span> <span class="title-active">{{ term.tkxh }}条.</span>
{{ term.title }} {{ term.fynr }}
</div> </div>
<div class="content"> <div class="content">
<span class="content-active">{{ term.headerEn }}</span> <span class="content-active">Sec.{{ term.tkxh }}</span>
{{ term.content }} {{ term.ywnr }}
</div> </div>
</div> </div>
<div class="tags-box"> <div class="tags-box">
<div <div
class="tag" class="tag"
v-for="(val, idx) in term.tags" v-for="(val, idx) in (term.hylyList || []).slice(0, 2)"
:key="idx" :key="idx"
:class="{ :class="{
tag1: val.status === 1, 'tag1': val === '人工智能',
tag2: val.status === 2, 'tag2': val === '新一代信息技术' || !['人工智能', '政治', '经济', '军事', '科技'].includes(val),
tag3: val.status === 3, 'tag3': val === '政治',
tag4: val.status === 4, 'tag4': val === '经济',
tag5: val.status === 5, 'tag5': val === '军事',
tag6: val.status === 6 'tag6': val === '科技'
}" }"
> >
{{ val.name }} {{ val }}
</div> </div>
</div> </div>
<div class="open"> <div class="open">
...@@ -63,10 +69,17 @@ ...@@ -63,10 +69,17 @@
</div> </div>
<div class="left-footer"> <div class="left-footer">
<div class="left-footer-text"> <div class="left-footer-text">
{{ "共96条涉华条款" }} {{ `共${total}条${checkedValue ? "涉华" : ""}条款` }}
</div> </div>
<div class="left-footer-right"> <div class="left-footer-right">
<el-pagination background layout="prev, pager, next" :total="96" /> <el-pagination
background
layout="prev, pager, next"
:total="total"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
@current-change="handleCurrentChange"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -137,16 +150,12 @@ import { getBillContentId, getBillContentTk, getBillContentXzfs, getBillHyly } f ...@@ -137,16 +150,12 @@ import { getBillContentId, getBillContentTk, getBillContentXzfs, getBillHyly } f
const curBill = ref(""); const curBill = ref("");
const curBillId = ref(null); const curBillId = ref(null);
const billList = ref([ const checkedValue = ref(false);
{ const searchValue = ref("");
value: "公法(2025年7月4日)", const billList = ref([]);
label: "公法(2025年7月4日)" const currentPage = ref(1);
}, const pageSize = ref(10);
{ const total = ref(0);
value: "公法(2025年7月8日)",
label: "公法(2025年7月8日)"
}
]);
const mainTermsList = ref([ const mainTermsList = ref([
{ {
...@@ -351,7 +360,13 @@ const setChart = (option, chartId) => { ...@@ -351,7 +360,13 @@ const setChart = (option, chartId) => {
// 切换原文 // 切换原文
const handleChangeBill = val => { const handleChangeBill = val => {
curBillId.value = val; curBill.value = val;
const item = billList.value.find(item => item.value === val);
if (item) {
curBillId.value = item.id;
currentPage.value = 1;
handleGetBillContentTk(checkedValue.value ? "Y" : "N");
}
}; };
// 获取法案id列表 // 获取法案id列表
...@@ -369,22 +384,69 @@ const handleGetBillList = async () => { ...@@ -369,22 +384,69 @@ const handleGetBillList = async () => {
id: item.ywid id: item.ywid
}; };
}); });
curBill.value = billList.value[0].label; if (billList.value.length > 0) {
curBill.value = billList.value[0].value;
curBillId.value = billList.value[0].id; curBillId.value = billList.value[0].id;
}
} catch (error) {} } catch (error) {}
}; };
const handleChangeCheckbox = val => {
currentPage.value = 1;
handleGetBillContentTk(val ? "Y" : "N");
};
const handleCurrentChange = val => {
currentPage.value = val;
handleGetBillContentTk(checkedValue.value ? "Y" : "N");
};
// 根据原文ID获取条款列表 // 根据原文ID获取条款列表
const handleGetBillContentTk = async cRelated => { const handleGetBillContentTk = async cRelated => {
const params = { const params = {
id: curBillId.value, billid: window.sessionStorage.getItem("billId"),
id: curBill.value,
cRelated: cRelated, cRelated: cRelated,
currentPage: 0, currentPage: currentPage.value - 1,
pageSize: 10 pageSize: pageSize.value
}; };
try { try {
const res = await getBillContentTk(params); const res = await getBillContentTk(params);
console.log("条款内容", res); console.log("条款内容", res);
mainTermsList.value = res.data.content.map(item => {
// 处理 fynr
if (item.fynr) {
const matchComplex = item.fynr.match(/^(?:正文内容[::]\s*)?[“"]?\s*第\s*([0-9a-zA-Z]+)\s*[条节][::\.\。]?[”"]?\s*/);
if (matchComplex) {
// 匹配 "第xxx条"、"正文内容:第xxx条"、"“第xxx条" 等
if (!item.tkxh) {
item.tkxh = matchComplex[1];
}
item.fynr = item.fynr.replace(matchComplex[0], "");
} else {
// 匹配 "xxx."
item.fynr = item.fynr.replace(/^\d+\.\s*/, "");
}
}
// 处理 ywnr
if (item.ywnr) {
const matchSec = item.ywnr.match(/^(?:SEC\.|SECTION)\s*([0-9a-zA-Z]+)[\.:]?\s*/i);
if (matchSec) {
if (!item.tkxh) {
item.tkxh = matchSec[1];
}
item.ywnr = item.ywnr.replace(matchSec[0], "");
} else {
item.ywnr = item.ywnr.replace(/^\d+\.\s*/, "");
}
}
// 处理 tkxh 末尾的点
if (item.tkxh) {
item.tkxh = item.tkxh.replace(/\.$/, "");
}
return item;
});
total.value = res.data.totalElements;
} catch (error) {} } catch (error) {}
}; };
...@@ -430,7 +492,7 @@ const handleGetBillHyly = async () => { ...@@ -430,7 +492,7 @@ const handleGetBillHyly = async () => {
onMounted(async () => { onMounted(async () => {
await handleGetBillList(); await handleGetBillList();
handleGetBillContentTk(false); handleGetBillContentTk("N");
await handleGetBillContentXzfs(); await handleGetBillContentXzfs();
await handleGetBillHyly(); await handleGetBillHyly();
let chart1 = getPieChart(chart1Data.value, chart1ColorList.value); let chart1 = getPieChart(chart1Data.value, chart1ColorList.value);
...@@ -528,6 +590,7 @@ onMounted(async () => { ...@@ -528,6 +590,7 @@ onMounted(async () => {
border-radius: 2px; border-radius: 2px;
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
display: flex; display: flex;
position: relative;
.id { .id {
margin-top: 20px; margin-top: 20px;
margin-left: 15px; margin-left: 15px;
...@@ -543,7 +606,7 @@ onMounted(async () => { ...@@ -543,7 +606,7 @@ onMounted(async () => {
.info { .info {
margin-left: 13px; margin-left: 13px;
margin-top: 15px; margin-top: 15px;
width: 813px; width: 780px;
.title { .title {
height: 14px; height: 14px;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
...@@ -577,20 +640,19 @@ onMounted(async () => { ...@@ -577,20 +640,19 @@ onMounted(async () => {
} }
} }
.tags-box { .tags-box {
margin-left: 20px;
margin-top: 21px;
width: 160px;
height: 22px;
display: flex; display: flex;
justify-content: flex-end; justify-content: right;
align-items: center;
flex: 1;
margin-right: 50px;
.tag { .tag {
height: 18px; text-align: right;
line-height: 18px; line-height: 18px;
padding: 0 8px; padding: 1px 8px;
border-radius: 4px; border-radius: 4px;
margin-left: 5px; margin-left: 5px;
font-size: 12px; font-size: 12px;
font-family: Microsoft YaHei; font-family: "Microsoft YaHei";
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
} }
...@@ -626,8 +688,9 @@ onMounted(async () => { ...@@ -626,8 +688,9 @@ onMounted(async () => {
} }
} }
.open { .open {
margin-left: 10px; position: absolute;
margin-top: 22px; top: 22px;
right: 23px;
width: 20px; width: 20px;
height: 20px; height: 20px;
img { img {
......
...@@ -4,6 +4,7 @@ const getPieChart = (data,colorList) => { ...@@ -4,6 +4,7 @@ const getPieChart = (data,colorList) => {
series: [ series: [
{ {
type: 'pie', type: 'pie',
minAngle: 28,
radius: [70, 100], radius: [70, 100],
height: '100%', height: '100%',
left: 'center', left: 'center',
...@@ -38,7 +39,9 @@ const getPieChart = (data,colorList) => { ...@@ -38,7 +39,9 @@ const getPieChart = (data,colorList) => {
? params.labelRect.x ? params.labelRect.x
: params.labelRect.x + params.labelRect.width; : params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points,
hideOverlap: false,
moveOverlap: 'shiftY'
}; };
}, },
data: data data: data
......
<template> <template>
<div class="fishbone"> <div class="fishbone">
<div class="main-line"></div> <div class="main-line"></div>
<div class="top-bone"> <div v-for="(causeGroup, groupIndex) in fishboneData.causes" :key="groupIndex" :class="getBoneClass(groupIndex)">
<div class="left-bone"> <div class="left-bone">
<div class="left-bone-item"> <div class="left-bone-item" v-for="(item, index) in getLeftItems(causeGroup.causes)" :key="index">
<div class="icon"> <!-- <div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" /> <img :src="item.picture" alt="" />
</div> </div> -->
<div class="text">{{ "商汤科技" }}</div> <div class="text">{{ item.name }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="top-bone1">
<div class="left-bone">
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="top-bone2">
<div class="left-bone">
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div> <div class="line"></div>
</div> </div>
</div> </div>
<div class="right-bone"> <div class="right-bone">
<div class="right-bone-item"> <div class="right-bone-item" v-for="(item, index) in getRightItems(causeGroup.causes)" :key="index">
<div class="line"></div> <div class="line"></div>
<div class="text">{{ "华为" }}</div> <div class="text">{{ item.name }}</div>
<div class="icon"> <!-- <div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" /> <img :src="item.picture" :alt="item.name" />
</div> -->
</div> </div>
</div> </div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div> </div>
</div> </div>
<div class="right-bone-item"> </template>
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="bottom-bone">
<div class="left-bone">
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div> <script setup>
<div class="line"></div> import { getChainFishbone } from "@/api/exportControl";
</div> import { onMounted, ref } from "vue";
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="bottom-bone1">
<div class="left-bone">
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div> const chainId = ref(1);
<div class="line"></div> const fishboneData = ref({
</div> text: "",
<div class="left-bone-item"> causes: []
<div class="icon"> });
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div> // 根据索引确定鱼骨图位置类名
<div class="line"></div> const getBoneClass = index => {
</div> const positions = ["top-bone", "top-bone1", "top-bone2", "bottom-bone", "bottom-bone1", "bottom-bone2"];
<div class="left-bone-item"> return positions[index] || "top-bone";
<div class="icon"> };
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
<div class="bottom-bone2">
<div class="left-bone">
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div> // 获取左侧显示的项目(前半部分)
<div class="line"></div> const getLeftItems = items => {
</div> const midpoint = Math.ceil(items.length / 2);
<div class="left-bone-item"> return items.slice(0, midpoint);
<div class="icon"> };
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div> // 获取右侧显示的项目(后半部分)
<div class="line"></div> const getRightItems = items => {
</div> const midpoint = Math.ceil(items.length / 2);
<div class="left-bone-item"> return items.slice(midpoint);
<div class="icon"> };
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
<div class="left-bone-item">
<div class="icon">
<img src="../../assets/images/company-logo1.png" alt="" />
</div>
<div class="text">{{ "商汤科技" }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
<div class="right-bone-item">
<div class="line"></div>
<div class="text">{{ "华为" }}</div>
<div class="icon">
<img src="../../assets/images/company-logo2.png" alt="" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { getChainFishbone } from "@/api/exportControl";
import { onMounted, ref } from "vue";
const chainId = ref("");
const chainFishbone = ref([]);
onMounted(async () => { onMounted(async () => {
try { try {
const chainFishboneData = await getChainFishbone(chainId.value); const chainFishboneData = await getChainFishbone(chainId.value);
chainFishbone.value = chainFishboneData ?? []; fishboneData.value = chainFishboneData ?? {
text: "",
causes: []
};
console.log("鱼骨图数据:", fishboneData.value);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
...@@ -487,11 +67,14 @@ onMounted(async () => { ...@@ -487,11 +67,14 @@ onMounted(async () => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* ... 原有的样式保持不变 ... */
.fishbone { .fishbone {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-top: 40px; margin-top: 40px;
overflow-x: auto;
.main-line { .main-line {
position: absolute; position: absolute;
top: 280px; top: 280px;
...@@ -501,6 +84,7 @@ onMounted(async () => { ...@@ -501,6 +84,7 @@ onMounted(async () => {
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
} }
.top-bone { .top-bone {
position: absolute; position: absolute;
top: 20px; top: 20px;
...@@ -517,13 +101,11 @@ onMounted(async () => { ...@@ -517,13 +101,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -537,8 +119,11 @@ onMounted(async () => { ...@@ -537,8 +119,11 @@ onMounted(async () => {
} }
.text { .text {
margin-left: 4px; margin-left: 4px;
height: 35px; height: 70px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -556,7 +141,6 @@ onMounted(async () => { ...@@ -556,7 +141,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
...@@ -564,7 +148,6 @@ onMounted(async () => { ...@@ -564,7 +148,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -572,11 +155,14 @@ onMounted(async () => { ...@@ -572,11 +155,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
...@@ -590,6 +176,7 @@ onMounted(async () => { ...@@ -590,6 +176,7 @@ onMounted(async () => {
} }
} }
} }
.top-bone1 { .top-bone1 {
position: absolute; position: absolute;
top: 20px; top: 20px;
...@@ -606,13 +193,11 @@ onMounted(async () => { ...@@ -606,13 +193,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -625,9 +210,13 @@ onMounted(async () => { ...@@ -625,9 +210,13 @@ onMounted(async () => {
} }
} }
.text { .text {
width: 100px;
margin-left: 4px; margin-left: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -645,7 +234,6 @@ onMounted(async () => { ...@@ -645,7 +234,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
...@@ -653,7 +241,6 @@ onMounted(async () => { ...@@ -653,7 +241,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -661,11 +248,14 @@ onMounted(async () => { ...@@ -661,11 +248,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
...@@ -679,6 +269,7 @@ onMounted(async () => { ...@@ -679,6 +269,7 @@ onMounted(async () => {
} }
} }
} }
.top-bone2 { .top-bone2 {
position: absolute; position: absolute;
top: 20px; top: 20px;
...@@ -695,13 +286,11 @@ onMounted(async () => { ...@@ -695,13 +286,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -714,9 +303,13 @@ onMounted(async () => { ...@@ -714,9 +303,13 @@ onMounted(async () => {
} }
} }
.text { .text {
width: 100px;
margin-left: 4px; margin-left: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -734,7 +327,6 @@ onMounted(async () => { ...@@ -734,7 +327,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(-30deg); transform: skew(-30deg);
height: 35px; height: 35px;
...@@ -742,7 +334,6 @@ onMounted(async () => { ...@@ -742,7 +334,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -750,11 +341,14 @@ onMounted(async () => { ...@@ -750,11 +341,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
...@@ -768,6 +362,7 @@ onMounted(async () => { ...@@ -768,6 +362,7 @@ onMounted(async () => {
} }
} }
} }
.bottom-bone { .bottom-bone {
position: absolute; position: absolute;
top: 280px; top: 280px;
...@@ -784,13 +379,11 @@ onMounted(async () => { ...@@ -784,13 +379,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -803,9 +396,13 @@ onMounted(async () => { ...@@ -803,9 +396,13 @@ onMounted(async () => {
} }
} }
.text { .text {
width: 100px;
margin-left: 4px; margin-left: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -823,7 +420,6 @@ onMounted(async () => { ...@@ -823,7 +420,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
...@@ -831,7 +427,6 @@ onMounted(async () => { ...@@ -831,7 +427,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -839,11 +434,14 @@ onMounted(async () => { ...@@ -839,11 +434,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
...@@ -857,6 +455,7 @@ onMounted(async () => { ...@@ -857,6 +455,7 @@ onMounted(async () => {
} }
} }
} }
.bottom-bone1 { .bottom-bone1 {
position: absolute; position: absolute;
top: 280px; top: 280px;
...@@ -873,13 +472,11 @@ onMounted(async () => { ...@@ -873,13 +472,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -892,9 +489,13 @@ onMounted(async () => { ...@@ -892,9 +489,13 @@ onMounted(async () => {
} }
} }
.text { .text {
width: 100px;
margin-left: 4px; margin-left: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -912,7 +513,6 @@ onMounted(async () => { ...@@ -912,7 +513,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
...@@ -920,7 +520,6 @@ onMounted(async () => { ...@@ -920,7 +520,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -928,11 +527,14 @@ onMounted(async () => { ...@@ -928,11 +527,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
...@@ -946,6 +548,7 @@ onMounted(async () => { ...@@ -946,6 +548,7 @@ onMounted(async () => {
} }
} }
} }
.bottom-bone2 { .bottom-bone2 {
position: absolute; position: absolute;
top: 280px; top: 280px;
...@@ -962,13 +565,11 @@ onMounted(async () => { ...@@ -962,13 +565,11 @@ onMounted(async () => {
left: -150px; left: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: orange;
.left-bone-item { .left-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 15px; margin-top: 15px;
// background: #fff;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon { .icon {
...@@ -981,9 +582,13 @@ onMounted(async () => { ...@@ -981,9 +582,13 @@ onMounted(async () => {
} }
} }
.text { .text {
width: 100px;
margin-left: 4px; margin-left: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.line { .line {
margin-left: 7px; margin-left: 7px;
...@@ -1001,7 +606,6 @@ onMounted(async () => { ...@@ -1001,7 +606,6 @@ onMounted(async () => {
right: -150px; right: -150px;
width: 150px; width: 150px;
height: 260px; height: 260px;
// background: pink;
.right-bone-item { .right-bone-item {
transform: skew(30deg); transform: skew(30deg);
height: 35px; height: 35px;
...@@ -1009,7 +613,6 @@ onMounted(async () => { ...@@ -1009,7 +613,6 @@ onMounted(async () => {
margin-top: 5px; margin-top: 5px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.line { .line {
margin-right: 7px; margin-right: 7px;
margin-top: 16px; margin-top: 16px;
...@@ -1017,11 +620,14 @@ onMounted(async () => { ...@@ -1017,11 +620,14 @@ onMounted(async () => {
height: 2px; height: 2px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
.text { .text {
width: 100px;
margin-right: 4px; margin-right: 4px;
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.icon { .icon {
margin-top: 7px; margin-top: 7px;
......
[ [
{ {
"id": null, "yearDomainCount": [
"name": "2025-10-09", {
"sanTypeName": null,
"year": 2020,
"domainCountInfo": [
{
"year": 2020,
"id": "1",
"name": "人工智能",
"count": 11
},
{
"year": 2020,
"id": "2",
"name": "生物科技",
"count": 2
},
{
"year": 2020,
"id": "3",
"name": "新一代信息技术",
"count": 13
},
{
"year": 2020,
"id": "4",
"name": "量子科技",
"count": 0
},
{
"year": 2020,
"id": "5",
"name": "新能源",
"count": 0
},
{
"year": 2020,
"id": "6",
"name": "集成电路",
"count": 1
},
{
"year": 2020,
"id": "7",
"name": "海洋",
"count": 47
},
{
"year": 2020,
"id": "8",
"name": "先进制造",
"count": 22
},
{
"year": 2020,
"id": "9",
"name": "新材料",
"count": 0
},
{
"year": 2020,
"id": "10",
"name": "航空航天",
"count": 18
},
{
"year": 2020,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2020,
"id": "13",
"name": "太空",
"count": 0
},
{
"year": 2020,
"id": "11",
"name": "深海",
"count": 0
},
{
"year": 2020,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2020,
"id": "14",
"name": "核",
"count": 10 "count": 10
}
]
},
{
"sanTypeName": null,
"year": 2021,
"domainCountInfo": [
{
"year": 2021,
"id": "1",
"name": "人工智能",
"count": 15
},
{
"year": 2021,
"id": "2",
"name": "生物科技",
"count": 12
},
{
"year": 2021,
"id": "3",
"name": "新一代信息技术",
"count": 5
},
{
"year": 2021,
"id": "4",
"name": "量子科技",
"count": 7
},
{
"year": 2021,
"id": "5",
"name": "新能源",
"count": 3
},
{
"year": 2021,
"id": "6",
"name": "集成电路",
"count": 22
},
{
"year": 2021,
"id": "7",
"name": "海洋",
"count": 5
},
{
"year": 2021,
"id": "8",
"name": "先进制造",
"count": 27
},
{
"year": 2021,
"id": "9",
"name": "新材料",
"count": 7
},
{
"year": 2021,
"id": "10",
"name": "航空航天",
"count": 4
},
{
"year": 2021,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2021,
"id": "13",
"name": "太空",
"count": 0
},
{
"year": 2021,
"id": "11",
"name": "深海",
"count": 3
},
{
"year": 2021,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2021,
"id": "14",
"name": "核",
"count": 3
}
]
},
{
"sanTypeName": null,
"year": 2022,
"domainCountInfo": [
{
"year": 2022,
"id": "1",
"name": "人工智能",
"count": 22
},
{
"year": 2022,
"id": "2",
"name": "生物科技",
"count": 0
},
{
"year": 2022,
"id": "3",
"name": "新一代信息技术",
"count": 2
},
{
"year": 2022,
"id": "4",
"name": "量子科技",
"count": 0
},
{
"year": 2022,
"id": "5",
"name": "新能源",
"count": 0
},
{
"year": 2022,
"id": "6",
"name": "集成电路",
"count": 33
},
{
"year": 2022,
"id": "7",
"name": "海洋",
"count": 7
},
{
"year": 2022,
"id": "8",
"name": "先进制造",
"count": 27
},
{
"year": 2022,
"id": "9",
"name": "新材料",
"count": 1
},
{
"year": 2022,
"id": "10",
"name": "航空航天",
"count": 12
},
{
"year": 2022,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2022,
"id": "13",
"name": "太空",
"count": 0
},
{
"year": 2022,
"id": "11",
"name": "深海",
"count": 2
},
{
"year": 2022,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2022,
"id": "14",
"name": "核",
"count": 0
}
]
},
{
"sanTypeName": null,
"year": 2023,
"domainCountInfo": [
{
"year": 2023,
"id": "1",
"name": "人工智能",
"count": 31
},
{
"year": 2023,
"id": "2",
"name": "生物科技",
"count": 5
},
{
"year": 2023,
"id": "3",
"name": "新一代信息技术",
"count": 3
},
{
"year": 2023,
"id": "4",
"name": "量子科技",
"count": 1
},
{
"year": 2023,
"id": "5",
"name": "新能源",
"count": 0
},
{
"year": 2023,
"id": "6",
"name": "集成电路",
"count": 82
},
{
"year": 2023,
"id": "7",
"name": "海洋",
"count": 7
}, },
{ {
"id": null, "year": 2023,
"name": "2025-10-08", "id": "8",
"count": 8 "name": "先进制造",
"count": 36
}, },
{ {
"id": null, "year": 2023,
"name": "2025-09-16", "id": "9",
"count": 16 "name": "新材料",
"count": 1
},
{
"year": 2023,
"id": "10",
"name": "航空航天",
"count": 55
},
{
"year": 2023,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2023,
"id": "13",
"name": "太空",
"count": 1
},
{
"year": 2023,
"id": "11",
"name": "深海",
"count": 0
},
{
"year": 2023,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2023,
"id": "14",
"name": "核",
"count": 7
}
]
},
{
"sanTypeName": null,
"year": 2024,
"domainCountInfo": [
{
"year": 2024,
"id": "1",
"name": "人工智能",
"count": 33
},
{
"year": 2024,
"id": "2",
"name": "生物科技",
"count": 0
},
{
"year": 2024,
"id": "3",
"name": "新一代信息技术",
"count": 10
},
{
"year": 2024,
"id": "4",
"name": "量子科技",
"count": 22
},
{
"year": 2024,
"id": "5",
"name": "新能源",
"count": 0
},
{
"year": 2024,
"id": "6",
"name": "集成电路",
"count": 190
},
{
"year": 2024,
"id": "7",
"name": "海洋",
"count": 0
},
{
"year": 2024,
"id": "8",
"name": "先进制造",
"count": 27
},
{
"year": 2024,
"id": "9",
"name": "新材料",
"count": 13
},
{
"year": 2024,
"id": "10",
"name": "航空航天",
"count": 29
},
{
"year": 2024,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2024,
"id": "13",
"name": "太空",
"count": 0
},
{
"year": 2024,
"id": "11",
"name": "深海",
"count": 0
},
{
"year": 2024,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2024,
"id": "14",
"name": "核",
"count": 2
}
]
},
{
"sanTypeName": null,
"year": 2025,
"domainCountInfo": [
{
"year": 2025,
"id": "1",
"name": "人工智能",
"count": 12
},
{
"year": 2025,
"id": "2",
"name": "生物科技",
"count": 0
},
{
"year": 2025,
"id": "3",
"name": "新一代信息技术",
"count": 11
},
{
"year": 2025,
"id": "4",
"name": "量子科技",
"count": 9
},
{
"year": 2025,
"id": "5",
"name": "新能源",
"count": 0
},
{
"year": 2025,
"id": "6",
"name": "集成电路",
"count": 35
},
{
"year": 2025,
"id": "7",
"name": "海洋",
"count": 0
},
{
"year": 2025,
"id": "8",
"name": "先进制造",
"count": 42
},
{
"year": 2025,
"id": "9",
"name": "新材料",
"count": 11
},
{
"year": 2025,
"id": "10",
"name": "航空航天",
"count": 26
},
{
"year": 2025,
"id": "99",
"name": "其他",
"count": 0
},
{
"year": 2025,
"id": "13",
"name": "太空",
"count": 1
},
{
"year": 2025,
"id": "11",
"name": "深海",
"count": 0
},
{
"year": 2025,
"id": "12",
"name": "极地",
"count": 0
},
{
"year": 2025,
"id": "14",
"name": "核",
"count": 1
}
]
}
],
"domians": [
{
"year": null,
"id": "1",
"name": "人工智能",
"count": null
},
{
"year": null,
"id": "2",
"name": "生物科技",
"count": null
},
{
"year": null,
"id": "3",
"name": "新一代信息技术",
"count": null
},
{
"year": null,
"id": "4",
"name": "量子科技",
"count": null
},
{
"year": null,
"id": "5",
"name": "新能源",
"count": null
},
{
"year": null,
"id": "6",
"name": "集成电路",
"count": null
},
{
"year": null,
"id": "7",
"name": "海洋",
"count": null
},
{
"year": null,
"id": "8",
"name": "先进制造",
"count": null
},
{
"year": null,
"id": "9",
"name": "新材料",
"count": null
},
{
"year": null,
"id": "10",
"name": "航空航天",
"count": null
},
{
"year": null,
"id": "99",
"name": "其他",
"count": null
},
{
"year": null,
"id": "13",
"name": "太空",
"count": null
},
{
"year": null,
"id": "11",
"name": "深海",
"count": null
},
{
"year": null,
"id": "12",
"name": "极地",
"count": null
},
{
"year": null,
"id": "14",
"name": "核",
"count": null
}
]
} }
] ]
\ No newline at end of file
<template> <template>
<div class="fishbone"> <div class="fishbone-wrapper">
<div class="main-line"></div> <div class="fishbone-scroll-container" ref="scrollContainerRef">
<div v-for="(causeGroup, groupIndex) in fishboneData.causes" :key="groupIndex" :class="getBoneClass(groupIndex)"> <div class="fishbone" ref="fishboneRef" v-if="fishboneData.length > 0">
<div class="main-line" :style="{ width: (fishboneData.length / 2) * 340 - 275 + 'px' }"></div>
<!-- 奇数索引的数据组放在上方 -->
<div
v-for="(causeGroup, groupIndex) in getOddGroups(fishboneData)"
:key="'top-' + groupIndex"
:class="getTopBoneClass(groupIndex)"
:style="{ left: groupIndex * 300 + 400 + 'px' }"
>
<div class="left-bone"> <div class="left-bone">
<div class="left-bone-item" v-for="(item, index) in getLeftItems(causeGroup.causes)" :key="index"> <div
<!-- <div class="icon"> class="left-bone-item"
<img :src="item.picture" alt="" /> v-for="(item, index) in getLeftItems(causeGroup.causes)"
</div> --> :key="'left-' + index"
>
<div class="text">{{ item.name }}</div> <div class="text">{{ item.name }}</div>
<div class="line"></div> <div class="line"></div>
</div> </div>
</div> </div>
<div class="right-bone"> <div class="right-bone">
<div class="right-bone-item" v-for="(item, index) in getRightItems(causeGroup.causes)" :key="index"> <div
class="right-bone-item"
v-for="(item, index) in getRightItems(causeGroup.causes)"
:key="'right-' + index"
>
<div class="line"></div> <div class="line"></div>
<div class="text">{{ item.name }}</div> <div class="text">{{ item.name }}</div>
<!-- <div class="icon">
<img :src="item.picture" :alt="item.name" />
</div> -->
</div> </div>
</div> </div>
</div> </div>
<!-- 偶数索引的数据组放在下方 -->
<div
v-for="(causeGroup, groupIndex) in getEvenGroups(fishboneData)"
:key="'bottom-' + groupIndex"
:class="getBottomBoneClass(groupIndex)"
:style="{ left: groupIndex * 300 + 200 + 'px' }"
>
<div class="left-bone">
<div
class="left-bone-item"
v-for="(item, index) in getLeftItems(causeGroup.causes)"
:key="'left-bottom-' + index"
>
<div class="text">{{ item.name }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div
class="right-bone-item"
v-for="(item, index) in getRightItems(causeGroup.causes)"
:key="'right-bottom-' + index"
>
<div class="line"></div>
<div class="text">{{ item.name }}</div>
</div>
</div>
</div>
</div>
<div v-else style="display: flex; justify-content: center; align-items: center; height: 200px; width: 100%">
<el-empty description="暂无相关数据" />
</div>
</div>
<!-- 滚动指示器 -->
<!-- <div class="scroll-indicators" v-if="showScrollIndicator">
<div class="scroll-btn left" :class="{ disabled: !canScrollLeft }" @click="scrollLeft">‹</div>
<div class="scroll-btn right" :class="{ disabled: !canScrollRight }" @click="scrollRight">›</div>
</div> -->
</div> </div>
</template> </template>
<script setup> <script setup>
import { getChainFishbone } from "@/api/exportControl"; import { getChainFishbone } from "@/api/exportControl";
import { onMounted, ref } from "vue"; import { onMounted, ref, nextTick, watch } from "vue";
// 这儿需要接收父组件传递来的产业链ID
const chainId = ref(1); const props = defineProps({
const fishboneData = ref({ chainId: {
text: "", type: Number,
causes: [] default: 1
}
}); });
// 根据索引确定鱼骨图位置类名 // const chainId = ref(1);
const getBoneClass = index => { const fishboneData = ref([]);
const positions = ["top-bone", "top-bone1", "top-bone2", "bottom-bone", "bottom-bone1", "bottom-bone2"]; const scrollContainerRef = ref(null);
return positions[index] || "top-bone"; const fishboneRef = ref(null);
const showScrollIndicator = ref(false);
const canScrollLeft = ref(false);
const canScrollRight = ref(true);
// 获取奇数索引的数据组(放在上方)
const getOddGroups = data => {
console.log(
"getOddGroups:",
data.filter((_, index) => index % 2 === 1)
);
return data.filter((_, index) => index % 2 === 1);
};
// 获取偶数索引的数据组(放在下方)
const getEvenGroups = data => {
console.log(
"getEvenGroups:",
data.filter((_, index) => index % 2 === 0)
);
return data.filter((_, index) => index % 2 === 0);
};
// 获取上方鱼骨图位置类名
const getTopBoneClass = index => {
const positions = ["top-bone", "top-bone1", "top-bone2"];
return positions[index % 3] || "top-bone";
};
// 获取下方鱼骨图位置类名
const getBottomBoneClass = index => {
const positions = ["bottom-bone", "bottom-bone1", "bottom-bone2"];
return positions[index % 3] || "bottom-bone";
}; };
// 获取左侧显示的项目(前半部分) // 获取左侧显示的项目(前半部分)
...@@ -52,32 +135,109 @@ const getRightItems = items => { ...@@ -52,32 +135,109 @@ const getRightItems = items => {
return items.slice(midpoint); return items.slice(midpoint);
}; };
// 检查滚动状态
const updateScrollState = () => {
if (!scrollContainerRef.value) return;
const container = scrollContainerRef.value;
canScrollLeft.value = container.scrollLeft > 0;
canScrollRight.value = container.scrollLeft < container.scrollWidth - container.clientWidth;
};
// 滚动处理
const scrollLeft = () => {
if (scrollContainerRef.value) {
scrollContainerRef.value.scrollBy({ left: -200, behavior: "smooth" });
}
};
const scrollRight = () => {
if (scrollContainerRef.value) {
scrollContainerRef.value.scrollBy({ left: 200, behavior: "smooth" });
}
};
// 处理滚动事件
const handleScroll = () => {
updateScrollState();
};
onMounted(async () => { onMounted(async () => {
try { try {
const chainFishboneData = await getChainFishbone(chainId.value); const chainFishboneData = await getChainFishbone(props.chainId);
fishboneData.value = chainFishboneData ?? { fishboneData.value = chainFishboneData?.causes ?? [];
text: "",
causes: [] // 等待DOM更新后检查是否需要滚动
}; nextTick(() => {
if (scrollContainerRef.value && fishboneRef.value) {
showScrollIndicator.value = fishboneRef.value.scrollWidth > scrollContainerRef.value.clientWidth;
updateScrollState();
}
});
console.log("鱼骨图数据:", fishboneData.value);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
}); });
// 监听props中的chainId变化
watch(
() => props.chainId,
async () => {
try {
const chainFishboneData = await getChainFishbone(props.chainId);
fishboneData.value = chainFishboneData?.causes ?? [];
} catch (error) {
console.log(error);
}
}
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.fishbone-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.fishbone-scroll-container {
display: flex;
align-items: center;
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(144, 202, 249, 0.5) transparent;
&::-webkit-scrollbar {
height: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(144, 202, 249, 0.5);
border-radius: 3px;
}
}
/* ... 原有的样式保持不变 ... */ /* ... 原有的样式保持不变 ... */
.fishbone { .fishbone {
position: relative; position: relative;
width: 100%; width: fit-content;
height: 100%; height: 100%;
margin-top: 40px; margin-top: 40px;
min-width: 100%;
padding-left: 275px;
.main-line { .main-line {
position: absolute; margin-top: 280px;
top: 280px; width: 1888px;
right: 0;
width: 888px;
height: 3px; height: 3px;
background: rgba(174, 208, 255, 1); background: rgba(174, 208, 255, 1);
} }
...@@ -106,15 +266,6 @@ onMounted(async () => { ...@@ -106,15 +266,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
margin-left: 4px; margin-left: 4px;
height: 70px; height: 70px;
...@@ -162,15 +313,6 @@ onMounted(async () => { ...@@ -162,15 +313,6 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
} }
} }
} }
...@@ -198,15 +340,6 @@ onMounted(async () => { ...@@ -198,15 +340,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
width: 100px; width: 100px;
margin-left: 4px; margin-left: 4px;
...@@ -255,15 +388,6 @@ onMounted(async () => { ...@@ -255,15 +388,6 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
} }
} }
} }
...@@ -291,15 +415,6 @@ onMounted(async () => { ...@@ -291,15 +415,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
width: 100px; width: 100px;
margin-left: 4px; margin-left: 4px;
...@@ -348,15 +463,6 @@ onMounted(async () => { ...@@ -348,15 +463,6 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
} }
} }
} }
...@@ -384,15 +490,6 @@ onMounted(async () => { ...@@ -384,15 +490,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
width: 100px; width: 100px;
margin-left: 4px; margin-left: 4px;
...@@ -441,15 +538,6 @@ onMounted(async () => { ...@@ -441,15 +538,6 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
} }
} }
} }
...@@ -477,15 +565,6 @@ onMounted(async () => { ...@@ -477,15 +565,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
width: 100px; width: 100px;
margin-left: 4px; margin-left: 4px;
...@@ -534,15 +613,6 @@ onMounted(async () => { ...@@ -534,15 +613,6 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
} }
} }
} }
...@@ -570,15 +640,6 @@ onMounted(async () => { ...@@ -570,15 +640,6 @@ onMounted(async () => {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text { .text {
width: 100px; width: 100px;
margin-left: 4px; margin-left: 4px;
...@@ -627,16 +688,49 @@ onMounted(async () => { ...@@ -627,16 +688,49 @@ onMounted(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
} }
} }
}
.scroll-indicators {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
pointer-events: none;
padding: 0 10px;
z-index: 10;
}
.scroll-btn {
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
color: #90caf9;
cursor: pointer;
pointer-events: auto;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
&:hover:not(.disabled) {
background: #90caf9;
color: white;
transform: scale(1.1);
} }
&.disabled {
color: #c0c4cc;
cursor: not-allowed;
background: rgba(255, 255, 255, 0.5);
} }
} }
</style> </style>
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<div class="chartsWrap"> <div class="chartsWrap">
<div class="right-main-content"> <div class="right-main-content">
<div class="right-main-content-main"> <div class="right-main-content-main">
<Fishbone /> <Fishbone :chainId="activeButtonId" />
</div> </div>
<div class="right-main-content-footer"> <div class="right-main-content-footer">
<div class="footer-item1"> <div class="footer-item1">
...@@ -76,30 +76,12 @@ import Echarts from "@/components/Chart/index.vue"; ...@@ -76,30 +76,12 @@ import Echarts from "@/components/Chart/index.vue";
import Hint from "./hint.vue"; import Hint from "./hint.vue";
import ButtonList from "@/components/buttonList/buttonList.vue"; import ButtonList from "@/components/buttonList/buttonList.vue";
import Fishbone from "./fishbone.vue"; import Fishbone from "./fishbone.vue";
import college1 from "../../assets/images/college1.png";
import college2 from "../../assets/images/college2.png";
import college3 from "../../assets/images/college3.png";
import college4 from "../../assets/images/college4.png";
import college5 from "../../assets/images/college5.png";
import college6 from "../../assets/images/college6.png";
import college7 from "../../assets/images/college7.png";
import college8 from "../../assets/images/college8.png";
import college9 from "../../assets/images/college9.png";
import college10 from "../../assets/images/college10.png";
import college11 from "../../assets/images/college11.png";
import { getHorizontalBarChart2 } from "../../utils/charts"; import { getHorizontalBarChart2 } from "../../utils/charts";
import { getDomainDistribution, getChainEntities } from "@/api/exportControl"; import { getDomainDistribution, getChainEntities } from "@/api/exportControl";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const route = useRoute(); const route = useRoute();
const buttonList = ref([ const buttonList = ref([]);
{ id: 1, text: "新能源" }, const activeButtonId = ref(buttonList.value[0]?.id || 1);
{ id: 2, text: "半导体" },
{ id: 3, text: "跨境电商" },
{ id: 4, text: "金融业" },
{ id: 5, text: "军工" },
{ id: 6, text: "贸易" }
]);
const activeButtonId = ref(buttonList.value[0].id);
const setActiveButtonId = id => { const setActiveButtonId = id => {
activeButtonId.value = id; activeButtonId.value = id;
}; };
...@@ -178,10 +160,12 @@ const fetchDomainDistribution = async () => { ...@@ -178,10 +160,12 @@ const fetchDomainDistribution = async () => {
horizontalBarOptions.value = getHorizontalBarChart2(yAxisData, seriesData, false); horizontalBarOptions.value = getHorizontalBarChart2(yAxisData, seriesData, false);
// 更新buttonList // 更新buttonList
buttonList.value = sortedData.map(item => ({ buttonList.value = sortedData
.map(item => ({
id: item.id, id: item.id,
text: item.name text: item.name
})); }))
.sort((a, b) => a.id - b.id);
console.log("buttonList.value", buttonList.value); console.log("buttonList.value", buttonList.value);
setActiveButtonId(buttonList.value[0].id); setActiveButtonId(buttonList.value[0].id);
} }
......
...@@ -8,9 +8,23 @@ ...@@ -8,9 +8,23 @@
import Echarts from "@/components/Chart/index.vue"; import Echarts from "@/components/Chart/index.vue";
import { getMapOption } from "../../utils/charts"; import { getMapOption } from "../../utils/charts";
import { ref, onMounted, shallowRef } from "vue"; import { ref, onMounted, shallowRef } from "vue";
import { getAreaDistribution } from "@/api/exportControl";
// 这儿接收父组件传递过来的date参数
const props = defineProps({
date: {
type: String,
default: ""
}
});
const mapOption = shallowRef({}); const mapOption = shallowRef({});
onMounted(() => { onMounted(() => {
mapOption.value = getMapOption(); mapOption.value = getMapOption();
// 区域分布查询
getAreaDistribution(props.date).then(res => {
console.log("res", res);
});
}); });
</script> </script>
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
size="mini" size="mini"
v-model="domainValue" v-model="domainValue"
placeholder="领域选择" placeholder="领域选择"
@change="handleDomainChange"
> >
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value"> <el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option> </el-option>
...@@ -24,7 +25,8 @@ ...@@ -24,7 +25,8 @@
</template> </template>
<div class="subPanel1"> <div class="subPanel1">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }"> <div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar1Option" height="100%"></Echarts> <Echarts v-if="!bar1DataIsEmpty" :option="bar1Option" height="100%"></Echarts>
<el-empty v-else description="暂无数据" />
</div> </div>
<Hint text="近几次新增受制裁实体中,中国实体占比提高,美方针对中国的出口管制风险显著增加。"></Hint> <Hint text="近几次新增受制裁实体中,中国实体占比提高,美方针对中国的出口管制风险显著增加。"></Hint>
</div> </div>
...@@ -123,7 +125,7 @@ onMounted(async () => { ...@@ -123,7 +125,7 @@ onMounted(async () => {
try { try {
const [entitiesGrowthTrendData, entitiesUpdateCountData] = await Promise.all([ const [entitiesGrowthTrendData, entitiesUpdateCountData] = await Promise.all([
getEntitiesGrowthTrend(), getEntitiesGrowthTrend(),
getEntitiesUpdateCount() getEntitiesUpdateCount(1)
]); ]);
const list = _.reverse(entitiesGrowthTrendData); const list = _.reverse(entitiesGrowthTrendData);
...@@ -133,12 +135,25 @@ onMounted(async () => { ...@@ -133,12 +135,25 @@ onMounted(async () => {
}); });
line1Option.value = getLineChart({ xAxisData, seriesData, name: "增长趋势", color: "rgba(146, 84, 222, 1)" }, true); line1Option.value = getLineChart({ xAxisData, seriesData, name: "增长趋势", color: "rgba(146, 84, 222, 1)" }, true);
if (entitiesUpdateCountData && Array.isArray(entitiesUpdateCountData)) {
const sortedData = _.sortBy(entitiesUpdateCountData, "year");
// 提取 x 轴数据(年份)
const xAxisData = sortedData.map(item => item.year.toString());
// 提取 y 轴数据(数量)
const seriesData = sortedData.map(item => item.count);
bar2Option.value = getBarChart(xAxisData, seriesData, ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"], "更新频率");
} else {
// 保持原有逻辑以防数据格式不符合预期
bar2Option.value = getBarChart( bar2Option.value = getBarChart(
_.reverse(entitiesUpdateCountData.xAxis), _.reverse(entitiesUpdateCountData.xAxis),
_.reverse(entitiesUpdateCountData.series), _.reverse(entitiesUpdateCountData.series),
["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"], ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"],
"更新频率" "更新频率"
); );
}
// 获取重点实体列表数据 // 获取重点实体列表数据
await fetchKeyEntityList(route.query.startTime); await fetchKeyEntityList(route.query.startTime);
} catch (err) { } catch (err) {
...@@ -248,11 +263,13 @@ const typeOptions = [ ...@@ -248,11 +263,13 @@ const typeOptions = [
const domainValue = ref(domainOptions[0].value); const domainValue = ref(domainOptions[0].value);
const typeValue = ref(typeOptions[0].value); const typeValue = ref(typeOptions[0].value);
const bar1Option = shallowRef({}); const bar1Option = shallowRef({});
const bar1DataIsEmpty = ref(false);
watch( watch(
[domainValue, typeValue], [domainValue, typeValue],
async ([domain, type]) => { async ([domain, type]) => {
let EntitiesChangeCount = await getEntitiesChangeCount(domain, type); let EntitiesChangeCount = await getEntitiesChangeCount(domain, type);
EntitiesChangeCount = _.reverse(EntitiesChangeCount); EntitiesChangeCount = _.reverse(EntitiesChangeCount);
bar1DataIsEmpty.value = EntitiesChangeCount.length === 0;
bar1Option.value = getBarChart( bar1Option.value = getBarChart(
_.map(EntitiesChangeCount, "year"), _.map(EntitiesChangeCount, "year"),
_.map(EntitiesChangeCount, "count"), _.map(EntitiesChangeCount, "count"),
...@@ -317,6 +334,10 @@ watch( ...@@ -317,6 +334,10 @@ watch(
await fetchKeyEntityList(route.query.startTime, newVal); await fetchKeyEntityList(route.query.startTime, newVal);
}, 300) }, 300)
); );
const handleDomainChange = async domain => {
await fetchKeyEntityList(route.query.startTime, value3.value, domain);
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
</CardCustom> </CardCustom>
</div> </div>
<div class="row"> <div class="row">
<CardCustom title="历制裁涉及领域数" :style="{ width: '798px', height: '422px' }"> <CardCustom title="历制裁涉及领域数" :style="{ width: '798px', height: '422px' }">
<div class="subPanel3"> <div class="subPanel3">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }"> <div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar2Option" height="100%"></Echarts> <Echarts :option="bar2Option" height="100%"></Echarts>
...@@ -46,7 +46,12 @@ import { Search } from "@element-plus/icons-vue"; ...@@ -46,7 +46,12 @@ import { Search } from "@element-plus/icons-vue";
import Echarts from "@/components/Chart/index.vue"; import Echarts from "@/components/Chart/index.vue";
import { getBarChart, getLineChart, getPieOption1 } from "../../utils/charts"; import { getBarChart, getLineChart, getPieOption1 } from "../../utils/charts";
import Hint from "./hint.vue"; import Hint from "./hint.vue";
import { getEntitiesAreaCountByYear, getEntitiesDomainCount, getCountThisDomain } from "@/api/exportControl"; import {
getEntitiesAreaCountByYear,
getEntitiesDomainCount,
getCountThisDomain,
getDomainDistribution
} from "@/api/exportControl";
import _ from "lodash"; import _ from "lodash";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const route = useRoute(); const route = useRoute();
...@@ -85,13 +90,18 @@ const fetchEntitiesDomainCount = async () => { ...@@ -85,13 +90,18 @@ const fetchEntitiesDomainCount = async () => {
onMounted(async () => { onMounted(async () => {
try { try {
const [entitiesAreaCountByYearData, countThisDomainData4, countThisDomainData10] = await Promise.all([ const [entitiesAreaCountByYearData, countThisDomainData4, countThisDomainData10] = await Promise.all([
getEntitiesAreaCountByYear(route.query.startTime), getDomainDistribution(route.query.startTime),
getCountThisDomain("4"), getCountThisDomain("4"),
// getEntitiesDomainCount(), // getEntitiesDomainCount(),
getCountThisDomain("10") getCountThisDomain("10")
]); ]);
pie1Option.value = getPieOption1(entitiesAreaCountByYearData ?? []); pie1Option.value = getPieOption1(
entitiesAreaCountByYearData.map(item => ({
name: item.name,
value: item.count
})) ?? []
);
const list4 = _.reverse(countThisDomainData4 ?? []); const list4 = _.reverse(countThisDomainData4 ?? []);
line1Option.value = getLineChart({ line1Option.value = getLineChart({
xAxisData: _.map(list4, "year"), xAxisData: _.map(list4, "year"),
......
...@@ -42,7 +42,7 @@ onMounted(async () => { ...@@ -42,7 +42,7 @@ onMounted(async () => {
.filter(item => item.count > 0) .filter(item => item.count > 0)
.map(item => { .map(item => {
return { return {
name: item?.type, name: item?.name,
value: item?.count value: item?.count
}; };
}) })
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
<div class="panel3"> <div class="panel3">
<div class="chartWrap"> <div class="chartWrap">
<PieCharts v-if="panel3ActiveIndex === 1"></PieCharts> <PieCharts v-if="panel3ActiveIndex === 1"></PieCharts>
<MapCharts v-if="panel3ActiveIndex === 2"></MapCharts> <MapCharts v-if="panel3ActiveIndex === 2" :date="route.query.startTime"></MapCharts>
</div> </div>
<Hint text="本次制裁共新增83个实体,其中53个中国大陆实体、1个中国台湾实体。"></Hint> <Hint text="本次制裁共新增83个实体,其中53个中国大陆实体、1个中国台湾实体。"></Hint>
</div> </div>
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
</div> </div>
</div> </div>
<div class="tableWrap"> <div class="tableWrap" ref="tableWrapRef" @scroll="handleScroll">
<el-table <el-table
:data="selectEntitiesList" :data="selectEntitiesList"
class="sanction-table" class="sanction-table"
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="domains" label="涉及领域" width="100"> <el-table-column prop="domains" label="涉及领域" width="180">
<template #default="{ row }"> <template #default="{ row }">
<div class="domain-tags"> <div class="domain-tags">
<el-tag v-for="tag in row.domains" :key="tag" :type="panel5TypeMap[tag]">{{ <el-tag v-for="tag in row.domains" :key="tag" :type="panel5TypeMap[tag]">{{
...@@ -125,11 +125,11 @@ ...@@ -125,11 +125,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="address" label="上市地点" width="90" align="center"> <!-- <el-table-column prop="address" label="上市地点" width="90" align="center">
<template #default="{ row }"> <template #default="{ row }">
{{ row.address }} {{ row.address }}
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column prop="time" label="制裁时间" width="120" align="center"> <el-table-column prop="time" label="制裁时间" width="120" align="center">
<template #default="{ row }"> <template #default="{ row }">
...@@ -137,11 +137,11 @@ ...@@ -137,11 +137,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="revenue" label="营收(亿元)" width="100" align="center"> <!-- <el-table-column prop="revenue" label="营收(亿元)" width="100" align="center">
<template #default="{ row }"> <template #default="{ row }">
{{ row.revenue }} {{ row.revenue }}
</template> </template>
</el-table-column> </el-table-column> -->
<el-table-column prop="subCompany" label="50%规则子企业" min-width="140" align="left"> <el-table-column prop="subCompany" label="50%规则子企业" min-width="140" align="left">
<template #default="{ row }"> <template #default="{ row }">
...@@ -183,7 +183,7 @@ import PieCharts from "../components/pieCharts.vue"; ...@@ -183,7 +183,7 @@ import PieCharts from "../components/pieCharts.vue";
import MapCharts from "../components/mapCharts.vue"; import MapCharts from "../components/mapCharts.vue";
import ButtonList from "@/components/buttonList/buttonList.vue"; import ButtonList from "@/components/buttonList/buttonList.vue";
import Hint from "../components/hint.vue"; import Hint from "../components/hint.vue";
import { onMounted, reactive, ref, shallowRef } from "vue"; import { onMounted, reactive, ref, shallowRef, watch } from "vue";
import panel1_1 from "../../assets/images/panel1_1.png"; import panel1_1 from "../../assets/images/panel1_1.png";
import panel2_1 from "../../assets/images/panel2_1.png"; import panel2_1 from "../../assets/images/panel2_1.png";
import panel2_2 from "../../assets/images/panel2_2.png"; import panel2_2 from "../../assets/images/panel2_2.png";
...@@ -197,7 +197,14 @@ import panel5_5 from "../../assets/images/panel5_5.png"; ...@@ -197,7 +197,14 @@ import panel5_5 from "../../assets/images/panel5_5.png";
import panel5_6 from "../../assets/images/panel5_6.png"; import panel5_6 from "../../assets/images/panel5_6.png";
import panel5_7 from "../../assets/images/panel5_7.png"; import panel5_7 from "../../assets/images/panel5_7.png";
import panel5_8 from "../../assets/images/panel5_8.png"; import panel5_8 from "../../assets/images/panel5_8.png";
import { getOrganizationInfo, getPersonList, getSanReasonSelect, getSelectEntitiesList } from "@/api/exportControl"; import {
getOrganizationInfo,
getPersonList,
getSanReasonSelect,
getSelectEntitiesList,
getEntitiesList
} from "@/api/exportControl";
import _ from "lodash"; import _ from "lodash";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { formatAnyDateToChinese } from "../../utils"; import { formatAnyDateToChinese } from "../../utils";
...@@ -246,15 +253,26 @@ const sanReasonSelect = shallowRef([ ...@@ -246,15 +253,26 @@ const sanReasonSelect = shallowRef([
text: "将中国定位为“通过强制技术转让获取先进制程的威胁”" text: "将中国定位为“通过强制技术转让获取先进制程的威胁”"
} }
]); ]);
const panel5IsChecked = ref(true);
const selectEntitiesList = shallowRef([]); const selectEntitiesList = shallowRef([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const loading = ref(false);
const tableWrapRef = ref(null);
const noMoreData = ref(false);
onMounted(async () => { onMounted(async () => {
try { try {
const [organizationInfoData, personListData, sanReasonSelectData, selectEntitiesListData] = await Promise.all([ const [organizationInfoData, sanReasonSelectData] = await Promise.all([
getOrganizationInfo(), getOrganizationInfo(),
getPersonList(), // getPersonList(),
getSanReasonSelect(route.query.startTime), getSanReasonSelect(route.query.startTime)
getSelectEntitiesList(route.query.startTime) // getSelectEntitiesList(route.query.startTime)
]); ]);
console.log("organizationInfoData", organizationInfoData);
organizationInfo.value = { organizationInfo.value = {
img: panel1_1, img: panel1_1,
mingcheng: organizationInfoData?.orgNameZh, mingcheng: organizationInfoData?.orgNameZh,
...@@ -274,22 +292,103 @@ onMounted(async () => { ...@@ -274,22 +292,103 @@ onMounted(async () => {
return { text: item }; return { text: item };
}); });
selectEntitiesList.value = _.map(selectEntitiesListData, item => { // selectEntitiesList.value = _.map(selectEntitiesListData, item => {
return { // return {
name: item?.entityNameZh, // name: item?.entityNameZh,
domains: item.techDomainList, // domains: item.techDomainList,
// address: "--",
// time: formatAnyDateToChinese(item?.startTime),
// isUp: true,
// revenue: "--",
// subCompany: "--",
// img: ""
// };
// });
// 初始化加载第一页数据
await fetchEntitiesList(currentPage.value, pageSize.value);
} catch (err) {
console.log(err);
}
});
// 获取实体清单数据
const fetchEntitiesList = async (page = 1, size = 10) => {
if (loading.value || noMoreData.value) return;
loading.value = true;
try {
const res = await getEntitiesList("实体清单", page, size, route.query.startTime, panel5IsChecked.value);
if (res) {
const newData = res.content.map(item => ({
...item,
name: item.entityNameZh,
enName: item.entityName,
domains: item.techDomains,
time: formatAnyDateToChinese(item.startTime),
address: "--", address: "--",
time: formatAnyDateToChinese(item?.startTime),
isUp: true,
revenue: "--",
subCompany: "--", subCompany: "--",
img: "" img: ""
}; }));
});
// 如果是第一页,替换数据;否则追加数据
if (page === 1) {
selectEntitiesList.value = newData;
} else {
selectEntitiesList.value = [...selectEntitiesList.value, ...newData];
}
total.value = res.totalElements;
currentPage.value = res.number + 1;
// 检查是否还有更多数据
if (selectEntitiesList.value.length >= total.value) {
noMoreData.value = true;
}
}
} catch (err) { } catch (err) {
console.log(err); console.error(err);
if (currentPage.value > 1) {
currentPage.value--;
} }
}); } finally {
loading.value = false;
}
};
watch(
() => panel5IsChecked.value,
newVal => {
fetchEntitiesList(1, 10);
}
);
// 处理滚动事件
const debounceFlag = ref(false);
const handleScroll = () => {
if (!tableWrapRef.value || debounceFlag.value) return;
const { scrollTop, scrollHeight, clientHeight } = tableWrapRef.value;
// 当距离底部小于50px时加载更多
if (scrollHeight - scrollTop - clientHeight < 50) {
// 设置防抖标志
debounceFlag.value = true;
loadMore();
// 延迟重置防抖标志,防止连续触发
setTimeout(() => {
debounceFlag.value = false;
}, 300);
}
};
// 加载更多数据
const loadMore = () => {
if (!loading.value && !noMoreData.value) {
currentPage.value++;
fetchEntitiesList(currentPage.value, pageSize.value);
}
};
const panel3ActiveIndex = ref(1); const panel3ActiveIndex = ref(1);
const setPanel3ActiveIndex = index => { const setPanel3ActiveIndex = index => {
...@@ -304,7 +403,7 @@ const panel5ButtonAcitveID = ref(panel5ButtonList[0].id); ...@@ -304,7 +403,7 @@ const panel5ButtonAcitveID = ref(panel5ButtonList[0].id);
const panel5SetButtonAcitveID = id => { const panel5SetButtonAcitveID = id => {
panel5ButtonAcitveID.value = id; panel5ButtonAcitveID.value = id;
}; };
const panel5IsChecked = ref(true);
const panel5TypeMap = { const panel5TypeMap = {
人工智能: "danger", 人工智能: "danger",
通信网络: "warning", 通信网络: "warning",
...@@ -662,6 +761,10 @@ const panel6 = ref([ ...@@ -662,6 +761,10 @@ const panel6 = ref([
margin-top: 14px; margin-top: 14px;
min-height: 0; min-height: 0;
overflow: auto; overflow: auto;
.domain-tags {
display: flex;
gap: 4px;
}
} }
.name { .name {
display: flex; display: flex;
......
...@@ -35,14 +35,19 @@ ...@@ -35,14 +35,19 @@
</div> </div>
<div class="layout-main-header-right-box"> <div class="layout-main-header-right-box">
<div class="right-box-top"> <div class="right-box-top">
<div class="time">{{ "2025年7月" }}</div> <div class="time">{{ route.query.startTime }}</div>
<div class="name">{{ "美国商务部工业与安全局" }}</div> <div class="name">{{ "美国商务部工业与安全局" }}</div>
</div> </div>
<div class="right-box-bottom"> <div class="right-box-bottom">
<el-button type="plain" size="large" icon="Search" @click="handleSwitchActiveName('实体清单原文')" <el-button
type="plain"
size="large"
disabled
icon="Search"
@click="handleSwitchActiveName('实体清单原文')"
>实体清单原文</el-button >实体清单原文</el-button
> >
<el-button type="primary" size="large" icon="EditPen">分析报告</el-button> <el-button type="primary" size="large" disabled icon="EditPen">分析报告</el-button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<div class="sub-title">{{ subtitle }}</div> <div class="sub-title">{{ subtitle }}</div>
</div> </div>
<div class="description">{{ description }}</div> <div class="description">{{ description }}</div>
<div v-if="quantity > 0" class="quantity" :style="{ color: color }">{{ quantity }}</div> <div v-if="quantity > 0" class="quantity" :style="{ color: color }">{{ quantity }}{{ unit || "个" }}</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -30,6 +30,10 @@ defineProps({ ...@@ -30,6 +30,10 @@ defineProps({
type: [Number, String], type: [Number, String],
default: 0 default: 0
}, },
unit: {
type: String,
default: ""
},
color: { color: {
type: String, type: String,
default: "#409EFF" default: "#409EFF"
......
...@@ -67,13 +67,14 @@ ...@@ -67,13 +67,14 @@
</div> </div>
<div class="home-main-header-footer-info"> <div class="home-main-header-footer-info">
<InfoCard <InfoCard
v-for="item in infoList" v-for="(item, index) in infoList"
:key="item.title" :key="item.id"
:title="item.title" :title="item.nameZh"
:subtitle="item.subTitle" :subtitle="item.nameAbbr"
:description="item.des" :description="item.description"
:quantity="item.num" :quantity="item.postCount"
:color="item.color" unit="次"
:color="infoListColor[index]"
/> />
</div> </div>
</div> </div>
...@@ -90,62 +91,38 @@ ...@@ -90,62 +91,38 @@
<template #default> <template #default>
<div class="box1"> <div class="box1">
<!-- <el-image <el-carousel ref="carouselRef" trigger="click" height="350px" :autoplay="true">
:src="box1Image" <el-carousel-item v-for="(item, index) in entitiesDataInfoList" :key="item.id + index">
alt="" <div>
style="width: 458px; height: 353px; object-fit: cover; flex-shrink: 0"
></el-image> -->
<!-- <div class="box1-right">
<div class="box1-right-title">关于进一步延长TikTok执法宽限期的行政令</div>
<div class="box1-right-tags">
<el-tag type="primary">互联网</el-tag>
<el-tag type="danger">人工智能</el-tag>
</div>
<div class="box1-right-content">
9月16日,美国白宫官方网站发布总统政令,再次推迟(第四次)对TikTok禁令的执法,新的宽限期截止日为2025年12月16日​。在宽限期内及对于宽限期前的行为,司法部不得强制执行​《保护美国人免受外国对手控制应用程序法》或因此处罚相关实体​(如TikTok及其分发平台)。司法部还需向提供商发出无违规和无责任的信函,并强调执行该法的权力专属联邦司法部长,意在阻止各州或私人提起诉讼。
</div>
<div class="box1-right-footer">
<span class="box1-right-footer-time"> 2025年9月16日 </span>
<el-button type="primary" link>
美国白宫官方网站
<el-image
src="./assets/images/icon-open.png"
alt=""
style="width: 16px; height: 16px; margin-left: 4px"
></el-image>
</el-button>
</div>
</div> -->
<div class="box1-top"> <div class="box1-top">
<div class="box1-top-title"> <div class="box1-top-title">
{{ entitiesDataInfoReactive.startTime }}——BIS《实体清单增列与修订条目》 {{ item.postDate }}——BIS《实体清单增列与修订条目》
</div> </div>
<div class="box1-top-content"> <div class="box1-top-content">
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 发布机构:</span> <span class="box1-top-content-item-title">· 发布机构:</span>
<span class="box1-top-content-item-content">{{ <span class="box1-top-content-item-content">{{ item.postOrgName }}</span>
entitiesDataInfoReactive.orgName
}}</span>
</div> </div>
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 生效日期:</span> <span class="box1-top-content-item-title">· 生效日期:</span>
<span class="box1-top-content-item-content">{{ <span class="box1-top-content-item-content">{{ item.postDate }}</span>
entitiesDataInfoReactive.startTime
}}</span>
</div> </div>
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 涉及领域:</span> <span class="box1-top-content-item-title">· 涉及领域:</span>
<div <div
class="box1-top-content-item-tags" class="box1-top-content-item-tags"
v-for="item in entitiesDataInfoReactive.domains" v-for="domainItem in item.domains"
:key="item" :key="domainItem"
> >
<el-tag <el-tag
:type=" :type="
item === '航空航天' ? 'primary' : item === '人工智能' ? 'danger' : 'info' domainItem === '航空航天'
? 'primary'
: item === '人工智能'
? 'danger'
: 'info'
" "
>{{ item }}</el-tag >{{ domainItem }}</el-tag
> >
</div> </div>
</div> </div>
...@@ -156,19 +133,25 @@ ...@@ -156,19 +133,25 @@
<div class="box1-bottom-content"> <div class="box1-bottom-content">
<div <div
class="box1-bottom-content-item" class="box1-bottom-content-item"
v-for="(item, index) in entitiesDataInfoReactive.entityList" v-for="(ett, index) in item.sanEntities"
:key="index" :key="index"
> >
<el-image <el-image
v-if="item.img" v-if="ett.img"
class="box1-bottom-content-item-img" class="box1-bottom-content-item-img"
:src="item.img" :src="ett.img"
alt="" alt=""
></el-image> ></el-image>
<div v-else class="box1-bottom-content-item-imgUndefined"> <div v-else class="box1-bottom-content-item-imgUndefined">
{{ (item.name || item.enName)?.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] }} {{
(ett.entityNameZh || ett.enName)?.match(
/[\u4e00-\u9fa5a-zA-Z0-9]/
)?.[0]
}}
</div>
<div class="box1-bottom-content-item-txt">
{{ ett.name || ett.entityNameZh }}
</div> </div>
<div class="box1-bottom-content-item-txt">{{ item.name || item.enName }}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -177,8 +160,11 @@ ...@@ -177,8 +160,11 @@
<el-icon><Warning color="rgba(206, 79, 81, 1)" /></el-icon> <el-icon><Warning color="rgba(206, 79, 81, 1)" /></el-icon>
<span>新增中国实体</span> <span>新增中国实体</span>
</div> </div>
<div class="box1-absolute-num">{{ entitiesDataInfoReactive.chNum }}</div> <div class="box1-absolute-num">{{ item.cnEntityCount }}</div>
</div>
</div> </div>
</el-carousel-item>
</el-carousel>
</div> </div>
</template> </template>
</custom-container> </custom-container>
...@@ -300,9 +286,11 @@ ...@@ -300,9 +286,11 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="重点领域" width="180"> <el-table-column label="重点领域" width="180" align="center">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center; gap: 5px"> <div
style="display: flex; justify-content: center; align-items: center; gap: 5px"
>
<el-tag <el-tag
v-for="tag in scope.row.tags" v-for="tag in scope.row.tags"
:key="tag" :key="tag"
...@@ -334,9 +322,11 @@ ...@@ -334,9 +322,11 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="重点领域" width="180"> <el-table-column label="重点领域" width="180" align="center">
<template #default="scope"> <template #default="scope">
<div style="display: flex; align-items: center; gap: 5px"> <div
style="display: flex; justify-content: center; align-items: center; gap: 5px"
>
<el-tag <el-tag
v-for="tag in scope.row.tags" v-for="tag in scope.row.tags"
:key="tag" :key="tag"
...@@ -352,7 +342,7 @@ ...@@ -352,7 +342,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<div class="box3-content"> <div class="box3-content" style="display: none">
<div class="box3-content-title">关键与新兴技术清单(CETs)</div> <div class="box3-content-title">关键与新兴技术清单(CETs)</div>
<el-table :data="tableData1" stripe style="width: 100%"> <el-table :data="tableData1" stripe style="width: 100%">
<el-table-column prop="year" label="年份" width="100" /> <el-table-column prop="year" label="年份" width="100" />
...@@ -394,19 +384,19 @@ ...@@ -394,19 +384,19 @@
<el-row :gutter="20" style="width: 1600px; margin: 0 auto"> <el-row :gutter="20" style="width: 1600px; margin: 0 auto">
<el-col :span="8"> <el-col :span="8">
<custom-container title="制裁领域分析" :titleIcon="radarIcon" height="450px"> <custom-container title="制裁领域分析" :titleIcon="radarIcon" height="480px">
<template #header-right> <template #header-right>
<el-checkbox v-model="checked" label="50%规则" size="large" /> <el-checkbox v-model="domainChecked" label="50%规则" size="large" />
</template> </template>
<template #default> <template #default>
<EChart :option="radarOption" autoresize :style="{ height: '380px' }" /> <EChart :option="radarOption" autoresize :style="{ height: '450px' }" />
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="450px"> <custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="480px">
<template #header-right> <template #header-right>
<el-checkbox v-model="checked" label="50%规则" size="large" /> <el-checkbox v-model="trendChecked" label="50%规则" size="large" />
</template> </template>
<template #default> <template #default>
<EChart :option="trendOption" autoresize :style="{ height: '400px' }" /> <EChart :option="trendOption" autoresize :style="{ height: '400px' }" />
...@@ -421,13 +411,14 @@ ...@@ -421,13 +411,14 @@
<custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px"> <custom-container title="历次制裁过程" :titleIcon="listIcon" height="845px">
<template #default> <template #default>
<div class="box4"> <div class="box4">
<div style="height: 90%; overflow-y: auto; padding-top: 10px">
<div class="box4-item" v-for="(item, idx) in sanctionProcessList" :key="item.title"> <div class="box4-item" v-for="(item, idx) in sanctionProcessList" :key="item.title">
<div class="box4-item-left"> <div class="box4-item-left">
<el-image :src="dotIcon" alt="图片" class="box4-item-left-icon" /> <el-image :src="dotIcon" alt="图片" class="box4-item-left-icon" />
<div class="box4-item-left-line" v-if="idx + 1 != sanctionProcessList.length"></div> <div class="box4-item-left-line" v-if="idx + 1 != sanctionProcessList.length"></div>
</div> </div>
<div class="box4-item-right"> <div class="box4-item-right">
<div class="box4-item-right-header"> <div class="box4-item-right-header" @click="handleSanc(item)">
<span class="box4-item-right-header-title">{{ item.title }}</span> <span class="box4-item-right-header-title">{{ item.title }}</span>
<span class="box4-item-right-header-desc">{{ item.desc }}</span> <span class="box4-item-right-header-desc">{{ item.desc }}</span>
</div> </div>
...@@ -436,8 +427,9 @@ ...@@ -436,8 +427,9 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="box4-footer" :style="{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }"> <div class="box4-footer" :style="{ marginTop: sanctionProcessList.length > 0 ? '0px' : 'auto' }">
<el-button type="primary" link :icon="DownRight" <el-button type="primary" link :icon="DownRight" @click="handleGetMore"
>查看更多 >查看更多
<el-icon><DArrowRight /></el-icon> <el-icon><DArrowRight /></el-icon>
</el-button> </el-button>
...@@ -470,7 +462,7 @@ ...@@ -470,7 +462,7 @@
<el-table-column prop="name" label="实体名称" min-width="200"> <el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope"> <template #default="scope">
<div class="tableName"> <div class="tableName" @click="handleCompClick(scope.row)">
<el-image <el-image
v-if="scope.row.img" v-if="scope.row.img"
class="box1-bottom-content-item-img" class="box1-bottom-content-item-img"
...@@ -571,7 +563,7 @@ ...@@ -571,7 +563,7 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref, computed, reactive, shallowRef } from "vue"; import { onMounted, ref, computed, reactive, shallowRef, watch } from "vue";
import scrollToTop from "@/utils/scrollToTop"; import scrollToTop from "@/utils/scrollToTop";
import * as echarts from "echarts"; import * as echarts from "echarts";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
...@@ -645,50 +637,45 @@ import { getMultipleBarChart_m } from "./utils/charts"; ...@@ -645,50 +637,45 @@ import { getMultipleBarChart_m } from "./utils/charts";
import { formatAnyDateToChinese } from "./utils"; import { formatAnyDateToChinese } from "./utils";
import _ from "lodash"; import _ from "lodash";
const handleCompClick = item => {
console.log("item", item);
const route = router.resolve({
path: "/companyPages",
query: {
id: item.id
}
});
window.open(route.href, "_blank");
};
const tagsType = ["primary", "success", "info", "warning", "danger"]; const tagsType = ["primary", "success", "info", "warning", "danger"];
//数据定义 //数据定义
const entitiesDataInfoReactive = shallowRef({ const entitiesDataInfoList = shallowRef([]);
chNum: undefined,
entityList: [],
domains: [],
startTime: "",
rawStartTime: "",
orgName: ""
});
// 趋势图 // 趋势图
const trendOption = ref({}); const trendOption = ref({});
const trendChecked = ref(false);
// 发布频度 // 发布频度
const tableData1 = ref([]); const tableData1 = ref([]);
// 历次制裁过程 // 历次制裁过程
const sanctionProcessList = ref([]); const sanctionProcessList = ref([]);
const sanctionPage = ref(1);
// 制裁实体清单 // 制裁实体清单
const entitiesList = ref([]); const entitiesList = ref([]);
onMounted(async () => { onMounted(async () => {
try { try {
const [dataCount, entitiesDataInfo, industryCountByYear, countDomainByYear, sanctionsInfoCount, entityBody] = const [dataCount, entitiesDataInfo, industryCountByYear, countDomainByYear] = await Promise.all([
await Promise.all([
getEntitiesDataCount(), getEntitiesDataCount(),
getEntitiesDataInfo(), getEntitiesDataInfo(),
getIndustryCountByYear(), getIndustryCountByYear(1),
getCountDomainByYear() getCountDomainByYear(trendChecked.value)
// getSanctionsInfoCount()
// getEntitiesList("实体清单")
]); ]);
infoList.value[0].num = dataCount; infoList.value = dataCount;
const entityList = _.map(entitiesDataInfo?.sanEntities ?? [], ({ entityNameZh, entityName }) => { const entityList = _.map(entitiesDataInfo?.sanEntities ?? [], ({ entityNameZh, entityName }) => {
return { name: entityNameZh, enName: entityName }; return { name: entityNameZh, enName: entityName };
}); });
entitiesDataInfoReactive.value = { entitiesDataInfoList.value = entitiesDataInfo || [];
entityList,
chNum: entitiesDataInfo?.cnEntityCount,
domains: entitiesDataInfo?.domains ?? [],
// startTime: formatAnyDateToChinese(entitiesDataInfo?.startTime ?? ""),
startTime: entitiesDataInfo.postDate,
rawStartTime: entitiesDataInfo?.startTime ?? "",
orgName: entitiesDataInfo?.postOrgName
};
const list = _.chain(industryCountByYear).filter("year").orderBy("year", "desc").value(); const list = _.chain(industryCountByYear).filter("year").orderBy("year", "desc").value();
const total = _.sumBy(list, "count"); const total = _.sumBy(list, "count");
tableData1.value = _.map(list, item => { tableData1.value = _.map(list, item => {
...@@ -699,8 +686,12 @@ onMounted(async () => { ...@@ -699,8 +686,12 @@ onMounted(async () => {
tags: item.domain tags: item.domain
}; };
}).slice(0, 5); }).slice(0, 5);
console.log("tableData1", tableData1.value); console.log("countDomainByYear", countDomainByYear);
trendOption.value = getMultipleBarChart_m(countDomainByYear); // 整理柱状图数据并应用到趋势图
if (countDomainByYear && countDomainByYear[0].yearDomainCount) {
trendOption.value = processYearDomainCountData(countDomainByYear[0].yearDomainCount);
}
// trendOption.value = getMultipleBarChart_m(countDomainByYear);
// sanctionProcessList.value = _.map(_.slice(sanctionsInfoCount, 0, 5), item => { // sanctionProcessList.value = _.map(_.slice(sanctionsInfoCount, 0, 5), item => {
// return { // return {
// title: item.tittle, // title: item.tittle,
...@@ -711,9 +702,9 @@ onMounted(async () => { ...@@ -711,9 +702,9 @@ onMounted(async () => {
// }; // };
// }); // });
await fetchEntitiesList(currentPage.value, pageSize.value); await fetchEntitiesList(currentPage.value, pageSize.value);
await fetchSanctionProcess(1, pageSize.value); await fetchSanctionProcess(sanctionPage.value, 10);
// 获取雷达图数据 // 获取雷达图数据
await fetchRadarData(); await fetchRadarData(domainChecked.value);
// console.log("entitiesList entitiesList", entityBody); // console.log("entitiesList entitiesList", entityBody);
// entitiesList.value = _.map(entityBody.content, item => { // entitiesList.value = _.map(entityBody.content, item => {
// return { // return {
...@@ -729,67 +720,93 @@ onMounted(async () => { ...@@ -729,67 +720,93 @@ onMounted(async () => {
console.log(err); console.log(err);
} }
}); });
// 新增函数:处理 yearDomainCount 数据并使用 getMultipleBarChart_m 方法生成图表配置
const processYearDomainCountData = yearDomainCountData => {
// 提取所有年份并排序
const years = [...new Set(yearDomainCountData.map(item => item.year))].sort();
// 提取所有领域名称
const allDomains = [...new Set(yearDomainCountData.flatMap(item => item.domainCountInfo.map(domain => domain.name)))];
// 构造 getMultipleBarChart_m 所需的数据结构
const chartData = {
domains: allDomains,
data: years.map(year => {
const yearData = yearDomainCountData.find(item => item.year === year);
const domainCounts = {};
// 初始化所有领域的计数为0
allDomains.forEach(domain => {
domainCounts[domain] = 0;
});
// 填充实际数据
if (yearData && yearData.domainCountInfo) {
yearData.domainCountInfo.forEach(domain => {
domainCounts[domain.name] = domain.count;
});
}
return {
year: year,
domainNum: domainCounts
};
})
};
// 使用 getMultipleBarChart_m 生成图表配置
return getMultipleBarChart_m(chartData);
};
watch(
() => trendChecked.value,
async checked => {
const res = await getCountDomainByYear(checked);
// if (res && Array.isArray(res) && res.length > 0) {
// trendOption.value = getMultipleBarChart_m(res);
// }
// 整理数据并更新趋势图
if (res && res.yearDomainCount) {
trendOption.value = processYearDomainCountData(res[0].yearDomainCount);
}
}
);
// 返回首页 // 返回首页
const handleBackHome = () => { const handleBackHome = () => {
router.push({ router.push({
path: "/overview" path: "/overview"
}); });
}; };
const carouselRef = ref(null);
const handleToDetail = () => { const handleToDetail = () => {
// router.push({ let activeIndex = 0;
// path: "/exportControlAnalysis" if (carouselRef.value) {
// }); activeIndex = carouselRef.value.activeIndex;
// router.push({ }
// path: "/exportControl/analysis"
// }); console.log("当前 Carousel 激活索引:", activeIndex);
// 使用当前激活项的数据
const currentItem = entitiesDataInfoList.value[activeIndex];
if (currentItem) {
const route = router.resolve({ const route = router.resolve({
path: "/exportControlAnalysis", path: "/exportControlAnalysis",
query: { query: {
startTime: entitiesDataInfoReactive.value.startTime startTime: currentItem.postDate
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}
}; };
const billList = ref([]); const billList = ref([]);
const curBillListIndex = ref(0); const curBillListIndex = ref(0);
const searchKey = ref(""); const searchKey = ref("");
const infoListColor = ref(["rgba(206, 79, 81, 1)", "rgba(132, 136, 142, 1)", "rgba(132, 136, 142, 1)", "rgba(132, 136, 142, 1)"]);
const infoList = ref([ const infoList = ref([]);
{
title: "实体清单",
subTitle: "Entity List",
des: "美国商务部工业与安全局依据《出口管理条例》建立的出口管制机制",
num: null,
color: "rgba(206, 79, 81, 1)"
},
{
title: "商业管制清单 ",
subTitle: "CCL",
des: "美国《出口管制条例》中列明受管制军民两用物项的清单",
num: 253,
// color: "rgba(114, 46, 209, 1)"
color: "rgba(132, 136, 142, 1)"
},
{
title: "关键与新兴技术清单",
subTitle: "CETs",
des: "美国为维护其技术领导地位与国家安全而制定的18项优先发展技术清单",
num: 52,
// color: "rgba(250, 140, 22, 1)"
color: "rgba(132, 136, 142, 1)"
},
{
title: "军事最终用户清单 ",
subTitle: "MEU",
des: "美国商务部制定的限制特定外国实体获取可能用于军事用途的美国技术的清单",
num: 0,
color: "rgba(132, 136, 142, 1)"
}
]);
const entityList = ref([ const entityList = ref([
{ {
...@@ -865,15 +882,20 @@ const customNewsData = ref([ ...@@ -865,15 +882,20 @@ const customNewsData = ref([
]); ]);
// 雷达图 // 雷达图
const domainChecked = ref(false);
const radarOption = ref({ const radarOption = ref({
title: { title: {
text: "" text: ""
}, },
legend: { legend: {
top: 0, top: "0%",
icon: "circle", icon: "circle",
data: ["实体清单", "商业管制清单", "关键和新型技术清单"] data: ["实体清单", "商业管制清单", "关键和新型技术清单"]
}, },
grid: {
top: "15%",
containLabel: true
},
radar: { radar: {
// shape: 'circle', // shape: 'circle',
indicator: [ indicator: [
...@@ -923,9 +945,9 @@ const radarOption = ref({ ...@@ -923,9 +945,9 @@ const radarOption = ref({
}); });
// 获取雷达图数据 // 获取雷达图数据
const fetchRadarData = async () => { const fetchRadarData = async checked => {
try { try {
const data = await getSanDomainCount(); const data = await getSanDomainCount(checked);
if (data && Array.isArray(data) && data.length > 0) { if (data && Array.isArray(data) && data.length > 0) {
// 收集所有可能的领域名称 // 收集所有可能的领域名称
const allDomains = new Set(); const allDomains = new Set();
...@@ -984,6 +1006,11 @@ const fetchRadarData = async () => { ...@@ -984,6 +1006,11 @@ const fetchRadarData = async () => {
} }
}; };
watch(
() => domainChecked.value,
() => fetchRadarData(domainChecked.value)
);
// 进度条状态 // 进度条状态
const getStatus = _percent => { const getStatus = _percent => {
const percent = _percent * 100; const percent = _percent * 100;
...@@ -1020,12 +1047,37 @@ const fetchEntitiesList = async (page = 1, size = 10) => { ...@@ -1020,12 +1047,37 @@ const fetchEntitiesList = async (page = 1, size = 10) => {
} }
}; };
const handleGetMore = async () => {
sanctionPage.value++;
try {
const res = await getSanctionProcess("实体清单", sanctionPage.value, 10);
if (res && res.content) {
// 将新数据合并到现有列表中
const newData = res.content.map(item => ({
...item,
title: item.name,
desc: `${item.cnEntityCount} 家中国实体`,
content:
item.summary ||
"2025年3月25日,美国商务部工业与安全局以从事有悖于美国国家安全和外交政策利益的活动为由,宣布将来自中国的54家实体新增至“实体清单”。"
}));
// 合并新数据到现有列表
sanctionProcessList.value = [...sanctionProcessList.value, ...newData];
}
} catch (err) {
console.error(err);
// 如果请求失败,回退页码
sanctionPage.value--;
}
};
// 获取历次制裁过程数据 // 获取历次制裁过程数据
const fetchSanctionProcess = async (page = 1, size = 10) => { const fetchSanctionProcess = async (page = 1, size = 10) => {
try { try {
const res = await getSanctionProcess("实体清单", page, size); const res = await getSanctionProcess("实体清单", page, size);
if (res) { if (res) {
sanctionProcessList.value = res.content.slice(0, 5).map(item => ({ sanctionProcessList.value = res.content.map(item => ({
...item, ...item,
title: item.name, title: item.name,
desc: `${item.cnEntityCount} 家中国实体`, desc: `${item.cnEntityCount} 家中国实体`,
...@@ -1416,6 +1468,17 @@ const chart1Data = ref({ ...@@ -1416,6 +1468,17 @@ const chart1Data = ref({
] ]
}); });
const handleSanc = item => {
console.log(item);
const route = router.resolve({
path: "/exportControlAnalysis",
query: {
startTime: item.postDate
}
});
window.open(route.href, "_blank");
};
// 获取热门法案 // 获取热门法案
const handleGetHotBills = async () => { const handleGetHotBills = async () => {
try { try {
...@@ -1742,8 +1805,9 @@ onMounted(async () => { ...@@ -1742,8 +1805,9 @@ onMounted(async () => {
.box3 { .box3 {
display: flex; display: flex;
justify-content: space-between; // justify-content: space-between;
align-items: flex-start; align-items: flex-start;
gap: 60px;
.box3-content-title { .box3-content-title {
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
...@@ -1769,7 +1833,10 @@ onMounted(async () => { ...@@ -1769,7 +1833,10 @@ onMounted(async () => {
overflow: auto; overflow: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between;
padding-top: 16px; padding-top: 16px;
// padding-bottom: 50px;
position: relative;
.box4-item { .box4-item {
display: flex; display: flex;
gap: 10px; gap: 10px;
...@@ -1802,6 +1869,7 @@ onMounted(async () => { ...@@ -1802,6 +1869,7 @@ onMounted(async () => {
position: relative; position: relative;
top: -7.5px; top: -7.5px;
padding-bottom: 8px; padding-bottom: 8px;
cursor: pointer;
&-title { &-title {
font-size: 18px; font-size: 18px;
color: $base-color; color: $base-color;
...@@ -1827,14 +1895,15 @@ onMounted(async () => { ...@@ -1827,14 +1895,15 @@ onMounted(async () => {
} }
} }
.box4-footer { .box4-footer {
margin-top: auto; position: absolute;
// margin-top: auto;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
bottom: 30px; bottom: 30px;
left: 50%; left: 50%;
margin-left: -20px; margin-left: -30px;
margin-bottom: 30px; // margin-bottom: 30px;
} }
} }
...@@ -2550,6 +2619,7 @@ onMounted(async () => { ...@@ -2550,6 +2619,7 @@ onMounted(async () => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
cursor: pointer;
.box1-bottom-content-item-imgUndefined { .box1-bottom-content-item-imgUndefined {
width: 24px; width: 24px;
height: 24px; height: 24px;
......
import * as echarts from 'echarts'; import * as echarts from "echarts";
import chinaJson from './China.json' import chinaJson from "./China.json";
import _ from 'lodash'; import _ from "lodash";
//饼图 //饼图
export function getPieOption(data, title) { export function getPieOption(data, title) {
let option = { let option = {
...@@ -17,25 +17,25 @@ export function getPieOption(data, title) { ...@@ -17,25 +17,25 @@ export function getPieOption(data, title) {
}, },
series: [ series: [
{ {
type: 'pie', type: "pie",
radius: [75, 132], radius: [75, 132],
height: '100%', height: "100%",
center: ['50%', '50%'], center: ["50%", "50%"],
width: '100%', width: "100%",
itemStyle: { itemStyle: {
borderColor: '#fff', borderColor: "#fff",
borderWidth: 1 borderWidth: 1
}, },
label: { label: {
alignTo: 'edge', alignTo: "edge",
formatter: '{b} {d}%', formatter: "{b} {d}%",
minMargin: 5, minMargin: 5,
edgeDistance: 10, edgeDistance: 10,
lineHeight: 15, lineHeight: 15,
rich: { rich: {
time: { time: {
fontSize: 10, fontSize: 10,
color: '#999' color: "#999"
} }
} }
}, },
...@@ -48,17 +48,16 @@ export function getPieOption(data, title) { ...@@ -48,17 +48,16 @@ export function getPieOption(data, title) {
const isLeft = params.labelRect.x < 556 / 2; const isLeft = params.labelRect.x < 556 / 2;
const points = params.labelLinePoints; const points = params.labelLinePoints;
// Update the end point. // Update the end point.
points[2][0] = isLeft points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
? params.labelRect.x
: params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
}, },
data: data data: data
}]
} }
return option ]
};
return option;
} }
export function getPieOption1(data, title) { export function getPieOption1(data, title) {
let option = { let option = {
...@@ -75,18 +74,18 @@ export function getPieOption1(data, title) { ...@@ -75,18 +74,18 @@ export function getPieOption1(data, title) {
}, },
series: [ series: [
{ {
type: 'pie', type: "pie",
radius: [70, 110], radius: [70, 110],
height: '100%', height: "100%",
center: ['50%', '50%'], center: ["50%", "50%"],
width: '100%', width: "100%",
itemStyle: { itemStyle: {
borderColor: '#fff', borderColor: "#fff",
borderWidth: 1 borderWidth: 1
}, },
label: { label: {
alignTo: 'edge', alignTo: "edge",
formatter: `{b}${data.length < 10 ? '\n' : ''} {two|{c}家 {d}%}`, formatter: `{b}${data.length < 10 ? "\n" : ""} {two|{c}家 {d}%}`,
fontSize: 17.6, fontSize: 17.6,
fontWeight: 700, fontWeight: 700,
minMargin: 5, minMargin: 5,
...@@ -95,7 +94,7 @@ export function getPieOption1(data, title) { ...@@ -95,7 +94,7 @@ export function getPieOption1(data, title) {
rich: { rich: {
two: { two: {
fontSize: 15, fontSize: 15,
color: ' rgba(95, 101, 108, 1)', color: " rgba(95, 101, 108, 1)"
} }
} }
}, },
...@@ -105,21 +104,26 @@ export function getPieOption1(data, title) { ...@@ -105,21 +104,26 @@ export function getPieOption1(data, title) {
maxSurfaceAngle: 80 maxSurfaceAngle: 80
}, },
labelLayout: function (params) { labelLayout: function (params) {
console.log('labelLayoutparams', params) console.log("labelLayoutparams", params);
const isLeft = params.labelRect.x < 556 / 2; const isLeft = params.labelRect.x < 556 / 2;
const points = params.labelLinePoints; const points = params.labelLinePoints;
// Update the end point. // Update the end point.
points[2][0] = isLeft points[2][0] = isLeft
? data.length < 10 ? params.labelRect.x : (params.labelRect.x + params.labelRect.width) ? data.length < 10
: data.length < 10 ? (params.labelRect.x + params.labelRect.width) : params.labelRect.x; ? params.labelRect.x
: params.labelRect.x + params.labelRect.width
: data.length < 10
? params.labelRect.x + params.labelRect.width
: params.labelRect.x;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
}, },
data: data data: data
}]
} }
return option ]
};
return option;
} }
export function getPieOption2(data, title) { export function getPieOption2(data, title) {
let option = { let option = {
...@@ -135,10 +139,10 @@ export function getPieOption2(data, title) { ...@@ -135,10 +139,10 @@ export function getPieOption2(data, title) {
} }
}, },
legend: { legend: {
icon: 'rect', icon: "rect",
top: 'center', top: "center",
right: '40', right: "40",
orient: 'vertical', orient: "vertical",
itemWidth: 30, itemWidth: 30,
itemHeight: 20, itemHeight: 20,
borderRadius: 2, borderRadius: 2,
...@@ -153,170 +157,204 @@ export function getPieOption2(data, title) { ...@@ -153,170 +157,204 @@ export function getPieOption2(data, title) {
currentValue = item.value; currentValue = item.value;
} }
}); });
return ` ${name} ${currentValue} %` return ` ${name} ${currentValue} %`;
}, },
itemGap: 10, itemGap: 10,
textStyle: { textStyle: {
color: 'rgba(59, 65, 75 ,0.8)', color: "rgba(59, 65, 75 ,0.8)",
fontSize: 18, fontSize: 18,
fontWeight: 500 fontWeight: 500
}, }
}, },
series: [ series: [
{ {
type: 'pie', type: "pie",
radius: [70, 120], radius: [70, 120],
height: '100%', height: "100%",
center: ['33%', '50%'], center: ["33%", "50%"],
width: '100%', width: "100%",
itemStyle: { itemStyle: {
borderColor: '#fff', borderColor: "#fff",
borderWidth: 1 borderWidth: 1
}, },
emphasis: { emphasis: {
scale: false, scale: false
}, },
label: { label: {
show: false, show: false
}, },
data: data data: data
}]
} }
return option ]
};
return option;
} }
export function getMapOption() { export function getMapOption() {
echarts.registerMap('china', chinaJson); echarts.registerMap("china", chinaJson);
let data = [{ let data = [
name: '2256', {
name: "2256",
value: 2256 value: 2256
}, { },
name: '578', {
name: "578",
value: 578 value: 578
}, { },
name: '744', {
name: "744",
value: 744 value: 744
}, { },
name: '806', {
name: "806",
value: 806 value: 806
}, { },
name: '336', {
name: "336",
value: 336 value: 336
}, { },
name: '325', {
name: "325",
value: 325 value: 325
}, { },
name: '487', {
name: "487",
value: 487 value: 487
}, { },
name: '343', {
name: "343",
value: 343 value: 343
}, { },
name: '432', {
name: "432",
value: 432 value: 432
}, { },
name: '273', {
name: "273",
value: 273 value: 273
}, { },
name: '1055', {
name: "1055",
value: 1055 value: 1055
}, { },
name: '590', {
name: "590",
value: 590 value: 590
}, { },
name: '319', {
name: "319",
value: 319 value: 319
}, { },
name: '349', {
name: "349",
value: 349 value: 349
}, { },
name: '126', {
name: "126",
value: 126 value: 126
}, { },
name: '97', {
name: "97",
value: 97 value: 97
}, { },
name: '201', {
name: "201",
value: 201 value: 201
}, { },
name: '398', {
name: "398",
value: 398 value: 398
}, { },
name: '795', {
name: "795",
value: 795 value: 795
}, { },
name: '655', {
name: "655",
value: 655 value: 655
}, { },
name: '295', {
name: "295",
value: 295 value: 295
}, { },
name: '311', {
name: "311",
value: 311 value: 311
}, { },
name: '993', {
name: "993",
value: 993 value: 993
}, { },
name: '601', {
name: "601",
value: 601 value: 601
}, { },
name: '275', {
name: "275",
value: 275 value: 275
}, { },
name: '317', {
name: "317",
value: 317 value: 317
}, { },
name: '1000', {
name: "1000",
value: 1000 value: 1000
}, { },
name: '186', {
name: "186",
value: 186 value: 186
}, { },
name: '261', {
name: "261",
value: 261 value: 261
}, { },
name: '132', {
name: "132",
value: 132 value: 132
}, { },
name: '18', {
name: "18",
value: 18 value: 18
}, { },
name: '11', {
name: "11",
value: 11 value: 11
},]; }
];
let geoCoordMap = { let geoCoordMap = {
'2256': [116.46, 39.92], 2256: [116.46, 39.92],
'578': [121.29, 31.14], 578: [121.29, 31.14],
'744': [117.2, 39.13], 744: [117.2, 39.13],
'806': [106.32, 29.32], 806: [106.32, 29.32],
'336': [126.41, 45.45], 336: [126.41, 45.45],
'325': [125.19, 43.52], 325: [125.19, 43.52],
'487': [123.24, 41.50], 487: [123.24, 41.5],
'343': [111.48, 40.49], 343: [111.48, 40.49],
'432': [114.28, 38.02], 432: [114.28, 38.02],
'273': [112.34, 37.52], 273: [112.34, 37.52],
'1055': [117, 36.38], 1055: [117, 36.38],
'590': [113.42, 34.48], 590: [113.42, 34.48],
'319': [108.54, 34.16], 319: [108.54, 34.16],
'349': [103.49, 36.03], 349: [103.49, 36.03],
'126': [106.16, 38.20], 126: [106.16, 38.2],
'97': [101.45, 36.38], 97: [101.45, 36.38],
'201': [87.36, 43.48], 201: [87.36, 43.48],
'398': [117.18, 31.51], 398: [117.18, 31.51],
'795': [118.50, 32.02], 795: [118.5, 32.02],
'655': [120.09, 30.14], 655: [120.09, 30.14],
'295': [113, 28.11], 295: [113, 28.11],
'311': [115.52, 28.41], 311: [115.52, 28.41],
'993': [114.21, 30.37], 993: [114.21, 30.37],
'601': [104.05, 30.39], 601: [104.05, 30.39],
'275': [106.42, 26.35], 275: [106.42, 26.35],
'317': [119.18, 26.05], 317: [119.18, 26.05],
'1000': [113.15, 23.08], 1000: [113.15, 23.08],
'186': [110.20, 20.02], 186: [110.2, 20.02],
'261': [108.20, 22.48], 261: [108.2, 22.48],
'132': [102.41, 25], 132: [102.41, 25],
'18': [91.10, 29.40], 18: [91.1, 29.4],
'11': [114.10, 22.18], 11: [114.1, 22.18]
}; };
function convertData(data) { function convertData(data) {
...@@ -330,7 +368,7 @@ export function getMapOption() { ...@@ -330,7 +368,7 @@ export function getMapOption() {
}); });
} }
} }
console.log(res) console.log(res);
return res; return res;
} }
...@@ -340,7 +378,7 @@ export function getMapOption() { ...@@ -340,7 +378,7 @@ export function getMapOption() {
}, },
geo: { geo: {
map: 'china', map: "china",
roam: true, roam: true,
center: [104.95, 35.27], center: [104.95, 35.27],
// 地图尺寸为容器宽高较小值的80% // 地图尺寸为容器宽高较小值的80%
...@@ -353,38 +391,38 @@ export function getMapOption() { ...@@ -353,38 +391,38 @@ export function getMapOption() {
normal: { normal: {
show: false, show: false,
textStyle: { textStyle: {
color: 'rgba(0,0,0,0.6)' color: "rgba(0,0,0,0.6)"
} }
} }
}, },
itemStyle: { itemStyle: {
areaColor: 'rgba(231, 243, 255, 1)', // 设置所有区域的默认填充色[citation:8] areaColor: "rgba(231, 243, 255, 1)", // 设置所有区域的默认填充色[citation:8]
borderColor: 'rgb(5, 95, 194)', // 设置边界线颜色[citation:8] borderColor: "rgb(5, 95, 194)", // 设置边界线颜色[citation:8]
borderWidth: 1 // 设置边界线宽度[citation:8] borderWidth: 1 // 设置边界线宽度[citation:8]
}, }
}, },
// backgroundColor: 'rgba(0,51,102, 1)', // backgroundColor: 'rgba(0,51,102, 1)',
series: [{ series: [
type: 'scatter', {
coordinateSystem: 'geo', type: "scatter",
coordinateSystem: "geo",
data: convertData(data), data: convertData(data),
symbolSize: 10, symbolSize: 10,
symbolRotate: 0, symbolRotate: 0,
symbolOffset: ['50%', '-100%'], symbolOffset: ["50%", "-100%"],
tooltip: { tooltip: {
show: true show: true
}, },
label: { label: {
normal: { normal: {
formatter: '{a}', formatter: "{a}",
position: 'top', position: "top",
show: false, show: false,
textStyle: { textStyle: {
color: '#000000', color: "#000000",
fontSize: 16 fontSize: 16
} }
}, },
emphasis: { emphasis: {
show: false show: false
...@@ -393,21 +431,21 @@ export function getMapOption() { ...@@ -393,21 +431,21 @@ export function getMapOption() {
itemStyle: { itemStyle: {
normal: { normal: {
borderWidth: 3, borderWidth: 3,
borderColor: 'rgba(255, 163, 158, 1)', borderColor: "rgba(255, 163, 158, 1)",
color: 'rgba(255, 77, 79, 1)' color: "rgba(255, 77, 79, 1)"
} }
} }
}] }
]
}; };
return option return option;
} }
export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)', 'rgba(255, 159, 22, 0)'], name) => { export const getBarChart = (nameList, valueList, color = ["rgba(255, 159, 22, 1)", "rgba(255, 159, 22, 0)"], name) => {
const option = { const option = {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
formatter: function (params) { formatter: function (params) {
let result = params[0].name + '<br/>'; let result = params[0].name + "<br/>";
params.forEach(function (item, index) { params.forEach(function (item, index) {
// 自定义颜色数组 // 自定义颜色数组
const customColors = [color[0]]; const customColors = [color[0]];
...@@ -420,10 +458,10 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1) ...@@ -420,10 +458,10 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)
} }
}, },
grid: { grid: {
top: '3%', top: "3%",
right: '3%', right: "3%",
bottom: '1%', bottom: "1%",
left: '1%', left: "1%",
containLabel: true containLabel: true
}, },
xAxis: { xAxis: {
...@@ -433,12 +471,11 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1) ...@@ -433,12 +471,11 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
} }
}, },
axisTick: axisTick: { show: false },
{ show: false },
type: "category", type: "category",
boundaryGap: [100, 100], boundaryGap: [100, 100],
axisLabel: { axisLabel: {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)"
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
}, },
...@@ -453,7 +490,7 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1) ...@@ -453,7 +490,7 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)
}, },
axisLabel: { axisLabel: {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)"
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
}, },
...@@ -463,20 +500,20 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1) ...@@ -463,20 +500,20 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)
width: 1, width: 1,
type: "dashed", type: "dashed",
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
}, }
} }
}, },
series: [{ series: [
{
name: name, name: name,
type: 'bar', type: "bar",
data: valueList, data: valueList,
barWidth: 12, barWidth: 12,
itemStyle: { itemStyle: {
color: function (params) { color: function (params) {
return new echarts.graphic.LinearGradient(0, 1, 0, 0, return new echarts.graphic.LinearGradient(0, 1, 0, 0, [
[{ {
offset: 0, offset: 0,
color: color[1] color: color[1]
}, },
...@@ -486,12 +523,13 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1) ...@@ -486,12 +523,13 @@ export const getBarChart = (nameList, valueList, color = ['rgba(255, 159, 22, 1)
} }
]); ]);
}, },
barBorderRadius: 10, barBorderRadius: 10
} }
}]
} }
return option ]
} };
return option;
};
export const getLineChart = (object, isPercent) => { export const getLineChart = (object, isPercent) => {
const option = { const option = {
title: { title: {
...@@ -500,23 +538,23 @@ export const getLineChart = (object, isPercent) => { ...@@ -500,23 +538,23 @@ export const getLineChart = (object, isPercent) => {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
formatter: function (params) { formatter: function (params) {
let result = params[0].name + '<br/>'; let result = params[0].name + "<br/>";
params.forEach(function (item, index) { params.forEach(function (item, index) {
// 自定义颜色数组 // 自定义颜色数组
const customColors = [object.color]; const customColors = [object.color];
const dotColor = customColors[index % customColors.length]; // 循环取色 const dotColor = customColors[index % customColors.length]; // 循环取色
// 创建彩色圆点图标 // 创建彩色圆点图标
const dot = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${dotColor};"></span>`; const dot = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${dotColor};"></span>`;
result += dot + `${item.seriesName}: ${item.value}${isPercent ? '%' : ''}<br/>`; result += dot + `${item.seriesName}: ${item.value}${isPercent ? "%" : ""}<br/>`;
}); });
return result; return result;
} }
}, },
grid: { grid: {
top: '3%', top: "3%",
right: '3%', right: "3%",
bottom: '1%', bottom: "1%",
left: '1%', left: "1%",
containLabel: true containLabel: true
}, },
// toolbox: { // toolbox: {
...@@ -531,12 +569,11 @@ export const getLineChart = (object, isPercent) => { ...@@ -531,12 +569,11 @@ export const getLineChart = (object, isPercent) => {
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
} }
}, },
axisTick: axisTick: { show: false },
{ show: false },
type: "category", type: "category",
boundaryGap: [100, 100], boundaryGap: [100, 100],
axisLabel: { axisLabel: {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)"
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
}, },
...@@ -554,7 +591,7 @@ export const getLineChart = (object, isPercent) => { ...@@ -554,7 +591,7 @@ export const getLineChart = (object, isPercent) => {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)",
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
formatter: `{value} ${isPercent ? '%' : ''}` formatter: `{value} ${isPercent ? "%" : ""}`
}, },
splitNumber: 8, splitNumber: 8,
...@@ -563,8 +600,7 @@ export const getLineChart = (object, isPercent) => { ...@@ -563,8 +600,7 @@ export const getLineChart = (object, isPercent) => {
width: 1, width: 1,
type: "dashed", type: "dashed",
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
}, }
} }
}, },
series: [ series: [
...@@ -572,42 +608,42 @@ export const getLineChart = (object, isPercent) => { ...@@ -572,42 +608,42 @@ export const getLineChart = (object, isPercent) => {
name: object.name, name: object.name,
type: "line", type: "line",
symbolSize: 8, symbolSize: 8,
symbol: 'circle', symbol: "circle",
itemStyle: { itemStyle: {
color: "#ffffff", color: "#ffffff",
borderColor: object.color, borderColor: object.color,
borderWidth: 3 borderWidth: 3
}, },
lineStyle: { lineStyle: {
color: object.color, color: object.color
}, },
data: object.seriesData data: object.seriesData
} }
] ]
}; };
return option; return option;
} };
export const getHorizontalBarChart1 = (nameList, valueList, isPer) => { export const getHorizontalBarChart1 = (nameList, valueList, isPer) => {
const colorList = ['#ce4f51', '#1778ff'] const colorList = ["#ce4f51", "#1778ff"];
const option = { const option = {
tooltip: {}, tooltip: {},
grid: { grid: {
top: '3%', top: "3%",
right: '3%', right: "3%",
bottom: '1%', bottom: "1%",
left: '1%', left: "1%",
containLabel: true containLabel: true
}, },
color: ['#ce4f51', '#1778ff'], color: ["#ce4f51", "#1778ff"],
xAxis: { xAxis: {
type: 'value', type: "value",
splitLine: { splitLine: {
show: false show: false
}, },
show: false show: false
}, },
yAxis: { yAxis: {
type: 'category', type: "category",
data: nameList, data: nameList,
splitLine: { splitLine: {
show: false show: false
...@@ -622,14 +658,15 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => { ...@@ -622,14 +658,15 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => {
show: true show: true
} }
}, },
series: [{ series: [
type: 'bar', {
type: "bar",
data: valueList.map((item, index) => { data: valueList.map((item, index) => {
return { return {
value: item, value: item,
label: { label: {
textStyle: { textStyle: {
color: index < 4 ? '#1778ff' : '#ce4f51' color: index < 4 ? "#1778ff" : "#ce4f51"
} }
} }
}; };
...@@ -638,17 +675,17 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => { ...@@ -638,17 +675,17 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => {
show: true, show: true,
position: [450, -2], position: [450, -2],
formatter: function (params) { formatter: function (params) {
return isPer ? params.value + '%' : params.value return isPer ? params.value + "%" : params.value;
} }
}, },
barWidth: 8, barWidth: 8,
itemStyle: { itemStyle: {
color: function (params) { color: function (params) {
if (params.dataIndex < 4) { if (params.dataIndex < 4) {
return new echarts.graphic.LinearGradient(0, 0, 1, 0, return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
[{ {
offset: 0, offset: 0,
color: 'rgba(22, 119, 255, 0)' color: "rgba(22, 119, 255, 0)"
}, },
{ {
offset: 1, offset: 1,
...@@ -656,10 +693,10 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => { ...@@ -656,10 +693,10 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => {
} }
]); ]);
} else { } else {
return new echarts.graphic.LinearGradient(0, 0, 1, 0, return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
[{ {
offset: 0, offset: 0,
color: 'rgba(206, 79, 81, 0)' color: "rgba(206, 79, 81, 0)"
}, },
{ {
offset: 1, offset: 1,
...@@ -667,42 +704,42 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => { ...@@ -667,42 +704,42 @@ export const getHorizontalBarChart1 = (nameList, valueList, isPer) => {
} }
]); ]);
} }
}, },
barBorderRadius: 4, barBorderRadius: 4
} }
}]
} }
return option ]
} };
return option;
};
export const getHorizontalBarChart2 = (nameList, valueList, isPer) => { export const getHorizontalBarChart2 = (nameList, valueList, isPer) => {
const colorList = [ const colorList = [
['rgba(64, 150, 255, 1)', 'rgba(64, 150, 255, 0)'], ["rgba(64, 150, 255, 1)", "rgba(64, 150, 255, 0)"],
['rgba(255, 120, 117, 1)', 'rgba(255, 120, 117, 0)'], ["rgba(255, 120, 117, 1)", "rgba(255, 120, 117, 0)"],
['rgba(89, 126, 247, 1)', 'rgba(89, 126, 247, 0)'], ["rgba(89, 126, 247, 1)", "rgba(89, 126, 247, 0)"],
['rgba(54, 207, 201, 1)', 'rgba(54, 207, 201, 0)'], ["rgba(54, 207, 201, 1)", "rgba(54, 207, 201, 0)"],
['rgba(255, 197, 61, 1)', 'rgba(255, 197, 61, 0)'], ["rgba(255, 197, 61, 1)", "rgba(255, 197, 61, 0)"],
['rgba(179, 127, 235, 1)', 'rgba(179, 127, 235, 0)'] ["rgba(179, 127, 235, 1)", "rgba(179, 127, 235, 0)"]
] ];
console.log(colorList) console.log(colorList);
const option = { const option = {
tooltip: {}, tooltip: {},
grid: { grid: {
top: '6%', top: "6%",
right: '6%', right: "6%",
bottom: '0', bottom: "0",
left: '1%', left: "1%",
containLabel: true containLabel: true
}, },
xAxis: { xAxis: {
type: 'value', type: "value",
splitLine: { splitLine: {
show: false show: false
}, },
show: false show: false
}, },
yAxis: { yAxis: {
type: 'category', type: "category",
data: nameList, data: nameList,
splitLine: { splitLine: {
show: false show: false
...@@ -717,14 +754,15 @@ export const getHorizontalBarChart2 = (nameList, valueList, isPer) => { ...@@ -717,14 +754,15 @@ export const getHorizontalBarChart2 = (nameList, valueList, isPer) => {
show: true show: true
} }
}, },
series: [{ series: [
type: 'bar', {
type: "bar",
data: valueList.map((item, index) => { data: valueList.map((item, index) => {
return { return {
value: item, value: item,
label: { label: {
textStyle: { textStyle: {
color: colorList[index][0] color: colorList[index % 6][0]
} }
} }
}; };
...@@ -733,33 +771,33 @@ export const getHorizontalBarChart2 = (nameList, valueList, isPer) => { ...@@ -733,33 +771,33 @@ export const getHorizontalBarChart2 = (nameList, valueList, isPer) => {
show: true, show: true,
position: [340, -2], position: [340, -2],
formatter: function (params) { formatter: function (params) {
return isPer ? params.value + '%' : params.value return isPer ? params.value + "%" : params.value;
} }
}, },
barWidth: 8, barWidth: 8,
itemStyle: { itemStyle: {
color: function (params) { color: function (params) {
console.log('params', params) console.log("params", params);
return new echarts.graphic.LinearGradient(0, 0, 1, 0, return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
[{ {
offset: 0, offset: 0,
color: colorList[params.dataIndex][1] color: colorList[params.dataIndex % 6][1]
}, },
{ {
offset: 1, offset: 1,
color: colorList[params.dataIndex][0] color: colorList[params.dataIndex % 6][0]
} }
]); ]);
}, },
barBorderRadius: 4, barBorderRadius: 4
} }
}]
} }
return option ]
} };
export const getMultipleLineChart = (obj) => { return option;
const color = ['rgba(19, 168, 168, 1)', 'rgba(146, 84, 222, 1)', 'rgba(250, 140, 22, 1)', 'rgba(206, 79, 81, 1)'] };
export const getMultipleLineChart = obj => {
const color = ["rgba(19, 168, 168, 1)", "rgba(146, 84, 222, 1)", "rgba(250, 140, 22, 1)", "rgba(206, 79, 81, 1)"];
const option = { const option = {
title: { title: {
text: "" text: ""
...@@ -767,7 +805,7 @@ export const getMultipleLineChart = (obj) => { ...@@ -767,7 +805,7 @@ export const getMultipleLineChart = (obj) => {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
formatter: function (params) { formatter: function (params) {
let result = params[0].name + '<br/>'; let result = params[0].name + "<br/>";
params.forEach(function (item, index) { params.forEach(function (item, index) {
// 自定义颜色数组 // 自定义颜色数组
const customColors = color; const customColors = color;
...@@ -780,10 +818,10 @@ export const getMultipleLineChart = (obj) => { ...@@ -780,10 +818,10 @@ export const getMultipleLineChart = (obj) => {
} }
}, },
grid: { grid: {
top: '12%', top: "12%",
right: '3%', right: "3%",
bottom: '3%', bottom: "3%",
left: '1%', left: "1%",
containLabel: true containLabel: true
}, },
legend: { legend: {
...@@ -797,7 +835,7 @@ export const getMultipleLineChart = (obj) => { ...@@ -797,7 +835,7 @@ export const getMultipleLineChart = (obj) => {
}, },
itemGap: 17, itemGap: 17,
data: obj.data.map((item, index) => { data: obj.data.map((item, index) => {
return { name: item.name, itemStyle: { color: color[index] } } return { name: item.name, itemStyle: { color: color[index] } };
}) })
}, },
xAxis: { xAxis: {
...@@ -807,12 +845,11 @@ export const getMultipleLineChart = (obj) => { ...@@ -807,12 +845,11 @@ export const getMultipleLineChart = (obj) => {
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
} }
}, },
axisTick: axisTick: { show: false },
{ show: false },
type: "category", type: "category",
boundaryGap: [100, 100], boundaryGap: [100, 100],
axisLabel: { axisLabel: {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)"
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
}, },
...@@ -827,7 +864,7 @@ export const getMultipleLineChart = (obj) => { ...@@ -827,7 +864,7 @@ export const getMultipleLineChart = (obj) => {
}, },
axisLabel: { axisLabel: {
color: "rgba(95, 101, 108, 1)", color: "rgba(95, 101, 108, 1)"
// fontSize: 22, // fontSize: 22,
// fontWeight: 400 // fontWeight: 400
}, },
...@@ -837,8 +874,7 @@ export const getMultipleLineChart = (obj) => { ...@@ -837,8 +874,7 @@ export const getMultipleLineChart = (obj) => {
width: 1, width: 1,
type: "dashed", type: "dashed",
color: "rgba(231, 243, 255, 1)" color: "rgba(231, 243, 255, 1)"
}, }
} }
}, },
series: obj.data.map((item, index) => { series: obj.data.map((item, index) => {
...@@ -846,7 +882,7 @@ export const getMultipleLineChart = (obj) => { ...@@ -846,7 +882,7 @@ export const getMultipleLineChart = (obj) => {
name: item.name, name: item.name,
type: "line", type: "line",
symbolSize: 6, symbolSize: 6,
symbol: 'circle', symbol: "circle",
itemStyle: { itemStyle: {
color: "#ffffff", color: "#ffffff",
borderColor: color[index], borderColor: color[index],
...@@ -854,30 +890,30 @@ export const getMultipleLineChart = (obj) => { ...@@ -854,30 +890,30 @@ export const getMultipleLineChart = (obj) => {
}, },
lineStyle: { lineStyle: {
width: 1, width: 1,
color: color[index], color: color[index]
}, },
data: item.value data: item.value
} };
}) })
}; };
return option; return option;
} };
//出口管制主页接口 //出口管制主页接口
export const getMultipleBarChart_m = (object) => { export const getMultipleBarChart_m = object => {
const list = _.chain(object.data).filter('year').orderBy('year', 'asc').value(); const list = _.chain(object.data).filter("year").orderBy("year", "asc").value();
const colors = [ const colors = [
['rgba(22, 119, 255, 1)', 'rgba(22, 119, 255, 0)'], ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"],
['rgba(206, 79, 81, 1)', 'rgba(206, 79, 81, 0)'], ["rgba(206, 79, 81, 1)", "rgba(206, 79, 81, 0)"],
['rgba(255, 197, 61, 1)', 'rgba(255, 197, 61, 0)'], ["rgba(255, 197, 61, 1)", "rgba(255, 197, 61, 0)"],
['rgba(255, 204, 199, 1)', 'rgba(255, 204, 199, 0)'], ["rgba(255, 204, 199, 1)", "rgba(255, 204, 199, 0)"],
['rgba(179, 127, 235, 1)', 'rgba(179, 127, 235, 0)'], ["rgba(179, 127, 235, 1)", "rgba(179, 127, 235, 0)"],
['rgba(127, 218, 235, 1)', 'rgba(127, 214, 235, 0)'], ["rgba(127, 218, 235, 1)", "rgba(127, 214, 235, 0)"]
]; ];
const names = _.map(list, 'year'); const names = _.map(list, "year");
const datas = _.chain(object.domains).splice(0, 6).map((name, index) => { const datas = _.chain(object.domains)
console.log(_.map(list, name)) .splice(0, 6)
.map((name, index) => {
console.log(_.map(list, name));
return { return {
name, name,
data: _.map(list, `domainNum.${name}`), data: _.map(list, `domainNum.${name}`),
...@@ -891,9 +927,10 @@ export const getMultipleBarChart_m = (object) => { ...@@ -891,9 +927,10 @@ export const getMultipleBarChart_m = (object) => {
]), ]),
borderRadius: [6, 6, 0, 0] borderRadius: [6, 6, 0, 0]
} }
} };
}).value(); })
console.log('names', names) .value();
console.log("names", names);
const option = { const option = {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
...@@ -908,7 +945,7 @@ export const getMultipleBarChart_m = (object) => { ...@@ -908,7 +945,7 @@ export const getMultipleBarChart_m = (object) => {
// type: "scroll", // type: "scroll",
// show: true, // show: true,
// orient: "horizontal", // orient: "horizontal",
icon: "circle", icon: "circle"
}, },
xAxis: { xAxis: {
type: "category", type: "category",
...@@ -918,7 +955,6 @@ export const getMultipleBarChart_m = (object) => { ...@@ -918,7 +955,6 @@ export const getMultipleBarChart_m = (object) => {
type: "value" type: "value"
}, },
series: datas series: datas
} };
return option; return option;
} };
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div class="divider"></div> <div class="divider"></div>
<div class="thematic-content"> <div class="thematic-content">
<div class="item-header"> <div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="../assets/icon1.png"></img>
<div class="item-header-text">科技博弈历程</div> <div class="item-header-text">科技博弈历程</div>
</div> </div>
......
...@@ -13,10 +13,12 @@ ...@@ -13,10 +13,12 @@
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科技人才对比分析</div> <div class="item-header-text">科技人才对比分析</div>
<div class="item-header-btn"> <div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img> <img class="item-header-btn-icon" src="@/assets/images/icon/header-btn.png"></img>
<div style="">
数据来源:美国某某发展基金会 数据来源:美国某某发展基金会
</div> </div>
</div> </div>
</div>
<div class="divider"></div> <div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char4"> <div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char4">
</div> </div>
...@@ -31,9 +33,11 @@ ...@@ -31,9 +33,11 @@
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科研投入对比分析</div> <div class="item-header-text">科研投入对比分析</div>
<div class="item-header-btn"> <div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img> <img class="item-header-btn-icon" src="@/assets/images/icon/header-btn.png"></img>
<div style="">
数据来源:美国某某发展基金会 数据来源:美国某某发展基金会
</div> </div>
</div>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
...@@ -55,10 +59,12 @@ ...@@ -55,10 +59,12 @@
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">创新主体对比分析</div> <div class="item-header-text">创新主体对比分析</div>
<div class="item-header-btn"> <div class="item-header-btn">
<img class="item-header-icon" src="@/assets/images/icon/header-btn.png"></img> <img class="item-header-btn-icon" src="@/assets/images/icon/header-btn.png"></img>
<div style="">
数据来源:美国某某发展基金会 数据来源:美国某某发展基金会
</div> </div>
</div> </div>
</div>
<div class="divider"></div> <div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char6"> <div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char6">
</div> </div>
...@@ -73,21 +79,23 @@ ...@@ -73,21 +79,23 @@
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img> <img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科学数据对比分析</div> <div class="item-header-text">科学数据对比分析</div>
<div class="item-header-btn"> <div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img> <img class="item-header-btn-icon" src="@/assets/images/icon/header-btn.png"></img>
<div style="">
数据来源:美国某某发展基金会 数据来源:美国某某发展基金会
</div> </div>
</div>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
<div style="display: flex;"> <div style="display: flex;">
<div style="display: flex;height: 320px;width: calc(50% - 40px) ;margin: 20px;" id="char7"> <div style="display: flex;height: 360px;width: 433px ; " id="char7">
</div> </div>
<div style="width: 50%;padding-top: 50px;"> <div style="width: 50%;padding-top: 34px;">
<div v-for="value in radar2Data" class="radar2Data-line"> <div v-for="value in radar2Data" class="radar2Data-line">
<div class="radar2Data-circle" :style="{ backgroundColor: value.color }"></div> <div class="radar2Data-circle" :style="{ backgroundColor: value.color }"></div>
<div style=" width: 40px;margin: 0 7px">{{ value.name }}</div> <div style=" margin: 0 5px;font-size: 16px;">{{ value.name }}</div>
<div style="width: calc(100% - 150px) ;padding: 10px;"> <el-progress :percentage="value.percent" <div style="width: 168px ; "> <el-progress :percentage="value.percent" :color="value.color"
:color="value.color" :show-text="false" /></div> :show-text="false" /></div>
<div style=" width: 70px;text-align: right;">{{ value.percent < 50 ? '低依赖' : value.percent < 80 <div style=" width: 70px;text-align: right;">{{ value.percent < 50 ? '低依赖' : value.percent < 80
? '中度依赖' : '高度依赖' }} </div> ? '中度依赖' : '高度依赖' }} </div>
</div> </div>
...@@ -312,7 +320,6 @@ onMounted(() => { ...@@ -312,7 +320,6 @@ onMounted(() => {
display: flex; display: flex;
color: rgba(132, 136, 142, 1); color: rgba(132, 136, 142, 1);
width: calc(100% - 210px);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
...@@ -322,10 +329,23 @@ onMounted(() => { ...@@ -322,10 +329,23 @@ onMounted(() => {
text-align: right; text-align: right;
} }
.item-header-ibtn-con { .item-header-btn-icon {
width: 16px; width: 14px;
height: 16px; height: 16px;
margin-top: 15px; margin-top: 15px;
margin-left: 270px;
margin-right: 4px;
}
.item-header-btn-text {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
} }
} }
...@@ -384,13 +404,17 @@ onMounted(() => { ...@@ -384,13 +404,17 @@ onMounted(() => {
width: 100%; width: 100%;
height: 30px; height: 30px;
margin-top: 18px; margin-top: 18px;
align-items: center;
/* 垂直居中 */
color: rgba(95, 101, 108, 1); color: rgba(95, 101, 108, 1);
font-size: 16px;
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400; font-weight: 400;
line-height: 24px; line-height: 22px;
letter-spacing: 0px; letter-spacing: 0px;
align-items: center; text-align: right;
/* 垂直居中 */
} }
...@@ -400,4 +424,9 @@ onMounted(() => { ...@@ -400,4 +424,9 @@ onMounted(() => {
height: 12px; height: 12px;
border-radius: 50%; border-radius: 50%;
} }
:deep(.el-progress-bar__outer) {
height: 8px !important;
}
</style> </style>
...@@ -20,13 +20,13 @@ ...@@ -20,13 +20,13 @@
</div> </div>
<!-- 卡片 --> <!-- 卡片 -->
<div class="card" :class="[cardPos(item), 'right-side']" :style="widthStyle()" <div class="card" :class="[cardPos(item), 'right-side']" @click="$emit('click-card', item)">
@click="$emit('click-card', item)">
<img :src="`/icon/${item.unit}.png`" class="icon"></img> <img :src="`/icon/${item.unit}.png`" class="icon"></img>
<div class="title">{{ item.time }}</div>
<div class="title" :style="{ <div class="title" :style="{
color: item.unit === '中国' ? ' #CF4F51' : '' color: item.unit === '中国' ? ' #CF4F51' : ''
}">{{ item.title }}</div> }">{{ item.title }}</div>
<div class="time">{{ item.time }}</div>
<div class="content">{{ item.content }}</div> <div class="content">{{ item.content }}</div>
</div> </div>
</div> </div>
...@@ -50,7 +50,15 @@ export default { ...@@ -50,7 +50,15 @@ export default {
}, },
/* 水平位置:按索引均匀分布 */ /* 水平位置:按索引均匀分布 */
leftStyle(i) { leftStyle(i) {
return { left: `${(i * 100) / (this.data.length - 1)}vw` }; // let pos = ``
// if (i === 0) {
// pos = 0
// } else {
// this.linePos(this.data[i - 1]) !== this.linePos(this.data[i]) ? pos = { left: `${(i * 270) - 125}px` } : pos = { left: `${(i * 270)}px` }
// }
return { left: `${(i * 270)}px` }
// return pos;
}, },
/* 卡片上下位置:unit=0 -> 下侧,其余 -> 上侧 */ /* 卡片上下位置:unit=0 -> 下侧,其余 -> 上侧 */
cardPos(item) { cardPos(item) {
...@@ -166,28 +174,30 @@ export default { ...@@ -166,28 +174,30 @@ export default {
.dot.up::after { .dot.up::after {
bottom: 100%; bottom: 100%;
height: 180px height: 240px
} }
.dot.down::after { .dot.down::after {
top: 100%; top: 100%;
height: 180px height: 240px
} }
.card { .card {
position: absolute; position: absolute;
height: 180px; /* 容器 28 */
width: 220px;
height: 176px;
padding: 8px 20px; padding: 8px 20px;
font-size: 14px; font-size: 14px;
cursor: pointer cursor: pointer
} }
.card.up { .card.up {
bottom: 20px bottom: 86px
} }
.card.down { .card.down {
top: 20px; top: 94px;
} }
...@@ -196,15 +206,39 @@ export default { ...@@ -196,15 +206,39 @@ export default {
} }
.title { .title {
/* 美国进一步收紧对华AI芯片出口限制 */
width: 220px;
color: #055fc2; color: #055fc2;
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
line-height: 26px line-height: 26px;
margin-top: -25px;
margin-left: 10px;
}
.time {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
margin-left: 10px;
} }
.content { .content {
color: #5f656c; width: 220px;
height: 90px;
color: rgba(59, 65, 75, 1);
margin-left: 10px;
font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
line-height: 24px font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
} }
</style> </style>
\ No newline at end of file
...@@ -46,13 +46,16 @@ ...@@ -46,13 +46,16 @@
</div> </div>
<div class="item-card-right"> <div class="item-card-right">
<div style="overflow: auto;height: 400px;"> <div style=" height: 400px;">
<div v-for="(item, index) in technologicalTrends.data" :key="index" style="height: 46px;"> <div v-for="(item, index) in technologicalTrends.data" :key="index" style="height: 46px;">
<div class="list-row"> <div class="list-row">
<div class="item-list-punblier">{{ item.tag1 }}</div> <div class="item-list-punblier">{{ item.tag1 }}</div>
<div class="item-list-content">{{ item.text }}</div> <div class="item-list-content">{{ item.text }}</div>
<div class="item-list-time">{{ item.time }}</div> <div class="item-list-time">{{ item.time }}</div>
<div class="item-list-punblier">{{ item.tag2 }}</div> <div class="item-list-punblier"
:style="{ color: item.tagColor, backgroundColor: item.tagBg, borderColor: item.color }">{{
item.tag2 }}
</div>
</div> </div>
<div class="item-header-divider" /> <div class="item-header-divider" />
</div> </div>
...@@ -130,14 +133,70 @@ const technologicalTrends = ref({ ...@@ -130,14 +133,70 @@ const technologicalTrends = ref({
txt: '2025年9月14日,欧盟委员会宣布通过“地平线欧洲”2025年工作计划投入约73亿欧元的专项资金,增强欧洲的科研创新引擎和', txt: '2025年9月14日,欧盟委员会宣布通过“地平线欧洲”2025年工作计划投入约73亿欧元的专项资金,增强欧洲的科研创新引擎和',
}, },
data: [ data: [
{ tag1: '科研仪器', text: '欧盟投资73亿欧元推进数字化转型', time: '1小时前', tag2: '新能源' }, {
{ tag1: '创新主体', text: '美财政部发布拟议规则限制对华网...', time: '3小时前', tag2: '人工智能' }, "tag1": "科研仪器",
{ tag1: '科技人才', text: '美NIST发布《美国关键和新兴技术....', time: '昨天', tag2: '量子科技' }, "text": "欧盟投资73亿欧元推进数字化转型",
{ tag1: '创新主体', text: '美《开创未来先进计算生态系统:战...', time: '昨天', tag2: '人工智能' }, "time": "1小时前",
{ tag1: '科研仪器', text: '欧盟启动初代“数字地球”系统..', time: '昨天', tag2: '量子科技' }, "tag2": "新能源",
{ tag1: '科研仪器', text: '美NSF投建国家AI可编程云实验室网...', time: '昨天', tag2: '新能源' }, "tagColor": "rgba(250, 140, 22, 1)",
{ tag1: '科研仪器', text: '英启动全球顶尖科技人才引进计划瞄...', time: '昨天', tag2: '新能源' }, "tagBg": "rgba(255, 247, 230, 1)"
{ tag1: '科研仪器', text: '美国家科学基金会致力改进下一代无...', time: '昨天', tag2: '新能源' }, },
{
"tag1": "创新主体",
"text": "美财政部发布拟议规则限制对华网...",
"time": "3小时前",
"tag2": "人工智能",
"tagColor": "rgba(114, 46, 209, 1)",
"tagBg": "rgba(249, 240, 255, 1)"
},
{
"tag1": "科技人才",
"text": "美NIST发布《美国关键和新兴技术....",
"time": "昨天",
"tag2": "量子科技",
"tagColor": "rgba(206, 79, 81, 1)",
"tagBg": "rgba(255, 241, 240, 1)"
},
{
"tag1": "创新主体",
"text": "美《开创未来先进计算生态系统:战...",
"time": "昨天",
"tag2": "人工智能",
"tagColor": "rgba(114, 46, 209, 1)",
"tagBg": "rgba(249, 240, 255, 1)"
},
{
"tag1": "科研仪器",
"text": "欧盟启动初代“数字地球”系统..",
"time": "昨天",
"tag2": "量子科技",
"tagColor": "rgba(206, 79, 81, 1)",
"tagBg": "rgba(255, 241, 240, 1)"
},
{
"tag1": "科研仪器",
"text": "美NSF投建国家AI可编程云实验室网...",
"time": "昨天",
"tag2": "新能源",
"tagColor": "rgba(250, 140, 22, 1)",
"tagBg": "rgba(255, 247, 230, 1)"
},
{
"tag1": "科研仪器",
"text": "英启动全球顶尖科技人才引进计划瞄...",
"time": "昨天",
"tag2": "新能源",
"tagColor": "rgba(250, 140, 22, 1)",
"tagBg": "rgba(255, 247, 230, 1)"
},
{
"tag1": "科研仪器",
"text": "美国家科学基金会致力改进下一代无...",
"time": "昨天",
"tag2": "新能源",
"tagColor": "rgba(250, 140, 22, 1)",
"tagBg": "rgba(255, 247, 230, 1)"
}
], ],
}); });
...@@ -158,10 +217,16 @@ const handleToMoreRiskSignal = () => { ...@@ -158,10 +217,16 @@ const handleToMoreRiskSignal = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
.content-main { .content-main {
width: 100%; // width: 100%;
height: 100%; // height: 100%;
overflow: hidden; // overflow: hidden;
font-family: Microsoft YaHei; // font-family: Microsoft YaHei;
width: 1920px;
margin: 0 auto;
background: url("./assets/bg.png");
background-repeat: no-repeat;
background-color: #fff;
background-size: contain;
} }
.content-title { .content-title {
...@@ -355,7 +420,7 @@ const handleToMoreRiskSignal = () => { ...@@ -355,7 +420,7 @@ const handleToMoreRiskSignal = () => {
.list-row { .list-row {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 10px 0; margin: 5px 0;
height: 40px; height: 40px;
...@@ -382,6 +447,7 @@ const handleToMoreRiskSignal = () => { ...@@ -382,6 +447,7 @@ const handleToMoreRiskSignal = () => {
.item-list-time { .item-list-time {
width: 68px; width: 68px;
text-align: right; text-align: right;
margin-right: 8px;
color: rgba(132, 136, 142, 1); color: rgba(132, 136, 142, 1);
} }
} }
......
...@@ -31,14 +31,22 @@ const getColumnChart = (nameList, series1, series2, isPer) => { ...@@ -31,14 +31,22 @@ const getColumnChart = (nameList, series1, series2, isPer) => {
axisLine: { show: false }, axisLine: { show: false },
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { show: true, color: '#666' }, axisLabel: { show: true, color: '#666' },
splitLine: { show: true, lineStyle: { color: '#ebebeb' } } splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "#E7F3FF"
}
},
}, },
series: [ series: [
{ {
name: '研究型大学', name: '研究型大学',
type: 'bar', type: 'bar',
data: series1, data: series1,
barWidth: 8, barWidth: 12,
barGap: '60%', // 同一类别内不同系列的间隔
barCategoryGap: "-60%",
label: { show: false }, label: { show: false },
itemStyle: { borderRadius: [8, 8, 0, 0], color: gradBlue } itemStyle: { borderRadius: [8, 8, 0, 0], color: gradBlue }
}, },
...@@ -46,7 +54,7 @@ const getColumnChart = (nameList, series1, series2, isPer) => { ...@@ -46,7 +54,7 @@ const getColumnChart = (nameList, series1, series2, isPer) => {
name: '科技企业', name: '科技企业',
type: 'bar', type: 'bar',
data: series2, data: series2,
barWidth: 8, barWidth: 12,
label: { show: false }, label: { show: false },
itemStyle: { borderRadius: [8, 8, 0, 0], color: gradCyan } itemStyle: { borderRadius: [8, 8, 0, 0], color: gradCyan }
}, },
...@@ -54,7 +62,7 @@ const getColumnChart = (nameList, series1, series2, isPer) => { ...@@ -54,7 +62,7 @@ const getColumnChart = (nameList, series1, series2, isPer) => {
name: '研究机构', name: '研究机构',
type: 'bar', type: 'bar',
data: series2, data: series2,
barWidth: 8, barWidth: 12,
label: { show: false }, label: { show: false },
itemStyle: { borderRadius: [8, 8, 0, 0], color: '#FFC63D' } itemStyle: { borderRadius: [8, 8, 0, 0], color: '#FFC63D' }
} }
......
...@@ -67,7 +67,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5) => { ...@@ -67,7 +67,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5) => {
return { return {
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } }, tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
grid: { top: '5%', right: '0%', bottom: '0%', left: '0%', containLabel: true }, grid: { top: '5%', right: '10%', bottom: '0%', left: '0%', containLabel: true },
xAxis: { type: 'category', boundaryGap: false, data: dataX }, xAxis: { type: 'category', boundaryGap: false, data: dataX },
yAxis: { yAxis: {
......
import * as echarts from "echarts"; import * as echarts from "echarts";
const colors = [
const getBarChart = (nameList, valueList, isPer) => { 'rgba(165, 42, 42, 1)', // 红色
'rgba(0, 0, 128, 1)', // 蓝色
'rgba(0, 128, 128, 1)', // 青色
'rgba(75, 0, 130, 1)', // 紫色
'rgba(255, 165, 0, 1)', // 橙色
'rgba(173, 216, 230, 1)' // 浅蓝色
];
const getRadarChart = () => {
const option = { const option = {
title: { text: '' },
tooltip: {},
radar: { radar: {
radius: '50%', // 关键:缩小整个雷达 radius: '50%',
center: ['50%', '45%'], // 可选:再往下挪一点,避免图例挤在一起 center: ['50%', '45%'],
indicator: [ indicator: [
{ name: '能源', max: 6500 }, { name: '能源领域', max: 100 },
{ name: '集成电路', max: 16000 }, { name: '集成电路', max: 100 },
{ name: '人工智能', max: 30000 }, { name: '生物科技', max: 100 },
{ name: '通信网络', max: 38000 }, { name: '人工智能', max: 100 },
{ name: '量子科技', max: 52000 }, { name: '通信网络', max: 100 },
{ name: '生物科技', max: 25000 } { name: '量子科技', max: 100 }
], ],
axisName: { axisName: {
formatter: '{value}', formatter: '{value}',
color: 'rgba(59, 65, 75, 1)', color: 'rgba(59, 65, 75, 1)',
fontSize: 14, fontSize: 16,
fontWeight: 400 fontWeight: 400
} }
}, },
series: [ series: [
{ {
name: 'Budget vs spending', name: '科技领域表现',
type: 'radar', type: 'radar',
symbol: 'none', symbol: 'none',
data: [ data: [
{ {
value: [4200, 3000, 20000, 35000, 50000, 18000], "value": [20, 95, 30, 85, 70, 60],
name: '中国', "name": "中国",
areaStyle: { color: 'rgba(10, 87, 166, 0.2)' } "areaStyle": { "color": "rgba(165, 42, 42, 0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(165, 42, 42, 1)' // 设置线条颜色
}
}, },
{ {
value: [5000, 14000, 28000, 26000, 42000, 21000], "value": [90, 40, 85, 20, 60, 75],
name: '美国', "name": "美国",
areaStyle: { color: 'rgba(206, 79, 81, 0.2)' } "areaStyle": { "color": "rgba(0, 0, 128, 0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(0, 0, 128, 1)' // 设置线条颜色
}
}, },
{ {
value: [4000, 14000, 18000, 21000, 32000, 10000], "value": [80, 68, 92, 82, 58, 88],
name: '欧盟', "name": "欧盟",
areaStyle: { color: 'rgba(250, 140, 22, 0.2)' } "areaStyle": { "color": "rgba(0, 128, 128, 0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(0, 128, 128, 1)' // 设置线条颜色
}
}, },
{ {
value: [4000, 14000, 18000, 21000, 32000, 10000], "value": [57, 81, 76, 91, 87, 67],
name: '英国', "name": "英国",
areaStyle: { color: 'rgba(250, 140, 22, 0.2)' } "areaStyle": { "color": "rgba(75, 0, 130, 0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(75, 0, 130, 1)' // 设置线条颜色
}
}, },
{ {
value: [4000, 14000, 18000, 21000, 32000, 10000], "value": [93, 59, 79, 89, 69, 83],
name: '日本', "name": "日本",
areaStyle: { color: 'rgba(250, 140, 22, 0.2)' } "areaStyle": { "color": "rgba(255, 165, 0, 0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(255, 165, 0, 1)' // 设置线条颜色
}
}, },
{ {
value: [4000, 14000, 18000, 21000, 32000, 10000], "value": [86, 56, 77, 94, 80, 66],
name: '韩国', "name": "韩国",
areaStyle: { color: 'rgba(250, 140, 22, 0.2)' } "areaStyle": { "color": "rgba(173, 216, 230,0.2)" },
lineStyle: {
width: 1.5, // 设置线条宽度为2
color: 'rgba(173, 216, 230, 1)' // 设置线条颜色
} }
]
} }
] ]
} }
return option ]
} };
return option;
};
export default getBarChart export default getRadarChart;
\ No newline at end of file \ No newline at end of file
<!-- SourceLibrary.vue --> <!-- SourceLibrary.vue -->
<template> <template>
<<<<<<< HEAD
<div class="source-library-container"> <div class="source-library-container">
<div class="source-library-grid"> <div class="source-library-grid">
<div v-for="(item, index) in sourceLibraryData" :key="index" class="source-library-card"> <div v-for="(item, index) in sourceLibraryData" :key="index" class="source-library-card">
...@@ -36,6 +37,42 @@ ...@@ -36,6 +37,42 @@
/> />
</div> </div>
</div> </div>
=======
<div class="source-library-container">
<div class="source-library-grid">
<div v-for="(item, index) in sourceLibraryData" :key="index" class="source-library-card">
<div class="source-library-avatar-wrapper">
<img :src="item.avatar" alt="" class="source-library-avatar" />
<div class="person-tags">
<div class="person-tag-bg" v-for="(tag, tIdx) in item.icon" :key="tIdx">
<img :src="'/public/icon/header-icon' + tag + '.png'" class="tag-icon" alt="tag" />
</div>
</div>
</div>
<div class="source-library-text-content">
<div style=" width: 240px;">
<h3 class="source-library-name">{{ item.name }}</h3>
<p class="source-library-title">{{ item.title }}</p>
<p class="source-library-tag" :style="{
background: item.colorArray[2],
color: item.colorArray[0],
borderColor: item.colorArray[1],
}">{{ item.tag }}</p>
</div>
</div>
</div>
</div>
<div class="page">
<div class="count">共1205项调查</div>
<el-pagination v-model:current-page="currentPage" :page-size="pageSize" :total="total" layout="prev, pager, next"
background @current-change="handlePageChange" />
</div>
</div>
>>>>>>> af566f60d3ab2563ec38173a60323b1404cd7081
</template> </template>
<script setup> <script setup>
...@@ -84,6 +121,28 @@ const handlePageChange = p => { ...@@ -84,6 +121,28 @@ const handlePageChange = p => {
margin-right: 18px; margin-right: 18px;
} }
.person-tags {
display: flex;
margin-top: -20px;
width: 42px;
padding-left: 28px;
text-align: center;
}
.person-tag-bg {
/* 椭圆 6 */
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
}
.tag-icon {
width: 24px;
height: 24px;
object-fit: contain;
}
.source-library-avatar { .source-library-avatar {
/* 椭圆 142 */ /* 椭圆 142 */
width: 88px; width: 88px;
...@@ -145,6 +204,7 @@ const handlePageChange = p => { ...@@ -145,6 +204,7 @@ const handlePageChange = p => {
} }
.page { .page {
<<<<<<< HEAD
/* width: 1221px; */ /* width: 1221px; */
width: 1600px; width: 1600px;
height: 40px; height: 40px;
...@@ -162,6 +222,27 @@ const handlePageChange = p => { ...@@ -162,6 +222,27 @@ const handlePageChange = p => {
letter-spacing: 0px; letter-spacing: 0px;
text-align: left; text-align: left;
} }
=======
width: 1600px;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 36px 0 0 0;
padding-left: 11px;
.count {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
>>>>>>> af566f60d3ab2563ec38173a60323b1404cd7081
} }
:deep(.el-pagination) { :deep(.el-pagination) {
......
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
<div class="box1-header"> <div class="box1-header">
<div class="box1-header-left"> <div class="box1-header-left">
<div class="icon"> <div class="icon">
<img src="./assets/images/box1-header-icon.png" alt="" /> <img src="./assets/images/TechnologyFigures-icon4.png" alt="" />
</div> </div>
<div class="title">{{ "人物新闻动态" }}</div> <div class="title">{{ "人物新闻动态" }}</div>
</div> </div>
...@@ -128,7 +128,7 @@ ...@@ -128,7 +128,7 @@
<div class="box3-header"> <div class="box3-header">
<div class="box3-header-left"> <div class="box3-header-left">
<div class="box3-header-icon"> <div class="box3-header-icon">
<img src="./assets/images/header-news.png" alt="" /> <img src="./assets/images/TechnologyFigures-icon3.png" alt="" />
</div> </div>
<!-- <div class="box3-header-title">{{ "人物动向" }}</div> --> <!-- <div class="box3-header-title">{{ "人物动向" }}</div> -->
<div class="header-title" <div class="header-title"
...@@ -178,7 +178,7 @@ ...@@ -178,7 +178,7 @@
<div class="box5-header"> <div class="box5-header">
<div class="box5-header-left"> <div class="box5-header-left">
<div class="box5-header-icon"> <div class="box5-header-icon">
<img src="./assets/images/box3-header-icon.png" alt="" <img src="./assets/images/TechnologyFigures-icon1.png" alt=""
style="margin: 13px 21px 13px 21px; height: 22px" /> style="margin: 13px 21px 13px 21px; height: 22px" />
<div class="box5-header-title">{{ "科技人物观点词云" }}</div> <div class="box5-header-title">{{ "科技人物观点词云" }}</div>
</div> </div>
...@@ -200,7 +200,7 @@ ...@@ -200,7 +200,7 @@
<div class="box6"> <div class="box6">
<div class="box6-header" style="width: 790px"> <div class="box6-header" style="width: 790px">
<div class="header-icon"> <div class="header-icon">
<img src="./assets/images/box6-header-icon.png" alt="" /> <img src="./assets/images/box3-header-icon.png" alt="" />
</div> </div>
<div class="header-title" <div class="header-title"
style="display: flex; width: 740px; justify-content: space-between; align-items: center"> style="display: flex; width: 740px; justify-content: space-between; align-items: center">
...@@ -223,7 +223,7 @@ ...@@ -223,7 +223,7 @@
<div class="box7-header"> <div class="box7-header">
<div class="box7-header-left"> <div class="box7-header-left">
<div class="box7-header-icon"> <div class="box7-header-icon">
<img src="./assets/images/box3-header-icon.png" alt="" /> <img src="./assets/images/TechnologyFigures-icon2.png" alt="" />
</div> </div>
<div class="box7-header-title">{{ "科技人物类型" }}</div> <div class="box7-header-title">{{ "科技人物类型" }}</div>
</div> </div>
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
<div class="box8-header"> <div class="box8-header">
<div class="box8-header-left"> <div class="box8-header-left">
<div class="box8-header-icon"> <div class="box8-header-icon">
<img src="./assets/images/box6-header-icon.png" alt="" /> <img src="./assets/images/TechnologyFigures-icon2.png" alt="" />
</div> </div>
<div style="display: flex; width: 730px; justify-content: space-between; align-items: center"> <div style="display: flex; width: 730px; justify-content: space-between; align-items: center">
<div class="box8-header-title">{{ "主要人物涉华观点统计" }}</div> <div class="box8-header-title">{{ "主要人物涉华观点统计" }}</div>
...@@ -839,7 +839,7 @@ onMounted(() => { ...@@ -839,7 +839,7 @@ onMounted(() => {
line-height: 47px; line-height: 47px;
color: rgba(132, 136, 142, 1); color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 14px; font-size: 16px;
font-weight: 400; font-weight: 400;
} }
} }
......
...@@ -4,111 +4,127 @@ ...@@ -4,111 +4,127 @@
"title": "美国总统(2017-2021、2025-至今),共和党党员", "title": "美国总统(2017-2021、2025-至今),共和党党员",
"tag": "行政主管", "tag": "行政主管",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"] "colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"],
"icon": ["1"]
}, },
{ {
"name": "詹姆斯·戴维·万斯", "name": "詹姆斯·戴维·万斯",
"title": "美国副总统、参议院议长、共和党全国委员会财务主席", "title": "美国副总统、参议院议长、共和党全国委员会财务主席",
"tag": "行政主管", "tag": "行政主管",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"] "colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"],
"icon": []
}, },
{ {
"name": "黄仁勋", "name": "黄仁勋",
"title": "NVIDIA公司创始人兼首席执行官,美国工程院院士", "title": "NVIDIA公司创始人兼首席执行官,美国工程院院士",
"tag": "科技企业领袖", "tag": "科技企业领袖",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"] "colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"],
"icon": ["1"]
}, },
{ {
"name": "马尔科·鲁比奥", "name": "马尔科·鲁比奥",
"title": "美国国务卿,美国总统国家安全事务临时助理", "title": "美国国务卿,美国总统国家安全事务临时助理",
"tag": "行政主管", "tag": "行政主管",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"] "colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"],
"icon": []
}, },
{ {
"name": "埃隆·马斯克", "name": "埃隆·马斯克",
"title": "特斯拉创始人兼首席执行官、SpaceX、美国党创始人", "title": "特斯拉创始人兼首席执行官、SpaceX、美国党创始人",
"tag": "科技企业领袖", "tag": "科技企业领袖",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"] "colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"],
"icon": ["1"]
}, },
{ {
"name": "乔迪·阿灵顿", "name": "乔迪·阿灵顿",
"title": "共和党党员,美国国会众议院议员,预算委员会主席", "title": "共和党党员,美国国会众议院议员,预算委员会主席",
"tag": "国会议员", "tag": "国会议员",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"] "colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"],
"icon": []
}, },
{ {
"name": "霍华德·卢特尼克", "name": "霍华德·卢特尼克",
"title": "美国商务部长", "title": "美国商务部长",
"tag": "行政主管", "tag": "行政主管",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"] "colorArray": ["#1677FF", "#BAE0FF", "#E6F4FF"],
"icon": ["1"]
}, },
{ {
"name": "蒂姆·库克", "name": "蒂姆·库克",
"title": "苹果公司首席执行官,清华大学经济管理学院顾问委员会主席", "title": "苹果公司首席执行官,清华大学经济管理学院顾问委员会主席",
"tag": "科技企业领袖", "tag": "科技企业领袖",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"] "colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"],
"icon": []
}, },
{ {
"name": "朱棣文", "name": "朱棣文",
"title": "第12任美国能源部部长,主要研究领域为原子物理、激光科学", "title": "第12任美国能源部部长,主要研究领域为原子物理、激光科学",
"tag": "顶级科学家", "tag": "顶级科学家",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"] "colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"],
"icon": ["1"]
}, },
{ {
"name": "约翰·图恩", "name": "约翰·图恩",
"title": "共和党党员,美国国会众议院议员", "title": "共和党党员,美国国会众议院议员",
"tag": "国会议员", "tag": "国会议员",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"] "colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"],
"icon": []
}, },
{ {
"name": "珍妮弗·道德纳", "name": "珍妮弗·道德纳",
"title": "主要研究领域为RNA领域和基因编辑技术", "title": "主要研究领域为RNA领域和基因编辑技术",
"tag": "顶级科学家", "tag": "顶级科学家",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"] "colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"],
"icon": ["1"]
}, },
{ {
"name": "迈克·约翰逊", "name": "迈克·约翰逊",
"title": "美国众议院议长", "title": "美国众议院议长",
"tag": "国会议员", "tag": "国会议员",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"] "colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"],
"icon": []
}, },
{ {
"name": "亚当·史密斯", "name": "亚当·史密斯",
"title": "美国国会众议院议员,众议院军事委员会民主党领袖", "title": "美国国会众议院议员,众议院军事委员会民主党领袖",
"tag": "国会议员", "tag": "国会议员",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"] "colorArray": ["#FAAD14", "#FFE58F", "#FFFBE6"],
"icon": ["1"]
}, },
{ {
"name": "查尔斯·本内特", "name": "查尔斯·本内特",
"title": "美国国家科学院院士、美国物理学会院士,量子信息论主要创立者", "title": "美国国家科学院院士、美国物理学会院士,量子信息论主要创立者",
"tag": "顶级科学家", "tag": "顶级科学家",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"] "colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"],
"icon": []
}, },
{ {
"name": "桑达尔·皮查伊", "name": "桑达尔·皮查伊",
"title": "谷歌母公司Alphabet首席执行官", "title": "谷歌母公司Alphabet首席执行官",
"tag": "科技企业领袖", "tag": "科技企业领袖",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"] "colorArray": ["#13A8A8", "#87E8DE", "#E6FFFFB"],
"icon": ["1"]
}, },
{ {
"name": "威康·汤姆逊", "name": "威康·汤姆逊",
"title": "美国国家科学院院士、美国物理学会院士,量子信息论主要创立者", "title": "美国国家科学院院士、美国物理学会院士,量子信息论主要创立者",
"tag": "顶级科学家", "tag": "顶级科学家",
"avatar": "/public/testData/data2.png", "avatar": "/public/testData/data2.png",
"colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"] "colorArray": ["#722ED1", "#D3ADF7", "#F9F0FF"],
"icon": []
} }
] ]
# 交互消息类
## ApiResult
```java
public class ApiResult<T> {
@ApiModelProperty("响应码")
private int code;
@ApiModelProperty("响应消息")
private String message;
@ApiModelProperty("是否成功")
private boolean success;
@ApiModelProperty("响应数据")
private T data;
}
```
## LatestExportControlInfo
```java
public class LatestExportControlInfo {
// 出口管制事件ID
private String id;
// 管制信息标题
private String name;
// 管制内容简述
private String summary;
// 发布机构名称
private String postOrgName;
// 发布时间
private Date postDate;
// 涉及领域
private List<String> domains;
// 涉及中国实体数
private Integer cnEntityCount;
// 涉及主要实体
private List<SanctionListBean> sanEntities;
}
```
## AnnualCount
```java
public class AnnualCount {
// 年份
private Integer year;
// 数量
private Integer count;
// 领域列表
private List<String> domain;
}
```
## DomainCount
```java
public class DomainCount {
// 制裁类型名称
private String sanTypeName;
// 领域统计信息
private List<BaseCount> domainCountInfo;
}
```
## BaseCount
```java
public class BaseCount {
// 统计名称
private String name;
// 数量
private Integer count;
}
```
## ExportPageQuery
```java
public class ExportPageQuery extends BasePageQuery {
// 类型名称(实体清单)
private String typeName;
// 制裁时间
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date sanctionDate;
// 是否只看中国实体
private Boolean isCn;
}
```
## BasePageQuery
```java
public class BasePageQuery {
// 查询页
private Integer pageNum = 1;
// 每页数量
private Integer pageSize = 10;
// 排序字段
private String sortField;
// 排序方式
private String sortOrder;
}
```
## SanctionProcess
```java
public class SanctionProcess {
// 制裁事件ID
private String id;
// 制裁时间
private Date postDate;
// 制裁标题
private String name;
// 制裁内容简述
private String summary;
// 涉及中国实体数
private Integer cnEntityCount;
}
```
## SanctionListBean
```java
public class SanctionListBean extends BaseBean {
@Id
@Column(name = "ID", nullable = false)
private String id;
@Column(name = "ENTITY_NAME")
private String entityName;
@Column(name = "SAN_TYPE_ID")
private Integer sanTypeId;
@Column(name = "ENTITY_TYPE")
private Integer entityType;
@Column(name = "ENTITY_ID")
private String entityId;
@Column(name = "ENTITY_NAME_ZH")
private String entityNameZh;
@Column(name = "COUNTRY_ID")
private String countryId;
@Column(name = "SAN_REASON")
private String sanReason;
@Column(name = "SAN_INTENSITY")
private char sanIntensity;
@Column(name = "START_TIME")
private Date startTime;
@Column(name = "END_TIME")
private Date endTime;
@Column(name = "IS_KEY")
private char isKey;
@Transient
private List<TechDomainBean> techDomainList;
/**
* 领域列表
*/
@Transient
private List<String> techDomains;
/**
* 50%规则子企业数
*/
@Transient
private Integer ruleOrgCount;
}
```
## OrgInfo
```java
public class OrgInfo {
// 机构id
private String id;
// 机构名称
private String orgName;
// 相关制裁措施列表
private List<String> sanTypeList;
// 机构职责
private String orgDuty;
// 机构图片
private String imageUrl;
// 人员列表
private List<PersonInfo> personList;
}
```
## PersonInfo
```java
public class PersonInfo {
// id
private String id;
// 姓名
private String name;
// 党派
private String party;
// 职位
private String position;
// 头像链接
private String imageUrl;
}
```
## SanCountInfo
```java
public class SanCountInfo {
// 实体数
private Integer entityNum;
// 实体变动数
private Integer entityChange;
// 上市公司数
private Integer listedCompanyNum;
// 上市公司变动数
private Integer listedCompanyChange;
// 涉及领域名数
private Integer domainNum;
// 涉及领域变动数
private Integer domainChange;
// 实体类型数
private Integer typeNum;
// 实体类型变动数
private Integer typeChange;
}
```
## Chain
```java
public class Chain {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// 名称
@Column(name = "name")
private String name;
// 中文名称
@Column(name = "name_zh")
private String nameZh;
// 值
@Column(name = "description")
private String description;
// 父级id
@Column(name = "parent_id")
private Integer parentId;
// 是否为产业链主分支
@Column(name = "is_main_branch")
private String isMainBranch;
// 下属产业链分支
@Transient
private List<Chain> children;
}
```
## FishboneResp
```java
public class FishboneResp<T> {
private String text;
private List<T> causes;
}
```
## AreasStreamResp
```java
public class AreasStreamResp {
// 上游国内企业数量
private int upstreamInternalCount;
// 上游国内占比
private double upstreamInternalRate;
// 上游受制裁企业数量
private int upstreamEntityCount;
// 上游受制裁占比
private double upstreamEntityRate;
// 中游国内企业数量
private int midstreamInternalCount;
// 中游国内占比
private double midstreamInternalRate;
// 中游受制裁企业数量
private int midstreamEntityCount;
// 中游受制裁占比
private double midstreamEntityRate;
// 下游国内企业数量
private int downstreamInternalCount;
// 下游国内占比
private double downstreamInternalRate;
// 下游受制裁企业数量
private int downstreamEntityCount;
// 下游受制裁占比
private double downstreamEntityRate;
}
```
## AnnualDomainQuery
```java
public class AnnualDomainQuery {
// 开始年份
private Integer startYear;
// 结束年份
private Integer endYear;
// 是否考虑50%规则
private Boolean isRule;
}
```
## AnnualDomainCount
```java
public class AnnualDomainCount {
// 年度领域统计
private List<DomainCount> yearDomainCount;
// 所有领域
private List<BaseCount> domians;
}
```
## SanctionTypeBean
```java
public class SanctionTypeBean extends BaseBean {
@Id
@Column(name = "ID", nullable = false)
private Integer id;
@Column(name = "NAME")
private String name;
@Column(name = "NAME_ZH")
private String nameZh;
@Column(name = "NAME_ABBR")
private String nameAbbr;
@Column(name = "DESCRIPTION")
private String description;
// 发布国家
@Column(name = "POST_COUNTRY_ID")
private String postCountryId;
// 发布机构
@Column(name = "POST_ORG_ID")
private String postOrgId;
// 是否出口管制 1:是 0:否
@Column(name = "IS_EXPORT_CONTROL")
private String isExportControl;
// 总发布次数
@Transient
private Integer postCount;
}
```
# 字典
## 领域类别(id:name)
1:人工智能、2:生物科技、3:新一代信息技术、4:量子科技、5:新能源、6:集成电路、7:海洋、8:先进制造、9:新材料、10:航空航天、11:深海、12:极地、13:太空、14:核
## 实体类别(id:name)
1:人物、2:机构
# 出口管制
## **获取出口管制类清单统计信息**
请求地址:/sanctionList/export/getTotalInfo
请求类型:GET
输入参数:
​ 参数:无输入
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<List<SanctionTypeBean>>
## 最新出口管制政策(4条)
请求地址:http://8.140.26.4:9085/entitiesDataInfo/getLatestInfo
请求类型:GET
输入参数:
​ 参数:无输入
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<LatestExportControlInfo>
## 发布(更新)频度
请求地址:http://8.140.26.4:9085/entitiesDataCount/getAnnualCount
请求类型:GET
输入参数:
​ 参数:Integer sanTypeId(制裁类别)
​ 实体清单发布频度:1;CCL发布频度:X(待定,暂无数据)
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<List<AnnualCount>>
## **制裁领域分析**(20251215)
请求地址:http://8.140.26.4:9085/entitiesDataCount/getSanDomainCount
请求类型:GET
输入参数:
​ 参数:Boolean rule(是否勾选50%规则)
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<List<DomainCount>>
## **历次制裁过程**
请求地址:http://8.140.26.4:9085/entitiesDataCount/getSanctionProcess
请求类型:POST
输入参数:
​ 参数:ExportPageQuery exportPageQuery
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<Page<SanctionProcess>>
## **制裁实体清单**列表(20251215)
请求地址:http://8.140.26.4:9085/sanctionList/pageQuery
请求类型:POST
输入参数:
​ 参数:ExportPageQuery exportPageQuery
​ 出口管制-概览页请求时:typeName=实体清单
​ 实体清单-制裁概览页请求时:typeName=实体清单,sanctionDate=该次制裁的具体时间
​ 请求头:携带token,内容为:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw
```
输出结果:ApiResult<Page<SanctionListBean>>
## **发布机构与重点人物**
请求地址:/sanctionList/getPublishedOrg
请求类型:GET
输入参数:
​ 参数:Integer sanTypeId
​ 暂时固定输入:1;对应实体清单发布机构
​ 请求头:携带token
输出结果:ApiResult<OrgInfo>
## **领域分布查询**
请求地址:/entitiesDataInfo/getDomianDistribution
请求类型:GET
输入参数:
​ 参数:String sanctionDate (制裁时间)
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **类型分布查询**
请求地址:/entitiesDataInfo/getTypeDistribution
请求类型:GET
输入参数:
​ 参数:String sanctionDate (制裁时间)
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **区域分布查询**
请求地址:/entitiesDataInfo/getRegionDistribution
请求类型:GET
输入参数:
​ 参数:String sanctionDate (制裁时间)
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **制裁理由查询**
请求地址:/entitiesDataInfo/getSanReason
请求类型:GET
输入参数:
​ 参数:String sanctionDate (制裁时间)
​ 请求头:携带token
输出结果:ApiResult<List<String>>
## **深度挖掘-制裁信息变化统计**
请求地址:/entitiesDataInfo/getSanCountInfo
请求类型:GET
输入参数:
​ 参数:String sanctionDate (制裁时间)
​ 请求头:携带token
输出结果:ApiResult<SanCountInfo>
## **年度实体数统计**
请求地址:/entitiesDataInfo/getCountByDomianAndType
请求类型:GET
输入参数:
​ 参数:String domianId (非必需,领域类别ID),Integer typeId (非必需,实体类别ID)
​ 参考字典
​ 请求头:携带token
输出结果:ApiResult<SanCountInfo>
## **重点实体列表查询**
请求地址:/entitiesDataInfo/getKeyEntities
请求类型:GET
输入参数:
​ 参数:String sanctionDate(必需),String searchText(非必需,检索文本)
​ 请求头:携带token
输出结果:ApiResult<List<OrgInfo>>
## **上市企业制裁强度**
请求地址:/entitiesDataInfo/listedEntity/sanInfo
请求类型:GET
输入参数:
​ 参数:无
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **上市企业融资变化情况**
请求地址:/entitiesDataInfo/listedEntity/financing
请求类型:GET
输入参数:
​ 参数:无
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **上市企业市值变化情况**
请求地址:/entitiesDataInfo/listedEntity/market
请求类型:GET
输入参数:
​ 参数:无
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **重点上市企业列表**
请求地址:/entitiesDataInfo/listedEntity/keyEntity
请求类型:GET
输入参数:
​ 参数:String sanctionDate(必需),String searchText(非必需,检索文本)
​ 请求头:携带token
输出结果:ApiResult<List<OrgInfo>>
## **历次制裁涉及领域数查询**
请求地址:/entitiesDataInfo/getPreviousDomian
请求类型:GET
输入参数:
​ 参数:无
​ 请求头:携带token
输出结果:ApiResult<List<BaseCount>>
## **具体领域的制裁实体数统计**
请求地址:/entitiesDataInfo/getDomianAnnual
请求类型:GET
输入参数:
​ 参数:String domainId
​ 请求头:携带token
输出结果:ApiResult<List<AnnualCount>>
## **具体实体类型的制裁实体数统计**
请求地址:/entitiesDataInfo/getEntityTypeAnnual
请求类型:GET
输入参数:
​ 参数:Integer entityTypeId
​ 请求头:携带token
输出结果:ApiResult<List<AnnualCount>>
## **产业链结构查询**
请求地址:/chain/getChainTree
请求类型:GET
输入参数:
​ 参数:Integer chainId,非必需
​ 请求头:携带token
输出结果:ApiResult<List<Chain>>
## **产业链鱼骨图信息查询**
请求地址:/chain/getChainFishbone
请求类型:GET
输入参数:
​ 参数:Integer chainId
​ 请求头:携带token
输出结果:ApiResult<FishboneResp>
## **产业链中国企业实体信息查询**
请求地址:/chain/getChainEntityStat
请求类型:GET
输入参数:
​ 参数:Integer chainId
​ 请求头:携带token
输出结果:ApiResult<AreasStreamResp>
## **实体列表查询**
请求地址:/entitiesDataInfo/getEntityList
请求类型:GET
输入参数:
​ 参数:String sanctionDate(制裁时间),String domainId(领域ID)
​ 请求头:携带token
输出结果:ApiResult<List<OrgInfo>>
## **历年制裁领域统计**
请求地址:/entitiesDataCount/getAnnualSanDomain
请求类型:POST
输入参数:
​ 参数:AnnualDomainQuery annualDomainQuery
​ 请求头:携带token
输出结果:ApiResult<List<AnnualDomainCount>>
## **新增实体数量增长趋势**
请求地址:/entitiesDataInfo/yoyComparison
请求类型:GET
输入参数:
​ 参数:无输入
​ 请求头:携带token
输出结果:ApiResult<List<AnnualCount>>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论