Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
ad82f0ce
提交
ad82f0ce
authored
3月 12, 2026
作者:
刘宇琪
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
刘宇琪 科技人物观点 概览页
上级
e370ef79
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
1567 行增加
和
527 行删除
+1567
-527
PersonTable.vue
src/views/technologyFigures/component/PersonTable.vue
+5
-3
SourceLibrary.vue
src/views/technologyFigures/component/SourceLibrary.vue
+11
-9
TimelineMap.vue
src/views/technologyFigures/component/TimelineMap.vue
+763
-320
speechStance.vue
src/views/technologyFigures/component/speechStance.vue
+63
-10
index.vue
src/views/technologyFigures/index.vue
+655
-185
useCharacterNav.js
src/views/technologyFigures/utils/useCharacterNav.js
+70
-0
没有找到文件。
src/views/technologyFigures/component/PersonTable.vue
浏览文件 @
ad82f0ce
...
...
@@ -20,7 +20,7 @@
</div>
</div>
</div>
<div
class=
"person-info"
>
<div
class=
"person-info"
@
click=
"handleClickToCharacter(item.personId)"
>
<div
class=
"person-name"
>
{{
item
.
name
}}
</div>
<div
class=
"person-position"
>
{{
item
.
position
}}
</div>
</div>
...
...
@@ -49,7 +49,8 @@
import
{
ref
,
onMounted
,
defineProps
,
watch
}
from
"vue"
;
import
personData
from
"../json/personData.json"
;
// 引入JSON数据
import
{
getMainCharactersView
}
from
"@/api/technologyFigures/technologyFigures"
;
import
{
useCharacterNav
}
from
"../utils/useCharacterNav"
;
const
{
handleClickToCharacter
}
=
useCharacterNav
();
const
props
=
defineProps
({
persontypeid
:
{
type
:
String
,
...
...
@@ -86,7 +87,8 @@ const handlegetMainCharactersViewFn = async () => {
position
:
item
.
positionTitle
,
tags
:
[
"1"
,
"2"
],
chinaRelatedCount
:
item
.
remarksCount
,
mediaQuoteCount
:
item
.
remarksCount
mediaQuoteCount
:
item
.
remarksCount
,
personId
:
item
.
personId
}
});
...
...
src/views/technologyFigures/component/SourceLibrary.vue
浏览文件 @
ad82f0ce
...
...
@@ -77,7 +77,7 @@ watch(
const
router
=
useRouter
();
const
total
=
ref
(
0
);
const
pageSize
=
ref
(
16
);
const
pageSize
=
ref
(
20
);
const
loading
=
ref
(
false
);
const
abortController
=
ref
(
null
);
const
currentPage
=
ref
(
1
);
...
...
@@ -138,16 +138,17 @@ const handleCurrentChange = page => {
// 跳转人物主页
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
);
console
.
log
(
"人物全局信息
2
"
,
res
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
const
arr
=
personTypeList
.
filter
(
item
=>
{
return
item
.
typeId
===
res
.
data
.
personType
;
...
...
@@ -158,13 +159,13 @@ const handleClcikToCharacter = async id => {
personTypeName
=
arr
[
0
].
typeName
;
console
.
log
(
"personTypeName"
,
personTypeName
);
if
(
personTypeName
===
"科技企业领袖"
)
{
if
(
personTypeName
===
"科技企业领袖"
||
personTypeName
===
'政府官员'
)
{
type
=
1
;
}
else
if
(
personTypeName
===
"国会议员"
)
{
type
=
2
;
}
else
if
(
personTypeName
===
"智库研究人员"
)
{
type
=
3
;
}
else
{
}
else
{
personTypeName
=
""
;
ElMessage
.
warning
(
"找不到当前人员的类型值!"
);
return
;
...
...
@@ -205,10 +206,11 @@ const handlePageChange = p => {
}
.
source
-
library
-
grid
{
width
:
1600
px
;
display
:
grid
;
grid
-
template
-
columns
:
repeat
(
4
,
1
fr
);
gap
:
16
px
16
px
;
width
:
1600
px
;
display
:
grid
;
grid
-
template
-
columns
:
repeat
(
4
,
1
fr
);
grid
-
template
-
rows
:
repeat
(
5
,
1
fr
);
gap
:
16
px
16
px
;
}
.
source
-
library
-
card
{
...
...
src/views/technologyFigures/component/TimelineMap.vue
浏览文件 @
ad82f0ce
<
template
>
<div
ref=
"map"
style=
"width: 1600px; height: 551px"
></div>
<div
style=
"width: 1231px; height: 72px; margin-top: -55px; background: linear-gradient(to top, #DAEBFD, #ffffff); margin-left: 182px"
>
<div
style=
"width: 1231px; height: 40px; display: flex; justify-content: space-between;"
>
<div
style=
"display: flex; align-self: center;"
>
<div
class=
"time-btn"
>
⏸
<div
class=
"map-wrapper"
>
<div
ref=
"map"
class=
"map-container"
></div>
<div
class=
"map-overlay"
ref=
"mapOverlay"
>
<div
v-for=
"(card, idx) in mapCards"
:key=
"idx"
class=
"map-card"
:style=
"
{ left: card.x + 'px', top: card.y + 'px' }"
>
<img
class=
"map-card__bg"
src=
"/icon/map-text-bg.png"
alt=
""
/>
<img
class=
"map-card__avatar"
:src=
"card.avatar"
alt=
""
/>
<div
class=
"map-card__info"
>
<div
class=
"map-card__time"
>
{{
card
.
time
}}
</div>
<div
class=
"map-card__text"
>
{{
card
.
text
}}
</div>
</div>
<div
class=
"time-btn"
>
⏹
</div>
</div>
<div
class=
"timeline-panel"
>
<div
class=
"control-bar"
>
<div
class=
"control-left"
>
<button
class=
"ctrl-btn"
@
click=
"handlePause"
:title=
"isPlaying ? '暂停' : '播放'"
>
<svg
v-if=
"isPlaying"
width=
"10"
height=
"14"
viewBox=
"0 0 10 14"
fill=
"none"
>
<rect
x=
"0"
y=
"0"
width=
"3"
height=
"14"
rx=
"0.5"
fill=
"#04295A"
/>
<rect
x=
"7"
y=
"0"
width=
"3"
height=
"14"
rx=
"0.5"
fill=
"#04295A"
/>
</svg>
<svg
v-else
width=
"10"
height=
"14"
viewBox=
"0 0 10 14"
fill=
"none"
>
<polygon
points=
"0,0 10,7 0,14"
fill=
"#04295A"
/>
</svg>
</button>
<button
class=
"ctrl-btn"
@
click=
"handleStop"
title=
"停止"
>
<svg
width=
"12"
height=
"12"
viewBox=
"0 0 12 12"
fill=
"none"
>
<rect
x=
"0"
y=
"0"
width=
"12"
height=
"12"
rx=
"1"
fill=
"#04295A"
/>
</svg>
</button>
<button
class=
"ctrl-btn ctrl-btn--text"
@
click=
"handleReset"
>
重置
</button>
</div>
<div
class=
"time-btn"
>
重置
<div
class=
"control-right"
>
<span
class=
"control-label"
>
当前时间
</span>
<el-date-picker
v-model=
"currentDate"
type=
"date"
format=
"YYYY-MM-DD"
value-format=
"YYYY-MM-DD"
placeholder=
"选择日期"
class=
"timeline-date-picker"
@
change=
"handleDateChange"
/>
<span
class=
"control-label control-label--gap"
>
刻度
</span>
<el-select
v-model=
"scaleMode"
class=
"timeline-select"
@
change=
"handleScaleChange"
>
<el-option
label=
"周制"
value=
"week"
/>
<el-option
label=
"月制"
value=
"month"
/>
<el-option
label=
"日制"
value=
"day"
/>
</el-select>
<span
class=
"control-label control-label--gap"
>
速率
</span>
<el-select
v-model=
"speedRate"
class=
"timeline-select"
@
change=
"handleSpeedChange"
>
<el-option
label=
"1X"
value=
"1"
/>
<el-option
label=
"2X"
value=
"2"
/>
<el-option
label=
"4X"
value=
"4"
/>
</el-select>
</div>
</div>
<div
style=
"display: flex; align-self: center;"
>
<div>
当前时间:
<div
class=
"timeline-track"
ref=
"timelineTrack"
@
mousedown=
"onTimelineMouseDown"
>
<div
class=
"timeline-inner"
ref=
"timelineInner"
>
<template
v-for=
"(tick, index) in visibleTicks"
:key=
"tick.date"
>
<div
class=
"tick-cell"
:style=
"
{ width: tickWidth + 'px' }" @click="onTickClick(tick.globalIndex)">
<div
class=
"tick-line"
:class=
"
{ 'tick-line--major': tick.showLabel }">
</div>
<span
class=
"tick-label"
v-if=
"tick.showLabel"
>
{{
tick
.
label
}}
</span>
</div>
</
template
>
<div
class=
"timeline-cursor"
v-if=
"cursorLeft >= 0"
:style=
"{ left: cursorLeft + 'px' }"
></div>
</div>
<input></input>
<div
style=
"margin: 0 10px 0 40px; color: #04295A;"
>
刻度
</div>
<select
name=
"firstSelect"
id=
"firstSelect"
>
<option
value=
"option1"
>
周制
</option>
</select>
<div
style=
"margin: 0 10px 0 40px; color: #04295A;"
>
速率
</div>
<select
name=
"secondSelect"
id=
"secondSelect"
style=
"margin-right: 20px;"
>
<option
value=
"optionA"
>
1X
</option>
<option
value=
"optionB"
>
2X
</option>
</select>
</div>
</div>
<div
ref=
"timeLineChart"
style=
"width: 1231px; height: 30px; background-color: #6091D6;"
></div>
</div>
</template>
<
script
>
import
*
as
echarts
from
"echarts"
;
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"
// }
// })
import
{
getCharacterTrends
}
from
"@/api/technologyFigures/technologyFigures"
;
import
{
ref
}
from
"vue"
;
// 获取人物动向
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
{
const
params
=
{
startTime
:
"2025-01-01"
||
date
.
value
,
};
try
{
const
res
=
await
getCharacterTrends
(
params
);
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
lon
:
item
.
lon
||
(
Math
.
random
()
*
360
-
180
).
toFixed
(
6
),
lat
:
item
.
lat
||
(
Math
.
random
()
*
180
-
90
).
toFixed
(
6
),
avatar
:
item
.
imageUrl
,
};
});
}
}
catch
(
error
)
{}
}
}
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
}
`
;
const
currentDate
=
new
Date
();
const
pastDate
=
new
Date
(
currentDate
);
pastDate
.
setDate
(
currentDate
.
getDate
()
-
days
);
const
year
=
pastDate
.
getFullYear
();
const
month
=
String
(
pastDate
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
pastDate
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
year
}
-
${
month
}
-
${
day
}
`
;
}
// onMounted(async () => {
// handlegetCharacterTrendsFn();
// })
function
generateTicks
(
startDate
,
endDate
,
mode
)
{
const
ticks
=
[];
const
start
=
new
Date
(
startDate
);
const
end
=
new
Date
(
endDate
);
const
current
=
new
Date
(
start
);
while
(
current
<=
end
)
{
const
m
=
String
(
current
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
d
=
String
(
current
.
getDate
()).
padStart
(
2
,
"0"
);
ticks
.
push
({
date
:
`
${
current
.
getFullYear
()}
-
${
m
}
-
${
d
}
`
,
label
:
`
${
m
}
.
${
d
}
`
,
showLabel
:
ticks
.
length
%
7
===
0
,
});
if
(
mode
===
"week"
)
{
current
.
setDate
(
current
.
getDate
()
+
7
);
}
else
if
(
mode
===
"month"
)
{
current
.
setMonth
(
current
.
getMonth
()
+
1
);
}
else
{
current
.
setDate
(
current
.
getDate
()
+
1
);
}
}
return
ticks
;
}
export
default
{
name
:
"MapAnimation"
,
props
:
{
peoDate
:
{
type
:
String
,
default
:
"本周"
}
default
:
"本周"
,
},
},
data
()
{
const
today
=
new
Date
();
const
todayStr
=
today
.
getFullYear
()
+
"-"
+
String
(
today
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
+
"-"
+
String
(
today
.
getDate
()).
padStart
(
2
,
"0"
);
return
{
isPlaying
:
false
,
playTimer
:
null
,
currentDate
:
todayStr
,
scaleMode
:
"day"
,
speedRate
:
"2"
,
currentTickIndex
:
0
,
timelineTicks
:
[],
startDate
:
""
,
endDate
:
""
,
chartInstance
:
null
,
mapCards
:
[],
tickWidth
:
10
,
viewStartIndex
:
0
,
viewDays
:
120
,
};
},
computed
:
{
visibleTicks
()
{
const
start
=
Math
.
max
(
0
,
this
.
viewStartIndex
);
const
end
=
Math
.
min
(
this
.
timelineTicks
.
length
,
start
+
this
.
viewDays
);
return
this
.
timelineTicks
.
slice
(
start
,
end
).
map
((
tick
,
i
)
=>
({
...
tick
,
globalIndex
:
start
+
i
,
}));
},
cursorLeft
()
{
const
offset
=
this
.
currentTickIndex
-
this
.
viewStartIndex
;
if
(
offset
<
0
||
offset
>=
this
.
viewDays
)
return
-
9999
;
return
offset
*
this
.
tickWidth
+
this
.
tickWidth
/
2
;
},
},
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
;
}
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
.
initTimelineFromData
();
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
()
{
time
.
value
=
this
.
peoDate
;
let
days
=
7
;
if
(
time
.
value
===
"本周"
)
days
=
7
;
else
if
(
time
.
value
===
"今天"
)
days
=
0
;
else
if
(
time
.
value
===
"昨天"
)
days
=
1
;
else
if
(
time
.
value
===
"近三天"
)
days
=
3
;
else
if
(
time
.
value
===
"本月"
)
days
=
new
Date
().
getDate
()
-
1
;
date
.
value
=
getDateDaysAgo
(
days
);
await
handlegetCharacterTrendsFn
();
this
.
initTimelineFromData
();
this
.
initMap
();
this
.
_tlWheelHandler
=
(
e
)
=>
{
e
.
preventDefault
();
this
.
onTimelineWheel
(
e
);
};
const
tlEl
=
this
.
$refs
.
timelineTrack
;
if
(
tlEl
)
{
tlEl
.
addEventListener
(
"wheel"
,
this
.
_tlWheelHandler
,
{
passive
:
false
});
}
},
beforeUnmount
()
{
this
.
clearPlayTimer
();
const
tlEl
=
this
.
$refs
.
timelineTrack
;
if
(
tlEl
&&
this
.
_tlWheelHandler
)
{
tlEl
.
removeEventListener
(
"wheel"
,
this
.
_tlWheelHandler
);
}
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
dispose
();
this
.
chartInstance
=
null
;
}
},
methods
:
{
/**
* 鼠标滚轮左右滚动时间轴
*/
onTimelineWheel
(
e
)
{
const
delta
=
e
.
deltaY
||
e
.
deltaX
;
const
step
=
delta
>
0
?
7
:
-
7
;
this
.
viewStartIndex
=
Math
.
max
(
0
,
Math
.
min
(
this
.
timelineTicks
.
length
-
this
.
viewDays
,
this
.
viewStartIndex
+
step
));
},
/**
* 鼠标拖拽滚动时间轴
*/
onTimelineMouseDown
(
e
)
{
const
startX
=
e
.
clientX
;
const
startViewIndex
=
this
.
viewStartIndex
;
const
onMove
=
(
ev
)
=>
{
const
dx
=
ev
.
clientX
-
startX
;
const
daysMoved
=
Math
.
round
(
dx
/
this
.
tickWidth
);
this
.
viewStartIndex
=
Math
.
max
(
0
,
Math
.
min
(
this
.
timelineTicks
.
length
-
this
.
viewDays
,
startViewIndex
-
daysMoved
));
};
const
onUp
=
()
=>
{
document
.
removeEventListener
(
"mousemove"
,
onMove
);
document
.
removeEventListener
(
"mouseup"
,
onUp
);
};
document
.
addEventListener
(
"mousemove"
,
onMove
);
document
.
addEventListener
(
"mouseup"
,
onUp
);
},
/**
* 点击某个刻度跳转
*/
onTickClick
(
globalIndex
)
{
if
(
this
.
isPlaying
)
this
.
pausePlay
();
this
.
currentTickIndex
=
globalIndex
;
this
.
syncDateFromTick
();
this
.
updateMapByDate
(
this
.
currentDate
);
this
.
$emit
(
"timeline-change"
,
this
.
currentDate
);
},
/**
* 将可视窗口居中到指定 tick
*/
scrollTimelineTo
(
tickIdx
)
{
const
half
=
Math
.
floor
(
this
.
viewDays
/
2
);
this
.
viewStartIndex
=
Math
.
max
(
0
,
Math
.
min
(
this
.
timelineTicks
.
length
-
this
.
viewDays
,
tickIdx
-
half
));
},
rebuildTimeline
()
{
if
(
!
this
.
startDate
||
!
this
.
endDate
)
return
;
this
.
timelineTicks
=
generateTicks
(
this
.
startDate
,
this
.
endDate
,
"day"
);
this
.
initTimelinePosition
();
this
.
scrollTimelineTo
(
this
.
currentTickIndex
);
},
handleDateChange
(
val
)
{
this
.
currentDate
=
val
;
if
(
val
<
this
.
startDate
||
val
>
this
.
endDate
)
{
const
y
=
parseInt
(
val
.
substring
(
0
,
4
));
if
(
val
<
this
.
startDate
)
{
this
.
startDate
=
y
+
"-01-01"
;
}
if
(
val
>
this
.
endDate
)
{
this
.
endDate
=
y
+
"-12-31"
;
}
this
.
timelineTicks
=
generateTicks
(
this
.
startDate
,
this
.
endDate
,
"day"
);
}
const
idx
=
this
.
timelineTicks
.
findIndex
((
t
)
=>
t
.
date
>=
val
);
this
.
currentTickIndex
=
idx
>=
0
?
idx
:
this
.
timelineTicks
.
length
-
1
;
this
.
currentDate
=
this
.
timelineTicks
[
this
.
currentTickIndex
].
date
;
this
.
updateMapByDate
(
this
.
currentDate
);
this
.
scrollTimelineTo
(
this
.
currentTickIndex
);
this
.
$emit
(
"timeline-change"
,
this
.
currentDate
);
},
handleScaleChange
(
val
)
{
this
.
$emit
(
"scale-change"
,
val
);
},
handleSpeedChange
(
val
)
{
this
.
$emit
(
"speed-change"
,
val
);
if
(
this
.
isPlaying
)
{
this
.
clearPlayTimer
();
this
.
scheduleNextTick
();
}
},
handlePause
()
{
if
(
this
.
isPlaying
)
{
this
.
pausePlay
();
}
else
{
if
(
this
.
currentTickIndex
>=
this
.
timelineTicks
.
length
-
1
)
{
return
;
}
this
.
startPlay
();
}
this
.
$emit
(
"play-toggle"
,
this
.
isPlaying
);
},
handleStop
()
{
this
.
clearPlayTimer
();
this
.
isPlaying
=
false
;
this
.
$emit
(
"stop"
,
this
.
currentDate
);
},
handleReset
()
{
this
.
clearPlayTimer
();
this
.
isPlaying
=
false
;
const
now
=
new
Date
();
this
.
currentDate
=
now
.
getFullYear
()
+
"-"
+
String
(
now
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
+
"-"
+
String
(
now
.
getDate
()).
padStart
(
2
,
"0"
);
this
.
initTimelinePosition
();
this
.
scrollTimelineTo
(
this
.
currentTickIndex
);
this
.
updateMapByDate
(
this
.
currentDate
);
this
.
$emit
(
"reset"
);
this
.
$emit
(
"timeline-change"
,
this
.
currentDate
);
},
startPlay
()
{
this
.
clearPlayTimer
();
this
.
isPlaying
=
true
;
this
.
scheduleNextTick
();
},
pausePlay
()
{
this
.
clearPlayTimer
();
this
.
isPlaying
=
false
;
},
scheduleNextTick
()
{
const
interval
=
1000
/
Number
(
this
.
speedRate
);
this
.
playTimer
=
setTimeout
(()
=>
{
if
(
this
.
currentTickIndex
<
this
.
timelineTicks
.
length
-
1
)
{
this
.
currentTickIndex
++
;
this
.
syncDateFromTick
();
this
.
updateMapByDate
(
this
.
currentDate
);
this
.
scrollTimelineTo
(
this
.
currentTickIndex
);
this
.
$emit
(
"timeline-change"
,
this
.
currentDate
);
this
.
scheduleNextTick
();
}
else
{
this
.
isPlaying
=
false
;
this
.
playTimer
=
null
;
this
.
$emit
(
"play-end"
);
}
},
interval
);
},
clearPlayTimer
()
{
if
(
this
.
playTimer
)
{
clearTimeout
(
this
.
playTimer
);
this
.
playTimer
=
null
;
}
},
syncDateFromTick
()
{
const
tick
=
this
.
timelineTicks
[
this
.
currentTickIndex
];
if
(
tick
)
{
this
.
currentDate
=
tick
.
date
;
}
},
initMap
()
{
// 注册自定义地图数据
echarts
.
registerMap
(
"China"
,
worldJson
);
// const eventsData = [
// {
// time: "9月23日",
// text: "随美国总统特朗普进行国事访问",
// lon: 116.46,
// lat: 39.92,
// avatar: "/testData/united_states 1 copy.png"
// },
// {
// time: "9月23日",
// text: "出席中国发展高层论坛2025年年会",
// lon: 116.46,
// lat: 39.92,
// avatar: "/testData/united_states 1 copy.png"
// },
// {
// time: "9月23日",
// text: "与民主党领导人查克·舒默及哈基姆...",
// lon: 1.46,
// lat: 39.92,
// avatar: "/testData/united_states 1 copy.png"
// },
// {
// time: "9月23日",
// text: "与阿拉伯国家领导人会晤,商讨加...",
// lon: 116.46,
// lat: -44.92,
// avatar: "/testData/united_states 1 copy.png"
// },
// {
// time: "9月23日",
// text: "对印度进行为期四天的访问,与总理...",
// lon: 78.1,
// lat: 20.7,
// avatar: "/testData/united_states 1 copy.png"
// }
// ];
const
eventsData
=
CharacterTrends
;
if
(
this
.
chartInstance
)
{
this
.
chartInstance
.
dispose
();
}
const
chart
=
echarts
.
init
(
this
.
$refs
.
map
);
this
.
chartInstance
=
chart
;
const
option
=
{
grid
:
{
...
...
@@ -206,211 +407,454 @@ export default {
right
:
"10%"
,
bottom
:
"10%"
,
top
:
"10%"
,
containLabel
:
true
containLabel
:
true
,
},
geo
:
{
map
:
"China"
,
roam
:
true
,
roam
:
'move'
,
label
:
{
emphasis
:
{
show
:
false
,
color
:
"#fff"
}
emphasis
:
{
show
:
false
,
color
:
"#fff"
},
},
silent
:
true
,
itemStyle
:
{
areaColor
:
"#F6FAFF"
,
borderColor
:
"#B9DCFF"
}
borderColor
:
"#B9DCFF"
,
}
,
},
series
:
[
{
name
:
"行程"
,
type
:
"effectScatter"
,
coordinateSystem
:
"geo"
,
data
:
eventsData
.
value
.
map
(
item
=>
({
value
:
[
item
.
lon
,
item
.
lat
],
time
:
item
.
time
,
text
:
item
.
text
,
avatar
:
item
.
avatar
,
itemStyle
:
{
color
:
"#ffcc00"
}
})),
data
:
[],
symbolSize
:
10
,
showEffectOn
:
"render"
,
rippleEffect
:
{
brushType
:
"stroke"
},
// label: {
// normal: {
// show: true,
// formatter: params => {
// const { time, text } = eventsData[params.dataIndex];
// return `{time|${time}} \n {text|${text}}`;
// },
// rich: {
// time: { fontSize: 16, color: 'rgba(5, 95, 194, 1)' },
// text: { fontSize: 14, color: 'rgb(95, 101, 108)' }
// }
// }
// },
rippleEffect
:
{
brushType
:
"stroke"
},
itemStyle
:
{
color
:
"#ddb926"
,
shadowBlur
:
10
,
shadowColor
:
"#333"
}
}
]
shadowColor
:
"#333"
,
}
,
}
,
]
,
};
chart
.
setOption
(
option
);
// 添加 graphic 元素并设置其位置
this
.
updateGraphics
(
chart
,
eventsData
);
// 监听地图缩放和移动事件,以更新 graphic 的位置
chart
.
on
(
'georoam'
,
()
=>
{
this
.
updateGraphics
(
chart
,
eventsData
);
// 地图拖拽/缩放时重新计算 DOM 卡片位置(加防抖避免频繁���发)
let
georoamTimer
=
null
;
chart
.
on
(
"georoam"
,
()
=>
{
if
(
georoamTimer
)
clearTimeout
(
georoamTimer
);
georoamTimer
=
setTimeout
(()
=>
{
const
events
=
this
.
_currentDisplayData
||
[];
this
.
updateMapCards
(
events
);
},
100
);
});
const
chart2
=
echarts
.
init
(
this
.
$refs
.
timeLineChart
);
const
option2
=
{
xAxis
:
{
type
:
"time"
,
position
:
"top"
,
axisLine
:
{
show
:
true
,
lineStyle
:
{
color
:
"#000"
}
},
axisTick
:
{
show
:
false
},
axisLabel
:
{
formatter
:
"{value}日"
,
color
:
"#000"
},
splitLine
:
{
show
:
true
},
data
:
[
"2024-03-25"
,
"2024-04-08"
,
"2024-04-15"
,
"2024-04-22"
,
"2024-04-29"
,
"2024-05-06"
,
"2024-05-13"
,
"2024-05-20"
,
"2024-05-27"
,
"2024-06-03"
,
"2024-06-10"
,
"2024-06-17"
,
"2024-06-24"
,
"2024-07-01"
,
"2024-07-08"
,
"2024-07-15"
,
"2024-07-22"
,
"2024-07-29"
]
},
yAxis
:
{
type
:
"value"
,
show
:
false
},
// 初始化:默认定位到当前日期
this
.
initTimelinePosition
();
this
.
scrollTimelineTo
(
this
.
currentTickIndex
);
this
.
updateMapByDate
(
this
.
currentDate
);
},
/**
* 根据数据范围 + 当前日期,构建覆盖完整年份的每日时间轴
* 取数据最小年份1月1日 ~ 当前年份12月31日
*/
initTimelineFromData
()
{
const
allData
=
CharacterTrends
.
value
||
[];
const
now
=
new
Date
();
const
currentYear
=
now
.
getFullYear
();
let
minYear
=
currentYear
;
let
maxYear
=
currentYear
;
// 从数据中找最早和最晚的年份
allData
.
forEach
((
item
)
=>
{
if
(
item
.
time
)
{
const
y
=
parseInt
(
item
.
time
.
substring
(
0
,
4
));
if
(
y
<
minYear
)
minYear
=
y
;
if
(
y
>
maxYear
)
maxYear
=
y
;
}
});
this
.
startDate
=
minYear
+
"-01-01"
;
this
.
endDate
=
maxYear
+
"-12-31"
;
this
.
timelineTicks
=
generateTicks
(
this
.
startDate
,
this
.
endDate
,
"day"
);
this
.
initTimelinePosition
();
},
/**
* 将时间轴指针定位到当前日期(today),如果 today 超出范围则取最近端
*/
initTimelinePosition
()
{
if
(
!
this
.
timelineTicks
.
length
)
return
;
const
idx
=
this
.
timelineTicks
.
findIndex
((
t
)
=>
t
.
date
>=
this
.
currentDate
);
if
(
idx
>=
0
)
{
this
.
currentTickIndex
=
idx
;
this
.
currentDate
=
this
.
timelineTicks
[
idx
].
date
;
}
else
{
// currentDate 超出时间轴末尾,定位到最后
this
.
currentTickIndex
=
this
.
timelineTicks
.
length
-
1
;
this
.
currentDate
=
this
.
timelineTicks
[
this
.
currentTickIndex
].
date
;
}
},
/**
* 按日期筛选数据并更新地图
* 窗口模式:仅显示 [currentDate - 7天, currentDate + 7天] 内的数据
*/
updateMapByDate
(
dateStr
)
{
const
chart
=
this
.
chartInstance
;
if
(
!
chart
)
return
;
const
allData
=
CharacterTrends
.
value
||
[];
// 计算前后一周范围
const
cur
=
new
Date
(
dateStr
);
const
weekBefore
=
new
Date
(
cur
);
weekBefore
.
setDate
(
cur
.
getDate
()
-
7
);
const
weekAfter
=
new
Date
(
cur
);
weekAfter
.
setDate
(
cur
.
getDate
()
+
7
);
const
fmt
=
(
d
)
=>
d
.
getFullYear
()
+
"-"
+
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
+
"-"
+
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
const
rangeStart
=
fmt
(
weekBefore
);
const
rangeEnd
=
fmt
(
weekAfter
);
const
filteredData
=
allData
.
filter
((
item
)
=>
item
.
time
>=
rangeStart
&&
item
.
time
<=
rangeEnd
);
// 保存当前显示的数据,供 georoam 时刷新位置用
this
.
_currentDisplayData
=
filteredData
;
// 更新散点
chart
.
setOption
({
series
:
[
{
data
:
[
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
],
type
:
"line"
,
areaStyle
:
{},
emphasis
:
{
focus
:
"series"
}
}
data
:
filteredData
.
map
((
item
)
=>
({
value
:
[
item
.
lon
,
item
.
lat
],
time
:
item
.
time
,
text
:
item
.
text
,
avatar
:
item
.
avatar
,
itemStyle
:
{
color
:
"#ffcc00"
},
})),
},
],
visualMap
:
{
show
:
false
,
dimension
:
1
,
pieces
:
[{
gt
:
0
,
lte
:
1
,
color
:
"#ffcc00"
}]
}
};
});
chart2
.
setOption
(
option2
);
// 更新 DOM 浮窗卡片位置
this
.
updateMapCards
(
filteredData
);
},
updateGraphics
(
chart
,
eventsData
)
{
const
graphics
=
eventsData
.
value
.
map
((
event
,
index
)
=>
{
const
position
=
chart
.
convertToPixel
({
geoIndex
:
0
},
[
event
.
lon
,
event
.
lat
]);
/**
* 根据事件数据更新 DOM 浮窗卡片(绝对定位在地图上方)
*/
updateMapCards
(
events
)
{
const
chart
=
this
.
chartInstance
;
if
(
!
chart
)
{
this
.
mapCards
=
[];
return
;
}
this
.
mapCards
=
events
.
map
((
event
)
=>
{
const
pos
=
chart
.
convertToPixel
({
geoIndex
:
0
},
[
event
.
lon
,
event
.
lat
]);
if
(
!
pos
)
return
null
;
return
{
type
:
'group'
,
position
:
position
,
children
:
[
{
type
:
'image'
,
x
:
-
170
,
y
:
-
72
,
style
:
{
image
:
"/icon/map-text-bg.png"
,
borderWidth
:
2
,
borderColor
:
'#1890ff'
,
width
:
339
,
height
:
72
}
},
{
type
:
'image'
,
x
:
-
160
,
y
:
-
63
,
style
:
{
image
:
event
.
avatar
,
borderWidth
:
2
,
borderColor
:
'#1890ff'
,
width
:
42
,
height
:
42
}
},
{
type
:
'text'
,
x
:
-
106
,
y
:
-
63
,
style
:
{
text
:
event
.
time
,
fontSize
:
16
,
fontWeight
:
'800'
,
fill
:
'rgba(5, 95, 194, 1)'
,
// 文字颜色(ECharts 中用 fill,不是 color)
textAlign
:
'left'
}
},
{
type
:
'text'
,
x
:
-
106
,
y
:
-
40
,
style
:
{
text
:
event
.
text
,
fontSize
:
16
,
fontWeight
:
'400'
,
fill
:
'rgba(59, 65, 75, 1)'
,
// 文字颜色(ECharts 中用 fill,不是 color)
textAlign
:
'left'
,
}
}
]
x
:
pos
[
0
]
-
170
,
y
:
pos
[
1
]
-
72
,
avatar
:
event
.
avatar
,
time
:
event
.
time
,
text
:
event
.
text
,
};
});
}).
filter
(
Boolean
);
},
chart
.
setOption
({
graphic
:
graphics
});
chart
.
resize
();
// 强制刷新图表
}
}
/**
* 地图拖拽/缩放后,重新计算 DOM 卡片位置
*/
refreshGraphicPositions
()
{
const
events
=
this
.
_currentDisplayData
||
[];
this
.
updateMapCards
(
events
);
},
},
};
</
script
>
<
style
scoped
>
#map
{
<
style
lang=
"scss"
scoped
>
.map-wrapper
{
width
:
1600px
;
height
:
581px
;
font-family
:
Microsoft
YaHei
;
position
:
relative
;
font-family
:
"Microsoft YaHei"
,
"PingFang SC"
,
sans-serif
;
}
.map-container
{
width
:
100%
;
height
:
551px
;
position
:
relative
;
z-index
:
1
;
}
/* ========== 地图浮窗卡片 ========== */
.map-overlay
{
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
551px
;
pointer-events
:
none
;
z-index
:
2
;
overflow
:
hidden
;
}
.map-card
{
position
:
absolute
;
width
:
339px
;
height
:
72px
;
pointer-events
:
auto
;
}
.map-card__bg
{
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
339px
;
height
:
72px
;
object-fit
:
fill
;
}
.map-card__avatar
{
position
:
absolute
;
top
:
9px
;
left
:
10px
;
width
:
42px
;
height
:
42px
;
border
:
2px
solid
#1890ff
;
border-radius
:
2px
;
object-fit
:
cover
;
z-index
:
1
;
}
.map-card__info
{
position
:
absolute
;
top
:
9px
;
left
:
64px
;
z-index
:
1
;
}
.map-card__time
{
font-size
:
16px
;
font-weight
:
800
;
color
:
rgba
(
5
,
95
,
194
,
1
);
line-height
:
1
.2
;
}
.map-card__text
{
font-size
:
16px
;
font-weight
:
400
;
color
:
rgba
(
59
,
65
,
75
,
1
);
line-height
:
1
.4
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
max-width
:
260px
;
}
/* ========== 时间线面板 ========== */
.timeline-panel
{
width
:
1231px
;
margin
:
-50px
auto
0
182px
;
background
:
#fff
;
border
:
1px
solid
#c8d8e8
;
border-radius
:
2px
;
overflow
:
hidden
;
position
:
relative
;
z-index
:
10
;
}
/* ========== 控制栏 ========== */
.control-bar
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
height
:
42px
;
padding
:
0
12px
;
}
/* -- 左侧按钮组 -- */
.control-left
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
.ctrl-btn
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-width
:
34px
;
height
:
28px
;
padding
:
0
8px
;
border
:
1px
solid
#04295a
;
border-radius
:
2px
;
background-color
:
#fff
;
color
:
#04295a
;
cursor
:
pointer
;
transition
:
all
0
.15s
;
position
:
relative
;
z-index
:
11
;
pointer-events
:
auto
;
&
:hover
{
background-color
:
#eaf2fd
;
}
&
:active
{
background-color
:
#d4e3f5
;
}
}
.ctrl-btn--text
{
font-size
:
14px
;
font-weight
:
400
;
padding
:
0
14px
;
color
:
#04295a
;
letter-spacing
:
2px
;
}
/* -- 右侧控制区 -- */
.control-right
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
.control-label
{
font-size
:
14px
;
color
:
#04295a
;
white-space
:
nowrap
;
user-select
:
none
;
}
.control-label--gap
{
margin-left
:
24px
;
}
/* Element Plus 日期选择器 */
.timeline-date-picker
{
width
:
160px
!
important
;
:deep
(
.el-input__wrapper
)
{
height
:
28px
;
border-radius
:
2px
;
box-shadow
:
0
0
0
1px
#b8cde5
inset
!
important
;
background
:
#fff
;
padding
:
0
8px
;
}
:deep
(
.el-input__inner
)
{
font-size
:
14px
;
color
:
#04295a
;
font-family
:
"Microsoft YaHei"
,
"PingFang SC"
,
sans-serif
;
}
:deep
(
.el-input__prefix
)
{
display
:
none
;
}
}
/* Element Plus 下拉选择器 */
.timeline-select
{
width
:
80px
!
important
;
:deep
(
.el-input__wrapper
)
{
height
:
28px
;
border-radius
:
2px
;
box-shadow
:
0
0
0
1px
#b8cde5
inset
!
important
;
background
:
#fff
;
padding
:
0
8px
;
}
:deep
(
.el-input__inner
)
{
font-size
:
14px
;
color
:
#04295a
;
font-family
:
"Microsoft YaHei"
,
"PingFang SC"
,
sans-serif
;
}
}
/* ========== 可滚动时间轴 ========== */
.timeline-track
{
width
:
100%
;
height
:
32px
;
background-color
:
#6593d7
;
position
:
relative
;
overflow
:
hidden
;
user-select
:
none
;
cursor
:
grab
;
&
:active
{
cursor
:
grabbing
;
}
/* 顶部 1px 白色横线 */
&
:
:
before
{
content
:
""
;
position
:
absolute
;
top
:
0
;
left
:
0
;
right
:
0
;
height
:
1px
;
background
:
rgba
(
255
,
255
,
255
,
0
.85
);
z-index
:
5
;
pointer-events
:
none
;
}
}
.timeline-inner
{
height
:
100%
;
position
:
relative
;
display
:
flex
;
align-items
:
flex-start
;
}
.tick-cell
{
flex-shrink
:
0
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
cursor
:
pointer
;
position
:
relative
;
&
:hover
{
background-color
:
rgba
(
255
,
255
,
255
,
0
.08
);
}
}
.tick-line
{
width
:
1px
;
height
:
5px
;
background
:
rgba
(
255
,
255
,
255
,
0
.35
);
margin-top
:
2px
;
}
.tick-line--major
{
height
:
8px
;
background
:
rgba
(
255
,
255
,
255
,
0
.6
);
}
.tick-label
{
position
:
absolute
;
top
:
12px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
font-size
:
11px
;
color
:
#fff
;
white-space
:
nowrap
;
pointer-events
:
none
;
font-family
:
"Microsoft YaHei"
,
"PingFang SC"
,
sans-serif
;
line-height
:
1
;
}
.time-btn
{
color
:
#04295A
;
background-color
:
#F9FDFE
;
border
:
1px
solid
#04295A
;
padding
:
0
10px
;
margin
:
0
10px
;
.timeline-cursor
{
position
:
absolute
;
top
:
0
;
bottom
:
0
;
width
:
2px
;
background
:
#fff
;
box-shadow
:
0
0
4px
rgba
(
255
,
255
,
255
,
0
.8
);
z-index
:
6
;
transform
:
translateX
(
-50%
);
pointer-events
:
none
;
}
</
style
>
\ No newline at end of file
</
style
>
src/views/technologyFigures/component/speechStance.vue
浏览文件 @
ad82f0ce
...
...
@@ -3,16 +3,16 @@
<div
class=
"speech-stance-container"
>
<div
class=
"speech-stance-grid"
>
<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"
@
click=
"handleClcikToCharacter(item.personId)"
>
<img
:src=
"item.personImage"
alt=
""
class=
"speech-stance-avatar"
/>
</div>
<div
class=
"speech-stance-text-content"
>
<div
class=
"speech-stance-text-content"
@
click=
"handleClcikToCharacter(item.personId)"
>
<div
style=
"display: flex; width: 683px;"
>
<h3
class=
"speech-stance-name"
>
{{
item
.
personName
}}
</h3>
<p
class=
"speech-stance-title"
>
{{
item
.
positionTitle
}}
</p>
</div>
<p
class=
"speech-stance-content"
>
{{
item
.
remarks
}}
</p>
<p
class=
"speech-stance-content"
:title=
"item.remarks"
>
{{
item
.
remarks
}}
</p>
</div>
</div>
...
...
@@ -26,6 +26,9 @@ import { onMounted, ref,defineProps,watch } from "vue";
import
speechStance
from
'../json/speechStance.json'
;
import
{
getPersonRelation
}
from
"@/api/technologyFigures/technologyFigures"
import
{
getPersonSummaryInfo
}
from
"@/api/technologyFigures/technologyFigures"
;
import
{
useRouter
}
from
"vue-router"
;
const
router
=
useRouter
();
const
props
=
defineProps
({
fieldSelected
:
{
type
:
String
,
...
...
@@ -36,7 +39,59 @@ const props = defineProps({
default
:
0
}
});
// 跳转人物主页
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
)
{}
};
const
aId
=
ref
();
const
params
=
ref
({});
...
...
@@ -149,11 +204,8 @@ onMounted(async () => {
line-height
:
30px
;
letter-spacing
:
0px
;
text-align
:
left
;
white-space
:
nowrap
;
/* 禁止换行 */
overflow
:
hidden
;
/* 隐藏溢出内容 */
text-overflow
:
ellipsis
;
/* 溢出部分显示省略号 */
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
</
style
>
\ No newline at end of file
src/views/technologyFigures/index.vue
浏览文件 @
ad82f0ce
...
...
@@ -20,8 +20,8 @@
<div
class=
"header-item"
>
科技人物观点
</div>
</div>
-->
<div
class=
"home-main-header-center"
>
<SearchContainer
style=
"margin-bottom: 0;margin-top: 48px; "
v-if=
"containerRef"
placeholder=
"搜索科技人物及观点"
:containerRef=
"containerRef"
areaName=
"人物"
/>
<SearchContainer
style=
"margin-bottom: 0;margin-top: 48px; "
v-if=
"containerRef"
placeholder=
"搜索科技人物及观点"
:containerRef=
"containerRef"
areaName=
"人物"
/>
<!--
<el-input
v-model=
"input"
style=
"width: 838px; height: 100%"
placeholder=
"搜索科技人物及观点"
/>
<div
class=
"search"
>
<div
class=
"search-icon"
>
...
...
@@ -78,131 +78,80 @@
<div
class=
"home-main-center"
>
<DivideHeader
id=
"position1"
class=
"divide-header"
:titleText=
"'最新动态'"
></DivideHeader>
<div
class=
"center-top"
>
<div
class=
"box1"
>
<div
class=
"box1-left"
@
click=
"handleSwithCurBill('left')"
>
<img
src=
"./assets/images/box1-left.png"
alt=
""
/>
</div>
<div
class=
"box1-right"
@
click=
"handleSwithCurBill('right')"
>
<img
src=
"./assets/images/box1-right.png"
alt=
""
/>
</div>
<div
class=
"box1-header"
>
<div
class=
"box1-header-left"
>
<div
class=
"icon"
>
<img
src=
"./assets/images/TechnologyFigures-icon4.png"
alt=
""
/>
</div>
<div
class=
"title"
>
{{
"人物新闻动态"
}}
</div>
<OverviewMainBox
title=
"人物新闻动态"
width=
"1064px"
height=
"460px"
@
to-detail=
"handleClcikToCharacter(curnews.personId, curnews.name)"
>
<!-- 自定义左上角图标插槽 -->
<template
#
headerIcon
>
<img
src=
"./assets/images/personNewIcon.svg "
alt=
""
/>
</
template
>
<!-- 主内容区域 -->
<div
class=
"box1-wrapper"
>
<div
class=
"box1-left"
@
click=
"handleSwithCurBill('left')"
>
<img
src=
"./assets/images/box1-left.png"
alt=
""
/>
</div>
<div
class=
"box1-
header-right"
@
click=
"handleClcikToCharacter(curnews.personId, curnews.name
)"
>
{{
"查看详情 >"
}}
<div
class=
"box1-
right"
@
click=
"handleSwithCurBill('right'
)"
>
<img
src=
"./assets/images/box1-right.png"
alt=
""
/>
</div>
</div>
<div
class=
"box1-main"
style=
"display: block"
>
<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
class=
"box1-main"
>
<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"
:key=
"person.personId"
>
<div
class=
"carousel-content"
>
<!-- 左侧:头像 -->
<div
class=
"avatar"
>
<img
:src=
"person.imageUrl"
alt=
"人物头像"
/>
</div>
<!-- 事件列表 -->
<div
class=
"events"
>
<!-- 头部区域 -->
<div
class=
"header"
>
<div
class=
"info"
>
<div>
<h2>
{{ person.name }}
</h2>
<p
class=
"newtitle"
>
{{ person.positionTitle }}
</p>
</div>
<!-- 使用 AreaTag 组件替换 -->
<div
class=
"tag-group"
>
<AreaTag
v-for=
"item in person.personTypeList"
:key=
"item.typeId"
:tag-name=
"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
class=
"line"
></div>
<div
class=
"timeline-wrapper"
>
<el-timeline>
<el-timeline-item
v-for=
"(activity, index) in person.newsList"
:key=
"index"
placement=
"top"
type=
"primary"
:hollow=
"true"
>
<
template
#
dot
>
<div
class=
"timeline-dot"
>
<div
class=
"dot-left"
>
<img
src=
"./assets/images/dot1.png"
alt=
""
class=
"dot-icon"
/>
<div
class=
"dot-date"
>
{{
activity
.
newsDate
}}
</div>
</div>
<img
src=
"./assets/images/dot2.png"
alt=
""
class=
"dot-arrow"
/>
</div>
<img
src=
"./assets/images/dot2.png"
alt=
""
style=
"width: 20px; height: 18px"
/>
</div>
</
template
>
{{ activity.newsContent }}
</el-timeline-item>
</el-timeline>
</
template
>
{{ activity.newsContent }}
</el-timeline-item>
</el-timeline>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
<!-- <div class="box2">
<div class="box2-header">
<div class="icon">
<img src="./assets/images/box2-header-icon.png" alt="" />
</div>
<div class="title">
<div class="text">{{ "风险信号" }}</div>
<div class="num">{{ warningList.length }}</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
<div class="box2-main">
<div class="box2-main-item" v-for="(item, index) in warningList" :key="index">
<div class="item-left" :class="{
itemLeftStatus1: item.signalLevel === '一般风险',
itemLeftStatus2: item.signalLevel === '重大风险'
}">
{{ item.signalLevel ? item.signalLevel : "一般风险" }}
</div>
<div class="item-right">
<div class="text">
{{ item.signalTitle }}
</div>
<div class="time">{{ item.signalTime }}</div>
</div>
</div>
</div>
<div class="box2-footer" @click="handleToMoreRiskSignal">
<div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" />
</div>
<div class="text">{{ "查看更多" }}</div>
</div>
</div> -->
</OverviewMainBox>
<RiskSignal
:list=
"warningList"
@
more-click=
"handleToMoreRiskSignal"
postDate=
"signalTime"
name=
"signalTitle"
riskLevel=
"signalLevel"
/>
</div>
...
...
@@ -270,13 +219,16 @@
</div>
<div>
<div
style=
"height: 45px; display: flex; align-items: center"
>
<el-select
v-model=
"wordCloudvalue"
style=
"width: 120px; height: 28px"
@
change=
"handleBox5Change"
>
<el-option
v-for=
"item in yearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-select
v-model=
"wordCloudvalue"
style=
"width: 120px; height: 28px"
@
change=
"handleBox5Change"
>
<el-option
v-for=
"item in yearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-select
v-model=
"wordCloudfield"
style=
"width: 120px; height: 28px; margin: 10px 24px 10px 5px"
<el-select
v-model=
"wordCloudfield"
style=
"width: 120px; height: 28px; margin: 10px 24px 10px 5px"
@
change=
"handleBox5areaChange"
>
<el-option
v-for=
"item in fieldSelectList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in fieldSelectList"
:key=
"item.value"
:
label=
"item.label"
:
value=
"item.value"
/>
</el-select>
</div>
</div>
...
...
@@ -296,7 +248,8 @@
</div>
<el-select
v-model=
"areaSelect"
style=
"width: 120px; height: 28px"
>
<el-option
v-for=
"item in options"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in options"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</div>
</div>
...
...
@@ -321,16 +274,17 @@
<div
class=
"box8-header-icon"
>
<img
src=
"./assets/images/TechnologyFigures-icon2.png"
alt=
""
/>
</div>
<div
style=
"display: flex; width: 730px; justify-content: space-between; align-items: center"
>
<div
class=
"box8-header-title"
>
{{ "主要人物涉华观点统计" }}
</div>
<div
style=
"gap: 2px; display: flex;width:510px;"
>
<div
v-for=
"value in PersonType"
:class=
"viewSelect !== value.typeName ? 'btn-box-samll' : 'btn-box-select-samll'"
@
click=
"selectpersontype(value)"
>
{{ value.typeName }}
</div>
<el-select
v-model=
"yearSelect"
style=
"width: 100px; height: 28px; margin-top: -5px"
>
<el-option
v-for=
"item in yearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<div
class=
"box8-header-content"
>
<div
class=
"box8-header-title"
>
观点统计
</div>
<div
class=
"box8-header-filters"
>
<el-select
v-model=
"viewSelect"
placeholder=
"请选择类型"
@
change=
"handlePersonTypeChange"
>
<el-option
v-for=
"item in PersonType"
:key=
"item.typeId"
:label=
"item.typeName"
:value=
"item.typeName"
/>
</el-select>
<el-select
v-model=
"yearSelect"
placeholder=
"请选择年份"
>
<el-option
v-for=
"item in yearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</div>
</div>
...
...
@@ -368,6 +322,7 @@ import { onMounted, ref } from "vue";
import
{
useRouter
}
from
"vue-router"
;
import
scrollToTop
from
"@/utils/scrollToTop"
;
import
DivideHeader
from
"@/components/DivideHeader.vue"
;
import
OverviewMainBox
from
'@/components/base/boxBackground/overviewMainBox.vue'
import
setChart
from
"@/utils/setChart"
;
import
{
getnewsDynamics
,
...
...
@@ -461,7 +416,12 @@ const handleGetBillRiskSignalFn = async () => {
// 获取行业领域
const
areaTypeList
=
ref
([]);
const
handlePersonTypeChange
=
(
typeName
)
=>
{
const
selectedType
=
PersonType
.
value
.
find
(
item
=>
item
.
typeName
===
typeName
)
if
(
selectedType
)
{
selectpersontype
(
selectedType
)
}
}
const
handlegetareaTypeFn
=
async
()
=>
{
try
{
const
res
=
await
getareaType
();
...
...
@@ -677,7 +637,7 @@ const handleClcikToCharacter = async (id, name) => {
};
try
{
const
res
=
await
getPersonSummaryInfo
(
params
);
console
.
log
(
"人物全局信息"
,
res
);
console
.
log
(
"人物全局信息
5
"
,
res
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
const
arr
=
personTypeList
.
filter
(
item
=>
{
return
item
.
typeId
===
res
.
data
.
personType
;
...
...
@@ -839,7 +799,7 @@ const handleClickCate = cate => {
// 查看更多风险信号
const
handleToMoreRiskSignal
=
()
=>
{
const
route
=
router
.
resolve
(
"/
viewR
iskSignal"
);
const
route
=
router
.
resolve
(
"/
r
iskSignal"
);
window
.
open
(
route
.
href
,
"_blank"
);
};
...
...
@@ -1752,55 +1712,77 @@ onMounted(async () => {
}
.box8
{
width
:
792px
;
height
:
460px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
.box8-header
{
height
:
53px
;
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
margin
:
0
auto
;
display
:
flex
;
justify-content
:
space-between
;
padding
:
0
20px
;
position
:
relative
;
.box8-header-left
{
display
:
flex
;
.box8-header-icon
{
margin-top
:
18px
;
margin-left
:
2px
;
width
:
19px
;
height
:
19px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box8-header-title
{
margin-left
:
20px
;
height
:
26px
;
color
:
rgba
(
20
,
89
,
187
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
20px
;
font-weight
:
700
;
line-height
:
26px
;
}
}
}
.box8-main
{
width
:
469px
;
height
:
360px
;
}
}
width
:
792px
;
height
:
460px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
.box8-header
{
height
:
53px
;
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
margin
:
0
auto
;
display
:
flex
;
justify-content
:
space-between
;
padding
:
0
20px
;
position
:
relative
;
.box8-header-left
{
display
:
flex
;
width
:
100%
;
.box8-header-icon
{
margin-top
:
18px
;
margin-left
:
2px
;
width
:
19px
;
height
:
19px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box8-header-content
{
display
:
flex
;
width
:
730px
;
justify-content
:
space-between
;
align-items
:
center
;
}
.box8-header-title
{
margin-left
:
20px
;
height
:
26px
;
color
:
rgba
(
20
,
89
,
187
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
20px
;
font-weight
:
700
;
line-height
:
26px
;
}
.box8-header-filters
{
display
:
flex
;
gap
:
10px
;
align-items
:
center
;
:deep
(
.el-select
)
{
width
:
120px
;
.el-input__wrapper
{
height
:
28px
;
}
}
}
}
}
.box8-main
{
width
:
469px
;
height
:
360px
;
}
}
}
}
...
...
@@ -2057,7 +2039,7 @@ onMounted(async () => {
}
.events
{
margin-left
:
2
4px
;
margin-left
:
4px
;
width
:
710px
;
}
...
...
@@ -2127,4 +2109,492 @@ onMounted(async () => {
text-align
:
justify
;
}
.box1-content
{
position
:
relative
;
height
:
100%
;
width
:
100%
;
display
:
block
;
}
.box1-left
,
.box1-right
{
position
:
absolute
;
top
:
50%
;
transform
:
translateY
(
-50%
);
cursor
:
pointer
;
z-index
:
10
;
}
.box1-left
{
left
:
0
;
}
.box1-right
{
right
:
0
;
}
.avatar
{
width
:
280px
;
height
:
354px
;
flex-shrink
:
0
;
img
{
width
:
100%
;
height
:
100%
;
object-fit
:
cover
;
}
}
.events
{
flex
:
1
;
padding
:
0
20px
;
.header
{
padding-top
:
16px
;
.info
{
display
:
flex
;
width
:
100%
;
justify-content
:
space-between
;
h2
{
color
:
rgba
(
20
,
89
,
187
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
24px
;
font-weight
:
700
;
margin
:
0
;
}
.newtitle
{
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
margin-top
:
8px
;
}
}
}
.tag-group
{
display
:
flex
;
gap
:
10px
;
}
.source-tag
{
padding
:
4px
12px
;
background
:
rgba
(
20
,
89
,
187
,
0
.1
);
color
:
rgba
(
20
,
89
,
187
,
1
);
border-radius
:
4px
;
font-size
:
14px
;
height
:
fit-content
;
}
.tag2
{
background
:
rgba
(
255
,
152
,
0
,
0
.1
);
color
:
rgba
(
255
,
152
,
0
,
1
);
}
.line
{
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
margin-top
:
16px
;
}
}
.timeline-wrapper
{
height
:
289px
;
width
:
688px
;
overflow-y
:
auto
;
overflow
:
auto
;
scrollbar-width
:
none
;
-ms-overflow-style
:
none
;
:deep
(
.el-timeline
)
{
margin-top
:
14px
;
padding-left
:
0
;
}
:deep
(
.el-timeline-item__wrapper
)
{
padding-left
:
40px
;
}
:deep
(
.el-timeline-item__tail
)
{
left
:
4px
;
}
:deep
(
.el-timeline-item__content
)
{
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
line-height
:
24px
;
}
}
.
timeline-wrapper
:
:-
webkit-scrollbar
{
display
:
none
;
}
.timeline-dot
{
display
:
flex
;
justify-content
:
space-between
;
width
:
688px
;
text-align
:
center
;
align-items
:
center
;
margin-bottom
:
12px
;
.dot-left
{
display
:
flex
;
align-items
:
center
;
}
.dot-icon
{
width
:
10px
;
height
:
10px
;
}
.dot-date
{
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
;
}
.dot-arrow
{
width
:
20px
;
height
:
18px
;
}
}
.box1-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
.box1-left
{
position
:
absolute
;
left
:
0
;
top
:
180px
;
width
:
24px
;
height
:
48px
;
cursor
:
pointer
;
z-index
:
10
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box1-right
{
position
:
absolute
;
right
:
0
;
top
:
180px
;
width
:
24px
;
height
:
48px
;
cursor
:
pointer
;
z-index
:
10
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box1-main
{
width
:
1009px
;
height
:
354px
;
margin-top
:
28px
;
margin-left
:
40px
;
}
.carousel-content
{
display
:
flex
;
height
:
100%
;
}
.avatar
{
width
:
280px
;
height
:
354px
;
flex-shrink
:
0
;
img
{
width
:
100%
;
height
:
100%
;
object-fit
:
cover
;
}
}
.events
{
flex
:
1
;
padding-left
:
20px
;
.header
{
.info
{
display
:
flex
;
width
:
100%
;
justify-content
:
space-between
;
padding-right
:
20px
;
h2
{
margin
:
0
;
color
:
rgba
(
20
,
89
,
187
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
24px
;
font-weight
:
700
;
}
.newtitle
{
margin-top
:
8px
;
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
}
}
}
.tag-group
{
display
:
flex
;
gap
:
10px
;
}
.source-tag
{
padding
:
4px
12px
;
background
:
rgba
(
20
,
89
,
187
,
0
.1
);
color
:
rgba
(
20
,
89
,
187
,
1
);
border-radius
:
4px
;
font-size
:
14px
;
height
:
fit-content
;
}
.tag2
{
background
:
rgba
(
255
,
152
,
0
,
0
.1
);
color
:
rgba
(
255
,
152
,
0
,
1
);
}
.line
{
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
margin-top
:
16px
;
}
}
.timeline-wrapper
{
height
:
289px
;
width
:
688px
;
overflow-y
:
auto
;
:deep
(
.el-timeline
)
{
margin-top
:
14px
;
padding-left
:
0
;
}
:deep
(
.el-timeline-item__wrapper
)
{
padding-left
:
40px
;
}
:deep
(
.el-timeline-item__tail
)
{
left
:
4px
;
}
:deep
(
.el-timeline-item__content
)
{
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
line-height
:
24px
;
}
}
.timeline-dot
{
display
:
flex
;
justify-content
:
space-between
;
width
:
688px
;
align-items
:
center
;
margin-bottom
:
12px
;
.dot-left
{
display
:
flex
;
align-items
:
center
;
}
.dot-icon
{
width
:
10px
;
height
:
10px
;
}
.dot-date
{
margin-left
:
16px
;
color
:
rgba
(
59
,
65
,
75
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
700
;
letter-spacing
:
1px
;
}
.dot-arrow
{
width
:
20px
;
height
:
18px
;
}
}
.box1-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
.box1-left
{
position
:
absolute
;
left
:
0
;
top
:
180px
;
width
:
24px
;
height
:
48px
;
cursor
:
pointer
;
z-index
:
10
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box1-right
{
position
:
absolute
;
right
:
0
;
top
:
180px
;
width
:
24px
;
height
:
48px
;
cursor
:
pointer
;
z-index
:
10
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.box1-main
{
width
:
1009px
;
height
:
354px
;
margin-top
:
28px
;
margin-left
:
40px
;
}
.carousel-content
{
display
:
flex
;
height
:
100%
;
}
.avatar
{
width
:
280px
;
height
:
354px
;
flex-shrink
:
0
;
img
{
width
:
100%
;
height
:
100%
;
object-fit
:
cover
;
}
}
.events
{
flex
:
1
;
padding-left
:
20px
;
.header
{
.info
{
display
:
flex
;
width
:
100%
;
justify-content
:
space-between
;
padding-right
:
20px
;
h2
{
margin
:
0
;
color
:
rgba
(
20
,
89
,
187
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
24px
;
font-weight
:
700
;
}
.newtitle
{
margin-top
:
8px
;
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
}
}
}
.tag-group
{
display
:
flex
;
gap
:
10px
;
}
.line
{
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
margin-top
:
16px
;
}
}
.timeline-wrapper
{
height
:
289px
;
width
:
688px
;
overflow-y
:
auto
;
:deep
(
.el-timeline
)
{
margin-top
:
14px
;
padding-left
:
0
;
}
:deep
(
.el-timeline-item__wrapper
)
{
padding-left
:
40px
;
}
:deep
(
.el-timeline-item__tail
)
{
left
:
4px
;
}
:deep
(
.el-timeline-item__content
)
{
color
:
rgba
(
102
,
102
,
102
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
line-height
:
24px
;
}
}
.timeline-dot
{
display
:
flex
;
justify-content
:
space-between
;
width
:
688px
;
align-items
:
center
;
margin-bottom
:
12px
;
.dot-left
{
display
:
flex
;
align-items
:
center
;
}
.dot-icon
{
width
:
10px
;
height
:
10px
;
}
.dot-date
{
margin-left
:
16px
;
color
:
rgba
(
59
,
65
,
75
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
700
;
letter-spacing
:
1px
;
}
.dot-arrow
{
width
:
20px
;
height
:
18px
;
}
}
</
style
>
src/views/technologyFigures/utils/useCharacterNav.js
0 → 100644
浏览文件 @
ad82f0ce
/**
* useCharacterNav - 人物跳转复用方法(Vue 3 Composable)
*
* 使用方式:
* import { useCharacterNav } from "@/hooks/useCharacterNav";
* const { handleClickToCharacter } = useCharacterNav();
* handleClickToCharacter(personId);
*/
import
{
useRouter
}
from
"vue-router"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
getPersonSummaryInfo
}
from
"@/api/technologyFigures/technologyFigures"
;
// 人物类型名称 -> type 映射
const
PERSON_TYPE_MAP
=
{
"科技企业领袖"
:
1
,
"国会议员"
:
2
,
"智库研究人员"
:
3
,
};
export
function
useCharacterNav
()
{
const
router
=
useRouter
();
/**
* 根据人物 ID 查询类型并跳转到人物详情页
* @param {string|number} id - 人物 ID
*/
const
handleClickToCharacter
=
async
(
id
)
=>
{
const
personTypeList
=
JSON
.
parse
(
window
.
sessionStorage
.
getItem
(
"personTypeList"
)
||
"[]"
);
const
params
=
{
personId
:
id
};
console
.
log
(
'dsdsdwdwf'
,
id
)
try
{
const
res
=
await
getPersonSummaryInfo
(
params
);
if
(
res
.
code
!==
200
||
!
res
.
data
)
{
ElMessage
.
warning
(
"找不到当前人员的类型值!"
);
return
;
}
const
matched
=
personTypeList
.
find
(
(
item
)
=>
item
.
typeId
===
res
.
data
.
personType
);
if
(
!
matched
)
{
ElMessage
.
warning
(
"找不到当前人员的类型值!"
);
return
;
}
const
personTypeName
=
matched
.
typeName
;
const
type
=
PERSON_TYPE_MAP
[
personTypeName
];
if
(
!
type
)
{
ElMessage
.
warning
(
"找不到当前人员的类型值!"
);
return
;
}
const
route
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
type
,
personId
:
id
},
});
window
.
open
(
route
.
href
,
"_blank"
);
}
catch
(
error
)
{
console
.
error
(
"查询人物信息失败:"
,
error
);
}
};
return
{
handleClickToCharacter
};
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论