提交 d0f3c225 authored 作者: 付康's avatar 付康

合并分支 'lzl-dev' 到 'master'

update 查看合并请求 !104
import request from "@/api/request.js";
// 全政府-获取美国政府部门制裁数据
/**
* @header token
*/
export function getAllGovernmentList() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentSanctionsData`
})
}
// 全政府-获取美对华制裁措施数量趋势
/**
* @header token
* @param {Object} params
* @param {String} params.field // 领域
* @param {String} params.monthNum = 12 // 月份数
* @param {String} params.orgId // 机构ID
* @param {String} params.sanType // 制裁手段
*/
export function getUSChinaSanctionTrend(params) {
return request({
method: 'GET',
url: `/api/rivalryIndex/sanctionsQuantitativeTrend`,
params
})
}
// 全政府-美政府部门打压遏制最新动态
/**
* @header token
*/
export function getUSGovernmentLatestDynamic() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentSanctionsDynamics`
})
}
// 全政府-美政府部门联合制裁排行
/**
* @header token
*/
export function getUSGovernmentJointSanctionRank() {
return request({
method: 'GET',
url: `/api/rivalryIndex/governmentJointSanctionsRanking`
})
}
// 全政府-美政府部门对我打压遏制时间线
/**
* @header token
* @param {Object} params
* @param {String} params.currentPage = 1 // 当前页
* @param {String} params.pageSize = 1000 // 每页数量
*/
export function getUSGovernmentSanctionHistory(params) {
return request({
method: 'GET',
url: `/api/rivalryIndex/getSanctionProcess`,
params
})
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div class="main-content"> <div class="main-content">
<div class="cards-mask"> <div class="cards-mask">
<div class="cards-container" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }"> <div class="cards-container" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }">
<div v-for="(card, index) in cardList" :key="index" class="government-card"> <div v-for="(card, index) in cardList" :key="index" class="government-card" @click="handleCardClick(card)">
<div class="card-bg" :style="{ backgroundImage: `url(${card.icon || defaultIcon})` }"></div> <div class="card-bg" :style="{ backgroundImage: `url(${card.icon || defaultIcon})` }"></div>
<div class="card-header"> <div class="card-header">
<span class="card-title">{{ card.title }}</span> <span class="card-title">{{ card.title }}</span>
...@@ -35,14 +35,14 @@ ...@@ -35,14 +35,14 @@
<span>美对华制裁措施数量趋势</span> <span>美对华制裁措施数量趋势</span>
</div> </div>
<div class="title-right"> <div class="title-right">
<el-select v-model="fieldValue" placeholder="全部领域" class="custom-select"> <el-select v-model="fieldValue" placeholder="全部领域" class="custom-select" @change="getUSChinaSanctionTrendData">
<el-option label="全部领域" value="" /> <el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
<el-select v-model="deptValue" placeholder="全部部门" class="custom-select"> <el-select v-model="deptValue" placeholder="全部部门" class="custom-select" @change="getUSChinaSanctionTrendData">
<el-option label="全部部门" value="" /> <el-option label="全部部门" value="" />
</el-select> </el-select>
<el-select v-model="methodValue" placeholder="全部制裁手段" class="custom-select"> <el-select v-model="methodValue" placeholder="全部制裁手段" class="custom-select" @change="getUSChinaSanctionTrendData">
<el-option label="全部制裁手段" value="" /> <el-option v-for="item in methodOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
</div> </div>
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
<img :src="defaultImg" alt="" class="item-icon" /> <img :src="defaultImg" alt="" class="item-icon" />
<div class="item-right"> <div class="item-right">
<div class="dynamic-item-header"> <div class="dynamic-item-header">
<span class="item-title">{{ item.title }}</span> <span class="item-title" @click="handleNewsClick(item)">{{ item.title }}</span>
<span class="item-date">{{ item.date }} · {{ item.type }}</span> <span class="item-date">{{ item.date }} · {{ item.type }}</span>
</div> </div>
<el-tooltip <el-tooltip
...@@ -154,7 +154,7 @@ ...@@ -154,7 +154,7 @@
<div class="timeline-list"> <div class="timeline-list">
<div v-for="(dept, index) in filteredTimelineList" :key="index" class="dept-row"> <div v-for="(dept, index) in filteredTimelineList" :key="index" class="dept-row">
<div class="dept-info"> <div class="dept-info">
<img :src="defaultImg" alt="" class="dept-icon" /> <img :src="dept.icon || defaultImg" alt="" class="dept-icon" />
<div class="dept-text"> <div class="dept-text">
<div class="dept-name">{{ dept.name }}</div> <div class="dept-name">{{ dept.name }}</div>
<div class="dept-count">{{ dept.count }}</div> <div class="dept-count">{{ dept.count }}</div>
...@@ -200,6 +200,7 @@ ...@@ -200,6 +200,7 @@
<script setup> <script setup>
import { onMounted, ref, computed } from "vue"; import { onMounted, ref, computed } from "vue";
import { useRouter } from "vue-router";
import * as echarts from "echarts"; import * as echarts from "echarts";
import defaultIcon from "../../assets/defaultIcon.png"; import defaultIcon from "../../assets/defaultIcon.png";
import leftBtn from "../../assets/left-btn.png"; import leftBtn from "../../assets/left-btn.png";
...@@ -210,6 +211,275 @@ import icon3 from "../../assets/icon3.png"; ...@@ -210,6 +211,275 @@ import icon3 from "../../assets/icon3.png";
import icon4 from "../../assets/icon4.png"; import icon4 from "../../assets/icon4.png";
import timelineBg from "../../assets/timeline.png"; import timelineBg from "../../assets/timeline.png";
import defaultImg from "../../../../assets/images/default-icon2.png"; import defaultImg from "../../../../assets/images/default-icon2.png";
// 传递接口
import { getAllGovernmentList, getUSChinaSanctionTrend, getUSGovernmentLatestDynamic,getUSGovernmentJointSanctionRank,getUSGovernmentSanctionHistory } from "@/api/allGovernment.js";
const router = useRouter();
// 全政府-美政府部门对我打压遏制时间线
const loadingHistory = ref(false);
const getUSGovernmentSanctionHistoryData = async () => {
loadingHistory.value = true;
try {
const res = await getUSGovernmentSanctionHistory({
currentPage: 1,
pageSize: 1000
});
if (res.code === 200 && res.data && res.data.content) {
const rawList = res.data.content;
const orgMap = {};
if (governmentList.value && governmentList.value.length) {
governmentList.value.forEach(g => {
if (g.departId) {
orgMap[g.departId] = g.title;
}
});
}
const grouped = {};
rawList.forEach(item => {
// 尝试获取部门名称
let deptName = '未知部门';
if (item.orgName) {
deptName = item.orgName;
} else if (item.orgId && orgMap[item.orgId]) {
deptName = orgMap[item.orgId];
} else if (item.orgId) {
if (item.orgId === '241') deptName = '商务部工业与安全局';
else if (item.orgId === '203') deptName = '海外资产控制办公室';
else deptName = '部门 ' + item.orgId;
}
if (!grouped[deptName]) {
grouped[deptName] = {
name: deptName,
icon: item.orgLogoUrl,
count: 0,
events: []
};
}
grouped[deptName].count++;
grouped[deptName].events.push({
date: item.postDate ? item.postDate.replace(/^(\d{4})-(\d{2})-(\d{2})$/, '$1年$2月$3日') : '',
content: item.name || item.summary,
tags: item.techDomainList ? item.techDomainList.slice(0, 2) : [],
level: getLevelByCount(item.cnEntityCount)
});
});
timelineList.value = Object.values(grouped);
initSlider();
}
} catch (error) {
console.error("获取制裁历程失败:", error);
} finally {
loadingHistory.value = false;
}
};
// 辅助函数:根据受影响实体数量生成level
const getLevelByCount = (count) => {
const c = count || 0;
if (c === 0) return 'green'; // 0 或空 绿色
if (c <= 10) return 'yellow'; // 1-10 黄色
return 'red'; // >10 红色
};
// 全政府-美政府部门联合制裁排行
const loadingJointRank = ref(false);
const getUSGovernmentJointSanctionRankData = async () => {
loadingJointRank.value = true;
try {
const res = await getUSGovernmentJointSanctionRank();
if (res.code === 200 && res.data) {
const rawData = res.data || [];
// 按 orderId 分组
const groupedData = {};
rawData.forEach(item => {
if (!groupedData[item.orderId]) {
groupedData[item.orderId] = {
depts: item.org ? item.org.map(o => o.orgName) : [],
count: item.jointCount,
items: []
};
}
groupedData[item.orderId].items.push({
type: item.sanTypeName || '其他',
title: item.sanName,
date: item.postDate ? item.postDate.replace(/^(\d{4})-(\d{2})-(\d{2})$/, '$1年$2月$3日') : ''
});
});
rankingList.value = Object.values(groupedData);
}
} catch (error) {
console.error("获取美政府部门联合制裁排行失败:", error);
} finally {
loadingJointRank.value = false;
}
};
// 全政府-美政府部门打压遏制最新动态
const loadingLatestDynamic = ref(false);
const getUSGovernmentLatestDynamicData = async () => {
loadingLatestDynamic.value = true;
try {
const res = await getUSGovernmentLatestDynamic();
if (res.code === 200 && res.data) {
dynamicList.value = res.data.map(item => ({
title: item.orgName ? `${item.orgName}${item.title}` : item.title,
date: item.time ? item.time.replace(/^(\d{4})-(\d{2})-(\d{2})$/, '$1年$2月$3日') : '',
type: item.sanTypeName || '',
content: item.content || item.title,
tags: item.industrylist || [],
id: item.id
}));
}
} catch (error) {
console.error("获取美政府部门打压遏制最新动态失败:", error);
} finally {
loadingLatestDynamic.value = false;
}
};
// 点击科技要闻-跳转详情页
const handleNewsClick = (item) => {
if (!item || !item.id) return;
// 打开新标签页
const { href } = router.resolve({
path: "/newsAnalysis",
query: {
newsId: item.id
}
});
window.open(href, '_blank');
};
// 全政府-获取美对华制裁措施数量趋势
const usChinaSanctionTrend = ref([]);
const loadingTrend = ref(false);
const getUSChinaSanctionTrendData = async () => {
loadingTrend.value = true;
try {
const params = {
monthNum: 12
};
if (fieldValue.value) {
params.field = fieldValue.value;
}
if (deptValue.value) {
params.orgId = deptValue.value;
}
if (methodValue.value) {
params.sanType = methodValue.value;
}
const res = await getUSChinaSanctionTrend(params);
if (res.code === 200 && res.data) {
usChinaSanctionTrend.value = res.data;
// 更新图表
const xAxisData = res.data.map((item) => `${item.year}-${item.month}`);
const seriesData = res.data.map((item) => item.sanctionCount);
initChart(xAxisData, seriesData);
}
} catch (error) {
console.error("获取美对华制裁措施数量趋势失败:", error);
} finally {
loadingTrend.value = false;
}
};
// 全政府-获取美国政府部门制裁数据
const governmentList = ref([]);
const loadingGovernment = ref(false);
const getGovernmentList = async () => {
loadingGovernment.value = true;
try {
const res = await getAllGovernmentList();
if (res.code === 200 && res.data) {
governmentList.value = res.data;
// 如果后端返回了数据,则更新 cardList
if (Array.isArray(res.data) && res.data.length > 0) {
cardList.value = res.data.map((item) => {
const stats = [];
if (item.parliament) {
// 议会类型
if (item.billCount) {
stats.push({
label: "法案(提出)",
value: (item.billCount.proposedBillCount || 0) + "项"
});
stats.push({
label: "法案(通过)",
value: (item.billCount.passedBillCount || 0) + "项"
});
}
} else {
// 非议会类型
// 1. 处理制裁清单
if (item.sanctionCountList && item.sanctionCountList.length > 0) {
item.sanctionCountList.forEach((sanction) => {
stats.push({
label: sanction.nameZh || sanction.name,
value: (sanction.postCount || 0) + "次"
});
});
}
// 2. 处理政令
stats.push({
label: "政令",
value: (item.administrativeOrderCount || 0) + "项"
});
}
return {
title: item.departName || "未知部门",
icon: defaultIcon, // 暂时使用默认图标
stats: stats,
departId: item.departId
};
});
}
}
} catch (error) {
console.error("获取美国政府部门制裁数据失败:", error);
} finally {
loadingGovernment.value = false;
}
};
// 跳转到机构页面
const handleCardClick = (card) => {
if (!card || !card.departId) return;
// 打开新标签页
const { href } = router.resolve({
path: "/institution",
query: {
id: card.departId
}
});
window.open(href, '_blank');
};
const cardList = ref([ const cardList = ref([
{ {
...@@ -277,8 +547,30 @@ const deptValue = ref(""); ...@@ -277,8 +547,30 @@ const deptValue = ref("");
const methodValue = ref(""); const methodValue = ref("");
const measureType = ref("history"); const measureType = ref("history");
const selectedField = ref("all"); const selectedField = ref("");
const fieldOptions = ref([{ label: "全部领域", value: "all" }]); const fieldOptions = ref([
{ label: "全部领域", value: "" },
{ label: "人工智能", value: "1" },
{ label: "生物科技", value: "2" },
{ label: "新一代信息技术", value: "3" },
{ label: "量子科技", value: "4" },
{ label: "新能源", value: "5" },
{ label: "集成电路", value: "6" },
{ label: "海洋", value: "7" },
{ label: "先进制造", value: "8" },
{ label: "新材料", value: "9" },
{ label: "航空航天", value: "10" },
{ label: "深海", value: "11" },
{ label: "极地", value: "12" },
{ label: "太空", value: "13" },
{ label: "核", value: "14" }
]);
const methodOptions = ref([
{ label: "全部制裁手段", value: "" },
{ label: "法案", value: "-1" },
{ label: "行政令", value: "-2" }
]);
const dynamicList = ref([ const dynamicList = ref([
{ {
...@@ -686,7 +978,7 @@ const initSlider = () => { ...@@ -686,7 +978,7 @@ const initSlider = () => {
}); });
}; };
const initChart = () => { const initChart = (xAxisData = [], seriesData = []) => {
if (!chartRef.value) return; if (!chartRef.value) return;
myChart = echarts.init(chartRef.value); myChart = echarts.init(chartRef.value);
const option = { const option = {
...@@ -714,20 +1006,7 @@ const initChart = () => { ...@@ -714,20 +1006,7 @@ const initChart = () => {
xAxis: { xAxis: {
type: "category", type: "category",
boundaryGap: false, boundaryGap: false,
data: [ data: xAxisData,
"2024-12",
"2025-1",
"2025-2",
"2025-3",
"2025-4",
"2025-5",
"2025-6",
"2025-7",
"2025-8",
"2025-9",
"2025-10",
"2025-11"
],
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: "#f0f0f0" color: "#f0f0f0"
...@@ -744,8 +1023,7 @@ const initChart = () => { ...@@ -744,8 +1023,7 @@ const initChart = () => {
yAxis: { yAxis: {
type: "value", type: "value",
min: 0, min: 0,
max: 100, minInterval: 1, // 保证最小间隔为1,避免小数
interval: 20,
axisLine: { axisLine: {
show: false show: false
}, },
...@@ -762,7 +1040,7 @@ const initChart = () => { ...@@ -762,7 +1040,7 @@ const initChart = () => {
}, },
series: [ series: [
{ {
data: [45, 52, 62, 62, 62, 65, 52, 75, 75, 75, 82, 92], data: seriesData,
type: "line", type: "line",
symbol: "circle", symbol: "circle",
symbolSize: 8, symbolSize: 8,
...@@ -796,6 +1074,11 @@ const initChart = () => { ...@@ -796,6 +1074,11 @@ const initChart = () => {
onMounted(() => { onMounted(() => {
initChart(); initChart();
initSlider(); initSlider();
getGovernmentList();
getUSChinaSanctionTrendData();
getUSGovernmentLatestDynamicData();
getUSGovernmentJointSanctionRankData();
getUSGovernmentSanctionHistoryData();
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
myChart && myChart.resize(); myChart && myChart.resize();
sliderChart && sliderChart.resize(); sliderChart && sliderChart.resize();
...@@ -1091,11 +1374,13 @@ const prev = () => { ...@@ -1091,11 +1374,13 @@ const prev = () => {
} }
.item-right { .item-right {
flex: 1; flex: 1;
min-width: 0;
.dynamic-item-header { .dynamic-item-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: center;
.item-title { .item-title {
cursor: pointer;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
...@@ -1104,7 +1389,9 @@ const prev = () => { ...@@ -1104,7 +1389,9 @@ const prev = () => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
max-width: 500px; flex: 1;
min-width: 0;
margin-right: 12px;
} }
.item-date { .item-date {
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
...@@ -1112,6 +1399,8 @@ const prev = () => { ...@@ -1112,6 +1399,8 @@ const prev = () => {
font-weight: 400; font-weight: 400;
line-height: 30px; line-height: 30px;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
flex-shrink: 0;
white-space: nowrap;
} }
} }
.dynamic-item-body { .dynamic-item-body {
...@@ -1153,6 +1442,21 @@ const prev = () => { ...@@ -1153,6 +1442,21 @@ const prev = () => {
background: rgba(250, 84, 28, 0.1); background: rgba(250, 84, 28, 0.1);
border: 1px solid rgba(250, 84, 28, 0.3); border: 1px solid rgba(250, 84, 28, 0.3);
} }
&.tag-orange {
color: #fa8c16;
background: rgba(250, 140, 22, 0.1);
border: 1px solid rgba(250, 140, 22, 0.3);
}
&.tag-purple {
color: #722ed1;
background: rgba(114, 46, 209, 0.1);
border: 1px solid rgba(114, 46, 209, 0.3);
}
&.tag-cyan {
color: #13c2c2;
background: rgba(19, 194, 194, 0.1);
border: 1px solid rgba(19, 194, 194, 0.3);
}
} }
} }
} }
...@@ -1233,6 +1537,8 @@ const prev = () => { ...@@ -1233,6 +1537,8 @@ const prev = () => {
font-weight: 400; font-weight: 400;
line-height: 20px; line-height: 20px;
margin-right: 13px; margin-right: 13px;
white-space: nowrap;
flex-shrink: 0;
&.type-bill { &.type-bill {
color: #2f79c4; color: #2f79c4;
background: rgba(47, 121, 196, 0.1); background: rgba(47, 121, 196, 0.1);
...@@ -1269,6 +1575,9 @@ const prev = () => { ...@@ -1269,6 +1575,9 @@ const prev = () => {
} }
} }
} }
.text-item:last-child {
padding-bottom: 22px;
}
} }
.main-bottom { .main-bottom {
width: 100%; width: 100%;
...@@ -1367,7 +1676,7 @@ const prev = () => { ...@@ -1367,7 +1676,7 @@ const prev = () => {
.timeline-list { .timeline-list {
flex: 1; flex: 1;
overflow-y: auto; overflow: auto;
padding-right: 8px; padding-right: 8px;
padding-bottom: 8px; padding-bottom: 8px;
.dept-row { .dept-row {
...@@ -1419,10 +1728,21 @@ const prev = () => { ...@@ -1419,10 +1728,21 @@ const prev = () => {
align-items: center; align-items: center;
gap: 8px; gap: 8px;
overflow-x: auto; overflow-x: auto;
// 隐藏滚动条 // 启用滚动条
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; height: 6px; /* 横向滚动条高度 */
display: block;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: transparent;
} }
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
padding-bottom: 4px; // 给滚动条留出空间
.event-card { .event-card {
min-width: 240px; min-width: 240px;
...@@ -1500,6 +1820,8 @@ const prev = () => { ...@@ -1500,6 +1820,8 @@ const prev = () => {
font-weight: 400; font-weight: 400;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
line-height: 24px; line-height: 24px;
min-height: 48px; // 强制最小高度为两行 (24px * 2)
height: 48px; // 固定高度为两行
// margin-bottom: auto; // margin-bottom: auto;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
...@@ -1510,17 +1832,21 @@ const prev = () => { ...@@ -1510,17 +1832,21 @@ const prev = () => {
.event-tags { .event-tags {
display: flex; display: flex;
gap: 8px; gap: 8px;
flex-wrap: wrap;
margin-top: 4px; margin-top: 4px;
white-space: nowrap;
.tag { .tag {
padding: 2px 8px; padding: 0 8px;
height: 22px;
line-height: 20px;
border-radius: 4px; border-radius: 4px;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 20px;
border: 1px solid; border: 1px solid;
flex-shrink: 0;
display: flex;
align-items: center;
&.tag-blue { &.tag-blue {
color: rgba(9, 88, 217, 1); color: rgba(9, 88, 217, 1);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论