提交 b4124538 authored 作者: 刘宇琪's avatar 刘宇琪

fix:3.23问题

上级 8f531fe8
......@@ -161,18 +161,7 @@ export function getareaType(params) {
params
})
}
// 获取人物教育履历
/**
* @param {personId}
* @header token
*/
export function getCharacterReducationResume(params) {
return request({
method: 'GET',
url: `/api/personHomepage/educationResume/${params.personId}`,
params,
})
}
export async function getFindingsReport(personId, params = {}) {
const queryParts = []
......@@ -211,11 +200,4 @@ export async function getSubjectList(params) {
method: 'GET',
params
})
}
export function getareaType(params) {
return request({
method: 'GET',
url: `/api/commonDict/areaType`,
params
})
}
}
<template>
<div class="school-detail-content">
<!-- 左侧 - 最新动态 -->
<div class="left-section">
<AnalysisBox title="最新动态" width="100%" height="100%" :show-all-btn="false" class="dynamics-box">
<div class="dynamics-list">
<div v-for="(item, index) in latestDynamics" :key="index" class="dynamic-item">
<!-- 左侧:日期 -->
<div class="time-col">
<div class="year">{{ item.year }}</div>
<div class="date">{{ item.date }}</div>
</div>
<!-- 中间:轴线三段式穿圆心 -->
<div class="axis-col">
<!-- 上段轴线,第一个节点不显示 -->
<div class="axis-line axis-top" :class="{ invisible: index === 0 }"></div>
<!-- 圆形节点 -->
<div :class="['timeline-node', item.isHighlight ? 'highlight' : 'normal']">
<img v-if="item.icon" :src="item.icon" alt="" class="node-img" />
</div>
<!-- 下段轴线,最后一个节点不显示 -->
<div class="axis-line axis-bottom" :class="{ invisible: index === latestDynamics.length - 1 }"></div>
</div>
<!-- 右侧:内容 -->
<div class="content-col">
<h3 class="title">{{ item.title }}</h3>
<p class="desc">{{ item.content }}</p>
<div class="tag-row">
<div class="tag-list">
<AreaTag v-for="(tag, tIndex) in item.tags" :key="tIndex" :tag-name="tag.name" :type="tag.type" />
</div>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination-area">
<div class="total">{{ `共${totalDynamics}项动态` }}</div>
<div class="pagination">
<button class="page-btn" :disabled="currentPage === 1" @click="handlePageChange(currentPage - 1)">&lt;</button>
<button
v-for="page in displayedPages"
:key="page"
:class="['page-num', { active: page === currentPage }]"
@click="typeof page === 'number' && handlePageChange(page)"
>
{{ page }}
</button>
<button class="page-btn" :disabled="currentPage === totalPages" @click="handlePageChange(currentPage + 1)">&gt;</button>
</div>
</div>
</AnalysisBox>
</div>
<!-- 右侧 -->
<div class="right-section">
<!-- 基本信息 -->
<AnalysisBox title="基本信息" width="100%" height="auto" :show-all-btn="false" class="basic-info-box">
<div class="basic-info">
<div class="university-image">
<img :src="basicInfo.image" alt="University" />
</div>
<div class="info-list">
<div class="info-item" v-for="(item, index) in infoList" :key="index">
<span class="label">{{ item.label }}</span>
<span class="value">{{ item.value }}</span>
</div>
<div class="info-item">
<span class="label">重点方向:</span>
<div class="focus-tags">
<AreaTag v-for="(tag, index) in basicInfo.focusTags" :key="index" :tag-name="tag.name" :type="tag.type" />
</div>
</div>
</div>
<!-- 重点人物 -->
<div class="key-people" v-if="keyPeople.length > 0">
<div class="section-label">重点人物:</div>
<!-- 最多展示2*2,超出可滚动 -->
<div class="people-grid" :class="{ 'single-row': keyPeople.length <= 2 }">
<div v-for="(person, index) in keyPeople" :key="index" class="person-card">
<img :src="person.avatar" :alt="person.name" class="avatar" />
<div class="person-info">
<div class="person-name">{{ person.name }}</div>
<div class="person-title">{{ person.title }}</div>
</div>
</div>
</div>
</div>
</div>
</AnalysisBox>
<!-- 统计卡片 -->
<div class="stats-cards">
<div v-for="(stat, index) in statistics" :key="index" class="stat-card">
<div class="stat-left">
<span class="stat-label">{{ stat.label }}</span>
</div>
<div class="stat-right">
<span class="stat-value" :style="{ color: stat.color }">{{ stat.value }}</span>
</div>
<div class="stat-indicator" :style="{ background: stat.color }"></div>
</div>
</div>
<!-- 历史时间轴 -->
<AnalysisBox title="历史时间轴" width="100%" height="auto" :show-all-btn="false" class="history-box">
<HistoryTimeline :events="historyEvents" />
</AnalysisBox>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import AreaTag from '@/components/base/AreaTag/index.vue'
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue'
import HistoryTimeline from '../components/HistoryTimeline.vue'
// Props
const props = defineProps({
basicInfo: {
type: Object,
default: () => ({
image: '',
establishedTime: '',
location: '',
nature: '',
studentCount: '',
staffCount: '',
qsRanking: '',
focusTags: []
})
},
keyPeople: {
type: Array,
default: () => []
},
statistics: {
type: Array,
default: () => []
},
historyEvents: {
type: Array,
default: () => []
},
latestDynamics: {
type: Array,
default: () => []
},
totalDynamics: {
type: Number,
default: 0
}
})
// Emits
const emit = defineEmits(['page-change'])
// 分页
const currentPage = ref(5)
const totalPages = computed(() => Math.ceil(props.totalDynamics / 7))
const displayedPages = computed(() => {
const pages = []
pages.push(1)
if (currentPage.value > 3) {
pages.push('...')
}
for (let i = Math.max(2, currentPage.value - 1); i <= Math.min(totalPages.value - 1, currentPage.value + 1); i++) {
if (!pages.includes(i)) {
pages.push(i)
}
}
if (currentPage.value < totalPages.value - 2) {
pages.push('...')
}
if (totalPages.value > 1) {
pages.push(totalPages.value)
}
return pages
})
const handlePageChange = (page) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
emit('page-change', page)
}
}
// 基本信息列表
const infoList = computed(() => [
{ label: '成立时间:', value: props.basicInfo.establishedTime },
{ label: '总部地点:', value: props.basicInfo.location },
{ label: '组织性质:', value: props.basicInfo.nature },
{ label: '在校人数:', value: props.basicInfo.studentCount },
{ label: '教职工数:', value: props.basicInfo.staffCount },
{ label: 'QS排名:', value: props.basicInfo.qsRanking }
])
</script>
<style lang="scss" scoped>
.school-detail-content {
display: flex;
gap: 16px;
align-items: stretch;
.left-section {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
:deep(.dynamics-box),
:deep(.analysis-box-wrapper) {
flex: 1;
display: flex;
flex-direction: column;
.wrapper-main {
flex: 1;
display: flex;
flex-direction: column;
}
}
:deep(.dynamics-box) {
height: 100%;
}
}
.right-section {
width: 520px;
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 16px;
}
}
$node-size: 24px;
$axis-width: 2px;
.dynamics-list {
padding: 8px 24px 16px;
flex: 1;
overflow-y: auto;
.dynamic-item {
display: flex;
align-items: stretch;
gap: 0;
.time-col {
width: 70px;
flex-shrink: 0;
text-align: right;
padding-right: 16px;
padding-top: calc(#{$node-size} / 2 - 12px);
.year, .date {
font-size: 16px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(5, 95, 194);
line-height: 24px;
}
}
.axis-col {
width: $node-size;
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
.axis-line {
width: $axis-width;
background-color: #D8D8D8;
flex: 1;
min-height: 12px;
&.invisible {
background-color: transparent;
}
}
.timeline-node {
width: $node-size;
height: $node-size;
border-radius: 50%;
flex-shrink: 0;
overflow: hidden;
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
&.highlight {
background-color: rgba(245, 34, 45, 1);
}
&.normal {
background-color: rgba(22, 119, 255, 1);
}
.node-img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
}
.content-col {
flex: 1;
min-width: 0;
padding-left: 16px;
padding-bottom: 24px;
padding-top: 4px;
.title {
font-size: 20px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
line-height: 26px;
margin: 0 0 8px 0;
cursor: pointer;
&:hover {
color: rgb(5, 95, 194);
}
}
.desc {
font-size: 16px;
font-weight: 400;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(95, 101, 108);
line-height: 24px;
margin: 0 0 8px 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.tag-row {
.tag-list {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
}
}
}
}
.pagination-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 30px;
border-top: 1px solid rgb(234, 236, 238);
.total {
font-size: 16px;
font-weight: 400;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
}
.pagination {
display: flex;
gap: 6px;
.page-btn,
.page-num {
min-width: 32px;
height: 32px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.15);
background: #fff;
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(95, 101, 108);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
&:disabled {
color: rgba(95, 101, 108, 0.45);
cursor: not-allowed;
}
&.active {
color: rgba(22, 119, 255, 1);
border-color: rgba(22, 119, 255, 1);
}
}
}
}
.basic-info-box {
:deep(.wrapper-main) {
overflow: visible;
}
}
.basic-info {
padding: 16px 24px;
.university-image {
width: 100%;
height: 200px;
border-radius: 8px;
overflow: hidden;
margin-bottom: 16px;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.info-list {
.info-item {
display: flex;
margin-bottom: 12px;
.label {
width: 88px;
flex-shrink: 0;
font-size: 16px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
line-height: 24px;
}
.value {
font-size: 16px;
font-weight: 400;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
line-height: 24px;
}
.focus-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
}
}
.key-people {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgb(234, 236, 238);
.section-label {
font-size: 16px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
margin-bottom: 12px;
}
.people-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
max-height: 144px;
overflow-y: auto;
&.single-row {
max-height: 64px;
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: rgb(185, 220, 255);
border-radius: 2px;
}
.person-card {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
height: 64px;
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.person-info {
flex: 1;
min-width: 0;
.person-name {
font-size: 16px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
line-height: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.person-title {
font-size: 14px;
font-weight: 400;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(95, 101, 108);
line-height: 22px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
}
.stats-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
.stat-card {
position: relative;
background: white;
border-radius: 10px;
border: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
padding: 16px 24px;
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
.stat-indicator {
position: absolute;
left: 0;
top: 15px;
bottom: 15px;
width: 4px;
border-radius: 0 2px 2px 0;
}
.stat-label {
font-size: 16px;
font-weight: 400;
font-family: 'Microsoft YaHei', sans-serif;
color: rgb(59, 65, 75);
}
.stat-value {
font-size: 30px;
font-weight: 700;
font-family: 'Microsoft YaHei', sans-serif;
}
}
}
</style>
......@@ -42,8 +42,8 @@
<el-option v-for="item in num" :key="item" :label="item" :value="item" />
</el-select>
</template>
<div class="echarts" ><WordCloudChart v-if="wordLoading" :data="characterView"/></div>
<div class="echarts" id="wordCloudChart">
</div>
</AnalysisBox>
<AnalysisBox title=" 金钱来源" width="1064px" height="512px" :show-all-btn="false" class="left-center">
......@@ -127,7 +127,7 @@
</div>
</div>
</div>
<div class="line-test"></div>
<!-- <div class="line-test"></div> -->
</div>
<div class="pagination">
<div class="total">{{ `共 ${total} 项` }}</div>
......@@ -305,7 +305,6 @@ import HistoricalProposal from "./components/historicalProposal/components/BillT
import PotentialNews from './components/historicalProposal/components/PotentialNews.vue'
import getWordCloudChart from "../../utils/worldCloudChart";
import setChart from "@/utils/setChart";
import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import {
getCharacterGlobalInfo,
getCharacterBasicInfo,
......@@ -435,7 +434,7 @@ const getCharacterBasicInfoFn = async () => {
console.error(error);
}
};
const wordLoading=ref(false)
// 获取人物观点
const characterView = ref({});
const getCharacterViewFn = async () => {
......@@ -445,7 +444,6 @@ const getCharacterViewFn = async () => {
if (numActive.value !== '全部') {
params.year = numActive.value;
}
wordLoading.value=false
try {
const res = await getCharacterView(params);
if (res.code === 200) {
......@@ -458,7 +456,6 @@ const getCharacterViewFn = async () => {
};
});
}
wordLoading.value=true
}
} catch (error) {
......@@ -468,15 +465,18 @@ const getCharacterViewFn = async () => {
const handleCharacterView = async () => {
await getCharacterViewFn();
// const wordCloudChart = getWordCloudChart(characterView.value);
// setChart(wordCloudChart, "wordCloudChart");
const wordCloudChart = getWordCloudChart(characterView.value);
setChart(wordCloudChart, "wordCloudChart");
};
const handleChangeYear = () => {
characterView.value = []
handleCharacterView()
}
}
const yearList = ref([
{
label: "全部",
......@@ -1061,10 +1061,15 @@ const handleClickTag = async (tag) => {
}
.main {
width: 1016px;
height: 360px;
width: 1016px;
margin-left: 24px;
margin-bottom: 16px;
width: 1064px;
min-height: auto;
box-sizing: border-box;
padding-right: 50px;
position: relative;
z-index: 110;
}
.bottom {
......@@ -1209,12 +1214,16 @@ const handleClickTag = async (tag) => {
padding-right: 50px;
position: relative;
z-index: 110;
.main-item {
width: 1014px;
margin-bottom: 40px;
display: flex;
flex-direction: row;
align-items: flex-start;
margin-bottom: 20px;
position: relative;
.time {
width: 77px;
box-sizing: border-box;
......@@ -1359,14 +1368,25 @@ const handleClickTag = async (tag) => {
}
}
}
.main-item::after {
content: '';
position: absolute;
left: 109px; /* 与圆点对齐 */
top: 24px; /* 从圆点下方开始 */
bottom: -20px; /* 延伸到下一个 item */
width: 1px;
background-color: rgb(230, 231, 232);
z-index: -1;
}
.line-test {
position: absolute;
top: 10px;
left: 109px;
height: 1000px;
left: 109px;
border: 1px solid rgb(230, 231, 232);
z-index: -1;
position: absolute;
bottom: 10px;
}
}
......
<template>
<div class="news-card">
<div class="news-card-image">
<img v-if="item.imageUrl" :src="item.imageUrl" :alt="item.name" />
<div v-else class="news-card-placeholder">
......
<template>
<div class="relation-graph-wrapper">
<div class="graph-controls">
<<<<<<< HEAD
<div
v-for="item in controlBtns"
:key="item.type"
:class="['control-btn', { 'control-btn-active': currentLayoutType === item.type }]"
@click="handleClickControlBtn(item.type)"
>
=======
<div v-for="item in controlBtns" :key="item.type"
:class="['control-btn', { 'control-btn-active': currentLayoutType === item.type }]"
@click="handleClickControlBtn(item.type)">
>>>>>>> master
<img :src="item.icon" alt="" />
</div>
</div>
<div ref="containerRef" class="graph-container"></div>
<<<<<<< HEAD
=======
>>>>>>> master
<div v-if="selectedNode" class="node-popup">
<div class="popup-header">
<img :src="selectedNode.image || defaultIcon" alt="" class="popup-icon" />
......@@ -45,10 +31,6 @@
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import G6 from '@antv/g6'
import { Close } from '@element-plus/icons-vue'
<<<<<<< HEAD
=======
>>>>>>> master
import echartsIcon01 from '../assets/echartsicon01.png'
import echartsIcon02 from '../assets/echartsicon02.png'
import echartsIcon03 from '../assets/echartsicon03.png'
......@@ -536,11 +518,6 @@ const processGraphData = (rawData) => {
rawData.nodes.forEach((node, index) => {
const nodeId = String(node.id || index)
<<<<<<< HEAD
=======
>>>>>>> master
if (nodeMap.has(nodeId)) {
return
}
......
......@@ -16,7 +16,7 @@
<img :src="item.avatar" class="avatar" alt="avatar" />
<div class="person-tags">
<div class="person-tag-bg" v-for="(tag, tIdx) in item.tags" :key="tIdx">
<img :src="'/public/icon/header-icon' + tag + '.png'" class="tag-icon" alt="tag" />
<img :src="getTagIconUrl(tag)" class="tag-icon" alt="tag" />
</div>
</div>
</div>
......@@ -43,8 +43,7 @@
</div>
</div>
</div>
</template>
</template>
<script setup>
import { ref,onMounted,defineProps,watch } from "vue";
import personData from "../json/personData.json"; // 引入JSON数据
......@@ -68,7 +67,14 @@ watch(() => [props.persontypeid,props.yearSelect], (val) => {
handlegetMainCharactersViewFn();
})
const getTagIconUrl = (tag) => {
// 用 import.meta.glob 预加载所有图标,支持动态匹配
const icons = import.meta.glob('../assets/images/header-icon*.png', { eager: true, as: 'url' });
// 拼接对应路径,匹配预加载的图标
const iconPath = `../assets/images/header-icon${tag}.png`;
// 兜底:如果没有对应tag的图片,返回默认图或空
return icons[iconPath] || '';
};
// 获取主要人物涉华观点统计
const handlegetMainCharactersViewFn = async () => {
const params = {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论