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

fix: 科技人物样式修改

上级 b450f7fd
...@@ -20,11 +20,15 @@ ...@@ -20,11 +20,15 @@
<button <button
:class="['news-tab', { active: activeTab === 'local' }]" :class="['news-tab', { active: activeTab === 'local' }]"
@click="$emit('update:activeTab', 'local')" @click="$emit('update:activeTab', 'local')"
> 报告</button> > 智库报告</button>
<button <button
:class="['news-tab', { active: activeTab === 'capital' }]" :class="['news-tab', { active: activeTab === 'capital' }]"
@click="$emit('update:activeTab', 'capital')" @click="$emit('update:activeTab', 'capital')"
> 项目</button> > 调查项目</button>
<button
:class="['news-tab', { active: activeTab === 'hearing' }]"
@click="$emit('update:activeTab', 'hearing')"
> 听证会</button>
</div> </div>
<div class="news-sort" ref="sortDropdownRef"> <div class="news-sort" ref="sortDropdownRef">
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
:sort-by="sortBy" :sort-by="sortBy"
@update:sort="updateSort" @update:sort="updateSort"
/> />
<div class="news-body">
<!-- 智库报告 / 调查项目 -->
<div class="news-body" v-if="activeTab !== 'hearing'">
<NewsSidebar <NewsSidebar
:domain-options="domainOptions" :domain-options="domainOptions"
:time-options="timeOptions" :time-options="timeOptions"
...@@ -30,6 +32,77 @@ ...@@ -30,6 +32,77 @@
/> />
</div> </div>
</div> </div>
<!-- 听证会 -->
<div class="hearing-body" v-else>
<div class="hearing-left">
<div class="hearing-filter-box">
<div class="hearing-filter-header">
<div class="hearing-filter-icon"></div>
<div class="hearing-filter-title">科技领域</div>
</div>
<div class="hearing-filter-main">
<el-checkbox-group class="hearing-checkbox-group" :model-value="hearingSelectedDomains" @change="handleHearingDomainChange">
<el-checkbox class="hearing-filter-checkbox" label="全部领域">全部领域</el-checkbox>
<el-checkbox v-for="item in domainOptions" :key="item.id" class="hearing-filter-checkbox" :label="item.label">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="hearing-filter-box">
<div class="hearing-filter-header">
<div class="hearing-filter-icon"></div>
<div class="hearing-filter-title">发布时间</div>
</div>
<div class="hearing-filter-main">
<el-checkbox-group class="hearing-checkbox-group" :model-value="hearingSelectedTimes" @change="handleHearingTimeChange">
<el-checkbox class="hearing-filter-checkbox" label="全部时间">全部时间</el-checkbox>
<el-checkbox v-for="item in timeOptions" :key="item.id" class="hearing-filter-checkbox" :label="item.label">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
<div class="hearing-right">
<div class="hearing-card-box">
<div class="hearing-card-content">
<template v-if="hearingList.length > 0">
<div v-for="(item, index) in hearingList" :key="item.id">
<div class="hearing-card-item">
<img class="hearing-card-img" :src="item.coverImgUrl" alt="" />
<div class="hearing-card-text">
<div class="hearing-card-title">{{ item.titleZh }}</div>
<div class="hearing-card-time">
{{ item.testimonyDate + ' · ' + item.thinkTankName + ' · ' + item.committeeZh }}
</div>
<div class="hearing-card-tags" v-if="item.domains && item.domains.length">
<AreaTag v-for="(tag, i) in item.domains" :key="i" :tagName="tag" />
</div>
</div>
</div>
<div class="hearing-divider" v-if="index !== hearingList.length - 1"></div>
</div>
</template>
<el-empty v-else description="暂无数据" :image-size="80" />
</div>
</div>
<div class="hearing-footer" v-if="hearingList.length > 0">
<div class="hearing-footer-info">共 {{ hearingTotal }} 篇国会听证会</div>
<el-pagination
:page-size="hearingPageSize"
background
layout="prev, pager, next"
:total="hearingTotal"
@current-change="handleHearingPageChange"
:current-page="hearingCurrentPage"
/>
</div>
</div>
</div>
</div> </div>
</template> </template>
...@@ -40,6 +113,7 @@ import NewsTopBar from './NewsTopBar.vue' ...@@ -40,6 +113,7 @@ import NewsTopBar from './NewsTopBar.vue'
import NewsSidebar from './NewsSidebar.vue' import NewsSidebar from './NewsSidebar.vue'
import NewsCard from './NewsCard.vue' import NewsCard from './NewsCard.vue'
import NewsPagination from './NewsPagination.vue' import NewsPagination from './NewsPagination.vue'
import AreaTag from '@/components/base/AreaTag/index.vue'
import { getIndustryKeyList } from '@/api/bill/billHome.js' import { getIndustryKeyList } from '@/api/bill/billHome.js'
import { getFindingsReport, getInvestigationProject } from '@/api/characterPage/characterPage.js' import { getFindingsReport, getInvestigationProject } from '@/api/characterPage/characterPage.js'
...@@ -63,6 +137,57 @@ const timeOptions = ref([]) ...@@ -63,6 +137,57 @@ const timeOptions = ref([])
const selectedDomains = ref(['all']) const selectedDomains = ref(['all'])
const selectedTimes = ref(['all']) const selectedTimes = ref(['all'])
// ====== 听证会 mock 数据 ======
const hearingSelectedDomains = ref(['全部领域'])
const hearingSelectedTimes = ref(['全部时间'])
const hearingCurrentPage = ref(1)
const hearingPageSize = 10
const hearingTotal = ref(0)
const hearingList = ref([])
const MOCK_HEARINGS = [
{ id: 1, titleZh: '人工智能在国防领域的应用前景与风险', testimonyDate: '2026-03-15', thinkTankName: '兰德公司', committeeZh: '众议院军事委员会', coverImgUrl: '', domains: ['人工智能', '国防安全'] },
{ id: 2, titleZh: '半导体供应链安全:对华出口管制的成效评估', testimonyDate: '2026-02-28', thinkTankName: '战略与国际研究中心', committeeZh: '参议院外交委员会', coverImgUrl: '', domains: ['半导体', '国际贸易'] },
{ id: 3, titleZh: '量子计算技术发展与美国国家安全战略', testimonyDate: '2026-01-20', thinkTankName: '布鲁金斯学会', committeeZh: '众议院科学委员会', coverImgUrl: '', domains: ['量子计算', '国家安全'] },
{ id: 4, titleZh: '中美科技竞争背景下的新兴技术标准制定', testimonyDate: '2025-12-10', thinkTankName: '卡内基国际和平基金会', committeeZh: '参议院商务委员会', coverImgUrl: '', domains: ['技术标准', '中美关系'] },
{ id: 5, titleZh: '生物技术发展与全球健康安全治理', testimonyDate: '2025-11-05', thinkTankName: '兰德公司', committeeZh: '众议院能源与商业委员会', coverImgUrl: '', domains: ['生物技术', '公共卫生'] },
{ id: 6, titleZh: '太空领域军备控制的挑战与机遇', testimonyDate: '2025-10-18', thinkTankName: '战略与国际研究中心', committeeZh: '参议院军事委员会', coverImgUrl: '', domains: ['太空技术', '国防安全'] },
{ id: 7, titleZh: '5G/6G网络基础设施安全与数据隐私保护', testimonyDate: '2025-09-22', thinkTankName: '布鲁金斯学会', committeeZh: '众议院能源与商业委员会', coverImgUrl: '', domains: ['通信技术', '数据安全'] },
{ id: 8, titleZh: '气候变化对国家安全的影响及应对策略', testimonyDate: '2025-08-14', thinkTankName: '卡内基国际和平基金会', committeeZh: '参议院环境与公共工程委员会', coverImgUrl: '', domains: ['气候变化', '能源技术'] },
]
function loadMockHearings() {
hearingTotal.value = MOCK_HEARINGS.length
const start = (hearingCurrentPage.value - 1) * hearingPageSize
const end = start + hearingPageSize
hearingList.value = MOCK_HEARINGS.slice(start, end).map(item => ({
...item,
coverImgUrl: item.coverImgUrl || 'https://via.placeholder.com/56x77?text=Hearing'
}))
}
function handleHearingDomainChange(val) {
if (val.includes('全部领域')) {
hearingSelectedDomains.value = ['全部领域']
} else {
hearingSelectedDomains.value = val.length > 0 ? val : ['全部领域']
}
}
function handleHearingTimeChange(val) {
if (val.includes('全部时间')) {
hearingSelectedTimes.value = ['全部时间']
} else {
hearingSelectedTimes.value = val.length > 0 ? val : ['全部时间']
}
}
function handleHearingPageChange(page) {
hearingCurrentPage.value = page
loadMockHearings()
}
// ====== 原有逻辑 ======
async function loadFilterOptions() { async function loadFilterOptions() {
const res = await getIndustryKeyList() const res = await getIndustryKeyList()
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
...@@ -186,8 +311,10 @@ watch(activeTab, (val) => { ...@@ -186,8 +311,10 @@ watch(activeTab, (val) => {
currentPage.value = 1 currentPage.value = 1
if (val === 'local') { if (val === 'local') {
loadNews() loadNews()
} else { } else if (val === 'capital') {
loadProjects() loadProjects()
} else if (val === 'hearing') {
loadMockHearings()
} }
}) })
...@@ -245,4 +372,148 @@ onMounted(async () => { ...@@ -245,4 +372,148 @@ onMounted(async () => {
align-content: flex-start; align-content: flex-start;
align-items: flex-start; align-items: flex-start;
} }
/* ====== 听证会布局 ====== */
.hearing-body {
display: flex;
gap: 16px;
align-items: flex-start;
}
.hearing-left {
width: 360px;
flex-shrink: 0;
padding-bottom: 24px;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(94, 95, 95, 0.1);
background: rgba(255, 255, 255, 1);
}
.hearing-filter-box {
margin-top: 16px;
}
.hearing-filter-header {
display: flex;
gap: 17px;
padding-left: 16px;
}
.hearing-filter-icon {
width: 8px;
height: 16px;
background: rgba(5, 95, 194, 1);
border-radius: 0 4px 4px 0;
margin-top: 4px;
}
.hearing-filter-title {
height: 24px;
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
}
.hearing-filter-main {
margin-left: 24px;
margin-top: 12px;
}
.hearing-checkbox-group {
display: grid;
grid-template-columns: repeat(2, 160px);
gap: 8px 4px;
}
.hearing-filter-checkbox {
width: 160px;
height: 24px;
margin-right: 0 !important;
}
:deep(.hearing-filter-checkbox .el-checkbox__label) {
font-size: 16px;
}
.hearing-right {
flex: 1;
min-width: 0;
}
.hearing-card-box {
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(94, 95, 95, 0.1);
}
.hearing-card-content {
padding: 33px 36px 27px 37px;
}
.hearing-card-item {
width: 100%;
display: flex;
}
.hearing-card-img {
width: 56px;
height: 77px;
margin-right: 22px;
flex-shrink: 0;
background: rgba(234, 236, 238, 1);
border-radius: 4px;
object-fit: cover;
}
.hearing-card-text {
display: flex;
flex-direction: column;
}
.hearing-card-title {
color: rgb(59, 65, 75);
font-size: 18px;
font-weight: 700;
line-height: 22px;
margin-bottom: 2px;
cursor: pointer;
}
.hearing-card-time {
color: rgb(95, 101, 108);
font-size: 14px;
font-weight: 400;
line-height: 22px;
margin-bottom: 7px;
}
.hearing-card-tags {
display: flex;
gap: 8px;
height: 24px;
}
.hearing-divider {
height: 1px;
background: rgb(234, 236, 238);
margin: 16px 0;
}
.hearing-footer {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.hearing-footer-info {
color: rgba(132, 136, 142, 1);
font-size: 14px;
font-weight: 400;
line-height: 18px;
}
</style> </style>
...@@ -12,12 +12,12 @@ export const CHARACTER_CONFIG = { ...@@ -12,12 +12,12 @@ export const CHARACTER_CONFIG = {
useImageProxy: false, useImageProxy: false,
headerTagType: "areaTag", headerTagType: "areaTag",
wordCloudTitle: "科技观点", wordCloudTitle: "科技观点",
yearDefault: "全部时间", yearDefault: "全部",
yearBuildMode: "dynamic", yearBuildMode: "dynamic",
yearStaticOptions: [], yearStaticOptions: [],
showFundSource: false, showFundSource: false,
resumeMode: "inline", resumeMode: "inline",
resumeTitle: "职业履历", resumeTitle: "生涯履历",
resumeHeight: "1336px", resumeHeight: "1336px",
companySectionTitle: "实体信息", companySectionTitle: "实体信息",
basicInfoFields: [ basicInfoFields: [
...@@ -51,13 +51,13 @@ export const CHARACTER_CONFIG = { ...@@ -51,13 +51,13 @@ export const CHARACTER_CONFIG = {
headerTagType: "inline", headerTagType: "inline",
wordCloudTitle: "科技观点", wordCloudTitle: "科技观点",
yearDefault: "全部", yearDefault: "全部",
yearBuildMode: "static", yearBuildMode: "dynamic",
yearStaticOptions: ["全部", "2025", "2024", "2023", "2022", "2021", "2020"], yearStaticOptions: [],
showFundSource: true, showFundSource: true,
resumeMode: "inline", resumeMode: "inline",
resumeTitle: "职业履历", resumeTitle: "生涯履历",
resumeHeight: "1556px", resumeHeight: "1556px",
companySectionTitle: "社交媒体", companySectionTitle: "实体信息",
basicInfoFields: [ basicInfoFields: [
{ label: "出生日期:", key: "birthday", type: "text" }, { label: "出生日期:", key: "birthday", type: "text" },
{ label: "现任职位:", key: "positionTitle", type: "text" }, { label: "现任职位:", key: "positionTitle", type: "text" },
...@@ -89,13 +89,13 @@ export const CHARACTER_CONFIG = { ...@@ -89,13 +89,13 @@ export const CHARACTER_CONFIG = {
headerTagType: "areaTag", headerTagType: "areaTag",
wordCloudTitle: "核心观点", wordCloudTitle: "核心观点",
yearDefault: "全部", yearDefault: "全部",
yearBuildMode: "static", yearBuildMode: "dynamic",
yearStaticOptions: ["全部", "2026", "2025", "2024", "2023", "2022", "2021"], yearStaticOptions: [],
showFundSource: false, showFundSource: false,
resumeMode: "card", resumeMode: "inline",
resumeTitle: "政治履历", resumeTitle: "生涯履历",
resumeHeight: null, resumeHeight: null,
companySectionTitle: "社交媒体", companySectionTitle: "实体信息",
basicInfoFields: [ basicInfoFields: [
{ label: "出生日期:", key: "birthday", type: "text" }, { label: "出生日期:", key: "birthday", type: "text" },
{ label: "现任职位:", key: "positionTitle", type: "text" }, { label: "现任职位:", key: "positionTitle", type: "text" },
......
...@@ -151,7 +151,7 @@ ...@@ -151,7 +151,7 @@
<div class="right"> <div class="right">
<!-- 基本信息 --> <!-- 基本信息 -->
<AnalysisBox title="基本信息" width="520px" :height="boxHeight" :show-all-btn="false" class="right-top" v-if="characterBasicInfo"> <AnalysisBox title="基本信息" width="520px" :show-all-btn="false" class="right-top auto-height-box" v-if="characterBasicInfo">
<div class="main-content"> <div class="main-content">
<div class="baseInfo"> <div class="baseInfo">
<div v-for="field in config.basicInfoFields" :key="field.key" class="baseInfo-item"> <div v-for="field in config.basicInfoFields" :key="field.key" class="baseInfo-item">
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
</div> </div>
<div class="company"> <div class="company">
<div class="company-title">{{ config.companySectionTitle }}</div> <div class="company-title">{{ config.companySectionTitle }}</div>
<div class="company-content"> <div class="company-content" v-if="characterBasicInfo.organizationList && characterBasicInfo.organizationList.length > 0">
<div v-for="item in characterBasicInfo.organizationList" :key="item" <div v-for="item in characterBasicInfo.organizationList" :key="item"
class="company-item"> class="company-item">
<img :src="item.imageUrl ? item.imageUrl : DefaultIcon2" alt="" /> <img :src="item.imageUrl ? item.imageUrl : DefaultIcon2" alt="" />
...@@ -193,6 +193,7 @@ ...@@ -193,6 +193,7 @@
</div> </div>
</div> </div>
</div> </div>
<el-empty v-else description="暂无数据" :image-size="60" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
......
<template> <template>
<div class="character-page"> <div class="character-page">
<img src="./assets/images/background.png" alt="" class="bg" /> <img src="./assets/images/background.png" alt="" class="bg" />
<ModuleHeader />
<!-- 主要内容 --> <!-- 主要内容 -->
<div class="main"> <div class="main">
<unified-character :type="type" :person-id="personId" /> <unified-character :type="type" :person-id="personId" />
...@@ -13,7 +12,6 @@ ...@@ -13,7 +12,6 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import UnifiedCharacter from './components/unified/index.vue'; import UnifiedCharacter from './components/unified/index.vue';
import ModuleHeader from '@/components/base/moduleHeader/index.vue';
import { getCharacterGlobalInfo } from "@/api/characterPage/characterPage.js"; import { getCharacterGlobalInfo } from "@/api/characterPage/characterPage.js";
......
...@@ -11,16 +11,19 @@ ...@@ -11,16 +11,19 @@
<div class="table-body"> <div class="table-body">
<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" @click="handleClickToCharacter(item.personId)">
<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-if="item.party === 'Republican' || item.party === '共和党'">
<img :src="getTagIconUrl(tag)" class="tag-icon" alt="tag" /> <img :src="getTagIconUrl('1')" class="tag-icon" alt="tag" />
</div> </div>
<div class="person-tag-bg" v-if="item.party && item.party !== 'Republican' && item.party !== '共和党'">
<img :src="getTagIconUrl('2')" class="tag-icon" alt="tag" />
</div> </div>
</div> </div>
<div class="person-info" @click="handleClickToCharacter(item.personId)"> </div>
<div class="person-info">
<div class="person-name">{{ item.name }}</div> <div class="person-name">{{ item.name }}</div>
<div class="person-position">{{ item.position }}</div> <div class="person-position">{{ item.position }}</div>
</div> </div>
...@@ -67,11 +70,8 @@ watch(() => [props.persontypeid,props.yearSelect], (val) => { ...@@ -67,11 +70,8 @@ watch(() => [props.persontypeid,props.yearSelect], (val) => {
}) })
const getTagIconUrl = (tag) => { const getTagIconUrl = (tag) => {
// 用 import.meta.glob 预加载所有图标,支持动态匹配
const icons = import.meta.glob('../assets/images/header-icon*.png', { eager: true, as: 'url' }); const icons = import.meta.glob('../assets/images/header-icon*.png', { eager: true, as: 'url' });
// 拼接对应路径,匹配预加载的图标
const iconPath = `../assets/images/header-icon${tag}.png`; const iconPath = `../assets/images/header-icon${tag}.png`;
// 兜底:如果没有对应tag的图片,返回默认图或空
return icons[iconPath] || ''; return icons[iconPath] || '';
}; };
// 获取主要人物涉华观点统计 // 获取主要人物涉华观点统计
...@@ -90,7 +90,7 @@ const handlegetMainCharactersViewFn = async () => { ...@@ -90,7 +90,7 @@ const handlegetMainCharactersViewFn = async () => {
avatar: item.personImage, avatar: item.personImage,
name: item.personName, name: item.personName,
position: item.positionTitle, position: item.positionTitle,
tags: ["1", "2"], party: item.party || '',
chinaRelatedCount: item.remarksCount, chinaRelatedCount: item.remarksCount,
mediaQuoteCount: item.remarksCount, mediaQuoteCount: item.remarksCount,
personId:item.personId personId:item.personId
...@@ -197,6 +197,7 @@ const getProgress = count => (count / getMaxCount()) * 100; ...@@ -197,6 +197,7 @@ const getProgress = count => (count / getMaxCount()) * 100;
/* 人物列样式 */ /* 人物列样式 */
.col-person { .col-person {
align-items: flex-start; align-items: flex-start;
cursor: pointer;
} }
.avatar { .avatar {
...@@ -244,9 +245,9 @@ const getProgress = count => (count / getMaxCount()) * 100; ...@@ -244,9 +245,9 @@ const getProgress = count => (count / getMaxCount()) * 100;
.person-tags { .person-tags {
display: flex; display: flex;
justify-content: space-between;
margin-top: -20px; margin-top: -20px;
width: 42px; width: 42px;
text-align: center; text-align: center;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论