Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
fead13ed
提交
fead13ed
authored
4月 13, 2026
作者:
刘宇琪
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: 科技人物样式修改
上级
b450f7fd
显示空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
303 行增加
和
28 行删除
+303
-28
NewsTopBar.vue
...n/components/historicalProposal/components/NewsTopBar.vue
+6
-2
NewsTracker.vue
.../components/historicalProposal/components/NewsTracker.vue
+273
-2
config.js
src/views/characterPage/components/unified/config.js
+11
-11
index.vue
src/views/characterPage/components/unified/index.vue
+3
-2
index.vue
src/views/characterPage/index.vue
+0
-2
PersonTable.vue
src/views/technologyFigures/component/PersonTable.vue
+10
-9
没有找到文件。
src/views/characterPage/components/thinkTankPerson/components/historicalProposal/components/NewsTopBar.vue
浏览文件 @
fead13ed
...
...
@@ -20,11 +20,15 @@
<button
:class=
"['news-tab',
{ active: activeTab === 'local' }]"
@click="$emit('update:activeTab', 'local')"
> 报告
</button>
>
智库
报告
</button>
<button
:class=
"['news-tab',
{ active: activeTab === 'capital' }]"
@click="$emit('update:activeTab', 'capital')"
> 项目
</button>
> 调查项目
</button>
<button
:class=
"['news-tab',
{ active: activeTab === 'hearing' }]"
@click="$emit('update:activeTab', 'hearing')"
> 听证会
</button>
</div>
<div
class=
"news-sort"
ref=
"sortDropdownRef"
>
...
...
src/views/characterPage/components/thinkTankPerson/components/historicalProposal/components/NewsTracker.vue
浏览文件 @
fead13ed
...
...
@@ -6,7 +6,9 @@
:sort-by=
"sortBy"
@
update:sort=
"updateSort"
/>
<div
class=
"news-body"
>
<!-- 智库报告 / 调查项目 -->
<div
class=
"news-body"
v-if=
"activeTab !== 'hearing'"
>
<NewsSidebar
:domain-options=
"domainOptions"
:time-options=
"timeOptions"
...
...
@@ -30,6 +32,77 @@
/>
</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>
</template>
...
...
@@ -40,6 +113,7 @@ import NewsTopBar from './NewsTopBar.vue'
import
NewsSidebar
from
'./NewsSidebar.vue'
import
NewsCard
from
'./NewsCard.vue'
import
NewsPagination
from
'./NewsPagination.vue'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
{
getIndustryKeyList
}
from
'@/api/bill/billHome.js'
import
{
getFindingsReport
,
getInvestigationProject
}
from
'@/api/characterPage/characterPage.js'
...
...
@@ -63,6 +137,57 @@ const timeOptions = ref([])
const
selectedDomains
=
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
()
{
const
res
=
await
getIndustryKeyList
()
if
(
res
.
code
===
200
&&
res
.
data
)
{
...
...
@@ -186,8 +311,10 @@ watch(activeTab, (val) => {
currentPage
.
value
=
1
if
(
val
===
'local'
)
{
loadNews
()
}
else
{
}
else
if
(
val
===
'capital'
)
{
loadProjects
()
}
else
if
(
val
===
'hearing'
)
{
loadMockHearings
()
}
})
...
...
@@ -245,4 +372,148 @@ onMounted(async () => {
align-content
:
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
>
src/views/characterPage/components/unified/config.js
浏览文件 @
fead13ed
...
...
@@ -12,12 +12,12 @@ export const CHARACTER_CONFIG = {
useImageProxy
:
false
,
headerTagType
:
"areaTag"
,
wordCloudTitle
:
"科技观点"
,
yearDefault
:
"全部
时间
"
,
yearDefault
:
"全部"
,
yearBuildMode
:
"dynamic"
,
yearStaticOptions
:
[],
showFundSource
:
false
,
resumeMode
:
"inline"
,
resumeTitle
:
"
职业
履历"
,
resumeTitle
:
"
生涯
履历"
,
resumeHeight
:
"1336px"
,
companySectionTitle
:
"实体信息"
,
basicInfoFields
:
[
...
...
@@ -51,13 +51,13 @@ export const CHARACTER_CONFIG = {
headerTagType
:
"inline"
,
wordCloudTitle
:
"科技观点"
,
yearDefault
:
"全部"
,
yearBuildMode
:
"
stat
ic"
,
yearStaticOptions
:
[
"全部"
,
"2025"
,
"2024"
,
"2023"
,
"2022"
,
"2021"
,
"2020"
],
yearBuildMode
:
"
dynam
ic"
,
yearStaticOptions
:
[],
showFundSource
:
true
,
resumeMode
:
"inline"
,
resumeTitle
:
"
职业
履历"
,
resumeTitle
:
"
生涯
履历"
,
resumeHeight
:
"1556px"
,
companySectionTitle
:
"
社交媒体
"
,
companySectionTitle
:
"
实体信息
"
,
basicInfoFields
:
[
{
label
:
"出生日期:"
,
key
:
"birthday"
,
type
:
"text"
},
{
label
:
"现任职位:"
,
key
:
"positionTitle"
,
type
:
"text"
},
...
...
@@ -89,13 +89,13 @@ export const CHARACTER_CONFIG = {
headerTagType
:
"areaTag"
,
wordCloudTitle
:
"核心观点"
,
yearDefault
:
"全部"
,
yearBuildMode
:
"
stat
ic"
,
yearStaticOptions
:
[
"全部"
,
"2026"
,
"2025"
,
"2024"
,
"2023"
,
"2022"
,
"2021"
],
yearBuildMode
:
"
dynam
ic"
,
yearStaticOptions
:
[],
showFundSource
:
false
,
resumeMode
:
"
card
"
,
resumeTitle
:
"
政治
履历"
,
resumeMode
:
"
inline
"
,
resumeTitle
:
"
生涯
履历"
,
resumeHeight
:
null
,
companySectionTitle
:
"
社交媒体
"
,
companySectionTitle
:
"
实体信息
"
,
basicInfoFields
:
[
{
label
:
"出生日期:"
,
key
:
"birthday"
,
type
:
"text"
},
{
label
:
"现任职位:"
,
key
:
"positionTitle"
,
type
:
"text"
},
...
...
src/views/characterPage/components/unified/index.vue
浏览文件 @
fead13ed
...
...
@@ -151,7 +151,7 @@
<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=
"baseInfo"
>
<div
v-for=
"field in config.basicInfoFields"
:key=
"field.key"
class=
"baseInfo-item"
>
...
...
@@ -183,7 +183,7 @@
</div>
<div
class=
"company"
>
<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"
class=
"company-item"
>
<img
:src=
"item.imageUrl ? item.imageUrl : DefaultIcon2"
alt=
""
/>
...
...
@@ -193,6 +193,7 @@
</div>
</div>
</div>
<el-empty
v-else
description=
"暂无数据"
:image-size=
"60"
/>
</div>
</div>
</AnalysisBox>
...
...
src/views/characterPage/index.vue
浏览文件 @
fead13ed
<
template
>
<div
class=
"character-page"
>
<img
src=
"./assets/images/background.png"
alt=
""
class=
"bg"
/>
<ModuleHeader
/>
<!-- 主要内容 -->
<div
class=
"main"
>
<unified-character
:type=
"type"
:person-id=
"personId"
/>
...
...
@@ -13,7 +12,6 @@
import
{
ref
,
onMounted
}
from
'vue'
;
import
{
useRoute
}
from
'vue-router'
;
import
UnifiedCharacter
from
'./components/unified/index.vue'
;
import
ModuleHeader
from
'@/components/base/moduleHeader/index.vue'
;
import
{
getCharacterGlobalInfo
}
from
"@/api/characterPage/characterPage.js"
;
...
...
src/views/technologyFigures/component/PersonTable.vue
浏览文件 @
fead13ed
...
...
@@ -11,16 +11,19 @@
<div
class=
"table-body"
>
<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;"
>
<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=
"getTagIconUrl(
tag
)"
class=
"tag-icon"
alt=
"tag"
/>
<div
class=
"person-tag-bg"
v-
if=
"item.party === 'Republican' || item.party === '共和党'
"
>
<img
:src=
"getTagIconUrl(
'1'
)"
class=
"tag-icon"
alt=
"tag"
/>
</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
class=
"person-info"
@
click=
"handleClickToCharacter(item.personId)"
>
</div>
<div
class=
"person-info"
>
<div
class=
"person-name"
>
{{
item
.
name
}}
</div>
<div
class=
"person-position"
>
{{
item
.
position
}}
</div>
</div>
...
...
@@ -67,11 +70,8 @@ watch(() => [props.persontypeid,props.yearSelect], (val) => {
})
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
]
||
''
;
};
// 获取主要人物涉华观点统计
...
...
@@ -90,7 +90,7 @@ const handlegetMainCharactersViewFn = async () => {
avatar
:
item
.
personImage
,
name
:
item
.
personName
,
position
:
item
.
positionTitle
,
tags
:
[
"1"
,
"2"
]
,
party
:
item
.
party
||
''
,
chinaRelatedCount
:
item
.
remarksCount
,
mediaQuoteCount
:
item
.
remarksCount
,
personId
:
item
.
personId
...
...
@@ -197,6 +197,7 @@ const getProgress = count => (count / getMaxCount()) * 100;
/* 人物列样式 */
.col-person
{
align-items
:
flex-start
;
cursor
:
pointer
;
}
.avatar
{
...
...
@@ -244,9 +245,9 @@ const getProgress = count => (count / getMaxCount()) * 100;
.person-tags
{
display
:
flex
;
justify-content
:
space-between
;
margin-top
:
-20px
;
width
:
42px
;
text-align
:
center
;
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论