提交 d3f0ef39 authored 作者: Vicky's avatar Vicky

科技人物观点数据接入

上级 f9bbc15d
...@@ -27,6 +27,7 @@ export function getMainCharactersView(params) { ...@@ -27,6 +27,7 @@ export function getMainCharactersView(params) {
/** /**
* @param {year} * @param {year}
* @header token * @header token
* @areaId
*/ */
export function getCharacterOpinionWordCloud(params) { export function getCharacterOpinionWordCloud(params) {
return request({ return request({
...@@ -57,7 +58,7 @@ export function getOptionAreaChange(params) { ...@@ -57,7 +58,7 @@ export function getOptionAreaChange(params) {
export function getPersonRelation(params) { export function getPersonRelation(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/personRemarksOverview/personRelation/${params.industryId}`, url: `/api/personRemarksOverview/personRelation`,
params, params,
}) })
} }
...@@ -107,3 +108,29 @@ export function getBillRiskSignal(params) { ...@@ -107,3 +108,29 @@ export function getBillRiskSignal(params) {
url: `/api/commonFeature/riskSignal/${params.moduleId}`, url: `/api/commonFeature/riskSignal/${params.moduleId}`,
}) })
} }
//获取人物类别
export function getPersonType(params) {
return request({
method: 'GET',
url: `/api/commonDict/personType`,
params
})
}
// 获取人物全局信息 通过personId 获取personType
export function getPersonSummaryInfo(params) {
return request({
method: 'GET',
url: `/api/personHomepage/summaryInfo/${params.personId}`,
})
}
// 获取人物全局信息 通过personId 获取personType
export function getareaType(params) {
return request({
method: 'GET',
url: `/api/commonDict/areaType`,
params
})
}
\ No newline at end of file
<!-- PersonNewsCard.vue --> <!-- PersonNewsCard.vue -->
<template> <template>
<div class="person-news-card"> <div class="person-news-card">
<el-carousel
ref="carouselRef"
height="354px"
:autoplay="true"
:interval="3000"
arrow="never"
indicator-position="none"
@change="handleCarouselChange"
>
<!-- <el-carousel-item v-for="(bill, billIndex) in hotBillList" :key="billIndex"> -->
<!-- 左侧:头像 --> <!-- 左侧:头像 -->
<div class="avatar"> <div class="avatar">
<img :src="person.image_url" alt="人物头像" /> <img :src="person.image_url" alt="人物头像" />
</div> </div>
<!-- 事件列表 --> <!-- 事件列表 -->
...@@ -67,6 +79,8 @@ ...@@ -67,6 +79,8 @@
</div> </div>
</div> --> </div> -->
</div> </div>
<!-- </el-carousel-item> -->
</el-carousel>
</div> </div>
</template> </template>
...@@ -74,6 +88,9 @@ ...@@ -74,6 +88,9 @@
// 导入数据 // 导入数据
import data from "../json/personUpdates"; // 假设你把 JSON 存在 data.json 文件中 import data from "../json/personUpdates"; // 假设你把 JSON 存在 data.json 文件中
export default { export default {
name: "PersonNewsCard", name: "PersonNewsCard",
data() { data() {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<div class="table-row" v-for="(item, index) in personList" :key="index"> <div class="table-row" v-for="(item, index) in personList" :key="index">
<!-- 人物信息列 --> <!-- 人物信息列 -->
<div class="row-col col-person"> <div class="row-col col-person">
<div style="margin: 7px 12px 7px 24px"> <div style="margin: 7px 12px 7px 24px;">
<img :src="item.avatar" class="avatar" alt="avatar" /> <img :src="item.avatar" class="avatar" alt="avatar" />
<div class="person-tags"> <div class="person-tags">
<div class="person-tag-bg" v-for="(tag, tIdx) in item.tags" :key="tIdx"> <div class="person-tag-bg" v-for="(tag, tIdx) in item.tags" :key="tIdx">
...@@ -46,10 +46,59 @@ ...@@ -46,10 +46,59 @@
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref,onMounted,defineProps,watch } from "vue";
import personData from "../json/personData.json"; // 引入JSON数据 import personData from "../json/personData.json"; // 引入JSON数据
import {getMainCharactersView } from "@/api/technologyFigures/technologyFigures";
const props = defineProps({
persontypeid: {
type: String,
default: "004"
},
yearSelect:{
type: String,
default: "2025"
}
});
watch(() => [props.persontypeid,props.yearSelect], (val) => {
handlegetMainCharactersViewFn();
})
// 获取主要人物涉华观点统计
const handlegetMainCharactersViewFn = async () => {
const params = {
personTypeId: props.persontypeid,
year:props.yearSelect
};
try {
personList.value = [];
const res = await getMainCharactersView(params);
console.log("主要人物涉华观点统计", res);
if (res.code === 200) {
personList.value = res.data.map(item=>{
return{
avatar: item.personImage,
name: item.personName,
position: item.positionTitle,
tags: ["1", "2"],
chinaRelatedCount: item.remarksCount,
mediaQuoteCount: item.remarksCount
}
});
}
} catch (error) {}
};
onMounted(async () => {
handlegetMainCharactersViewFn();
});
const personList = ref(personData); const personList = ref();
// 进度条状态 // 进度条状态
const getStatus = _percent => { const getStatus = _percent => {
const percent = _percent; const percent = _percent;
...@@ -106,6 +155,9 @@ const getProgress = count => (count / getMaxCount()) * 100; ...@@ -106,6 +155,9 @@ const getProgress = count => (count / getMaxCount()) * 100;
/* 表格内容样式 */ /* 表格内容样式 */
.table-body { .table-body {
/* background-color: #fff; */ /* background-color: #fff; */
height: 300px;
overflow-y:scroll;
overflow-x:hidden;
} }
.table-row { .table-row {
...@@ -148,6 +200,7 @@ const getProgress = count => (count / getMaxCount()) * 100; ...@@ -148,6 +200,7 @@ const getProgress = count => (count / getMaxCount()) * 100;
.person-info { .person-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.person-name { .person-name {
...@@ -160,6 +213,11 @@ const getProgress = count => (count / getMaxCount()) * 100; ...@@ -160,6 +213,11 @@ const getProgress = count => (count / getMaxCount()) * 100;
line-height: 24px; line-height: 24px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: left; text-align: left;
width: 200px;
white-space:nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.person-position { .person-position {
......
...@@ -2,37 +2,42 @@ ...@@ -2,37 +2,42 @@
<template> <template>
<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 PersonResource" :key="index" class="source-library-card" @click="handleClcikToCharacter(item.id)">
<div class="source-library-avatar-wrapper"> <div class="source-library-avatar-wrapper">
<img :src="item.avatar" alt="" class="source-library-avatar" /> <img :src="item.avatar" alt="" class="source-library-avatar" />
</div> </div>
<div class="source-library-text-content"> <div class="source-library-text-content">
<div style="width: 240px"> <div style="width: 240px;height: 120px;display: flex; flex-direction: column;">
<h3 class="source-library-name">{{ item.name }}</h3> <h3 class="source-library-name">{{ item.name }}</h3>
<p class="source-library-title">{{ item.title }}</p> <p class="source-library-title">{{ item.title }}</p>
<div class="taglist">
<p <p
class="source-library-tag" class="source-library-tag"
:style="{ v-for="value in item.tag"
background: item.colorArray[2], :class="{tag1: value.typeId === '001',
color: item.colorArray[0], tag2: value.typeId === '002',
borderColor: item.colorArray[1] tag3: value.typeId === '003',
tag4: value.typeId === '004',
tag5: value.typeId === '005',
tag6: value.typeId === '006',
}" }"
> >
{{ item.tag }} {{ value.typeName }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="page"> <div class="page">
<div class="count">共1205项调查</div> <div class="count">{{ `共${total}项调查`}}</div>
<el-pagination <el-pagination
v-model:current-page="currentPage" @current-change="handleCurrentChange"
:page-size="pageSize" :page-size="pageSize"
:total="total" :current-page="currentPage"
layout="prev, pager, next"
background background
@current-change="handlePageChange" layout="prev, pager, next"
:total="total"
/> />
</div> </div>
</div> </div>
...@@ -41,10 +46,134 @@ ...@@ -41,10 +46,134 @@
<script setup> <script setup>
// 导入数据(建议使用更具语义的变量名) // 导入数据(建议使用更具语义的变量名)
import sourceLibraryData from "../json/source.json"; import sourceLibraryData from "../json/source.json";
import { ref } from "vue"; import { ref,onMounted } from "vue";
const total = ref(1205); import { useRouter } from "vue-router";
const pageSize = ref(121); import DefaultIcon1 from '@/assets/icons/default-icon1.png'
const currentPage = ref(5); import DefaultIcon2 from '@/assets/icons/default-icon2.png'
import {getPersonResource,getPersonSummaryInfo } from "@/api/technologyFigures/technologyFigures";
const router = useRouter();
const total = ref(0);
const pageSize = ref(16);
const loading = ref(false);
const abortController = ref(null);
const currentPage = ref(1);
const PersonResource = ref([]);
// 获取人物资源库
const handlegetPersonResourceFn = async () => {
// 取消上一次未完成的请求
if (abortController.value) {
abortController.value.abort();
}
// 创建新的 AbortController
abortController.value = new AbortController();
loading.value = true;
const params = {
currentPage: currentPage.value - 1, // Standard Spring Boot page index is 0-based
pageSize: pageSize.value
};
try {
const res = await getPersonResource(params,abortController.value.signal);
console.log("人物资源库", res);
if (res.code === 200) {
if (res.data && res.data.content) {
PersonResource.value = res.data.content.map(item => ({
id: item.personId,
name: item.personName,
title: item.positionTitle,
tag: item.personTypeList,
avatar: item.personImage || DefaultIcon1
}));
total.value = res.data.totalElements;
} else {
PersonResource.value = [];
total.value = 0;
}
} else {
PersonResource.value = [];
total.value = 0;
}
loading.value = false;
} catch (error) {
if (error.name !== "AbortError") {
console.error(error);
loading.value = false;
}
}
};
// 处理页码改变事件
const handleCurrentChange = page => {
currentPage.value = page;
handlegetPersonResourceFn();
};
// 跳转人物主页
const handleClcikToCharacter = async (id) => {
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
let personTypeName = "";
const params = {
personId: id
};
try {
const res = await getPersonSummaryInfo(params);
console.log("人物全局信息", res);
if (res.code === 200 && res.data) {
const arr = personTypeList.filter(item => {
return item.typeId === res.data.personType;
});
console.log("arr", arr);
if (arr && arr.length > 0) {
personTypeName = arr[0].typeName;
console.log("personTypeName", personTypeName);
if (personTypeName === "科技企业领袖") {
type = 1;
} else if (personTypeName === "国会议员") {
type = 2;
} else if (personTypeName === "智库研究人员") {
type = 3;
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
const route = router.resolve({
path: "/characterPage",
query: {
type: type, // type=1为科技企业领袖,2为国会议员,3为智库研究人员
personId: id
}
});
window.open(route.href, "_blank");
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
} else {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
} catch (error) {}
};
onMounted(async () => {
handlegetPersonResourceFn();
});
const handlePageChange = p => { const handlePageChange = p => {
currentPage.value = p; currentPage.value = p;
}; };
...@@ -73,6 +202,8 @@ const handlePageChange = p => { ...@@ -73,6 +202,8 @@ const handlePageChange = p => {
padding: 20px 18px; padding: 20px 18px;
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);
cursor: pointer;
} }
.source-library-card:hover { .source-library-card:hover {
...@@ -116,6 +247,7 @@ const handlePageChange = p => { ...@@ -116,6 +247,7 @@ const handlePageChange = p => {
.source-library-text-content { .source-library-text-content {
width: 656px; width: 656px;
flex: 1; flex: 1;
} }
.source-library-name { .source-library-name {
...@@ -133,7 +265,7 @@ const handlePageChange = p => { ...@@ -133,7 +265,7 @@ const handlePageChange = p => {
width: 240px; width: 240px;
height: 48px; height: 48px;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
margin: 11px 0 14px 0; margin: 11px 0 0px 0;
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
...@@ -142,6 +274,13 @@ const handlePageChange = p => { ...@@ -142,6 +274,13 @@ const handlePageChange = p => {
text-align: left; text-align: left;
} }
.taglist{
display: flex;
gap: 10px;
margin-top: auto;
margin-bottom: 0px;
}
.source-library-tag { .source-library-tag {
/* width: 72px; */ /* width: 72px; */
height: 22px; height: 22px;
...@@ -153,10 +292,7 @@ const handlePageChange = p => { ...@@ -153,10 +292,7 @@ const handlePageChange = p => {
padding: 1px 8px 1px 8px; padding: 1px 8px 1px 8px;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid rgba(255, 229, 143, 1);
border-radius: 4px; border-radius: 4px;
background: rgba(255, 251, 230, 1);
color: rgba(22, 119, 255, 1);
margin: 0; margin: 0;
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 14px; font-size: 14px;
...@@ -166,6 +302,37 @@ const handlePageChange = p => { ...@@ -166,6 +302,37 @@ const handlePageChange = p => {
text-align: left; text-align: left;
} }
.tag1 {
border: 1px solid rgba(217, 247, 190, 1);
background: rgba(246, 255, 237, 1);
color: rgba(82, 196, 26, 1);
}
.tag2 {
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
color: rgba(22, 119, 255, 1);
}
.tag3 {
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
.tag4 {
border: 1px solid rgba(211, 173, 247, 1);
background: rgba(249, 240, 255, 1);
color: rgba(114, 46, 209, 1);
}
.tag5 {
border: 1px solid rgba(255, 229, 143, 1);
background: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
}
.tag6 {
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
.page { .page {
/* width: 1221px; */ /* width: 1221px; */
width: 1600px; width: 1600px;
......
...@@ -38,54 +38,168 @@ ...@@ -38,54 +38,168 @@
<script> <script>
import * as echarts from "echarts"; import * as echarts from "echarts";
import worldJson from "@/assets/json/world.json"; import worldJson from "@/assets/json/world.json";
import {getCharacterTrends} from "@/api/technologyFigures/technologyFigures"
import { ref,onMounted } from "vue";
// const props = defineProps({
// peoDate: {
// type: String,
// default:"jinri"
// }
// })
// 获取人物动向
const CharacterTrends = ref([]);
const time = ref("");
const date = ref("");
const handlegetCharacterTrendsFn = async () => {
const params = {
startTime: "2025-01-01" || date.value
};
try {
const res = await getCharacterTrends(params);
console.log("人物动向", res);
if (res.code === 200) {
CharacterTrends.value = res.data.map(item=>{
return{
time: item.newsDate,
text: item.newsContent.substring(0, 17),
lon: item.lon || (Math.random() * 360 - 180).toFixed(6), //没数据
lat: item.lat || (Math.random() * 180 - 90).toFixed(6), //没数据
avatar: item.imageUrl
};
});
}
} catch (error) {}
};
function getDateDaysAgo(days) {
// 获取当前日期
const currentDate = new Date();
// 计算指定月数之前的日期
const pastDate = new Date(currentDate);
pastDate.setDate(currentDate.getDate() - days); // 减去指定的天数
// 格式化日期为 "YYYY-MM-DD" 的形式
const year = pastDate.getFullYear();
const month = String(pastDate.getMonth() + 1).padStart(2, "0"); // 月份从0开始,需要加1
const day = String(pastDate.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
// onMounted(async () => {
// handlegetCharacterTrendsFn();
// })
export default { export default {
name: "MapAnimation", name: "MapAnimation",
mounted() { props: {
peoDate: {
type: String,
default:"本周"
}
},
watch: {
async peoDate(Val) {
time.value = Val;
const days = ref();
if(time.value === "本周"){
days.value = 7;
}else if(time.value === "今天"){
days.value = 0;
}else if(time.value === "昨天"){
days.value = 1;
}else if(time.value === "近三天"){
days.value = 3;
}else if(time.value === "本月"){
days.value = new Date().getDate()-1;
}
date.value = getDateDaysAgo(days.value);
await handlegetCharacterTrendsFn();
this.initMap();
}
},
setup(props) {
onMounted(async () => {
time.value = props.peoDate;
const days = ref();
if(time.value === "本周"){
days.value = 7;
}else if(time.value === "今天"){
days.value = 0;
}else if(time.value === "昨天"){
days.value = 1;
}else if(time.value === "近三天"){
days.value = 3;
}else if(time.value === "本月"){
days.value = new Date().getDate()-1;
}
date.value = getDateDaysAgo(days.value);
});
},
async mounted() {
await handlegetCharacterTrendsFn();
this.initMap(); this.initMap();
}, },
methods: { methods: {
initMap() { initMap() {
// 注册自定义地图数据 // 注册自定义地图数据
echarts.registerMap("China", worldJson); echarts.registerMap("China", worldJson);
const eventsData = [ // const eventsData = [
{ // {
time: "9月23日", // time: "9月23日",
text: "随美国总统特朗普进行国事访问", // text: "随美国总统特朗普进行国事访问",
lon: 116.46, // lon: 116.46,
lat: 39.92, // lat: 39.92,
avatar: "/testData/united_states 1 copy.png" // avatar: "/testData/united_states 1 copy.png"
}, // },
{ // {
time: "9月23日", // time: "9月23日",
text: "出席中国发展高层论坛2025年年会", // text: "出席中国发展高层论坛2025年年会",
lon: 116.46, // lon: 116.46,
lat: 39.92, // lat: 39.92,
avatar: "/testData/united_states 1 copy.png" // avatar: "/testData/united_states 1 copy.png"
}, // },
{ // {
time: "9月23日", // time: "9月23日",
text: "与民主党领导人查克·舒默及哈基姆...", // text: "与民主党领导人查克·舒默及哈基姆...",
lon: 1.46, // lon: 1.46,
lat: 39.92, // lat: 39.92,
avatar: "/testData/united_states 1 copy.png" // avatar: "/testData/united_states 1 copy.png"
}, // },
{ // {
time: "9月23日", // time: "9月23日",
text: "与阿拉伯国家领导人会晤,商讨加...", // text: "与阿拉伯国家领导人会晤,商讨加...",
lon: 116.46, // lon: 116.46,
lat: -44.92, // lat: -44.92,
avatar: "/testData/united_states 1 copy.png" // avatar: "/testData/united_states 1 copy.png"
}, // },
{ // {
time: "9月23日", // time: "9月23日",
text: "对印度进行为期四天的访问,与总理...", // text: "对印度进行为期四天的访问,与总理...",
lon: 78.1, // lon: 78.1,
lat: 20.7, // lat: 20.7,
avatar: "/testData/united_states 1 copy.png" // avatar: "/testData/united_states 1 copy.png"
} // }
]; // ];
const eventsData = CharacterTrends;
const chart = echarts.init(this.$refs.map); const chart = echarts.init(this.$refs.map);
const option = { const option = {
grid: { grid: {
left: "10%", left: "10%",
...@@ -114,7 +228,7 @@ export default { ...@@ -114,7 +228,7 @@ export default {
name: "行程", name: "行程",
type: "effectScatter", type: "effectScatter",
coordinateSystem: "geo", coordinateSystem: "geo",
data: eventsData.map(item => ({ data: eventsData.value.map(item => ({
value: [item.lon, item.lat], value: [item.lon, item.lat],
time: item.time, time: item.time,
text: item.text, text: item.text,
...@@ -149,6 +263,7 @@ export default { ...@@ -149,6 +263,7 @@ export default {
}; };
chart.setOption(option); chart.setOption(option);
// 添加 graphic 元素并设置其位置 // 添加 graphic 元素并设置其位置
this.updateGraphics(chart, eventsData); this.updateGraphics(chart, eventsData);
...@@ -224,7 +339,7 @@ export default { ...@@ -224,7 +339,7 @@ export default {
chart2.setOption(option2); chart2.setOption(option2);
}, },
updateGraphics(chart, eventsData) { updateGraphics(chart, eventsData) {
const graphics = eventsData.map((event, index) => { const graphics = eventsData.value.map((event, index) => {
const position = chart.convertToPixel({ geoIndex: 0 }, [event.lon, event.lat]); const position = chart.convertToPixel({ geoIndex: 0 }, [event.lon, event.lat]);
return { return {
type: 'group', type: 'group',
...@@ -267,7 +382,8 @@ export default { ...@@ -267,7 +382,8 @@ export default {
fontSize: 16, fontSize: 16,
fontWeight: '400', fontWeight: '400',
fill: 'rgba(59, 65, 75, 1)', // 文字颜色(ECharts 中用 fill,不是 color) fill: 'rgba(59, 65, 75, 1)', // 文字颜色(ECharts 中用 fill,不是 color)
textAlign: 'left' textAlign: 'left',
} }
} }
] ]
......
...@@ -2,18 +2,19 @@ ...@@ -2,18 +2,19 @@
<template> <template>
<div class="speech-stance-container"> <div class="speech-stance-container">
<div class="speech-stance-grid"> <div class="speech-stance-grid">
<div v-for="(item, index) in speechStance" :key="index" class="speech-stance-card"> <div v-for="(item, index) in PersonRelation" :key="index" class="speech-stance-card">
<div class="speech-stance-avatar-wrapper"> <div class="speech-stance-avatar-wrapper">
<img :src="item.avatar" alt="" class="speech-stance-avatar" /> <img :src="item.personImage" alt="" class="speech-stance-avatar" />
</div> </div>
<div class="speech-stance-text-content"> <div class="speech-stance-text-content">
<div style="display: flex; width: 683px;"> <div style="display: flex; width: 683px;">
<h3 class="speech-stance-name">{{ item.name }}</h3> <h3 class="speech-stance-name">{{ item.personName }}</h3>
<p class="speech-stance-title">{{ item.title }}</p> <p class="speech-stance-title">{{ item.positionTitle }}</p>
</div> </div>
<p class="speech-stance-content">{{ item.content }}</p> <p class="speech-stance-content">{{ item.remarks }}</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
...@@ -21,7 +22,53 @@ ...@@ -21,7 +22,53 @@
<script setup> <script setup>
// 导入数据 // 导入数据
import { onMounted, ref,defineProps,watch } from "vue";
import speechStance from '../json/speechStance.json'; import speechStance from '../json/speechStance.json';
import {getPersonRelation} from "@/api/technologyFigures/technologyFigures"
const props = defineProps({
fieldSelected: {
type: String,
default: "全部领域"
},
areaId: {
type: String,
default: 0
}
});
const aId = ref();
const params = ref({});
watch(() => props.fieldSelected, (val) => {
aId.value = props.areaId;
if(val !== "全部领域"){
params.value.industryId = aId.value;
}else{
params.value = {};
}
handlegetPersonRelationFn();
})
// 获取重要人物言论及立场
const PersonRelation = ref([]);
const handlegetPersonRelationFn = async () => {
try {
const res = await getPersonRelation(params.value);
console.log("重要人物言论及立场", res);
if (res.code === 200) {
PersonRelation.value = res.data;
}
} catch (error) {}
};
onMounted(async () => {
handlegetPersonRelationFn();
});
</script> </script>
<style scoped> <style scoped>
......
<template> <template>
<div class="home-wrapper"> <div class="home-wrapper">
<div class="home-main"> <div class="search-header" v-show="isShow">
<div class="home-main-header"> <div class="home-main-header-center">
<div class="home-main-header-top"> <el-input v-model="input" style="width: 680px; height: 100%" placeholder="搜索科技人物及观点" />
<div class="search">
<div class="search-icon">
<img src="./assets/images/search-icon.png" alt="" />
</div>
<div class="search-text" @click="handleSearch">搜索</div>
</div>
</div>
<div class="home-main-header-btn-box">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">{{ "言论动态" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">{{ "数据总览" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">{{ "资源库" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
<div class="home-box" :class="{ scrollHomeBox: isShow }" ref="containerRef">
<div class="home-header" v-show="!isShow">
<div class="header-item">国家科技安全</div> <div class="header-item">国家科技安全</div>
<div class="header-item">></div> <div class="header-item">></div>
<div class="header-item back-item" @click="handleBackHome">中美博弈概览</div> <div class="header-item back-item" @click="handleBackHome">中美博弈概览</div>
<div class="header-item">></div> <div class="header-item">></div>
<div class="header-item">科技人物观点</div> <div class="header-item">科技人物观点</div>
</div> </div>
<div class="home-main">
<div class="home-main-header" v-show="!isShow">
<!-- <div class="home-main-header-top">
<div class="header-item">国家科技安全</div>
<div class="header-item">></div>
<div class="header-item back-item" @click="handleBackHome">中美博弈概览</div>
<div class="header-item">></div>
<div class="header-item">科技人物观点</div>
</div> -->
<div class="home-main-header-center"> <div class="home-main-header-center">
<el-input v-model="input" style="width: 838px; height: 100%" placeholder="搜索科技人物及观点" /> <el-input v-model="input" style="width: 838px; height: 100%" placeholder="搜索科技人物及观点" />
<div class="search"> <div class="search">
...@@ -37,25 +82,25 @@ ...@@ -37,25 +82,25 @@
</div> </div>
</div> --> </div> -->
<div class="home-main-header-btn-box"> <div class="home-main-header-btn-box">
<div class="btn" @click="scrollToTop('position1')"> <div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">{{ "最新动态" }}</div> <div class="btn-text">{{ "最新动态" }}</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
</div> </div>
<div class="btn" @click="scrollToTop('position2')"> <div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">{{ "资讯要闻" }}</div> <div class="btn-text">{{ "言论动态" }}</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
</div> </div>
<div class="btn" @click="scrollToTop('position3')"> <div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">{{ "统计概览" }}</div> <div class="btn-text">{{ "数据总览" }}</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
</div> </div>
<div class="btn" @click="scrollToTop('position4')"> <div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">{{ "资源库" }}</div> <div class="btn-text">{{ "资源库" }}</div>
<div class="btn-icon"> <div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" /> <img src="@/assets/icons/arrow-right-icon.png" alt="" />
...@@ -80,12 +125,89 @@ ...@@ -80,12 +125,89 @@
</div> </div>
<div class="title">{{ "人物新闻动态" }}</div> <div class="title">{{ "人物新闻动态" }}</div>
</div> </div>
<div class="box1-header-right" @click="handleClickToDetail('337')"> <div class="box1-header-right" @click="handleClcikToCharacter(curnews.personId)">
{{ "查看详情 >" }} {{ "查看详情 >" }}
</div> </div>
</div> </div>
<div class="box1-main" style="display: block"> <div class="box1-main" style="display: block">
<PersonNewsCard /> <el-carousel
ref="carouselRef"
height="354px"
:autoplay="true"
:interval="3000"
arrow="never"
indicator-position="none"
@change="handleCarouselChange"
>
<el-carousel-item v-for="person in newsDynamics">
<div class="carousel-content" style="display: flex; height: 100%">
<!-- 左侧:头像 -->
<div class="avatar">
<img :src="person.imageUrl" alt="人物头像" />
</div>
<!-- 事件列表 -->
<div class="events">
<!-- 头部区域 -->
<div class="header">
<!-- 右侧:信息 -->
<div class="info" style="display: flex; width: 100%; justify-content: space-between">
<div>
<h2>{{ person.name }}</h2>
<p class="newtitle">{{ person.positionTitle }}</p>
</div>
<div style="display: flex; gap: 10px;">
<div class="source-tag" v-for="item in person.personTypeList"
:class="{tag2: item.typeId === '002'}">{{ item.typeName }}</div>
</div>
</div>
</div>
<div class="line"></div>
<div style="height: 289px; width: 688px;">
<el-timeline style="margin-top: 14px">
<el-timeline-item placement="top" v-for="(activity, index) in person.newsList" :key="index" type="primary"
:hollow="true">
<template #dot>
<div style="
display: flex;
justify-content: space-between;
width: 688px;
text-align: center;
align-items: center;
margin-bottom: 12px;
">
<div style="display: flex">
<img src="./assets/images/dot1.png" alt="" style="width: 10px; height: 10px" />
<div style="
margin-left: 16px;
margin-top: -6px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
letter-spacing: 1px;
text-align: left;
">
{{ activity.newsDate }}
</div>
</div>
<img src="./assets/images/dot2.png" alt="" style="width: 20px; height: 18px" />
</div>
</template>
{{ activity.newsContent }}
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div> </div>
</div> </div>
<div class="box2"> <div class="box2">
...@@ -103,17 +225,17 @@ ...@@ -103,17 +225,17 @@
<div <div
class="item-left" class="item-left"
:class="{ :class="{
itemLeftStatus1: item.status === '一般风险', itemLeftStatus1: item.signalLevel === '一般风险',
itemLeftStatus2: item.status === '重大风险' itemLeftStatus2: item.signalLevel === '重大风险'
}" }"
> >
{{ item.status }} {{ item.signalLevel ? item.signalLevel : "一般风险" }}
</div> </div>
<div class="item-right"> <div class="item-right">
<div class="text"> <div class="text">
{{ item.title }} {{ item.signalTitle }}
</div> </div>
<div class="time">{{ item.time }}</div> <div class="time">{{ item.signalTime }}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -145,6 +267,7 @@ ...@@ -145,6 +267,7 @@
<div <div
v-for="value in peoDateList" v-for="value in peoDateList"
:class="peoDate !== value ? 'btn-box-samll' : 'btn-box-select-samll'" :class="peoDate !== value ? 'btn-box-samll' : 'btn-box-select-samll'"
@click="ChooseTime(value)"
> >
{{ value }} {{ value }}
</div> </div>
...@@ -153,7 +276,7 @@ ...@@ -153,7 +276,7 @@
</div> </div>
</div> </div>
<div class="box3-main"> <div class="box3-main">
<TimelineMap /> <TimelineMap :peoDate="peoDate"/>
</div> </div>
</div> </div>
<div class="box4"> <div class="box4">
...@@ -170,16 +293,17 @@ ...@@ -170,16 +293,17 @@
</div> </div>
<div style="display: flex; gap: 8px; margin-right: 12px"> <div style="display: flex; gap: 8px; margin-right: 12px">
<div <div
v-for="value in fields" v-for="value in areaTypeList"
:class="fieldSelect !== value ? 'btn-box-samll' : 'btn-box-select-samll'" :class="fieldSelected !== value.name ? 'btn-box-samll' : 'btn-box-select-samll'"
@click="ChooseArea(value)"
> >
{{ value }} {{ value.name }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="box4-main"> <div class="box4-main">
<SpeechStance /> <SpeechStance :fieldSelected="fieldSelected" :areaId="areaId"/>
</div> </div>
</div> </div>
</div> </div>
...@@ -198,7 +322,7 @@ ...@@ -198,7 +322,7 @@
</div> </div>
<div> <div>
<div style="height: 45px; display: flex; align-items: center"> <div style="height: 45px; display: flex; align-items: center">
<el-select v-model="wordCloudvalue" style="width: 120px; height: 28px"> <el-select v-model="wordCloudvalue" style="width: 120px; height: 28px" @change="handleBox5Change">
<el-option <el-option
v-for="item in yearList" v-for="item in yearList"
:key="item.value" :key="item.value"
...@@ -209,9 +333,10 @@ ...@@ -209,9 +333,10 @@
<el-select <el-select
v-model="wordCloudfield" v-model="wordCloudfield"
style="width: 120px; height: 28px; margin: 10px 24px 10px 5px" style="width: 120px; height: 28px; margin: 10px 24px 10px 5px"
@change="handleBox5areaChange"
> >
<el-option <el-option
v-for="item in fieldSelect" v-for="item in fieldSelectList"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
...@@ -269,14 +394,15 @@ ...@@ -269,14 +394,15 @@
</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>
<div style="gap: 8px; display: flex"> <div style="gap: 2px; display: flex;width:510px;">
<div <div
v-for="value in viewOption" v-for="value in PersonType"
:class="viewSelect !== value ? 'btn-box-samll' : 'btn-box-select-samll'" :class="viewSelect !== value.typeName ? 'btn-box-samll' : 'btn-box-select-samll'"
@click="selectpersontype(value)"
> >
{{ value }} {{ value.typeName }}
</div> </div>
<el-select v-model="wordCloudvalue" style="width: 120px; height: 28px; margin-top: -5px"> <el-select v-model="yearSelect" style="width: 100px; height: 28px; margin-top: -5px" >
<el-option <el-option
v-for="item in yearList" v-for="item in yearList"
:key="item.value" :key="item.value"
...@@ -289,7 +415,7 @@ ...@@ -289,7 +415,7 @@
</div> </div>
</div> </div>
<div class="box8-main"> <div class="box8-main">
<PersonTable /> <PersonTable :persontypeid="persontypeid" :yearSelect="yearSelect"/>
</div> </div>
</div> </div>
</div> </div>
...@@ -316,6 +442,7 @@ ...@@ -316,6 +442,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script setup> <script setup>
...@@ -324,6 +451,12 @@ import { useRouter } from "vue-router"; ...@@ -324,6 +451,12 @@ import { useRouter } from "vue-router";
import scrollToTop from "@/utils/scrollToTop"; import scrollToTop from "@/utils/scrollToTop";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import {getnewsDynamics,
getBillRiskSignal,
getCharacterOpinionWordCloud,
getOptionAreaChange,
getPersonTypeCount,
getPersonSummaryInfo,getareaType,getPersonType} from "@/api/technologyFigures/technologyFigures"
// 图表工具函数(仅保留用到的) // 图表工具函数(仅保留用到的)
import getWordCloudChart from "./utils/worldCloudChart"; import getWordCloudChart from "./utils/worldCloudChart";
...@@ -336,9 +469,206 @@ import PersonNewsCard from "./component/PersonNewsCard.vue"; ...@@ -336,9 +469,206 @@ import PersonNewsCard from "./component/PersonNewsCard.vue";
import SpeechStance from "./component/speechStance.vue"; import SpeechStance from "./component/speechStance.vue";
import PersonTable from "./component/PersonTable.vue"; import PersonTable from "./component/PersonTable.vue";
import SourceLibrary from "./component/SourceLibrary.vue"; import SourceLibrary from "./component/SourceLibrary.vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
const router = useRouter(); const router = useRouter();
const containerRef = ref(null);
const { isShow } = useContainerScroll(containerRef);
// 当前人物新闻动态
const curnews = ref({
imageUrl: "",
name: "",
newsList: [],
personId: "",
personTypeList: [],
positionTitle: "",
});
const curnewsDynamicsIndex = ref(0);
const handleCarouselChange = index => {
curnewsDynamicsIndex.value = index;
if (newsDynamics.value && newsDynamics.value.length > 0) {
curnews.value = newsDynamics.value[index];
}
};
// 获取人物新闻动态
const newsDynamics = ref([]);
const handlgetnewsDynamicsFn = async () => {
try {
const res = await getnewsDynamics();
console.log("人物新闻动态", res);
newsDynamics.value = res.data;
} catch (error) {
console.error(error);
}
};
// 获取人物类别
const PersonType = ref([]);
const handlgetPersonTypeFn = async () => {
try {
const res = await getPersonType();
console.log("人物类别", res);
PersonType.value = res.data;
} catch (error) {
console.error(error);
}
};
// 获取风险信号
const handleGetBillRiskSignalFn = async () => {
const params = {
moduleId: "0100"
};
try {
const res = await getBillRiskSignal(params);
console.log("风险信号", res);
if (res.code === 200) {
warningList.value = res.data;
}
} catch (error) {}
};
// 获取行业领域
const areaTypeList = ref([]);
const handlegetareaTypeFn = async () => {
try {
const res = await getareaType();
console.log("行业领域", res);
if (res.code === 200) {
areaTypeList.value = res.data.map(item=>{
return{
id:item.id,
name: item.name
}
});
areaTypeList.value.unshift({id:"0",name:"全部领域"});
fieldSelectList.value = areaTypeList.value.map(item=>{
return {
label: item.name,
value: item.id
}
})
}
} catch (error) {}
};
// 获取科技人物观点词云
const CharacterOpinionWordCloud = ref([]);
const handlegetCharacterOpinionWordCloudFn = async () => {
const params = {
year: wordCloudvalue.value,
};
if(wordCloudfield.value !== "0"){
params.areaId = wordCloudfield.value;
}
try {
const res = await getCharacterOpinionWordCloud(params);
console.log("科技人物观点词云", res);
CharacterOpinionWordCloud.value = [];
if (res.code === 200) {
CharacterOpinionWordCloud.value = res.data.map(item=>{
return{
name: item.option,
value: item.count
}
});
}
} catch (error) {}
};
// 科技人物观点涉及领域变化趋势
const box6Chart = ref({
title: ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: [
{ name: "人工智能", value: [150, 70, 80, 90, 75, 85, 100, 140, 130, 150, 190, 170] },
{ name: "集成电路", value: [70, 85, 120, 115, 100, 120, 160, 160, 160, 160, 165, 175] },
{ name: "量子科技", value: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15] },
{ name: "生物科技", value: [5, 0, 10, 15, 18, 20, 25, 30, 30, 35, 40, 45] },
{ name: "通信网络", value: [40, 35, 45, 48, 45, 45, 50, 55, 50, 50, 55, 50] },
{ name: "能源", value: [90, 70, 75, 70, 65, 75, 80, 80, 75, 85, 80, 70] }
]
});
const box6HasData = ref(true);
const handlegetOptionAreaChangeFn = async () => {
const params = {
startTime: "2015-01-01"
};
try {
const res = await getOptionAreaChange(params);
console.log("科技人物观点涉及领域变化趋势", res);
if (res.code === 200 && res.data && res.data.length > 0) {
box6HasData.value = true;
const sortedData = res.data.sort((a, b) => a.year.localeCompare(b.year));
//提取年份作为 title
const years = sortedData.map(item => item.year);
//收集所有行业名称
const allIndustries = new Set();
sortedData.forEach(yearData => {
yearData.industryList.forEach(industry => {
allIndustries.add(industry.industryName);
});
});
//构建结果数据
const industryMap = new Map();
//初始化每个行业的 value 数组
Array.from(allIndustries).forEach(industryName => {
industryMap.set(industryName, new Array(years.length).fill(0));
});
// 填充每个行业每年的数据
sortedData.forEach((yearData, yearIndex) => {
yearData.industryList.forEach(industry => {
const industryValues = industryMap.get(industry.industryName);
if (industryValues) {
industryValues[yearIndex] = industry.amount;
}
});
});
box6Chart.value = {
title: years,
data: Array.from(industryMap.entries()).map(([name, value]) => ({
name,
value
}))
};
}
} catch (error) {}
};
// 科技人物类型
const chart7Data = ref({
name: ["国会议员", "行政主官", "科技领袖", "顶尖科学家", "其他"],
value: [482, 41, 83, 201, 25]
});
const handlegetPersonTypeCountFn = async () => {
try {
const res = await getPersonTypeCount();
console.log("科技人物类型", res);
chart7Data.value = {
name:res.data.map(item => item.typeName),
value:res.data.map(item => item.countPerson)
};
console.log("chart7Data", chart7Data);
} catch (error) {
console.error(error);
}
};
// 搜索输入 // 搜索输入
const input = ref(""); const input = ref("");
...@@ -350,21 +680,131 @@ const handleBackHome = () => { ...@@ -350,21 +680,131 @@ const handleBackHome = () => {
const carouselRef = ref(null); const carouselRef = ref(null);
// 切换人物新闻动态 // 切换人物新闻动态
const handleSwithCurBill = name => { const handleSwithCurBill = name => {
if (name === "left") { if (name === "left") {
carouselRef.value.prev(); carouselRef.value.prev();
} else { } else {
carouselRef.value.next(); carouselRef.value.next();
} }
};
const handleSearch = () => {
const curRoute = router.resolve({
path: "/searchResults",
query: {
searchText: input.value,
areaName: "人物"
}
});
window.open(curRoute.href, "_blank");
}; };
const handleToPosi = id => {
const element = document.getElementById(id);
if (element && containerRef.value) {
const containerRect = containerRef.value.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
const top = elementRect.top - containerRect.top + containerRef.value.scrollTop;
containerRef.value.scrollTo({
top: top,
behavior: "smooth"
});
}
};
const ChooseTime = value => {
peoDate.value = value;
}
const areaId = ref();
const ChooseArea = value => {
fieldSelected.value = value.name;
areaId.value = value.id;
}
const handleBox5Change = async () => {
await handlegetCharacterOpinionWordCloudFn();
const wordCloudChart = getWordCloudChart(CharacterOpinionWordCloud.value);
setChart(wordCloudChart, "box5Chart");
}
const handleBox5areaChange = async () =>{
await handlegetCharacterOpinionWordCloudFn();
const wordCloudChart = getWordCloudChart(CharacterOpinionWordCloud.value);
setChart(wordCloudChart, "box5Chart");
}
const persontypeid = ref();
const selectpersontype = value =>{
viewSelect.value = value.typeName;
persontypeid.value = value.typeId;
}
// 查看详情 // 查看详情
const handleClickToDetail = id => { const handleClickToDetail = () => {
window.sessionStorage.setItem("billId", curBill.value.billId);
const route = router.resolve("/billLayout?billId=" + curBill.value.billId);
window.open(route.href, "_blank");
};
// 跳转人物主页
const handleClcikToCharacter = async (id) => {
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
let personTypeName = "";
const params = {
personId: id
};
try {
const res = await getPersonSummaryInfo(params);
console.log("人物全局信息", res);
if (res.code === 200 && res.data) {
const arr = personTypeList.filter(item => {
return item.typeId === res.data.personType;
});
console.log("arr", arr);
if (arr && arr.length > 0) {
personTypeName = arr[0].typeName;
console.log("personTypeName", personTypeName);
if (personTypeName === "科技企业领袖") {
type = 1;
} else if (personTypeName === "国会议员") {
type = 2;
} else if (personTypeName === "智库研究人员") {
type = 3;
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
const route = router.resolve({ const route = router.resolve({
path: "/marketAccessLayout", path: "/characterPage",
query: { id } query: {
type: type, // type=1为科技企业领袖,2为国会议员,3为智库研究人员
personId: id
}
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
} else {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
} catch (error) {}
}; };
// 风险信号 // 风险信号
...@@ -377,10 +817,73 @@ const warningList = ref([ ...@@ -377,10 +817,73 @@ const warningList = ref([
]); ]);
//人物动向 //人物动向
const peoDate = ref("今天"); const peoDate = ref("本周");
const peoDateList = ["今天", "昨天", "近三天", "本周", "本月"]; const peoDateList = ["今天", "昨天", "近三天", "本周", "本月"];
//重要人物言论及立场选择按钮 //重要人物言论及立场选择按钮
const fieldSelect = ref("全部领域"); const fieldSelected = ref("全部领域");
const fieldSelectList = ref([
{
label: "全部领域",
value: "0"
},
{
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 fields = [ const fields = [
"全部领域", "全部领域",
"集成电路", "集成电路",
...@@ -394,7 +897,24 @@ const fields = [ ...@@ -394,7 +897,24 @@ const fields = [
"新材料", "新材料",
"先进制造" "先进制造"
]; ];
const yearList = ref(["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"]); const yearList = ref([
{
label: "2025年",
value: "2025"
},
{
label: "2024年",
value: "2024"
},
{
label: "2023年",
value: "2023"
},
{
label: "2022年",
value: "2022"
}
]);
// 资源库分类 // 资源库分类
const categoryList = ref(["全部人物", "国会议员", "行政主官", "科技领袖", "顶尖科学家"]); const categoryList = ref(["全部人物", "国会议员", "行政主官", "科技领袖", "顶尖科学家"]);
const activeCate = ref("全部人物"); const activeCate = ref("全部人物");
...@@ -411,6 +931,7 @@ const handleToMoreRiskSignal = () => { ...@@ -411,6 +931,7 @@ const handleToMoreRiskSignal = () => {
// === 图表数据 === // === 图表数据 ===
// 词云数据 // 词云数据
const wordCloudData = ref([ const wordCloudData = ref([
{ name: "人工智能 (AI)", value: 100 }, { name: "人工智能 (AI)", value: 100 },
...@@ -435,8 +956,9 @@ const wordCloudData = ref([ ...@@ -435,8 +956,9 @@ const wordCloudData = ref([
{ name: "无人机先进空中交通", value: 32 }, { name: "无人机先进空中交通", value: 32 },
{ name: "能源与基础设施", value: 30 } { name: "能源与基础设施", value: 30 }
]); ]);
const wordCloudfield = ref("全部领域"); const wordCloudfield = ref("0");
const wordCloudvalue = ref("2025"); const wordCloudvalue = ref("2025");
const yearSelect = ref("2025");
const areaSelect = ref("近十年"); const areaSelect = ref("近十年");
const options = [ const options = [
{ {
...@@ -448,30 +970,25 @@ const options = [ ...@@ -448,30 +970,25 @@ const options = [
label: "近五年" label: "近五年"
} }
]; ];
// 领域趋势数据
const box6Chart = ref({
title: ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: [
{ name: "人工智能", value: [150, 70, 80, 90, 75, 85, 100, 140, 130, 150, 190, 170] },
{ name: "集成电路", value: [70, 85, 120, 115, 100, 120, 160, 160, 160, 160, 165, 175] },
{ name: "量子科技", value: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15] },
{ name: "生物科技", value: [5, 0, 10, 15, 18, 20, 25, 30, 30, 35, 40, 45] },
{ name: "通信网络", value: [40, 35, 45, 48, 45, 45, 50, 55, 50, 50, 55, 50] },
{ name: "能源", value: [90, 70, 75, 70, 65, 75, 80, 80, 75, 85, 80, 70] }
]
});
const viewOption = ref(["行政主管", "国会议员", "科技领袖", "顶尖科学家"]); const viewOption = ref(["行政主管", "国会议员", "科技领袖", "顶尖科学家"]);
const viewSelect = ref("行政主管"); const viewSelect = ref("国会议员");
// 科技人物类型
const chart7Data = ref({
name: ["国会议员", "行政主官", "科技领袖", "顶尖科学家", "其他"], onMounted(async () => {
value: [482, 41, 83, 201, 25]
}); await handlgetnewsDynamicsFn();
curnews.value = newsDynamics.value[0];
handleGetBillRiskSignalFn();
await handlegetCharacterOpinionWordCloudFn();
await handlegetOptionAreaChangeFn();
await handlegetPersonTypeCountFn();
handlegetareaTypeFn();
await handlgetPersonTypeFn();
onMounted(() => {
// 词云 // 词云
const wordCloudChart = getWordCloudChart(wordCloudData.value); const wordCloudChart = getWordCloudChart(CharacterOpinionWordCloud.value);
setChart(wordCloudChart, "box5Chart"); setChart(wordCloudChart, "box5Chart");
// 领域趋势 // 领域趋势
...@@ -494,7 +1011,135 @@ onMounted(() => { ...@@ -494,7 +1011,135 @@ onMounted(() => {
width: 100%; width: 100%;
height: calc(100vh - 96px); height: calc(100vh - 96px);
position: relative; position: relative;
overflow-y: hidden; // overflow-y: hidden;
.search-header {
width: 100%;
height: 144px;
background: #fff;
overflow: hidden;
.home-main-header-center {
margin-top: 20px;
margin-left: 200px;
width: 800px;
height: 48px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
padding: 1px;
position: relative;
border: 1px solid transparent;
&:hover {
border: 1px solid var(--color-main-active);
}
.search {
position: absolute;
right: -1px;
top: 0px;
width: 120px;
height: 46px;
border-radius: 10px;
background: var(--color-main-active);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
.search-icon {
width: 18px;
height: 18px;
img {
width: 100%;
height: 100%;
}
}
.search-text {
margin-left: 8px;
height: 22px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
}
}
}
.home-main-header-btn-box {
margin-top: 20px;
margin-left: 200px;
display: flex;
gap: 16px;
.btn {
display: flex;
align-items: center;
gap: 9px;
width: 160px;
height: 48px;
border: 1px solid #aed6ff;
box-sizing: border-box;
border-radius: 24px;
background: #e7f3ff;
cursor: pointer;
position: relative;
&:hover {
background: #cae3fc;
}
.btn-text {
width: 80px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 48px;
margin-left: 36px;
text-align: center;
}
.btn-icon {
position: absolute;
top: 16px;
right: 19px;
width: 6px;
height: 12px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.scrollHomeBox {
width: 100%;
height: calc(100% - 144px);
overflow-y: auto;
}
.home-box {
width: 100%;
height: 100%;
background: url("./assets/images/background.png");
background-size: 100% 100%;
overflow-y: auto;
.home-header {
height: 64px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
line-height: 64px;
background: url("./assets/images/header-bg.png");
box-sizing: border-box;
padding-left: 160px;
display: flex;
.header-item {
margin: 0 3px;
}
.back-item {
cursor: pointer;
&:hover {
color: #ccc;
}
}
}
.home-main { .home-main {
// width: 1920px; // width: 1920px;
// margin: 0 auto; // margin: 0 auto;
...@@ -502,11 +1147,14 @@ onMounted(() => { ...@@ -502,11 +1147,14 @@ onMounted(() => {
// background-repeat: no-repeat; // background-repeat: no-repeat;
// background-color: #fff; // background-color: #fff;
// background-size: contain; // background-size: contain;
width: 100%; // width: 100%;
height: 100%; // height: 100%;
overflow-y: auto; // overflow-y: auto;
background: url("./assets/images/background.png"); // background: url("./assets/images/background.png");
background-size: 100% 100%; // background-size: 100% 100%;
width: 1600px;
margin: 0 auto;
margin-top: 48px;
.home-main-header { .home-main-header {
display: flex; display: flex;
...@@ -682,14 +1330,14 @@ onMounted(() => { ...@@ -682,14 +1330,14 @@ onMounted(() => {
margin-top: 34px; margin-top: 34px;
.center-top { .center-top {
height: 450px; height: 460px;
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 20px; gap: 20px;
.box1 { .box1 {
width: 1064px; width: 1064px;
height: 450px; height: 460px;
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: #fff; background: #fff;
...@@ -699,7 +1347,7 @@ onMounted(() => { ...@@ -699,7 +1347,7 @@ onMounted(() => {
.box1-left { .box1-left {
position: absolute; position: absolute;
left: 0; left: 0;
top: 220px; top: 228px;
width: 24px; width: 24px;
height: 48px; height: 48px;
cursor: pointer; cursor: pointer;
...@@ -713,7 +1361,7 @@ onMounted(() => { ...@@ -713,7 +1361,7 @@ onMounted(() => {
.box1-right { .box1-right {
position: absolute; position: absolute;
right: 0; right: 0;
top: 220px; top: 228px;
width: 24px; width: 24px;
height: 48px; height: 48px;
cursor: pointer; cursor: pointer;
...@@ -725,7 +1373,7 @@ onMounted(() => { ...@@ -725,7 +1373,7 @@ onMounted(() => {
} }
.box1-header { .box1-header {
height: 53px; height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
...@@ -736,9 +1384,9 @@ onMounted(() => { ...@@ -736,9 +1384,9 @@ onMounted(() => {
display: flex; display: flex;
.icon { .icon {
width: 18px; width: 22px;
height: 18px; height: 22px;
margin-top: 19px; margin-top: 14px;
img { img {
width: 100%; width: 100%;
...@@ -748,7 +1396,7 @@ onMounted(() => { ...@@ -748,7 +1396,7 @@ onMounted(() => {
.title { .title {
width: 152px; width: 152px;
height: 53px; height: 48px;
margin-left: 18px; margin-left: 18px;
color: #fff; color: #fff;
background: var(--color-main-active); background: var(--color-main-active);
...@@ -761,7 +1409,7 @@ onMounted(() => { ...@@ -761,7 +1409,7 @@ onMounted(() => {
} }
.box1-header-right { .box1-header-right {
margin-top: 19px; margin-top: 17px;
height: 16px; height: 16px;
color: rgba(20, 89, 187, 1); color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
...@@ -781,12 +1429,12 @@ onMounted(() => { ...@@ -781,12 +1429,12 @@ onMounted(() => {
} }
.box2 { .box2 {
width: 521px; width: 520px;
height: 450px; height: 460px;
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; position: relative;
border-radius: 10px;
.box2-header { .box2-header {
height: 48px; height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
...@@ -884,7 +1532,7 @@ onMounted(() => { ...@@ -884,7 +1532,7 @@ onMounted(() => {
.item-right { .item-right {
margin-left: 13px; margin-left: 13px;
width: 408px; width: 408px;
height: 47px; height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex; display: flex;
...@@ -895,15 +1543,23 @@ onMounted(() => { ...@@ -895,15 +1543,23 @@ onMounted(() => {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 47px; line-height: 47px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.time { .time {
margin-left: 10px; width: 100px;
margin-left: 5px;
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: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
} }
...@@ -1315,12 +1971,13 @@ onMounted(() => { ...@@ -1315,12 +1971,13 @@ onMounted(() => {
width: 1600px; width: 1600px;
margin-bottom: 20px; margin-bottom: 20px;
height: 985px; height: 985px;
margin: 0 160px; margin: 0 auto;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
} }
} }
} }
}
} }
.divide-header { .divide-header {
...@@ -1369,6 +2026,10 @@ onMounted(() => { ...@@ -1369,6 +2026,10 @@ onMounted(() => {
line-height: 30px; line-height: 30px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: left; text-align: left;
cursor: pointer;
text-overflow: ellipsis;
white-space: nowrap;
} }
.btn-box-select-samll { .btn-box-select-samll {
...@@ -1391,5 +2052,190 @@ onMounted(() => { ...@@ -1391,5 +2052,190 @@ onMounted(() => {
line-height: 30px; line-height: 30px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: left; text-align: left;
cursor: pointer;
text-overflow: ellipsis;
white-space: nowrap;
}
.person-news-card {
width: 1016px;
height: 354px;
display: flex;
}
.header {
display: flex;
height: 73px;
}
.avatar {
/* 容器 552 */
width: 266px;
height: 352px;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.info h2 {
color: rgba(5, 95, 194, 1);
margin: 0;
font-family: Microsoft YaHei;
font-size: 24px;
font-weight: 700;
line-height: 32px;
letter-spacing: 0px;
text-align: left;
}
.info .newtitle {
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;
margin-top: 4px;
} }
.source-tag {
/* 数据展示/Tag标签/亮色/蓝 */
width: 100px;
height: 24px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 1px 6px 1px 6px;
box-sizing: border-box;
border: 1px solid rgba(135, 232, 222, 1);
border-radius: 4px;
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0px;
text-align: left;
.tag1 {
border: 1px solid rgba(217, 247, 190, 1);
background: rgba(246, 255, 237, 1);
color: rgba(82, 196, 26, 1);
}
.tag2 {
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
color: rgba(22, 119, 255, 1);
}
.tag3 {
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
.tag4 {
border: 1px solid rgba(211, 173, 247, 1);
background: rgba(249, 240, 255, 1);
color: rgba(114, 46, 209, 1);
}
.tag5 {
border: 1px solid rgba(255, 229, 143, 1);
background: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
}
.tag6 {
border: 1px solid rgba(255, 163, 158, 1);
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
}
.line {
/* 矩形 277 */
width: 686px;
height: 1px;
background: rgba(234, 236, 238, 1);
}
.events {
margin-left: 24px;
width: 710px;
}
.event-item {
margin-bottom: 16px;
padding: 12px;
background-color: #fff;
border-left: 4px solid #007bff;
border-radius: 6px;
position: relative;
}
.event-date {
font-size: 0.85em;
color: #999;
margin-bottom: 8px;
display: inline-block;
}
.event-content {
display: flex;
align-items: flex-start;
line-height: 1.5;
}
.event-content .icon {
margin-right: 8px;
font-size: 1.2em;
color: #ff6b00;
vertical-align: middle;
}
.event-content span {
color: #333;
font-size: 0.95em;
}
:deep(.el-timeline) {
max-width: 688px !important;
padding: 0;
}
:deep(.el-timeline-item__wrapper) {
padding-left: 16px;
}
:deep(.el-timeline-item__timestamp.is-top) {
margin-bottom: 0;
padding-top: 0;
height: 12px;
}
:deep(.el-timeline-item__content) {
margin-left: 10px;
}
:deep(.el-timeline-item__wrapper) {
padding-top: 6px;
top: 0;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
</style> </style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论