Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
1
合并请求
1
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
82f15736
提交
82f15736
authored
3月 18, 2026
作者:
朱政
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat:查看全部智库,智库报告-报告原文,智库报告-政策追踪样式与功能开发
上级
20ebbfed
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
23 个修改的文件
包含
334 行增加
和
159 行删除
+334
-159
overview.js
src/api/thinkTank/overview.js
+27
-3
thinktank.js
src/router/modules/thinktank.js
+9
-0
index.vue
src/views/bill/billHome/index.vue
+16
-28
index.vue
src/views/exportControl/index.vue
+1
-0
image-open2.png
src/views/thinkTank/ReportDetail/images/image-open2.png
+0
-0
image-pdf.png
src/views/thinkTank/ReportDetail/images/image-pdf.png
+0
-0
image-right.png
src/views/thinkTank/ReportDetail/images/image-right.png
+0
-0
image-translate.png
src/views/thinkTank/ReportDetail/images/image-translate.png
+0
-0
index.vue
src/views/thinkTank/ReportDetail/index.vue
+52
-2
index.vue
src/views/thinkTank/ReportDetail/policyTracking/index.vue
+0
-0
index.vue
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
+5
-0
index.vue
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
+3
-4
multiLineChart.js
...nk/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
+8
-27
index.vue
src/views/thinkTank/allThinkTank/index.vue
+0
-0
Line_Search.png
src/views/thinkTank/assets/images/Line_Search.png
+0
-0
arrow-left.png
src/views/thinkTank/assets/images/arrow-left.png
+0
-0
blue-right.png
src/views/thinkTank/assets/images/blue-right.png
+0
-0
info.png
src/views/thinkTank/assets/images/info.png
+0
-0
sort-asc.png
src/views/thinkTank/assets/images/sort-asc.png
+0
-0
sort-desc.png
src/views/thinkTank/assets/images/sort-desc.png
+0
-0
index.vue
src/views/thinkTank/index.vue
+126
-74
index.vue
src/views/thinkTank/reportOriginal/index.vue
+0
-0
pdf.vue
src/views/thinkTank/reportOriginal/pdf.vue
+87
-21
没有找到文件。
src/api/thinkTank/overview.js
浏览文件 @
82f15736
...
...
@@ -10,6 +10,13 @@ export function getThinkTankList() {
})
}
export
function
getAllThinkTankList
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
'/api/thinkTankOverview/thinkTanks/page'
,
params
:
params
})
}
//智库概览:获取智库发布
export
function
getNewReport
()
{
return
request
({
...
...
@@ -299,9 +306,26 @@ export function getThinkTankReportPolicy(params) {
//获取相关政策动态
export
function
getThinkTankReportPolicyAction
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/thinkTankReport/policyAction/
${
params
}
`
,
const
{
reportId
,
currentPage
,
pageSize
,
keyword
=
""
,
orgIds
=
""
,
// 新增:按科技领域 / 标签过滤
industryName
=
""
}
=
params
;
return
request
({
method
:
'GET'
,
url
:
`/api/thinkTankReport/policyDetail/
${
reportId
}
`
,
params
:
{
currentPage
,
pageSize
,
keyword
,
// 后端按标签过滤使用的字段
industryName
,
orgIds
,
}
})
}
...
...
src/router/modules/thinktank.js
浏览文件 @
82f15736
...
...
@@ -3,6 +3,7 @@ const thinkTank = () => import('@/views/thinkTank/index.vue')
const
ThinkTankDetail
=
()
=>
import
(
'@/views/thinkTank/ThinkTankDetail/index.vue'
)
const
ReportDetail
=
()
=>
import
(
'@/views/thinkTank/ReportDetail/index.vue'
)
const
ReportOriginal
=
()
=>
import
(
'@/views/thinkTank/reportOriginal/index.vue'
)
const
allThinkTank
=
()
=>
import
(
'@/views/thinkTank/allThinkTank/index.vue'
)
const
thinktankRoutes
=
[
// 智库系统的主要路由
...
...
@@ -40,6 +41,14 @@ const thinktankRoutes = [
// title: "报告原文"
// }
},
{
path
:
"/thinkTank/allThinkTank"
,
name
:
"allThinkTank"
,
component
:
allThinkTank
,
// meta: {
// title: "报告原文"
// }
},
]
...
...
src/views/bill/billHome/index.vue
浏览文件 @
82f15736
...
...
@@ -5,9 +5,8 @@
<div
class=
"home-content"
>
<div
class=
"home-content-header"
>
<SearchContainer
style=
"margin-bottom: 0; height: fit-content"
v-if=
"containerRef"
placeholder=
"搜索科技法案"
:containerRef=
"containerRef"
areaName=
"法案"
:enableBillTypeSwitch=
"true"
defaultBillSearchType=
"federal"
/>
<SearchContainer
style=
"margin-bottom: 0; height: fit-content"
v-if=
"containerRef"
placeholder=
"搜索科技法案"
:containerRef=
"containerRef"
areaName=
"法案"
:enableBillTypeSwitch=
"true"
defaultBillSearchType=
"federal"
/>
</div>
<DivideHeader
id=
"position1"
class=
"divide1"
:titleText=
"'最新动态'"
></DivideHeader>
...
...
@@ -28,8 +27,8 @@
</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
ref=
"carouselRef"
height=
"354px"
:autoplay=
"true"
:interval=
"3000"
arrow=
"never"
indicator-position=
"none"
@
change=
"handleCarouselChange"
>
<el-carousel-item
v-for=
"(bill, billIndex) in hotBillList"
:key=
"billIndex"
>
<div
class=
"carousel-content"
style=
"display: flex; height: 100%"
>
<div
class=
"box1-main-left"
>
...
...
@@ -37,8 +36,7 @@
{{ bill.billName }}
</div>
<div
class=
"box1-main-left-info"
>
<AreaTag
v-for=
"(item, index) in bill.hylyList"
:key=
"index"
:tagName=
"item.industryName"
>
<AreaTag
v-for=
"(item, index) in bill.hylyList"
:key=
"index"
:tagName=
"item.industryName"
>
</AreaTag>
</div>
<div
class=
"box1-main-left-info1"
>
...
...
@@ -56,18 +54,15 @@
</div>
</div>
<div
class=
"box1-main-left-info2"
>
<div
class=
"info2-item"
v-for=
"(item, index) in bill.dyqkList"
:key=
"index"
>
<div
class=
"time-line"
v-if=
"index !== bill.dyqkList.length - 1"
></div>
<div
class=
"info2-item"
v-for=
"(item, index) in bill.dyqkList"
:key=
"index"
>
<div
class=
"time-line"
v-if=
"index !== bill.dyqkList.length - 1"
></div>
<div
class=
"item-icon"
>
<img
src=
"./assets/images/info2-icon.png"
alt=
""
/>
</div>
<div
class=
"item-time"
:class=
"{ itemTimeActive: index === 0 }"
>
{{ item.actionDate }}
</div>
<div
class=
"item-title"
:class=
"{ itemTitleActive: index === 0 }"
>
<div
class=
"item-title"
:class=
"{ itemTitleActive: index === 0 }"
>
{{ item.actionContentCn }}
</div>
</div>
...
...
@@ -94,25 +89,17 @@
</el-carousel>
</div>
</overviewMainBox>
<RiskSignal
:list=
"warningList"
@
more-click=
"handleToMoreRiskSignal"
@
item-click=
"handleClickToDetailO"
riskLevel=
"signalLevel"
postDate=
"signalTime"
name=
"signalTitle"
/>
<RiskSignal
:list=
"warningList"
@
more-click=
"handleToMoreRiskSignal"
@
item-click=
"handleClickToDetailO"
riskLevel=
"signalLevel"
postDate=
"signalTime"
name=
"signalTitle"
/>
</div>
<DivideHeader
id=
"position2"
class=
"divide2"
:titleText=
"'资讯要闻'"
></DivideHeader>
<div
class=
"center-center"
>
<NewsList
:newsList=
"newsList"
img=
"newsImage"
title=
"newsTitle"
from=
"from"
content=
"newsContent"
/>
<MessageBubble
:messageList=
"messageList"
imageUrl=
"personImage"
@
more-click=
"handleToSocialDetail"
@
person-click=
"handleClickToCharacter"
name=
"personName"
content=
"remarks"
source=
"orgName"
/>
<NewsList
:newsList=
"newsList"
img=
"newsImage"
title=
"newsTitle"
from=
"from"
content=
"newsContent"
/>
<MessageBubble
:messageList=
"messageList"
imageUrl=
"personImage"
@
more-click=
"handleToSocialDetail"
@
person-click=
"handleClickToCharacter"
name=
"personName"
content=
"remarks"
source=
"orgName"
/>
</div>
<DivideHeader
id=
"position3"
class=
"divide3"
:titleText=
"'数据总览'"
></DivideHeader>
<div
class=
"center-footer"
>
<OverviewCard
class=
"overview-card--double box5"
title=
"涉华法案数量变化趋势"
:icon=
"box5HeaderIcon"
>
...
...
@@ -1783,7 +1770,7 @@ onUnmounted(() => {
margin-top
:
21px
;
height
:
450px
;
display
:
flex
;
gap
:
16px
;
gap
:
16px
;
.box3
{
width
:
792px
;
...
...
@@ -2074,6 +2061,7 @@ onUnmounted(() => {
.overview-card--double
{
width
:
calc
(
#{
$overview-single-width
}
*
2
+
#{
$overview-card-gap
}
);
}
.box5-main
,
.box6-main
,
.box7-main
,
...
...
src/views/exportControl/index.vue
浏览文件 @
82f15736
...
...
@@ -591,6 +591,7 @@
</template>
<
script
setup
>
//这是一个备注
import
NewsList
from
"@/components/base/newsList/index.vue"
;
import
RiskSignal
from
"@/components/base/RiskSignal/index.vue"
;
import
{
onMounted
,
ref
,
computed
,
reactive
,
shallowRef
,
watch
,
nextTick
}
from
"vue"
;
...
...
src/views/thinkTank/ReportDetail/images/image-open2.png
0 → 100644
浏览文件 @
82f15736
22.7 KB
src/views/thinkTank/ReportDetail/images/image-pdf.png
0 → 100644
浏览文件 @
82f15736
26.7 KB
src/views/thinkTank/ReportDetail/images/image-right.png
0 → 100644
浏览文件 @
82f15736
21.3 KB
src/views/thinkTank/ReportDetail/images/image-translate.png
0 → 100644
浏览文件 @
82f15736
25.4 KB
src/views/thinkTank/ReportDetail/index.vue
浏览文件 @
82f15736
...
...
@@ -62,7 +62,7 @@
</div>
<div
class=
"text"
@
click=
"toReport()"
>
{{
"报告原文"
}}
</div>
</div>
<div
class=
"btn"
>
<div
class=
"btn"
@
click=
"handleDownloadDocument"
>
<div
class=
"icon"
>
<img
src=
"./images/btn-icon3.png"
alt=
""
/>
</div>
...
...
@@ -89,7 +89,7 @@ import { ref, onMounted } from "vue";
import
ReportAnalysis
from
"./reportAnalysis/index.vue"
;
import
PolicyTracking
from
"./policyTracking/index.vue"
;
import
{
getThinkTankReportSummary
,
getThinkTankReportRelated
}
from
"@/api/thinkTank/overview"
;
import
{
getThinkTankReportSummary
,
getThinkTankReportRelated
,
getThinkTankReportcontentUrl
}
from
"@/api/thinkTank/overview"
;
import
{
useRoute
,
useRouter
}
from
"vue-router"
;
const
router
=
useRouter
();
const
route
=
useRoute
();
...
...
@@ -163,6 +163,56 @@ const goToOfficialWebsite = () => {
window
.
open
(
url
,
"_blank"
);
};
// 文档下载:先拉取 PDF 地址,再中英文都下载(与报告原文页相同方式)
const
downloadOnePdf
=
async
(
url
,
filename
)
=>
{
const
response
=
await
fetch
(
url
,
{
method
:
"GET"
,
headers
:
{
"Content-Type"
:
"application/pdf"
},
});
if
(
!
response
.
ok
)
throw
new
Error
(
`HTTP error! status:
${
response
.
status
}
`
);
const
blob
=
await
response
.
blob
();
const
blobUrl
=
window
.
URL
.
createObjectURL
(
blob
);
const
link
=
document
.
createElement
(
"a"
);
link
.
href
=
blobUrl
;
link
.
download
=
filename
;
document
.
body
.
appendChild
(
link
);
link
.
click
();
document
.
body
.
removeChild
(
link
);
window
.
URL
.
revokeObjectURL
(
blobUrl
);
};
const
handleDownloadDocument
=
async
()
=>
{
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
const
res
=
await
getThinkTankReportcontentUrl
(
router
.
currentRoute
.
_value
.
params
.
id
);
if
(
res
.
code
!==
200
||
!
res
.
data
)
{
ElMessage
.
warning
(
"暂无下载链接"
);
return
;
}
const
urlZh
=
res
.
data
.
content
?
String
(
res
.
data
.
content
).
split
(
"#"
)[
0
]
:
""
;
const
urlEn
=
res
.
data
.
contentEn
?
String
(
res
.
data
.
contentEn
).
split
(
"#"
)[
0
]
:
""
;
if
(
!
urlZh
&&
!
urlEn
)
{
ElMessage
.
warning
(
"暂无下载链接"
);
return
;
}
const
baseName
=
(
thinkInfo
.
value
?.
name
||
"报告原文"
).
replace
(
/
[/\\
?%*:|"<>
]
/g
,
"-"
);
if
(
urlZh
)
{
await
downloadOnePdf
(
urlZh
,
`
${
baseName
}
_中文.pdf`
);
}
if
(
urlEn
)
{
if
(
urlZh
)
await
new
Promise
(
r
=>
setTimeout
(
r
,
300
));
await
downloadOnePdf
(
urlEn
,
`
${
baseName
}
_英文.pdf`
);
}
ElMessage
.
success
(
urlZh
&&
urlEn
?
"已下载中文、英文两份 PDF"
:
"下载成功"
);
}
catch
(
error
)
{
console
.
error
(
"文档下载失败:"
,
error
);
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
ElMessage
.
error
(
"PDF 下载失败,请稍后重试"
);
}
catch
(
_
)
{
}
}
};
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
src/views/thinkTank/ReportDetail/policyTracking/index.vue
浏览文件 @
82f15736
差异被折叠。
点击展开。
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
浏览文件 @
82f15736
...
...
@@ -1129,5 +1129,10 @@ onMounted(() => {
:deep
(
.analysis-box-wrapper
.wrapper-header
)
{
height
:
54px
!
important
;
display
:
flex
;
align-items
:
center
;
.header-title
>
div
{
line-height
:
54px
;
}
}
</
style
>
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
浏览文件 @
82f15736
...
...
@@ -456,11 +456,10 @@ const handleGetThinkPolicyIndustryChange = async () => {
frontendData
.
data
.
push
(
industryData
);
});
box3Data
.
value
=
frontendData
;
let
box3Chart
=
getMultiLineChart
(
// 传入全部行业数据,支持 13 条线同时展示
const
box3Chart
=
getMultiLineChart
(
box3Data
.
value
.
title
,
box3Data
.
value
.
data
[
0
].
value
,
box3Data
.
value
.
data
[
1
].
value
,
box3Data
.
value
.
data
[
2
].
value
box3Data
.
value
.
data
);
setChart
(
box3Chart
,
"box3Chart"
);
}
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
浏览文件 @
82f15736
import
*
as
echarts
from
'echarts'
import
{
size
,
split
}
from
'lodash'
const
getMultiLineChart
=
(
dataX
,
dataY1
,
dataY2
,
dataY3
)
=>
{
// data: [{ name: string, value: number[] }, ...]
const
getMultiLineChart
=
(
dataX
,
seriesData
)
=>
{
return
{
tooltip
:
{
trigger
:
'axis'
,
...
...
@@ -71,32 +72,12 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
}
}
],
series
:
[
{
name
:
'人工智能'
,
type
:
'line'
,
emphasis
:
{
focus
:
'series'
},
data
:
dataY1
},
{
name
:
'集成电路'
,
type
:
'line'
,
emphasis
:
{
focus
:
'series'
},
data
:
dataY2
},
{
name
:
'量子科技'
,
type
:
'line'
,
emphasis
:
{
focus
:
'series'
},
data
:
dataY3
}
]
series
:
(
seriesData
||
[]).
map
(
item
=>
({
name
:
item
.
name
,
type
:
'line'
,
emphasis
:
{
focus
:
'series'
},
data
:
item
.
value
}))
}
}
...
...
src/views/thinkTank/allThinkTank/index.vue
0 → 100644
浏览文件 @
82f15736
差异被折叠。
点击展开。
src/views/thinkTank/assets/images/Line_Search.png
0 → 100644
浏览文件 @
82f15736
21.4 KB
src/views/thinkTank/assets/images/arrow-left.png
0 → 100644
浏览文件 @
82f15736
13.9 KB
src/views/thinkTank/assets/images/blue-right.png
0 → 100644
浏览文件 @
82f15736
16.4 KB
src/views/thinkTank/assets/images/info.png
0 → 100644
浏览文件 @
82f15736
20.8 KB
src/views/thinkTank/assets/images/sort-asc.png
0 → 100644
浏览文件 @
82f15736
5.1 KB
src/views/thinkTank/assets/images/sort-desc.png
0 → 100644
浏览文件 @
82f15736
22.1 KB
src/views/thinkTank/index.vue
浏览文件 @
82f15736
...
...
@@ -42,9 +42,18 @@
<div
class=
"item-footer"
>
热点科技领域
</div>
</div>
</div>
-->
<div
class=
"title-info-bar"
>
<div
class=
"title-info"
>
<div
class=
"info"
>
<img
src=
"./assets/images/info.png"
/>
</div>
<div
class=
"title"
>
{{
"近期美国智库机构发布涉华报告数量汇总"
}}
</div>
</div>
</div>
<div
class=
"home-main-header-card-box"
>
<div
class=
"card"
v-for=
"(item, index) in
c
ardList"
:key=
"index"
@
click=
"handleClick(item)"
>
<div
class=
"card"
v-for=
"(item, index) in
sortedC
ardList"
:key=
"index"
@
click=
"handleClick(item)"
>
<div
class=
"card-header"
>
<div
class=
"icon"
>
<img
:src=
"item.logo"
alt=
""
/>
...
...
@@ -52,9 +61,8 @@
<!--
<div
class=
"rank"
:class=
"
{ rank1: item.rank === 1, rank2: item.rank === 2, rank3: item.rank === 3 }">
{{
"No."
+
(
index
+
1
)
}}
</div>
-->
<div
class=
"rank"
:class=
"
{ 'rank-number-one': index === 0, 'rank-number-two': index === 1, 'rank-number-three': index === 2, 'rank-number-four': index === 3, 'rank-number-five': index === 4 }">
<div
class=
"number"
>
{{
"No."
+
(
index
+
1
)
}}
</div>
<div
class=
"rank"
>
<div
class=
" number"
>
{{
item
.
reportNumber
}}
{{
"篇报告"
}}
</div>
</div>
</div>
...
...
@@ -74,9 +82,12 @@
<AreaTag
v-for=
"(val, idx) in item.tagList"
:key=
"idx"
:tagName=
"val.industryName"
></AreaTag>
</div>
</div>
<div
class=
"card"
>
<div
class=
"card"
@
click=
"goToAllThinkTank()"
>
<div
class=
"more"
>
{{ "查看全部智库 >" }}
{{ "查看全部智库" }}{{ "(" }}{{ totalAllItem }}{{ ")" }}{{ "家" }}
<div
class=
"blue-right"
>
<img
src=
"./assets/images/blue-right.png"
alt=
""
/>
</div>
</div>
</div>
</div>
...
...
@@ -170,7 +181,8 @@
</div>
</div>
<div
class=
"box5-main"
>
<div
id=
"box5Chart"
></div>
<el-empty
v-if=
"!hasBox5ChartData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
id=
"box5Chart"
v-else
></div>
<div
class=
"source"
>
<div
class=
"info"
><img
src=
"./assets/images/image-exclamation.png"
></div>
<div
class=
"text"
>
数据来源:美国国会官网,数据时间:2015.1至2025.12
</div>
...
...
@@ -331,6 +343,7 @@ import {
getThinkTankPolicyIndustryChange
,
getThinkTankPolicyIndustry
,
getThinkTankDonation
,
getAllThinkTankList
,
getThinkTankHot
,
getNewReport
,
getHylyList
,
...
...
@@ -378,8 +391,34 @@ import Box1Img from "./assets/images/box1-img.png";
import
Box1Logo
from
"./assets/images/box1-logo.png"
;
import
{
setCanvasCreator
}
from
"echarts/core"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
useRouter
}
from
'vue-router'
;
const
containerRef
=
ref
(
null
);
const
statCountInfo
=
ref
([]);
const
pageSize
=
ref
(
15
)
const
totalAllItem
=
ref
(
0
)
const
handleGetAllThinkTankList
=
async
()
=>
{
try
{
const
res
=
await
getAllThinkTankList
({
// 后端通常是 0-based,这里做一次转换
currentPage
:
currentPage
.
value
-
1
,
pageSize
:
pageSize
.
value
});
console
.
log
(
"智库列表"
,
res
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
totalAllItem
.
value
=
res
.
data
.
totalElements
;
}
}
catch
(
error
)
{
console
.
error
(
"获取智库列表error"
,
error
);
}
};
const
routerTo
=
useRouter
()
// 跳转到全部智库页面
const
goToAllThinkTank
=
()
=>
{
// 替换为你的实际路由路径
routerTo
.
push
(
'/thinkTank/allThinkTank'
);
};
const
getStatCountInfo
=
async
()
=>
{
...
...
@@ -446,6 +485,13 @@ const cardList = ref([
// ]
// }
]);
const
sortedCardList
=
computed
(()
=>
{
return
[...
cardList
.
value
].
sort
((
a
,
b
)
=>
{
const
an
=
Number
(
a
?.
reportNumber
??
0
);
const
bn
=
Number
(
b
?.
reportNumber
??
0
);
return
bn
-
an
;
});
});
// 获取智库列表
const
handleGetThinkTankList
=
async
()
=>
{
try
{
...
...
@@ -729,6 +775,11 @@ const box5Data = ref({
// }
// ]
});
const
hasBox5ChartData
=
computed
(()
=>
{
const
v
=
box5Data
.
value
;
if
(
!
v
||
typeof
v
!==
"object"
)
return
false
;
return
Array
.
isArray
(
v
.
title
)
&&
v
.
title
.
length
>
0
&&
Array
.
isArray
(
v
.
data
)
&&
v
.
data
.
length
>
0
;
});
//获取当前时间x年前的日期
function
getDateYearsAgo
(
years
)
{
...
...
@@ -814,7 +865,7 @@ const handleGetThinkTankPolicyIndustryChange = async date => {
});
box5Data
.
value
=
result
;
}
else
{
box5Data
.
value
=
[]
;
box5Data
.
value
=
{
title
:
[],
data
:
[]
}
;
}
}
catch
(
error
)
{
console
.
error
(
"获取政策建议趋势分布error"
,
error
);
...
...
@@ -1739,6 +1790,7 @@ const handleSearch = () => {
onMounted
(
async
()
=>
{
handleGetThinkTankList
();
handleGetAllThinkTankList
()
await
getStatCountInfo
();
// 定义一个定时器,每隔2秒轮播一次
setInterval
(()
=>
{
...
...
@@ -2009,8 +2061,43 @@ onMounted(async () => {
}
}
.home-main-header-card-box
{
.title-info-bar
{
width
:
1600px
;
height
:
36px
;
margin-top
:
64px
;
display
:
flex
;
.title-info
{
height
:
24px
;
margin-top
:
6px
;
display
:
flex
;
.info
{
width
:
16px
;
height
:
16px
;
margin-top
:
4px
;
margin-right
:
8px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.title
{
font-family
:
"Source Han Sans CN"
;
font-weight
:
400
;
font-size
:
18px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
left
;
color
:
rgb
(
59
,
65
,
75
);
}
}
}
.home-main-header-card-box
{
margin-top
:
30px
;
display
:
flex
;
justify-content
:
space-between
;
...
...
@@ -2050,77 +2137,26 @@ onMounted(async () => {
}
.rank
{
width
:
1
0
0px
;
width
:
1
8
0px
;
height
:
32px
;
background
:
linear-gradient
(
270deg
,
rgba
(
5
,
95
,
194
,
0
.1
)
,
rgba
(
5
,
95
,
194
,
0
)
100%
);
.number
{
line-height
:
32px
;
text-align
:
left
;
font-family
:
"YouSheBiaoTiHei"
;
font-size
:
24px
;
font-weight
:
400
;
line-height
:
31px
;
letter-spacing
:
0px
;
overflow
:
hidden
;
height
:
31px
;
font-family
:
"Source Han Sans CN"
;
font-weight
:
700
;
font-size
:
18px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
right
;
width
:
136px
;
margin-top
:
4px
;
margin-left
:
26px
;
color
:
rgb
(
5
,
95
,
194
);
}
}
// text-overflow: ellipsis;
// white-space: nowrap;
.rank-number-one
{
background
:
linear-gradient
(
90deg
,
rgba
(
206
,
79
,
81
,
0
)
,
rgba
(
206
,
79
,
81
,
0
.3
)
100%
);
color
:
rgb
(
206
,
79
,
81
);
padding-left
:
33px
;
}
.rank-number-two
{
background
:
linear-gradient
(
270deg
,
rgba
(
255
,
172
,
77
,
0
.3
)
,
rgba
(
255
,
172
,
77
,
0
)
100%
);
color
:
rgb
(
255
,
149
,
77
);
padding-left
:
23px
;
}
.rank-number-three
{
background
:
linear-gradient
(
270deg
,
rgba
(
255
,
197
,
61
,
0
.3
)
,
rgba
(
255
,
197
,
61
,
0
)
100%
);
color
:
rgba
(
255
,
197
,
61
,
1
);
padding-left
:
23px
;
}
.rank-number-four
{
background
:
linear-gradient
(
270deg
,
rgba
(
10
,
87
,
166
,
0
.3
)
,
rgba
(
10
,
87
,
166
,
0
)
100%
);
color
:
rgba
(
5
,
95
,
194
,
1
);
padding-left
:
23px
;
}
.rank-number-five
{
background
:
linear-gradient
(
270deg
,
rgba
(
10
,
87
,
166
,
0
.3
)
,
rgba
(
10
,
87
,
166
,
0
)
100%
);
color
:
rgba
(
5
,
95
,
194
,
1
);
padding-left
:
23px
;
}
.rank1
{
background
:
linear-gradient
(
270deg
,
rgba
(
206
,
79
,
81
,
0
.3
)
,
rgba
(
206
,
79
,
81
,
0
)
100%
);
color
:
rgba
(
206
,
79
,
81
,
1
);
}
.rank2
{
background
:
linear-gradient
(
270deg
,
rgba
(
255
,
172
,
77
,
0
.3
)
,
rgba
(
255
,
172
,
77
,
0
)
100%
);
color
:
rgba
(
255
,
149
,
77
,
1
);
}
.rank3
{
background
:
linear-gradient
(
270deg
,
rgba
(
255
,
197
,
61
,
0
.3
)
,
rgba
(
255
,
197
,
61
,
0
)
100%
);
color
:
rgba
(
255
,
197
,
61
,
1
);
}
}
.card-title
{
...
...
@@ -2252,7 +2288,7 @@ onMounted(async () => {
.more
{
margin
:
103px
auto
;
height
:
2
2
px
;
height
:
2
4
px
;
color
:
rgba
(
5
,
95
,
194
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
...
...
@@ -2261,6 +2297,22 @@ onMounted(async () => {
letter-spacing
:
1px
;
text-align
:
center
;
cursor
:
pointer
;
display
:
flex
;
justify-content
:
center
;
/* 👈 水平居中 */
.blue-right
{
width
:
16px
;
height
:
16px
;
margin-top
:
5px
;
margin-left
:
8px
;
img
{
width
:
100%
;
height
:
100%
;
display
:
block
;
}
}
}
}
}
...
...
src/views/thinkTank/reportOriginal/index.vue
浏览文件 @
82f15736
差异被折叠。
点击展开。
src/views/thinkTank/reportOriginal/pdf.vue
浏览文件 @
82f15736
<
template
>
<div
class=
"pdf-viewer"
>
<canvas
ref=
"pdfCanvas"
></canvas>
<canvas
v-for=
"page in pageCount"
:key=
"page"
:ref=
"el => setCanvasRef(page, el)"
></canvas>
<div
v-if=
"loading"
class=
"loading"
>
加载中...
</div>
</div>
</
template
>
<
script
>
import
{
ref
,
onMounted
}
from
'vue'
;
import
{
ref
,
onMounted
,
nextTick
}
from
'vue'
;
import
*
as
pdfjsLib
from
'pdfjs-dist'
;
...
...
@@ -24,37 +28,98 @@ export default {
}
},
setup
(
props
)
{
const
pdfCanvas
=
ref
(
null
);
// 非响应式的 canvas 映射,避免触发布局递归更新
const
canvasMap
=
{};
const
pageCount
=
ref
(
0
);
const
loading
=
ref
(
true
);
const
pdfDocRef
=
ref
(
null
);
onMounted
(
async
()
=>
{
const
setCanvasRef
=
(
page
,
el
)
=>
{
if
(
!
el
)
return
;
canvasMap
[
page
]
=
el
;
};
const
parsePdfUrl
=
(
pdfUrl
)
=>
{
if
(
!
pdfUrl
||
typeof
pdfUrl
!==
'string'
)
return
''
;
const
[
urlPart
]
=
pdfUrl
.
split
(
'#'
);
return
urlPart
;
}
const
renderPdf
=
async
(
pdfUrl
)
=>
{
const
url
=
parsePdfUrl
(
pdfUrl
)
if
(
!
url
)
return
loading
.
value
=
true
pdfDocRef
.
value
=
null
try
{
const
loadingTask
=
pdfjsLib
.
getDocument
(
props
.
pdfU
rl
);
const
loadingTask
=
pdfjsLib
.
getDocument
(
u
rl
);
const
pdf
=
await
loadingTask
.
promise
;
const
page
=
await
pdf
.
getPage
(
1
);
// 加载第一页
pdfDocRef
.
value
=
pdf
pageCount
.
value
=
pdf
.
numPages
;
const
viewport
=
page
.
getViewport
({
scale
:
1.5
});
const
context
=
pdfCanvas
.
value
.
getContext
(
'2d'
);
const
renderContext
=
{
canvasContext
:
context
,
viewport
:
viewport
};
// 等待 canvas 按 pageCount 渲染出来
await
nextTick
();
pdfCanvas
.
value
.
width
=
viewport
.
width
;
pdfCanvas
.
value
.
height
=
viewport
.
height
;
for
(
let
p
=
1
;
p
<=
pdf
.
numPages
;
p
++
)
{
const
pdfPage
=
await
pdf
.
getPage
(
p
);
const
viewport
=
pdfPage
.
getViewport
({
scale
:
1.5
});
const
canvas
=
canvasMap
[
p
];
if
(
!
canvas
)
continue
;
const
context
=
canvas
.
getContext
(
'2d'
);
const
renderContext
=
{
canvasContext
:
context
,
viewport
:
viewport
};
await
page
.
render
(
renderContext
).
promise
;
canvas
.
width
=
viewport
.
width
;
canvas
.
height
=
viewport
.
height
;
await
pdfPage
.
render
(
renderContext
).
promise
;
}
}
catch
(
error
)
{
console
.
error
(
'加载 PDF 出错:'
,
error
);
}
finally
{
loading
.
value
=
false
;
}
});
}
/** 在 PDF 中查找关键词,返回首次出现的页码(1-based),未找到返回 0 */
const
searchKeyword
=
async
(
keyword
)
=>
{
const
doc
=
pdfDocRef
.
value
if
(
!
doc
||
!
keyword
||
!
String
(
keyword
).
trim
())
return
0
const
k
=
String
(
keyword
).
trim
()
const
num
=
doc
.
numPages
for
(
let
p
=
1
;
p
<=
num
;
p
++
)
{
const
page
=
await
doc
.
getPage
(
p
)
const
content
=
await
page
.
getTextContent
()
const
text
=
(
content
.
items
||
[]).
map
(
it
=>
it
.
str
||
''
).
join
(
''
)
if
(
text
.
includes
(
k
))
return
p
}
return
0
}
/** 滚动到指定页码(1-based)对应的 canvas */
const
goToPage
=
(
pageNum
)
=>
{
const
canvas
=
canvasMap
[
pageNum
]
if
(
canvas
&&
typeof
canvas
.
scrollIntoView
===
'function'
)
{
canvas
.
scrollIntoView
({
behavior
:
'smooth'
,
block
:
'start'
})
}
}
// 首次挂载后再根据当前 url 渲染,避免 canvas 还没准备好
onMounted
(()
=>
{
if
(
props
.
pdfUrl
)
{
renderPdf
(
props
.
pdfUrl
)
}
})
return
{
pdfCanvas
,
loading
};
pageCount
,
setCanvasRef
,
loading
,
searchKeyword
,
goToPage
}
}
};
</
script
>
...
...
@@ -63,12 +128,13 @@ export default {
.pdf-viewer
{
position
:
relative
;
width
:
100%
;
height
:
800px
;
/* 高度由内容决定,让外层容器控制滚动 */
}
canvas
{
width
:
100%
;
height
:
100%
;
height
:
auto
;
display
:
block
;
}
.loading
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论