Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
84861ffe
提交
84861ffe
authored
4月 22, 2026
作者:
coderBryanFu
浏览文件
操作
浏览文件
下载
差异文件
feat:新增goToPage.js文件
上级
1b6a90f4
7cd28bf2
流水线
#586
已通过 于阶段
in 1 分 38 秒
变更
33
流水线
1
显示空白字符变更
内嵌
并排
正在显示
33 个修改的文件
包含
1062 行增加
和
454 行删除
+1062
-454
overview.js
src/api/thinkTank/overview.js
+3
-2
thinktank.js
src/router/modules/thinktank.js
+1
-1
setChart.js
src/utils/setChart.js
+32
-0
index.vue
src/views/coopRestriction/components/dataSub/index.vue
+32
-2
index.vue
src/views/coopRestriction/components/resLib/index.vue
+14
-6
index.vue
...ews/exportControl/v2.0SingleSanction/originPage/index.vue
+13
-33
index.vue
src/views/scientificFunding/components/dataSub/index.vue
+72
-25
index.vue
src/views/scientificFunding/components/resLib/index.vue
+7
-3
index.vue
src/views/thinkTank/CongressHearingView/index.vue
+19
-7
index.vue
src/views/thinkTank/ReportDetail/index.vue
+1
-1
index.vue
src/views/thinkTank/ReportDetail/policyTracking/index.vue
+10
-4
index.vue
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
+20
-8
index.vue
src/views/thinkTank/SurveyProjectView/index.vue
+19
-7
index.vue
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
+147
-30
multiLineChart.js
...nk/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
+49
-97
piechart.js
...hinkTank/ThinkTankDetail/PolicyTracking/utils/piechart.js
+14
-9
index.vue
...k/ThinkTankDetail/thinkDynamics/CongressHearing/index.vue
+33
-3
index.vue
...nkTank/ThinkTankDetail/thinkDynamics/SurveyForm/index.vue
+32
-3
index.vue
...k/ThinkTankDetail/thinkDynamics/ThinkTankReport/index.vue
+33
-3
index.vue
src/views/thinkTank/ThinkTankDetail/thinkDynamics/index.vue
+24
-2
piechart.js
...ews/thinkTank/ThinkTankDetail/thinkInfo/utils/piechart.js
+6
-1
treeMapChart.js
...thinkTank/ThinkTankDetail/thinkInfo/utils/treeMapChart.js
+12
-1
index.vue
src/views/thinkTank/allThinkTank/index.vue
+1
-1
HomeMainFooterMain.vue
src/views/thinkTank/components/HomeMainFooterMain.vue
+32
-5
HomeMainFooterSurvey.vue
src/views/thinkTank/components/HomeMainFooterSurvey.vue
+32
-5
ThinkTankCongressHearingOverview.vue
...thinkTank/components/ThinkTankCongressHearingOverview.vue
+32
-9
ThinkTankPolicyAdviceOverview.vue
...ws/thinkTank/components/ThinkTankPolicyAdviceOverview.vue
+31
-5
index.vue
src/views/thinkTank/index.vue
+247
-80
index.vue
src/views/thinkTank/reportOriginal/index.vue
+8
-6
multiLineChart.js
src/views/thinkTank/utils/multiLineChart.js
+21
-82
piechart.js
src/views/thinkTank/utils/piechart.js
+15
-9
resourceLibraryFilters.js
src/views/thinkTank/utils/resourceLibraryFilters.js
+17
-4
sankey.js
src/views/thinkTank/utils/sankey.js
+33
-0
没有找到文件。
src/api/thinkTank/overview.js
浏览文件 @
84861ffe
...
@@ -75,7 +75,7 @@ export function getThinkTankReportDomainStats(params) {
...
@@ -75,7 +75,7 @@ export function getThinkTankReportDomainStats(params) {
export
function
getThinkTankPolicyIndustry
(
params
)
{
export
function
getThinkTankPolicyIndustry
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/thinkTankOverview/policyIndustry
/
${
params
.
year
}
`
,
url
:
`/api/thinkTankOverview/policyIndustry`
,
params
params
})
})
}
}
...
@@ -180,7 +180,8 @@ export function getThinkTankTestimoniesByThinkTankId(params) {
...
@@ -180,7 +180,8 @@ export function getThinkTankTestimoniesByThinkTankId(params) {
params
:
{
params
:
{
pageNum
:
params
.
pageNum
,
pageNum
:
params
.
pageNum
,
pageSize
:
params
.
pageSize
,
pageSize
:
params
.
pageSize
,
sortField
:
params
.
sortField
,
sortOrder
:
params
.
sortOrder
,
domainIds
:
params
.
domainIds
,
domainIds
:
params
.
domainIds
,
startDate
:
params
.
startDate
,
startDate
:
params
.
startDate
,
endDate
:
params
.
endDate
,
endDate
:
params
.
endDate
,
...
...
src/router/modules/thinktank.js
浏览文件 @
84861ffe
...
@@ -15,7 +15,7 @@ const thinktankRoutes = [
...
@@ -15,7 +15,7 @@ const thinktankRoutes = [
name
:
"thinkTank"
,
name
:
"thinkTank"
,
component
:
thinkTank
,
component
:
thinkTank
,
meta
:
{
meta
:
{
title
:
"科技智库概览"
,
title
:
"
美国
科技智库概览"
,
isShowHeader
:
true
isShowHeader
:
true
}
}
},
},
...
...
src/utils/setChart.js
浏览文件 @
84861ffe
...
@@ -13,8 +13,39 @@ const setChart = (option, chartId, allowClick, selectParam) => {
...
@@ -13,8 +13,39 @@ const setChart = (option, chartId, allowClick, selectParam) => {
chartDom
.
removeAttribute
(
"_echarts_instance_"
);
chartDom
.
removeAttribute
(
"_echarts_instance_"
);
let
chart
=
echarts
.
init
(
chartDom
);
let
chart
=
echarts
.
init
(
chartDom
);
chart
.
setOption
(
option
);
chart
.
setOption
(
option
);
// 处理自定义图例分页箭头(左右分布,隐藏页码)
// 约定:graphic 元素 name 为 __legend_prev__ / __legend_next__
chart
.
on
(
'click'
,
function
(
params
)
{
if
(
params
?.
componentType
!==
'graphic'
)
return
;
if
(
!
LEGEND_ARROW_NAMES
.
has
(
params
?.
name
))
return
;
const
opt
=
chart
.
getOption
?.()
||
{};
const
legend
=
Array
.
isArray
(
opt
.
legend
)
?
opt
.
legend
[
0
]
:
null
;
if
(
!
legend
||
legend
.
type
!==
'scroll'
)
return
;
const
dataLen
=
Array
.
isArray
(
legend
.
data
)
?
legend
.
data
.
length
:
0
;
if
(
dataLen
<=
0
)
return
;
const
cur
=
Number
(
legend
.
scrollDataIndex
||
0
);
const
nextIndex
=
params
.
name
===
'__legend_prev__'
?
Math
.
max
(
0
,
cur
-
1
)
:
Math
.
min
(
dataLen
-
1
,
cur
+
1
);
if
(
nextIndex
===
cur
)
return
;
chart
.
dispatchAction
({
type
:
'legendScroll'
,
scrollDataIndex
:
nextIndex
});
});
// 初次渲染后判断是否需要显示左右箭头(可一行展示则隐藏)
applyLegendPagingArrowVisibility
(
chart
,
option
)
if
(
allowClick
)
{
if
(
allowClick
)
{
chart
.
on
(
'click'
,
function
(
params
)
{
chart
.
on
(
'click'
,
function
(
params
)
{
// 图例分页箭头只负责翻页,不走任何跳转
if
(
params
?.
componentType
===
'graphic'
&&
LEGEND_ARROW_NAMES
.
has
(
params
?.
name
))
{
return
}
switch
(
selectParam
.
moduleType
)
{
switch
(
selectParam
.
moduleType
)
{
case
'国会法案'
:
case
'国会法案'
:
if
(
selectParam
.
key
===
1
)
{
if
(
selectParam
.
key
===
1
)
{
...
@@ -121,6 +152,7 @@ const setChart = (option, chartId, allowClick, selectParam) => {
...
@@ -121,6 +152,7 @@ const setChart = (option, chartId, allowClick, selectParam) => {
// 容器可能受布局/异步渲染影响,强制一次 resize 保证 canvas 与容器一致
// 容器可能受布局/异步渲染影响,强制一次 resize 保证 canvas 与容器一致
setTimeout
(()
=>
{
setTimeout
(()
=>
{
chart
.
resize
();
chart
.
resize
();
applyLegendPagingArrowVisibility
(
chart
,
option
)
},
0
);
},
0
);
return
chart
;
return
chart
;
};
};
...
...
src/views/coopRestriction/components/dataSub/index.vue
浏览文件 @
84861ffe
...
@@ -186,18 +186,26 @@ let leftChart;
...
@@ -186,18 +186,26 @@ let leftChart;
const
rightChartRef
=
ref
(
null
);
const
rightChartRef
=
ref
(
null
);
let
rightChart
;
let
rightChart
;
const
isShowAiLeft
=
ref
(
tru
e
);
const
isShowAiLeft
=
ref
(
fals
e
);
const
aiContentLeft
=
ref
(
""
);
const
aiContentLeft
=
ref
(
""
);
const
isLeftInterpretLoading
=
ref
(
false
);
const
isLeftInterpretLoading
=
ref
(
false
);
const
leftAiAbortController
=
ref
(
null
);
const
isShowAiRight
=
ref
(
tru
e
);
const
isShowAiRight
=
ref
(
fals
e
);
const
aiContentRight
=
ref
(
""
);
const
aiContentRight
=
ref
(
""
);
const
isRightInterpretLoading
=
ref
(
false
);
const
isRightInterpretLoading
=
ref
(
false
);
const
rightAiAbortController
=
ref
(
null
);
const
handleSwitchAiLeft
=
(
val
)
=>
{
const
handleSwitchAiLeft
=
(
val
)
=>
{
isShowAiLeft
.
value
=
val
;
isShowAiLeft
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchLeftInterpretation
();
fetchLeftInterpretation
();
}
else
{
if
(
leftAiAbortController
.
value
)
{
leftAiAbortController
.
value
.
abort
();
leftAiAbortController
.
value
=
null
;
}
isLeftInterpretLoading
.
value
=
false
;
}
}
};
};
...
@@ -205,6 +213,12 @@ const handleSwitchAiRight = (val) => {
...
@@ -205,6 +213,12 @@ const handleSwitchAiRight = (val) => {
isShowAiRight
.
value
=
val
;
isShowAiRight
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchRightInterpretation
();
fetchRightInterpretation
();
}
else
{
if
(
rightAiAbortController
.
value
)
{
rightAiAbortController
.
value
.
abort
();
rightAiAbortController
.
value
=
null
;
}
isRightInterpretLoading
.
value
=
false
;
}
}
};
};
...
@@ -276,12 +290,17 @@ const fetchLeftInterpretation = async () => {
...
@@ -276,12 +290,17 @@ const fetchLeftInterpretation = async () => {
if
(
hasValidContent
||
isLeftInterpretLoading
.
value
)
{
if
(
hasValidContent
||
isLeftInterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
leftAiAbortController
.
value
)
{
leftAiAbortController
.
value
.
abort
();
}
leftAiAbortController
.
value
=
new
AbortController
();
isLeftInterpretLoading
.
value
=
true
;
isLeftInterpretLoading
.
value
=
true
;
aiContentLeft
.
value
=
"解读生成中…"
;
aiContentLeft
.
value
=
"解读生成中…"
;
try
{
try
{
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
text
:
JSON
.
stringify
(
payload
)
},
{
{
signal
:
leftAiAbortController
.
value
.
signal
,
onChunk
:
(
chunk
)
=>
{
onChunk
:
(
chunk
)
=>
{
// 与智库概览「数量变化趋势」一致:按 chunk 增量拼接展示
// 与智库概览「数量变化趋势」一致:按 chunk 增量拼接展示
appendAiInterpretationChunk
(
aiContentLeft
,
chunk
);
appendAiInterpretationChunk
(
aiContentLeft
,
chunk
);
...
@@ -292,10 +311,13 @@ const fetchLeftInterpretation = async () => {
...
@@ -292,10 +311,13 @@ const fetchLeftInterpretation = async () => {
// 与智库概览一致:优先用最终「解读」收口;否则保留已拼接内容
// 与智库概览一致:优先用最终「解读」收口;否则保留已拼接内容
aiContentLeft
.
value
=
text
||
aiContentLeft
.
value
||
"未返回有效解读内容"
;
aiContentLeft
.
value
=
text
||
aiContentLeft
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"合作限制政策对比图表解读请求失败"
,
error
);
console
.
error
(
"合作限制政策对比图表解读请求失败"
,
error
);
aiContentLeft
.
value
=
"解读加载失败"
;
aiContentLeft
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isLeftInterpretLoading
.
value
=
false
;
isLeftInterpretLoading
.
value
=
false
;
leftAiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -348,12 +370,17 @@ const fetchRightInterpretation = async () => {
...
@@ -348,12 +370,17 @@ const fetchRightInterpretation = async () => {
if
(
hasValidContent
||
isRightInterpretLoading
.
value
)
{
if
(
hasValidContent
||
isRightInterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
rightAiAbortController
.
value
)
{
rightAiAbortController
.
value
.
abort
();
}
rightAiAbortController
.
value
=
new
AbortController
();
isRightInterpretLoading
.
value
=
true
;
isRightInterpretLoading
.
value
=
true
;
aiContentRight
.
value
=
"解读生成中…"
;
aiContentRight
.
value
=
"解读生成中…"
;
try
{
try
{
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
text
:
JSON
.
stringify
(
payload
)
},
{
{
signal
:
rightAiAbortController
.
value
.
signal
,
onChunk
:
(
chunk
)
=>
{
onChunk
:
(
chunk
)
=>
{
appendAiInterpretationChunk
(
aiContentRight
,
chunk
);
appendAiInterpretationChunk
(
aiContentRight
,
chunk
);
}
}
...
@@ -362,10 +389,13 @@ const fetchRightInterpretation = async () => {
...
@@ -362,10 +389,13 @@ const fetchRightInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentRight
.
value
=
text
||
aiContentRight
.
value
||
"未返回有效解读内容"
;
aiContentRight
.
value
=
text
||
aiContentRight
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"合作限制领域分布图表解读请求失败"
,
error
);
console
.
error
(
"合作限制领域分布图表解读请求失败"
,
error
);
aiContentRight
.
value
=
"解读加载失败"
;
aiContentRight
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isRightInterpretLoading
.
value
=
false
;
isRightInterpretLoading
.
value
=
false
;
rightAiAbortController
.
value
=
null
;
}
}
};
};
...
...
src/views/coopRestriction/components/resLib/index.vue
浏览文件 @
84861ffe
...
@@ -61,17 +61,17 @@
...
@@ -61,17 +61,17 @@
</div>
</div>
</div>
</div>
</div>
</div>
<div
class=
"page"
>
<div
class=
"count"
>
共
{{
total
}}
项调查
</div>
<el-pagination
v-model:current-page=
"currentPage"
:page-size=
"pageSize"
:total=
"total"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChange"
/>
</div>
</
template
>
</
template
>
<
template
v-else
>
<
template
v-else
>
<div
class=
"right-main-empty"
>
<div
class=
"right-main-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</div>
</
template
>
</
template
>
<div
class=
"page"
>
<div
class=
"count"
>
共 {{ total }} 项调查
</div>
<el-pagination
v-model:current-page=
"currentPage"
:page-size=
"pageSize"
:page-count=
"pageCount"
layout=
"prev, pager, next"
background
@
current-change=
"handlePageChange"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -293,6 +293,11 @@ const total = ref(0);
...
@@ -293,6 +293,11 @@ const total = ref(0);
const
pageSize
=
ref
(
10
);
const
pageSize
=
ref
(
10
);
const
currentPage
=
ref
(
1
);
const
currentPage
=
ref
(
1
);
const
reslibContainer
=
ref
(
null
);
const
reslibContainer
=
ref
(
null
);
const
pageCount
=
computed
(()
=>
{
const
size
=
Number
(
pageSize
.
value
||
10
)
||
10
;
const
t
=
Number
(
total
.
value
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
t
/
size
));
});
const
getTypeClass
=
(
type
)
=>
{
const
getTypeClass
=
(
type
)
=>
{
const
map
=
{
const
map
=
{
...
@@ -658,13 +663,16 @@ watch(currentPage, () => {
...
@@ -658,13 +663,16 @@ watch(currentPage, () => {
.title
{
.title
{
font-size
:
20px
;
font-size
:
20px
;
width
:
950px
;
max-
width
:
950px
;
font-weight
:
700
;
font-weight
:
700
;
font-family
:
"Microsoft YaHei"
;
font-family
:
"Microsoft YaHei"
;
line-height
:
26px
;
line-height
:
26px
;
color
:
rgb
(
59
,
65
,
75
);
color
:
rgb
(
59
,
65
,
75
);
margin-bottom
:
9px
;
margin-bottom
:
9px
;
cursor
:
pointer
;
cursor
:
pointer
;
white-space
:
normal
;
overflow-wrap
:
anywhere
;
word-break
:
break-word
;
}
}
.content
{
.content
{
...
...
src/views/exportControl/v2.0SingleSanction/originPage/index.vue
浏览文件 @
84861ffe
...
@@ -26,27 +26,15 @@
...
@@ -26,27 +26,15 @@
<div
class=
"btn-box"
>
<div
class=
"btn-box"
>
<div
class=
"translate"
>
<div
class=
"translate"
>
<div
class=
"search-input-wrap"
v-if=
"showSearchInput"
>
<div
class=
"search-input-wrap"
v-if=
"showSearchInput"
>
<input
<input
v-model=
"searchKeywordText"
class=
"search-input"
placeholder=
"回车查询"
v-model=
"searchKeywordText"
@
keyup
.
enter=
"handleSearchInPdf"
/>
class=
"search-input"
placeholder=
"回车查询"
@
keyup
.
enter=
"handleSearchInPdf"
/>
<div
class=
"search-match-count"
>
{{
matchInfo
.
current
}}
/
{{
matchInfo
.
total
}}
</div>
<div
class=
"search-match-count"
>
{{
matchInfo
.
current
}}
/
{{
matchInfo
.
total
}}
</div>
<button
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handlePrevMatch"
class=
"search-nav-btn"
:disabled=
"matchInfo.total === 0 || matchInfo.current
<
=
1
"
>
type=
"button"
@
click=
"handlePrevMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current
<
=
1
"
>
上一个
上一个
</button>
</button>
<button
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handleNextMatch"
class=
"search-nav-btn"
:disabled=
"matchInfo.total === 0 || matchInfo.current >= matchInfo.total"
>
type=
"button"
@
click=
"handleNextMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current >= matchInfo.total"
>
下一个
下一个
</button>
</button>
</div>
</div>
...
@@ -54,19 +42,14 @@
...
@@ -54,19 +42,14 @@
<el-switch
v-model=
"valueSwitch"
/>
<el-switch
v-model=
"valueSwitch"
/>
</div>
</div>
<div
class=
"translate-image"
>
<div
class=
"translate-image"
>
<img
<img
class=
"translate-icon"
src=
"../assets/icon-translation.png"
alt=
""
style=
"
class=
"translate-icon"
src=
"../assets/icon-translation.png"
alt=
""
style=
"
width: 16px;
width: 16px;
height: 16px;
height: 16px;
max-width: 16px;
max-width: 16px;
max-height: 16px;
max-height: 16px;
display: block;
display: block;
object-fit: contain;
object-fit: contain;
"
"
/>
/>
</div>
</div>
<div
class=
"translate-text"
>
{{
"显示原文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示原文"
}}
</div>
</div>
</div>
...
@@ -83,12 +66,8 @@
...
@@ -83,12 +66,8 @@
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`" ref="rightPdfRef" :pdfUrl="reportUrlWithPage"
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`"
class="pdf-pane-inner" />
ref="rightPdfRef"
:pdfUrl="reportUrlWithPage"
class="pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -203,7 +182,7 @@ const handleSearchInPdf = async () => {
...
@@ -203,7 +182,7 @@ const handleSearchInPdf = async () => {
try
{
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
ElMessage
.
warning
(
"未找到包含该关键词的页面"
);
ElMessage
.
warning
(
"未找到包含该关键词的页面"
);
}
catch
(
_
)
{}
}
catch
(
_
)
{
}
}
}
};
};
...
@@ -246,7 +225,7 @@ const handleDownload = async () => {
...
@@ -246,7 +225,7 @@ const handleDownload = async () => {
try
{
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
ElMessage
.
warning
(
"暂无下载链接"
);
ElMessage
.
warning
(
"暂无下载链接"
);
}
catch
(
_
)
{}
}
catch
(
_
)
{
}
return
;
return
;
}
}
const
baseName
=
(
thinkInfo
.
value
?.
name
||
"报告原文"
).
replace
(
/
[/\\
?%*:|"<>
]
/g
,
"-"
);
const
baseName
=
(
thinkInfo
.
value
?.
name
||
"报告原文"
).
replace
(
/
[/\\
?%*:|"<>
]
/g
,
"-"
);
...
@@ -659,6 +638,7 @@ onMounted(async () => {
...
@@ -659,6 +638,7 @@ onMounted(async () => {
display
:
flex
;
display
:
flex
;
gap
:
8px
;
gap
:
8px
;
cursor
:
pointer
;
cursor
:
pointer
;
.icon
{
.icon
{
width
:
16px
;
width
:
16px
;
height
:
16px
;
height
:
16px
;
...
...
src/views/scientificFunding/components/dataSub/index.vue
浏览文件 @
84861ffe
...
@@ -171,6 +171,10 @@ const options = [
...
@@ -171,6 +171,10 @@ const options = [
];
];
/** value 须与 v-model 类型一致(数字),否则 el-select 无法匹配 label,会显示成「2025」而非「2025年」 */
/** value 须与 v-model 类型一致(数字),否则 el-select 无法匹配 label,会显示成「2025」而非「2025年」 */
const
options1
=
[
const
options1
=
[
{
value
:
2026
,
label
:
"2026年"
},
{
{
value
:
2025
,
value
:
2025
,
label
:
"2025年"
label
:
"2025年"
...
@@ -719,11 +723,11 @@ let rightChart1;
...
@@ -719,11 +723,11 @@ let rightChart1;
let
leftSankey
;
let
leftSankey
;
let
boxplotChart
;
let
boxplotChart
;
// ------- AI 解读(
刷新后默认展开,行为对齐智库概览
) -------
// ------- AI 解读(
默认仅展示 AiButton,悬停后再请求 AI
) -------
const
isShowAiContentLeft1
=
ref
(
tru
e
);
const
isShowAiContentLeft1
=
ref
(
fals
e
);
const
isShowAiContentLeft2
=
ref
(
tru
e
);
const
isShowAiContentLeft2
=
ref
(
fals
e
);
const
isShowAiContentRight1
=
ref
(
tru
e
);
const
isShowAiContentRight1
=
ref
(
fals
e
);
const
isShowAiContentRight2
=
ref
(
tru
e
);
const
isShowAiContentRight2
=
ref
(
fals
e
);
const
aiContentLeft1
=
ref
(
""
);
const
aiContentLeft1
=
ref
(
""
);
const
aiContentLeft2
=
ref
(
""
);
const
aiContentLeft2
=
ref
(
""
);
...
@@ -735,6 +739,11 @@ const isAiLoadingLeft2 = ref(false);
...
@@ -735,6 +739,11 @@ const isAiLoadingLeft2 = ref(false);
const
isAiLoadingRight1
=
ref
(
false
);
const
isAiLoadingRight1
=
ref
(
false
);
const
isAiLoadingRight2
=
ref
(
false
);
const
isAiLoadingRight2
=
ref
(
false
);
const
left1AiAbortController
=
ref
(
null
);
const
left2AiAbortController
=
ref
(
null
);
const
right1AiAbortController
=
ref
(
null
);
const
right2AiAbortController
=
ref
(
null
);
const
AI_LOADING_TEXT
=
"解读生成中…"
;
const
AI_LOADING_TEXT
=
"解读生成中…"
;
// 用于保证“切换筛选后只写入最新一次解读结果”
// 用于保证“切换筛选后只写入最新一次解读结果”
const
left1AiSeq
=
ref
(
0
);
const
left1AiSeq
=
ref
(
0
);
...
@@ -767,7 +776,13 @@ const getInterpretationTextFromChartResponse = (res) => {
...
@@ -767,7 +776,13 @@ const getInterpretationTextFromChartResponse = (res) => {
);
);
};
};
const
fetchChartInterpretationOnce
=
async
(
payload
,
targetRef
,
loadingRef
,
aiSeqRef
)
=>
{
const
fetchChartInterpretationOnce
=
async
(
payload
,
targetRef
,
loadingRef
,
aiSeqRef
,
abortControllerRef
)
=>
{
if
(
loadingRef
.
value
)
return
;
if
(
loadingRef
.
value
)
return
;
const
hasValidContent
=
const
hasValidContent
=
...
@@ -778,12 +793,19 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
...
@@ -778,12 +793,19 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
if
(
hasValidContent
)
return
;
if
(
hasValidContent
)
return
;
const
localSeq
=
aiSeqRef
.
value
;
const
localSeq
=
aiSeqRef
.
value
;
if
(
abortControllerRef
?.
value
)
{
abortControllerRef
.
value
.
abort
();
}
if
(
abortControllerRef
)
{
abortControllerRef
.
value
=
new
AbortController
();
}
loadingRef
.
value
=
true
;
loadingRef
.
value
=
true
;
targetRef
.
value
=
AI_LOADING_TEXT
;
targetRef
.
value
=
AI_LOADING_TEXT
;
try
{
try
{
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
text
:
JSON
.
stringify
(
payload
)
},
{
{
...(
abortControllerRef
?.
value
?.
signal
?
{
signal
:
abortControllerRef
.
value
.
signal
}
:
{}),
onChunk
:
(
chunk
)
=>
{
onChunk
:
(
chunk
)
=>
{
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
appendAiInterpretationChunk
(
targetRef
,
chunk
,
AI_LOADING_TEXT
);
appendAiInterpretationChunk
(
targetRef
,
chunk
,
AI_LOADING_TEXT
);
...
@@ -796,10 +818,13 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
...
@@ -796,10 +818,13 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
targetRef
.
value
=
text
||
targetRef
.
value
||
"未返回有效解读内容"
;
targetRef
.
value
=
text
||
targetRef
.
value
||
"未返回有效解读内容"
;
}
catch
(
e
)
{
}
catch
(
e
)
{
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
if
(
e
?.
name
!==
"AbortError"
)
{
console
.
error
(
"图表解读请求失败"
,
e
);
console
.
error
(
"图表解读请求失败"
,
e
);
targetRef
.
value
=
"解读加载失败"
;
targetRef
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
if
(
aiSeqRef
.
value
===
localSeq
)
loadingRef
.
value
=
false
;
if
(
aiSeqRef
.
value
===
localSeq
)
loadingRef
.
value
=
false
;
if
(
abortControllerRef
)
abortControllerRef
.
value
=
null
;
}
}
};
};
...
@@ -842,43 +867,71 @@ const buildPayloadRight2 = () => {
...
@@ -842,43 +867,71 @@ const buildPayloadRight2 = () => {
const
handleSwitchAiLeft1
=
async
(
val
)
=>
{
const
handleSwitchAiLeft1
=
async
(
val
)
=>
{
isShowAiContentLeft1
.
value
=
val
;
isShowAiContentLeft1
.
value
=
val
;
if
(
!
val
)
return
;
if
(
!
val
)
{
if
(
left1AiAbortController
.
value
)
{
left1AiAbortController
.
value
.
abort
();
left1AiAbortController
.
value
=
null
;
}
isAiLoadingLeft1
.
value
=
false
;
return
;
}
const
payload
=
buildPayloadLeft1
();
const
payload
=
buildPayloadLeft1
();
if
(
!
payload
)
{
if
(
!
payload
)
{
aiContentLeft1
.
value
=
"暂无图表数据"
;
aiContentLeft1
.
value
=
"暂无图表数据"
;
return
;
return
;
}
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft1
,
isAiLoadingLeft1
,
left1AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft1
,
isAiLoadingLeft1
,
left1AiSeq
,
left1AiAbortController
);
};
};
const
handleSwitchAiLeft2
=
async
(
val
)
=>
{
const
handleSwitchAiLeft2
=
async
(
val
)
=>
{
isShowAiContentLeft2
.
value
=
val
;
isShowAiContentLeft2
.
value
=
val
;
if
(
!
val
)
return
;
if
(
!
val
)
{
if
(
left2AiAbortController
.
value
)
{
left2AiAbortController
.
value
.
abort
();
left2AiAbortController
.
value
=
null
;
}
isAiLoadingLeft2
.
value
=
false
;
return
;
}
const
payload
=
buildPayloadLeft2
();
const
payload
=
buildPayloadLeft2
();
if
(
!
payload
)
{
if
(
!
payload
)
{
aiContentLeft2
.
value
=
"暂无图表数据"
;
aiContentLeft2
.
value
=
"暂无图表数据"
;
return
;
return
;
}
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft2
,
isAiLoadingLeft2
,
left2AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft2
,
isAiLoadingLeft2
,
left2AiSeq
,
left2AiAbortController
);
};
};
const
handleSwitchAiRight1
=
async
(
val
)
=>
{
const
handleSwitchAiRight1
=
async
(
val
)
=>
{
isShowAiContentRight1
.
value
=
val
;
isShowAiContentRight1
.
value
=
val
;
if
(
!
val
)
return
;
if
(
!
val
)
{
if
(
right1AiAbortController
.
value
)
{
right1AiAbortController
.
value
.
abort
();
right1AiAbortController
.
value
=
null
;
}
isAiLoadingRight1
.
value
=
false
;
return
;
}
const
payload
=
buildPayloadRight1
();
const
payload
=
buildPayloadRight1
();
if
(
!
payload
)
{
if
(
!
payload
)
{
aiContentRight1
.
value
=
"暂无图表数据"
;
aiContentRight1
.
value
=
"暂无图表数据"
;
return
;
return
;
}
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight1
,
isAiLoadingRight1
,
right1AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight1
,
isAiLoadingRight1
,
right1AiSeq
,
right1AiAbortController
);
};
};
const
handleSwitchAiRight2
=
async
(
val
)
=>
{
const
handleSwitchAiRight2
=
async
(
val
)
=>
{
isShowAiContentRight2
.
value
=
val
;
isShowAiContentRight2
.
value
=
val
;
if
(
!
val
)
return
;
if
(
!
val
)
{
if
(
right2AiAbortController
.
value
)
{
right2AiAbortController
.
value
.
abort
();
right2AiAbortController
.
value
=
null
;
}
isAiLoadingRight2
.
value
=
false
;
return
;
}
const
payload
=
buildPayloadRight2
();
const
payload
=
buildPayloadRight2
();
if
(
!
payload
)
{
if
(
!
payload
)
{
aiContentRight2
.
value
=
"暂无图表数据"
;
aiContentRight2
.
value
=
"暂无图表数据"
;
return
;
return
;
}
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight2
,
isAiLoadingRight2
,
right2AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight2
,
isAiLoadingRight2
,
right2AiSeq
,
right2AiAbortController
);
};
};
...
@@ -1136,21 +1189,15 @@ const initLeftSankey = (data) => {
...
@@ -1136,21 +1189,15 @@ const initLeftSankey = (data) => {
// };
// };
onMounted
(()
=>
{
onMounted
(()
=>
{
// 刷新后 AiPane 默认展开:先给出“解读生成中…”占位,再在数据到位后触发解读请求
// 仅拉取图表数据;AI 解读仅在用户悬停打开面板时触发
aiContentLeft1
.
value
=
"解读生成中…"
;
void
handleGetFundField
();
aiContentLeft2
.
value
=
"解读生成中…"
;
aiContentRight1
.
value
=
"解读生成中…"
;
aiContentRight2
.
value
=
"解读生成中…"
;
// 先拉数据;每块数据到位后立即触发一次 AI 解读(不必等其它块完成)
void
handleGetFundField
().
then
(()
=>
handleSwitchAiLeft1
(
true
));
void
handleFindCountryProjectAreaList
();
void
handleFindCountryProjectAreaList
();
void
handlegetCountryFundingChange
()
.
then
(()
=>
handleSwitchAiRight1
(
true
))
;
void
handlegetCountryFundingChange
();
void
handlegetCountryFundProjectChange
();
void
handlegetCountryFundProjectChange
();
void
handleGetOrgFundsArea
()
.
then
(()
=>
handleSwitchAiLeft2
(
true
))
;
void
handleGetOrgFundsArea
();
void
handlegetOrgFundStrength
()
.
then
(()
=>
handleSwitchAiRight2
(
true
))
;
void
handlegetOrgFundStrength
();
});
});
// onBeforeUnmount(() => {
// onBeforeUnmount(() => {
// window.removeEventListener("resize", handleResize);
// window.removeEventListener("resize", handleResize);
...
...
src/views/scientificFunding/components/resLib/index.vue
浏览文件 @
84861ffe
...
@@ -166,6 +166,10 @@ const handleTimeGroupChange = (val) => {
...
@@ -166,6 +166,10 @@ const handleTimeGroupChange = (val) => {
};
};
const
pubTimeList
=
ref
([
const
pubTimeList
=
ref
([
{
id
:
2026
,
name
:
"2026年"
},
{
{
id
:
2025
,
id
:
2025
,
name
:
"2025年"
name
:
"2025年"
...
@@ -192,13 +196,13 @@ const pubTimeList = ref([
...
@@ -192,13 +196,13 @@ const pubTimeList = ref([
}
}
]);
]);
/** 选择「全部时间」时,yearlist 传 2000~202
5
逐年 */
/** 选择「全部时间」时,yearlist 传 2000~202
6
逐年 */
const
YEAR_ALL_RANGE_START
=
2000
;
const
YEAR_ALL_RANGE_START
=
2000
;
const
YEAR_ALL_RANGE_END
=
202
5
;
const
YEAR_ALL_RANGE_END
=
202
6
;
const
buildYearlistForRequest
=
(
selectedTimeModel
)
=>
{
const
buildYearlistForRequest
=
(
selectedTimeModel
)
=>
{
const
strippedTime
=
stripAllTimeForRequest
(
selectedTimeModel
);
const
strippedTime
=
stripAllTimeForRequest
(
selectedTimeModel
);
// 仅勾选「全部时间」、未选具体年份时,传 2000~202
5
逐年
// 仅勾选「全部时间」、未选具体年份时,传 2000~202
6
逐年
if
(
strippedTime
.
length
===
0
)
{
if
(
strippedTime
.
length
===
0
)
{
const
out
=
[];
const
out
=
[];
for
(
let
y
=
YEAR_ALL_RANGE_START
;
y
<=
YEAR_ALL_RANGE_END
;
y
+=
1
)
{
for
(
let
y
=
YEAR_ALL_RANGE_START
;
y
<=
YEAR_ALL_RANGE_END
;
y
+=
1
)
{
...
...
src/views/thinkTank/CongressHearingView/index.vue
浏览文件 @
84861ffe
...
@@ -136,7 +136,7 @@
...
@@ -136,7 +136,7 @@
<div
class=
"box5-footer"
>
<div
class=
"box5-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
<AiButton
/>
</div>
</div>
...
@@ -305,14 +305,21 @@ const handleGetThinkTankHearingInfo = async () => {
...
@@ -305,14 +305,21 @@ const handleGetThinkTankHearingInfo = async () => {
};
};
const
REPORT_ANALYSIS_TIP_BOX5
=
const
REPORT_ANALYSIS_TIP_BOX5
=
"国会听证会关键词云,数据来源:美国兰德公司官网"
;
"国会听证会关键词云,数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
tru
e
);
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox5ChartInterpretation
();
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
}
};
};
...
@@ -510,10 +517,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
...
@@ -510,10 +517,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
box5WordCloudKey
.
value
+=
1
;
}
}
// 刷新后默认展开 AI:数据就绪即触发解读
// 仅在用户打开 AI 面板时才请求解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
}
else
{
}
else
{
box5Data
.
value
=
[];
box5Data
.
value
=
[];
}
}
...
@@ -663,6 +667,10 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -663,6 +667,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -677,6 +685,7 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -677,6 +685,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
}
...
@@ -685,10 +694,13 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -685,10 +694,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
}
};
};
...
...
src/views/thinkTank/ReportDetail/index.vue
浏览文件 @
84861ffe
...
@@ -248,7 +248,7 @@ const handleDownloadDocument = async () => {
...
@@ -248,7 +248,7 @@ const handleDownloadDocument = async () => {
display
:
flex
;
display
:
flex
;
img
{
img
{
width
:
72
px
;
width
:
178
px
;
height
:
88px
;
height
:
88px
;
}
}
...
...
src/views/thinkTank/ReportDetail/policyTracking/index.vue
浏览文件 @
84861ffe
...
@@ -68,11 +68,11 @@
...
@@ -68,11 +68,11 @@
<div
class=
"info-content"
>
<div
class=
"info-content"
>
<div
class=
"info-item"
>
<div
class=
"info-item"
>
<div
class=
"info-text"
>
{{
"相关领域:"
}}
</div>
<div
class=
"info-text"
>
{{
"相关领域:"
}}
</div>
<div
class=
"info-right"
v-if=
"
box1DataItem && box1DataItem.domain
s"
>
<div
class=
"info-right"
v-if=
"
hasDomainTag
s"
>
<div
class=
"tag-box"
>
<div
class=
"tag-box"
>
<div
class=
"tag"
v-for=
"(
item, index) in box1DataItem.domains"
:key=
"index"
v-show=
"item"
>
{{
<div
class=
"tag"
v-for=
"(
name, index) in domainTags"
:key=
"name + '-' + index"
>
item
.
industryName
{{
name
}}
}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -155,6 +155,12 @@ const activeItemIndex = ref(0);
...
@@ -155,6 +155,12 @@ const activeItemIndex = ref(0);
const
pageSize
=
ref
(
10
);
const
pageSize
=
ref
(
10
);
// 当前选中 item 的数据
// 当前选中 item 的数据
const
box1DataItem
=
ref
(
null
);
const
box1DataItem
=
ref
(
null
);
const
domainTags
=
computed
(()
=>
{
const
domains
=
box1DataItem
.
value
?.
domains
;
const
list
=
Array
.
isArray
(
domains
)
?
domains
:
[];
return
list
.
map
((
d
)
=>
d
?.
industryName
).
filter
(
Boolean
);
});
const
hasDomainTags
=
computed
(()
=>
domainTags
.
value
.
length
>
0
);
// 整个页面容器,用于分页后滚回 wrap 顶部
// 整个页面容器,用于分页后滚回 wrap 顶部
const
wrapRef
=
ref
(
null
);
const
wrapRef
=
ref
(
null
);
const
reportUrl
=
ref
(
""
);
const
reportUrl
=
ref
(
""
);
...
...
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
浏览文件 @
84861ffe
...
@@ -79,7 +79,7 @@
...
@@ -79,7 +79,7 @@
<div
class=
"box5-footer"
>
<div
class=
"box5-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
<AiButton
/>
</div>
</div>
...
@@ -259,14 +259,21 @@ const props = defineProps({
...
@@ -259,14 +259,21 @@ const props = defineProps({
});
});
const
REPORT_ANALYSIS_TIP_BOX5
=
const
REPORT_ANALYSIS_TIP_BOX5
=
"智库报告关键词云,数据来源:美国兰德公司官网"
;
"智库报告关键词云,数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
tru
e
);
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox5ChartInterpretation
();
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
}
};
};
...
@@ -463,10 +470,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
...
@@ -463,10 +470,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
box5WordCloudKey
.
value
+=
1
;
}
}
// 刷新后默认展开 AI:数据就绪即触发解读
// 仅在用户打开 AI 面板时才请求解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
}
else
{
}
else
{
box5Data
.
value
=
[];
box5Data
.
value
=
[];
}
}
...
@@ -617,6 +621,10 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -617,6 +621,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -631,6 +639,7 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -631,6 +639,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
}
...
@@ -639,10 +648,13 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -639,10 +648,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -963,7 +975,7 @@ onMounted(() => {
...
@@ -963,7 +975,7 @@ onMounted(() => {
display
:
flex
;
display
:
flex
;
.left
{
.left
{
width
:
56
px
;
width
:
150
px
;
height
:
74px
;
height
:
74px
;
margin-top
:
8px
;
margin-top
:
8px
;
...
...
src/views/thinkTank/SurveyProjectView/index.vue
浏览文件 @
84861ffe
...
@@ -43,7 +43,7 @@
...
@@ -43,7 +43,7 @@
<div
class=
"box1-footer"
>
<div
class=
"box1-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
<AiButton
/>
</div>
</div>
...
@@ -185,14 +185,21 @@ const applySurveyProjectDocumentTitle = (title) => {
...
@@ -185,14 +185,21 @@ const applySurveyProjectDocumentTitle = (title) => {
};
};
const
REPORT_ANALYSIS_TIP_BOX5
=
const
REPORT_ANALYSIS_TIP_BOX5
=
"调查项目关键词云,数据来源:美国兰德公司官网"
;
"调查项目关键词云,数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
tru
e
);
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox5ChartInterpretation
();
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
}
};
};
const
searchOpinions
=
ref
(
''
);
const
searchOpinions
=
ref
(
''
);
...
@@ -403,10 +410,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
...
@@ -403,10 +410,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
box5WordCloudKey
.
value
+=
1
;
}
}
// 刷新后默认展开 AI:数据就绪即触发解读
// 仅在用户打开 AI 面板时才请求解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
}
else
{
}
else
{
box5Data
.
value
=
[];
box5Data
.
value
=
[];
}
}
...
@@ -548,6 +552,10 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -548,6 +552,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -562,6 +570,7 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -562,6 +570,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
}
...
@@ -570,10 +579,13 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -570,10 +579,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
}
};
};
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
浏览文件 @
84861ffe
<
template
>
<
template
>
<div
class=
"wrap"
>
<div
class=
"wrap"
ref=
"pageScrollRef"
>
<div
class=
"top"
>
<div
class=
"top"
>
<div
class=
"box1 box"
>
<div
class=
"box1 box"
>
<div
class=
"box-header"
>
<div
class=
"box-header"
>
...
@@ -228,6 +228,7 @@
...
@@ -228,6 +228,7 @@
</div>
</div>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"right-main"
>
<div
class=
"right-main"
>
<
template
v-if=
"policyList && policyList.length"
>
<div
class=
"right-main-item"
v-for=
"item in policyList"
:key=
"item.id"
>
<div
class=
"right-main-item"
v-for=
"item in policyList"
:key=
"item.id"
>
<div
class=
"item-left"
>
<div
class=
"item-left"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
<img
:src=
"item.imageUrl"
alt=
""
/>
...
@@ -240,7 +241,6 @@
...
@@ -240,7 +241,6 @@
<span
v-html=
"highlightPolicyText(`$
{item.reportDateDisplay}·${item.reportName}`)">
</span>
<span
v-html=
"highlightPolicyText(`$
{item.reportDateDisplay}·${item.reportName}`)">
</span>
</div>
</div>
<div
class=
"more"
@
click=
"toDetail(item)"
>
<div
class=
"more"
@
click=
"toDetail(item)"
>
<img
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open.png"
alt=
""
/>
<img
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open.png"
alt=
""
/>
</div>
</div>
</div>
</div>
...
@@ -269,11 +269,17 @@
...
@@ -269,11 +269,17 @@
<div></div>
<div></div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"policy-tracking-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共{{ total }}条政策建议
</div>
<div
class=
"info"
>
共{{ total }}条政策建议
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"10"
background
layout=
"prev, pager, next"
:total=
"total"
<el-pagination
:page-size=
"10"
:page-count=
"pageCount"
background
layout=
"prev, pager, next"
:total=
"total"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -309,6 +315,7 @@ import { useRouter } from "vue-router";
...
@@ -309,6 +315,7 @@ import { useRouter } from "vue-router";
const
router
=
useRouter
();
const
router
=
useRouter
();
const
loading
=
ref
(
false
);
const
loading
=
ref
(
false
);
const
pageScrollRef
=
ref
(
null
);
/** 与 AreaTag 一致的领域色(取 tag 的文字色) */
/** 与 AreaTag 一致的领域色(取 tag 的文字色) */
const
AREA_TAG_COLOR_BY_NAME
=
{
const
AREA_TAG_COLOR_BY_NAME
=
{
...
@@ -357,34 +364,55 @@ const POLICY_FILTER_ALL_AREA = "全部领域";
...
@@ -357,34 +364,55 @@ const POLICY_FILTER_ALL_AREA = "全部领域";
const
POLICY_FILTER_ALL_TIME
=
"全部时间"
;
const
POLICY_FILTER_ALL_TIME
=
"全部时间"
;
const
POLICY_FILTER_ALL_DEPT
=
"全部部门"
;
const
POLICY_FILTER_ALL_DEPT
=
"全部部门"
;
//
刷新后默认展示 3 个图表 AI 总结
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentPolicyPt1
=
ref
(
tru
e
);
const
isShowAiContentPolicyPt1
=
ref
(
fals
e
);
const
aiContentPolicyPt1
=
ref
(
""
);
const
aiContentPolicyPt1
=
ref
(
""
);
const
isPolicyPt1InterpretLoading
=
ref
(
false
);
const
isPolicyPt1InterpretLoading
=
ref
(
false
);
const
pt1AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowPolicyPt1
=
(
val
)
=>
{
const
handleSwitchAiContentShowPolicyPt1
=
(
val
)
=>
{
isShowAiContentPolicyPt1
.
value
=
val
;
isShowAiContentPolicyPt1
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchPolicyPtBox1ChartInterpretation
();
fetchPolicyPtBox1ChartInterpretation
();
}
else
{
if
(
pt1AiAbortController
.
value
)
{
pt1AiAbortController
.
value
.
abort
();
pt1AiAbortController
.
value
=
null
;
}
isPolicyPt1InterpretLoading
.
value
=
false
;
}
}
};
};
const
isShowAiContentPolicyPt2
=
ref
(
tru
e
);
const
isShowAiContentPolicyPt2
=
ref
(
fals
e
);
const
aiContentPolicyPt2
=
ref
(
""
);
const
aiContentPolicyPt2
=
ref
(
""
);
const
isPolicyPt2InterpretLoading
=
ref
(
false
);
const
isPolicyPt2InterpretLoading
=
ref
(
false
);
const
pt2AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowPolicyPt2
=
(
val
)
=>
{
const
handleSwitchAiContentShowPolicyPt2
=
(
val
)
=>
{
isShowAiContentPolicyPt2
.
value
=
val
;
isShowAiContentPolicyPt2
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchPolicyPtBox2ChartInterpretation
();
fetchPolicyPtBox2ChartInterpretation
();
}
else
{
if
(
pt2AiAbortController
.
value
)
{
pt2AiAbortController
.
value
.
abort
();
pt2AiAbortController
.
value
=
null
;
}
isPolicyPt2InterpretLoading
.
value
=
false
;
}
}
};
};
const
isShowAiContentPolicyPt3
=
ref
(
tru
e
);
const
isShowAiContentPolicyPt3
=
ref
(
fals
e
);
const
aiContentPolicyPt3
=
ref
(
""
);
const
aiContentPolicyPt3
=
ref
(
""
);
const
isPolicyPt3InterpretLoading
=
ref
(
false
);
const
isPolicyPt3InterpretLoading
=
ref
(
false
);
const
pt3AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowPolicyPt3
=
(
val
)
=>
{
const
handleSwitchAiContentShowPolicyPt3
=
(
val
)
=>
{
isShowAiContentPolicyPt3
.
value
=
val
;
isShowAiContentPolicyPt3
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchPolicyPtBox3ChartInterpretation
();
fetchPolicyPtBox3ChartInterpretation
();
}
else
{
if
(
pt3AiAbortController
.
value
)
{
pt3AiAbortController
.
value
.
abort
();
pt3AiAbortController
.
value
=
null
;
}
isPolicyPt3InterpretLoading
.
value
=
false
;
}
}
};
};
// import Img1 from "./images/img1.png";
// import Img1 from "./images/img1.png";
...
@@ -460,7 +488,7 @@ const box1YearList = ref([
...
@@ -460,7 +488,7 @@ const box1YearList = ref([
value
:
"2022"
value
:
"2022"
},
},
]);
]);
const
selectableYears
=
ref
([
"2025年"
,
"2024年"
,
"2023年"
,
"2022年"
,
"2021年"
,
"更早"
]);
const
selectableYears
=
ref
([
"202
6年"
,
"202
5年"
,
"2024年"
,
"2023年"
,
"2022年"
,
"2021年"
,
"更早"
]);
const
selectableDepartment
=
ref
([]);
const
selectableDepartment
=
ref
([]);
const
handleGetThinkPolicyIndustry
=
async
()
=>
{
const
handleGetThinkPolicyIndustry
=
async
()
=>
{
aiContentPolicyPt1
.
value
=
""
;
aiContentPolicyPt1
.
value
=
""
;
...
@@ -472,25 +500,48 @@ const handleGetThinkPolicyIndustry = async () => {
...
@@ -472,25 +500,48 @@ const handleGetThinkPolicyIndustry = async () => {
const
res
=
await
getThinkPolicyIndustry
(
parmas
);
const
res
=
await
getThinkPolicyIndustry
(
parmas
);
console
.
log
(
"提出建议领域分布"
,
res
);
console
.
log
(
"提出建议领域分布"
,
res
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
if
(
res
.
code
===
200
&&
res
.
data
)
{
const
list
=
Array
.
isArray
(
res
.
data
)
?
res
.
data
.
slice
(
0
,
7
)
:
[];
const
raw
=
Array
.
isArray
(
res
.
data
)
?
res
.
data
:
[];
if
(
!
list
.
length
)
{
if
(
!
raw
.
length
)
{
box1Data
.
value
=
[];
box1Data
.
value
=
[];
return
;
return
;
}
}
const
data
=
list
.
map
((
item
,
idx
)
=>
({
// 前端统一计算:总项数 + 百分比(保留两位小数),展示前 7,若超过 7 则追加“其他”
const
listSorted
=
raw
.
map
((
item
)
=>
({
industry
:
String
(
item
?.
industry
||
""
).
trim
(),
amount
:
Number
(
item
?.
amount
??
0
)
||
0
}))
.
filter
((
item
)
=>
item
.
industry
)
.
sort
((
a
,
b
)
=>
b
.
amount
-
a
.
amount
);
const
total
=
listSorted
.
reduce
((
s
,
it
)
=>
s
+
(
Number
(
it
.
amount
)
||
0
),
0
);
const
top
=
listSorted
.
slice
(
0
,
7
);
const
rest
=
listSorted
.
slice
(
7
);
const
topSum
=
top
.
reduce
((
s
,
it
)
=>
s
+
(
Number
(
it
.
amount
)
||
0
),
0
);
const
data
=
top
.
map
((
item
,
idx
)
=>
({
name
:
item
.
industry
,
name
:
item
.
industry
,
value
:
item
.
amount
,
value
:
item
.
amount
,
percent
:
item
.
percent
,
percent
:
total
>
0
?
((
item
.
amount
/
total
)
*
100
).
toFixed
(
2
)
:
"0.00"
,
color
:
getAreaTagColor
(
item
.
industry
,
idx
)
color
:
getAreaTagColor
(
item
.
industry
,
idx
)
}));
}));
// 超过 7 个领域时:追加“其他”
if
(
rest
.
length
>
0
&&
top
.
length
===
7
)
{
const
otherValue
=
Math
.
max
(
0
,
total
-
topSum
);
if
(
otherValue
>
0
)
{
data
.
push
({
name
:
"其他"
,
value
:
otherValue
,
percent
:
total
>
0
?
((
otherValue
/
total
)
*
100
).
toFixed
(
2
)
:
"0.00"
,
color
:
getAreaTagColor
(
"其他"
,
data
.
length
)
});
}
}
box1Data
.
value
=
data
;
box1Data
.
value
=
data
;
/* v-if 有数据后才挂载 #box1Chart,须等 DOM 更新后再 init echarts */
/* v-if 有数据后才挂载 #box1Chart,须等 DOM 更新后再 init echarts */
await
nextTick
();
await
nextTick
();
const
box1Chart
=
getPieChart
(
box1Data
.
value
);
const
box1Chart
=
getPieChart
(
box1Data
.
value
);
setChart
(
box1Chart
,
"box1Chart"
);
setChart
(
box1Chart
,
"box1Chart"
);
if
(
isShowAiContentPolicyPt1
.
value
)
{
// 仅在用户打开 AI 面板时才请求解读
fetchPolicyPtBox1ChartInterpretation
();
}
}
else
{
}
else
{
box1Data
.
value
=
[];
box1Data
.
value
=
[];
}
}
...
@@ -560,9 +611,7 @@ const handleGetPolicyAdviceDeptDistribution = async () => {
...
@@ -560,9 +611,7 @@ const handleGetPolicyAdviceDeptDistribution = async () => {
await
nextTick
();
await
nextTick
();
const
box2Chart
=
getPieChart
(
box2Data
.
value
);
const
box2Chart
=
getPieChart
(
box2Data
.
value
);
setChart
(
box2Chart
,
"box2Chart"
);
setChart
(
box2Chart
,
"box2Chart"
);
if
(
isShowAiContentPolicyPt2
.
value
)
{
// 仅在用户打开 AI 面板时才请求解读
fetchPolicyPtBox2ChartInterpretation
();
}
}
else
{
}
else
{
box2Data
.
value
=
[];
box2Data
.
value
=
[];
}
}
...
@@ -726,9 +775,7 @@ const handleGetThinkPolicyIndustryChange = async () => {
...
@@ -726,9 +775,7 @@ const handleGetThinkPolicyIndustryChange = async () => {
}
}
box3Data
.
value
=
frontendData
;
box3Data
.
value
=
frontendData
;
await
renderBox3Chart
();
await
renderBox3Chart
();
if
(
isShowAiContentPolicyPt3
.
value
)
{
// 仅在用户打开 AI 面板时才请求解读
fetchPolicyPtBox3ChartInterpretation
();
}
}
else
{
}
else
{
box3Data
.
value
=
{
title
:
[],
data
:
[]
};
box3Data
.
value
=
{
title
:
[],
data
:
[]
};
}
}
...
@@ -776,6 +823,10 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
...
@@ -776,6 +823,10 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
if
(
hasValidContent
||
isPolicyPt1InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isPolicyPt1InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
pt1AiAbortController
.
value
)
{
pt1AiAbortController
.
value
.
abort
();
}
pt1AiAbortController
.
value
=
new
AbortController
();
isPolicyPt1InterpretLoading
.
value
=
true
;
isPolicyPt1InterpretLoading
.
value
=
true
;
aiContentPolicyPt1
.
value
=
"解读生成中…"
;
aiContentPolicyPt1
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -791,6 +842,7 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
...
@@ -791,6 +842,7 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
pt1AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentPolicyPt1
,
chunk
);
appendAiInterpretationChunk
(
aiContentPolicyPt1
,
chunk
);
}
}
...
@@ -799,10 +851,13 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
...
@@ -799,10 +851,13 @@ const fetchPolicyPtBox1ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentPolicyPt1
.
value
=
text
||
aiContentPolicyPt1
.
value
||
"未返回有效解读内容"
;
aiContentPolicyPt1
.
value
=
text
||
aiContentPolicyPt1
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"政策追踪领域分布图表解读请求失败"
,
error
);
console
.
error
(
"政策追踪领域分布图表解读请求失败"
,
error
);
aiContentPolicyPt1
.
value
=
"解读加载失败"
;
aiContentPolicyPt1
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isPolicyPt1InterpretLoading
.
value
=
false
;
isPolicyPt1InterpretLoading
.
value
=
false
;
pt1AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -821,6 +876,10 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
...
@@ -821,6 +876,10 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
if
(
hasValidContent
||
isPolicyPt2InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isPolicyPt2InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
pt2AiAbortController
.
value
)
{
pt2AiAbortController
.
value
.
abort
();
}
pt2AiAbortController
.
value
=
new
AbortController
();
isPolicyPt2InterpretLoading
.
value
=
true
;
isPolicyPt2InterpretLoading
.
value
=
true
;
aiContentPolicyPt2
.
value
=
"解读生成中…"
;
aiContentPolicyPt2
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -836,6 +895,7 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
...
@@ -836,6 +895,7 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
pt2AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentPolicyPt2
,
chunk
);
appendAiInterpretationChunk
(
aiContentPolicyPt2
,
chunk
);
}
}
...
@@ -844,10 +904,13 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
...
@@ -844,10 +904,13 @@ const fetchPolicyPtBox2ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentPolicyPt2
.
value
=
text
||
aiContentPolicyPt2
.
value
||
"未返回有效解读内容"
;
aiContentPolicyPt2
.
value
=
text
||
aiContentPolicyPt2
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"政策追踪部门分布图表解读请求失败"
,
error
);
console
.
error
(
"政策追踪部门分布图表解读请求失败"
,
error
);
aiContentPolicyPt2
.
value
=
"解读加载失败"
;
aiContentPolicyPt2
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isPolicyPt2InterpretLoading
.
value
=
false
;
isPolicyPt2InterpretLoading
.
value
=
false
;
pt2AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -872,6 +935,10 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
...
@@ -872,6 +935,10 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
if
(
hasValidContent
||
isPolicyPt3InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isPolicyPt3InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
pt3AiAbortController
.
value
)
{
pt3AiAbortController
.
value
.
abort
();
}
pt3AiAbortController
.
value
=
new
AbortController
();
isPolicyPt3InterpretLoading
.
value
=
true
;
isPolicyPt3InterpretLoading
.
value
=
true
;
aiContentPolicyPt3
.
value
=
"解读生成中…"
;
aiContentPolicyPt3
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -889,6 +956,7 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
...
@@ -889,6 +956,7 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
pt3AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentPolicyPt3
,
chunk
);
appendAiInterpretationChunk
(
aiContentPolicyPt3
,
chunk
);
}
}
...
@@ -897,10 +965,13 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
...
@@ -897,10 +965,13 @@ const fetchPolicyPtBox3ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentPolicyPt3
.
value
=
text
||
aiContentPolicyPt3
.
value
||
"未返回有效解读内容"
;
aiContentPolicyPt3
.
value
=
text
||
aiContentPolicyPt3
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"政策追踪研究领域趋势图表解读请求失败"
,
error
);
console
.
error
(
"政策追踪研究领域趋势图表解读请求失败"
,
error
);
aiContentPolicyPt3
.
value
=
"解读加载失败"
;
aiContentPolicyPt3
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isPolicyPt3InterpretLoading
.
value
=
false
;
isPolicyPt3InterpretLoading
.
value
=
false
;
pt3AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -1124,16 +1195,23 @@ const POLICY_YEAR_EARLIER_START = 2000;
...
@@ -1124,16 +1195,23 @@ const POLICY_YEAR_EARLIER_START = 2000;
const
POLICY_YEAR_EARLIER_END
=
2020
;
const
POLICY_YEAR_EARLIER_END
=
2020
;
const
POLICY_YEAR_LABEL_RE
=
/^
(\d{4})
年$/
;
const
POLICY_YEAR_LABEL_RE
=
/^
(\d{4})
年$/
;
/** 勾选「全部时间」时的固定起止(结束日
按产品要求写死
) */
/** 勾选「全部时间」时的固定起止(结束日
为当前日期的前一天
) */
const
POLICY_ALL_TIME_START_DATE
=
"2000-01-01"
;
const
POLICY_ALL_TIME_START_DATE
=
"2000-01-01"
;
const
POLICY_ALL_TIME_END_DATE
=
"2025-12-31"
;
const
getPolicyAllTimeEndYmd
=
()
=>
{
const
d
=
new
Date
();
d
.
setDate
(
d
.
getDate
()
-
1
);
const
y
=
d
.
getFullYear
();
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
y
}
-
${
m
}
-
${
day
}
`
;
};
/**
/**
* 根据发布时间多选(如「2025年」「更早」)推导列表查询 startDate / endDate
* 根据发布时间多选(如「2025年」「更早」)推导列表查询 startDate / endDate
* - 仅选
「2025年」→ 2025-01-01 ~ 2025-12-31
* - 仅选
当前年(如「2026年」)→ 2026-01-01 ~ 昨天
* - 多选多个自然年 → 取最小年 01-01 与最大年 12-31 的包络
* - 多选多个自然年 → 取最小年 01-01 与最大年 12-31 的包络
* - 「更早」→ 2000-01-01 ~ 2020-12-31,可与具体年份合并为并集区间
* - 「更早」→ 2000-01-01 ~ 2020-12-31,可与具体年份合并为并集区间
* - 仅「全部时间」→ 2000-01-01 ~
2025-12-31(写死)
* - 仅「全部时间」→ 2000-01-01 ~
昨天
* - 未选任何项(无「全部时间」)→ 与顶部「近一年/两年/三年」一致
* - 未选任何项(无「全部时间」)→ 与顶部「近一年/两年/三年」一致
*/
*/
function
getPolicyListDateRangeFromYearList
(
labels
,
relativeYearYears
)
{
function
getPolicyListDateRangeFromYearList
(
labels
,
relativeYearYears
)
{
...
@@ -1143,7 +1221,7 @@ function getPolicyListDateRangeFromYearList(labels, relativeYearYears) {
...
@@ -1143,7 +1221,7 @@ function getPolicyListDateRangeFromYearList(labels, relativeYearYears) {
set
.
delete
(
POLICY_FILTER_ALL_TIME
);
set
.
delete
(
POLICY_FILTER_ALL_TIME
);
if
(
set
.
size
===
0
)
{
if
(
set
.
size
===
0
)
{
if
(
hasAllTime
)
{
if
(
hasAllTime
)
{
return
{
startDate
:
POLICY_ALL_TIME_START_DATE
,
endDate
:
POLICY_ALL_TIME_END_DATE
};
return
{
startDate
:
POLICY_ALL_TIME_START_DATE
,
endDate
:
getPolicyAllTimeEndYmd
()
};
}
}
const
years
=
Number
(
relativeYearYears
)
>
0
?
Number
(
relativeYearYears
)
:
1
;
const
years
=
Number
(
relativeYearYears
)
>
0
?
Number
(
relativeYearYears
)
:
1
;
return
{
startDate
:
getDateYearsAgo
(
years
),
endDate
:
getTodayYmd
()
};
return
{
startDate
:
getDateYearsAgo
(
years
),
endDate
:
getTodayYmd
()
};
...
@@ -1169,7 +1247,7 @@ function getPolicyListDateRangeFromYearList(labels, relativeYearYears) {
...
@@ -1169,7 +1247,7 @@ function getPolicyListDateRangeFromYearList(labels, relativeYearYears) {
}
}
return
{
return
{
startDate
:
`
${
minY
}
-01-01`
,
startDate
:
`
${
minY
}
-01-01`
,
endDate
:
`
${
maxY
}
-12-31`
endDate
:
maxY
===
new
Date
().
getFullYear
()
?
getPolicyAllTimeEndYmd
()
:
`
${
maxY
}
-12-31`
};
};
}
}
...
@@ -1200,10 +1278,11 @@ function normalizePolicyDomains(domains) {
...
@@ -1200,10 +1278,11 @@ function normalizePolicyDomains(domains) {
.
filter
(
Boolean
);
.
filter
(
Boolean
);
}
}
/** 列表行映射:左侧图
为 organizations[0].logoUrl,文案与法案/政令用接口字段名
*/
/** 列表行映射:左侧图
优先 coverImageUrl(兼容 coverImage),回退 organizations[0].logoUrl
*/
function
mapPolicyRowToView
(
row
)
{
function
mapPolicyRowToView
(
row
)
{
const
orgs
=
Array
.
isArray
(
row
.
organizations
)
?
row
.
organizations
:
[];
const
orgs
=
Array
.
isArray
(
row
.
organizations
)
?
row
.
organizations
:
[];
const
logoUrl
=
orgs
[
0
]?.
logoUrl
||
defaultNewsIcon
;
const
cover
=
row
?.
coverImageUrl
||
row
?.
coverImage
||
""
;
const
logoUrl
=
cover
||
orgs
[
0
]?.
logoUrl
||
defaultNewsIcon
;
const
reportDateDisplay
=
formatPolicyReportDateToCn
(
row
.
reportDate
);
const
reportDateDisplay
=
formatPolicyReportDateToCn
(
row
.
reportDate
);
return
{
return
{
...
row
,
...
row
,
...
@@ -1241,9 +1320,35 @@ const handleSwithSort = () => {
...
@@ -1241,9 +1320,35 @@ const handleSwithSort = () => {
};
};
const
currentPage
=
ref
(
1
);
const
currentPage
=
ref
(
1
);
const
pageCount
=
computed
(()
=>
{
const
size
=
10
;
const
t
=
Number
(
total
.
value
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
t
/
size
));
});
const
getScrollableParent
=
(
el
)
=>
{
let
cur
=
el
;
while
(
cur
&&
cur
!==
document
.
body
&&
cur
!==
document
.
documentElement
)
{
const
style
=
window
.
getComputedStyle
(
cur
);
const
overflowY
=
style
?.
overflowY
;
const
isScrollable
=
overflowY
===
"auto"
||
overflowY
===
"scroll"
;
if
(
isScrollable
&&
cur
.
scrollHeight
>
cur
.
clientHeight
+
1
)
{
return
cur
;
}
cur
=
cur
.
parentElement
;
}
return
null
;
};
const
scrollToTop
=
async
()
=>
{
await
nextTick
();
const
anchor
=
pageScrollRef
.
value
;
if
(
!
anchor
)
return
;
const
scrollEl
=
getScrollableParent
(
anchor
)
||
anchor
;
scrollEl
.
scrollTop
=
0
;
};
// 处理页码改变事件
// 处理页码改变事件
const
handleCurrentChange
=
page
=>
{
const
handleCurrentChange
=
page
=>
{
currentPage
.
value
=
page
;
currentPage
.
value
=
page
;
scrollToTop
();
handleGetThinkPolicy
();
handleGetThinkPolicy
();
};
};
...
@@ -1323,6 +1428,7 @@ onMounted(async () => {
...
@@ -1323,6 +1428,7 @@ onMounted(async () => {
.wrap
{
.wrap
{
width
:
100%
;
width
:
100%
;
height
:
100%
;
height
:
100%
;
overflow-y
:
auto
;
.policy-tracking-el-empty
{
.policy-tracking-el-empty
{
padding
:
0
;
padding
:
0
;
...
@@ -2051,6 +2157,8 @@ onMounted(async () => {
...
@@ -2051,6 +2157,8 @@ onMounted(async () => {
border-radius
:
10px
;
border-radius
:
10px
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
display
:
flex
;
flex-direction
:
column
;
.right-main
{
.right-main
{
margin-top
:
17px
;
margin-top
:
17px
;
...
@@ -2060,6 +2168,15 @@ onMounted(async () => {
...
@@ -2060,6 +2168,15 @@ onMounted(async () => {
padding-left
:
37px
;
padding-left
:
37px
;
padding-right
:
0
;
padding-right
:
0
;
max-height
:
1540px
;
max-height
:
1540px
;
flex
:
1
;
.right-empty
{
height
:
100%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.right-main-item
{
.right-main-item
{
// height: 154px;
// height: 154px;
...
@@ -2075,7 +2192,7 @@ onMounted(async () => {
...
@@ -2075,7 +2192,7 @@ onMounted(async () => {
display
:
flex
;
display
:
flex
;
.item-left
{
.item-left
{
width
:
57
px
;
width
:
156
px
;
height
:
77px
;
height
:
77px
;
img
{
img
{
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
浏览文件 @
84861ffe
import
*
as
echarts
from
'echarts'
import
*
as
echarts
from
'echarts'
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
// 按 AreaTag 的颜色规则映射到折线图配色(取 tag 的文字色)
const
parseHexToRgb
=
(
hex
)
=>
{
const
AREA_TAG_COLOR_BY_NAME
=
{
const
h
=
String
(
hex
||
''
).
replace
(
'#'
,
''
).
trim
()
'人工智能'
:
'rgba(245, 34, 45, 1)'
,
// tag1
if
(
h
.
length
!==
6
)
return
{
r
:
0
,
g
:
0
,
b
:
0
}
'生物科技'
:
'rgba(19, 168, 168, 1)'
,
// tag2
'新一代通信网络'
:
'rgba(5, 95, 194, 1)'
,
// tag3
// 兼容常见写法
'通信网络'
:
'rgba(5, 95, 194, 1)'
,
'量子科技'
:
'rgba(114, 46, 209, 1)'
,
// tag4
'新能源'
:
'rgba(82, 196, 26, 1)'
,
// tag5
'集成电路'
:
'rgba(22, 119, 255, 1)'
,
// tag6
'海洋'
:
'rgba(15, 120, 199, 1)'
,
// tag7
'先进制造'
:
'rgba(250, 173, 20, 1)'
,
// tag8
'新材料'
:
'rgba(250, 140, 22, 1)'
,
// tag9
'航空航天'
:
'rgba(47, 84, 235, 1)'
,
// tag10
'太空'
:
'rgba(47, 84, 235, 1)'
,
// tag11
'深海'
:
'rgba(73, 104, 161, 1)'
,
// tag12
'极地'
:
'rgba(133, 165, 255, 1)'
,
// tag13
'核'
:
'rgba(250, 84, 28, 1)'
,
// tag14
'其他'
:
'rgba(82, 196, 26, 1)'
// tag15
}
const
fallbackColorList
=
[
'rgba(5, 95, 194, 1)'
,
'rgba(245, 34, 45, 1)'
,
'rgba(19, 168, 168, 1)'
,
'rgba(250, 140, 22, 1)'
,
'rgba(114, 46, 209, 1)'
,
'rgba(82, 196, 26, 1)'
,
'rgba(22, 119, 255, 1)'
,
'rgba(250, 84, 28, 1)'
,
'rgba(47, 84, 235, 1)'
,
'rgba(133, 165, 255, 1)'
]
const
parseRgba
=
(
colorStr
)
=>
{
const
match
=
colorStr
.
match
(
/rgba
\((\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
(\.\d
+
)?)\)
/
)
if
(
match
)
{
return
{
return
{
r
:
parseInt
(
match
[
1
],
10
),
r
:
parseInt
(
h
.
slice
(
0
,
2
),
16
),
g
:
parseInt
(
match
[
2
],
10
),
g
:
parseInt
(
h
.
slice
(
2
,
4
),
16
),
b
:
parseInt
(
match
[
3
],
10
),
b
:
parseInt
(
h
.
slice
(
4
,
6
),
16
),
a
:
parseFloat
(
match
[
4
])
}
}
}
return
{
r
:
0
,
g
:
0
,
b
:
0
,
a
:
1
}
}
}
/**
/**
...
@@ -55,22 +19,14 @@ const getMultiLineChart = (chartInput) => {
...
@@ -55,22 +19,14 @@ const getMultiLineChart = (chartInput) => {
const
series
=
chartInput
.
data
||
[]
const
series
=
chartInput
.
data
||
[]
const
allNames
=
series
.
map
((
item
)
=>
item
.
name
)
const
allNames
=
series
.
map
((
item
)
=>
item
.
name
)
const
lineSize
=
Math
.
ceil
(
allNames
.
length
/
3
)
const
legendLine1
=
allNames
.
slice
(
0
,
lineSize
)
const
legendLine2
=
allNames
.
slice
(
lineSize
,
lineSize
*
2
)
const
legendLine3
=
allNames
.
slice
(
lineSize
*
2
)
const
xCount
=
Array
.
isArray
(
title
)
?
title
.
length
:
0
const
xCount
=
Array
.
isArray
(
title
)
?
title
.
length
:
0
const
labelFontSize
=
xCount
>
8
?
10
:
xCount
>
5
?
11
:
12
const
labelFontSize
=
xCount
>
8
?
10
:
xCount
>
5
?
11
:
12
const
labelRotate
=
xCount
>
6
?
28
:
0
const
labelRotate
=
xCount
>
6
?
28
:
0
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
const
baseColor
=
const
baseColor
=
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
||
'#055FC2'
item
.
color
||
const
{
r
,
g
,
b
}
=
parseHexToRgb
(
baseColor
)
AREA_TAG_COLOR_BY_NAME
[
item
.
name
]
||
fallbackColorList
[
index
%
fallbackColorList
.
length
]
||
`rgba(
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
, 1)`
const
{
r
,
g
,
b
}
=
parseRgba
(
baseColor
)
return
{
return
{
name
:
item
.
name
,
name
:
item
.
name
,
...
@@ -102,39 +58,31 @@ const getMultiLineChart = (chartInput) => {
...
@@ -102,39 +58,31 @@ const getMultiLineChart = (chartInput) => {
},
},
/* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */
/* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */
grid
:
{
grid
:
{
top
:
92
,
top
:
60
,
right
:
10
,
right
:
10
,
bottom
:
0
,
bottom
:
0
,
left
:
20
,
left
:
20
,
containLabel
:
true
containLabel
:
true
},
},
legend
:
[
legend
:
{
{
show
:
true
,
show
:
true
,
type
:
'plain'
,
type
:
'scroll'
,
data
:
legendLine1
,
orient
:
'horizontal'
,
top
:
4
,
left
:
8
,
left
:
'center'
,
top
:
6
,
width
:
'95%'
,
height
:
24
,
padding
:
[
0
,
24
,
0
,
24
],
icon
:
'circle'
,
icon
:
'circle'
,
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontSize
:
14
,
lineHeight
:
24
,
letterSpacing
:
0
,
align
:
'left'
,
color
:
'rgb(95, 101, 108)'
},
itemWidth
:
12
,
itemWidth
:
12
,
itemHeight
:
12
itemHeight
:
12
,
},
data
:
allNames
,
{
// 隐藏内置分页按钮与页码(使用 graphic 自定义左右箭头)
show
:
legendLine2
.
length
>
0
,
pageButtonPosition
:
'end'
,
type
:
'plain'
,
pageIconSize
:
0
,
data
:
legendLine2
,
pageButtonGap
:
0
,
top
:
30
,
pageFormatter
:
()
=>
''
,
left
:
'center'
,
pageTextStyle
:
{
fontSize
:
0
,
color
:
'transparent'
},
icon
:
'circle'
,
textStyle
:
{
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontWeight
:
400
,
...
@@ -143,28 +91,32 @@ const getMultiLineChart = (chartInput) => {
...
@@ -143,28 +91,32 @@ const getMultiLineChart = (chartInput) => {
letterSpacing
:
0
,
letterSpacing
:
0
,
align
:
'left'
,
align
:
'left'
,
color
:
'rgb(95, 101, 108)'
color
:
'rgb(95, 101, 108)'
}
},
},
itemWidth
:
12
,
graphic
:
[
itemHeight
:
12
},
{
{
show
:
legendLine3
.
length
>
0
,
type
:
'polygon'
,
type
:
'plain'
,
name
:
'__legend_prev__'
,
data
:
legendLine3
,
left
:
10
,
top
:
56
,
top
:
14
,
left
:
'center'
,
shape
:
{
points
:
[[
8
,
0
],
[
0
,
6
],
[
8
,
12
]]
},
icon
:
'circle'
,
style
:
{
fill
:
'rgb(95, 101, 108)'
},
textStyle
:
{
cursor
:
'pointer'
,
fontFamily
:
'Source Han Sans CN'
,
tooltip
:
{
show
:
false
},
fontWeight
:
400
,
silent
:
false
,
fontSize
:
14
,
z
:
100
lineHeight
:
24
,
letterSpacing
:
0
,
align
:
'left'
,
color
:
'rgb(95, 101, 108)'
},
},
itemWidth
:
12
,
{
itemHeight
:
12
type
:
'polygon'
,
name
:
'__legend_next__'
,
right
:
10
,
top
:
14
,
shape
:
{
points
:
[[
0
,
0
],
[
8
,
6
],
[
0
,
12
]]
},
style
:
{
fill
:
'rgb(95, 101, 108)'
},
cursor
:
'pointer'
,
tooltip
:
{
show
:
false
},
silent
:
false
,
z
:
100
}
}
],
],
// 不使用全局 color,改为每条 series 自己定色(与 AreaTag 一致)
// 不使用全局 color,改为每条 series 自己定色(与 AreaTag 一致)
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/piechart.js
浏览文件 @
84861ffe
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getPieChart
=
(
data
)
=>
{
const
getPieChart
=
(
data
)
=>
{
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
)
=>
{
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
,
index
)
=>
{
const
color
=
d
?.
color
const
color
=
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
if
(
!
color
)
return
d
return
{
return
{
...
d
,
...
d
,
itemStyle
:
{
...(
d
.
itemStyle
||
{}),
color
},
itemStyle
:
{
...(
d
?
.
itemStyle
||
{}),
color
},
// “飞线”(labelLine)跟随领域色
// “飞线”(labelLine)跟随领域色
labelLine
:
{
labelLine
:
{
...(
d
.
labelLine
||
{}),
...(
d
?
.
labelLine
||
{}),
lineStyle
:
{
...(
d
.
labelLine
?.
lineStyle
||
{}),
color
}
lineStyle
:
{
...(
d
?
.
labelLine
?.
lineStyle
||
{}),
color
}
}
}
}
}
})
})
...
@@ -29,10 +30,14 @@ const getPieChart = (data) => {
...
@@ -29,10 +30,14 @@ const getPieChart = (data) => {
alignTo
:
'edge'
,
alignTo
:
'edge'
,
formatter
:
params
=>
{
formatter
:
params
=>
{
const
name
=
params
.
name
||
""
;
const
name
=
params
.
name
||
""
;
const
value
=
params
.
value
??
""
;
const
value
=
Number
(
params
.
value
??
0
)
||
0
;
const
percent
=
params
.
percent
!=
null
?
Math
.
round
(
params
.
percent
)
:
0
;
const
rawPercent
=
params
?.
data
?.
percent
!=
null
?
params
.
data
.
percent
:
params
.
percent
;
const
percent
=
rawPercent
!=
null
?
Number
(
rawPercent
).
toFixed
(
2
)
:
"0.00"
;
return
`{name|
${
name
}
}\n{time|
${
percent
}
%}`
;
return
`{name|
${
name
}
}\n{time|
${
value
}
项
${
percent
}
%}`
;
},
},
minMargin
:
10
,
minMargin
:
10
,
edgeDistance
:
20
,
edgeDistance
:
20
,
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/CongressHearing/index.vue
浏览文件 @
84861ffe
...
@@ -72,8 +72,9 @@
...
@@ -72,8 +72,9 @@
</div>
-->
</div>
-->
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"card-content"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id"
>
<div
class=
"card-item"
>
<div
class=
"card-item"
>
...
@@ -87,7 +88,8 @@
...
@@ -87,7 +88,8 @@
<span
v-html=
"highlightText(item.time + ' · ' + item.content)"
></span>
<span
v-html=
"highlightText(item.time + ' · ' + item.content)"
></span>
<img
src=
"../images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
<img
src=
"../images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
</div>
</div>
<div
class=
"card-item-category"
>
<div
class=
"card-item-category"
v-if=
"Array.isArray(item.category) && item.category.some(v => String(v || '').trim())"
>
<AreaTag
v-for=
"(val, idx) in item.category"
:key=
"idx"
:tagName=
"val"
/>
<AreaTag
v-for=
"(val, idx) in item.category"
:key=
"idx"
:tagName=
"val"
/>
</div>
</div>
</div>
</div>
...
@@ -97,13 +99,19 @@
...
@@ -97,13 +99,19 @@
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
<div
class=
"info"
>
共 {{ total }} 篇国会听证会
共 {{ total }} 篇国会听证会
</div>
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"10"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"10"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -169,6 +177,10 @@ const props = defineProps({
...
@@ -169,6 +177,10 @@ const props = defineProps({
searchKeyword
:
{
searchKeyword
:
{
type
:
String
,
type
:
String
,
default
:
""
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
}
});
});
...
@@ -181,6 +193,12 @@ const emit = defineEmits([
...
@@ -181,6 +193,12 @@ const emit = defineEmits([
// 解构 props,保持模板里变量名不变
// 解构 props,保持模板里变量名不变
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
,
researchHearingList
,
hearingData
,
selectedYear
}
=
toRefs
(
props
);
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
,
researchHearingList
,
hearingData
,
selectedYear
}
=
toRefs
(
props
);
const
hasData
=
computed
(()
=>
Array
.
isArray
(
hearingData
.
value
)
&&
hearingData
.
value
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
10
;
const
t
=
Number
(
total
.
value
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
t
/
size
));
});
const
pageSize
=
10
;
const
pageSize
=
10
;
function
getDateYearsAgo
(
years
)
{
function
getDateYearsAgo
(
years
)
{
...
@@ -488,8 +506,11 @@ const handleToReportDetail = item => {
...
@@ -488,8 +506,11 @@ const handleToReportDetail = item => {
.right
{
.right
{
width
:
1224px
;
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
flex
:
1
;
...
@@ -498,6 +519,15 @@ const handleToReportDetail = item => {
...
@@ -498,6 +519,15 @@ const handleToReportDetail = item => {
background
:
rgba
(
255
,
255
,
255
,
1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
box-sizing
:
border-box
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
border-radius
:
10px
;
border-radius
:
10px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/SurveyForm/index.vue
浏览文件 @
84861ffe
...
@@ -52,8 +52,9 @@
...
@@ -52,8 +52,9 @@
</div>
-->
</div>
-->
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<div
class=
"footer-card-top"
>
...
@@ -67,13 +68,19 @@
...
@@ -67,13 +68,19 @@
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
<div
class=
"info"
>
共 {{ total }} 篇调查项目
共 {{ total }} 篇调查项目
</div>
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"12"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"12"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -81,7 +88,7 @@
...
@@ -81,7 +88,7 @@
</div>
</div>
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
,
toRefs
,
watch
}
from
"vue"
;
import
{
ref
,
toRefs
,
watch
,
computed
}
from
"vue"
;
import
{
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
RESOURCE_FILTER_ALL_TIME
,
...
@@ -126,6 +133,10 @@ const props = defineProps({
...
@@ -126,6 +133,10 @@ const props = defineProps({
searchKeyword
:
{
searchKeyword
:
{
type
:
String
,
type
:
String
,
default
:
""
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
}
});
});
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
// 解构 props,保持模板里变量名不变
// 解构 props,保持模板里变量名不变
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
}
=
toRefs
(
props
);
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
}
=
toRefs
(
props
);
const
hasData
=
computed
(()
=>
Array
.
isArray
(
curFooterList
.
value
)
&&
curFooterList
.
value
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
12
;
const
t
=
Number
(
total
.
value
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
t
/
size
));
});
const
selectedResearchIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedResearchIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
const
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
...
@@ -360,15 +377,27 @@ const handleToReportDetail = item => {
...
@@ -360,15 +377,27 @@ const handleToReportDetail = item => {
.right
{
.right
{
width
:
1284px
;
width
:
1284px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
flex
:
1
;
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
13px
;
gap
:
13px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
.footer-card
{
width
:
398px
;
width
:
398px
;
height
:
300px
;
height
:
300px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/ThinkTankReport/index.vue
浏览文件 @
84861ffe
...
@@ -52,8 +52,9 @@
...
@@ -52,8 +52,9 @@
</div>
-->
</div>
-->
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<div
class=
"footer-card-top"
>
...
@@ -67,13 +68,19 @@
...
@@ -67,13 +68,19 @@
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
<div
class=
"info"
>
共 {{ total }} 篇智库报告
共 {{ total }} 篇智库报告
</div>
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"12"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"12"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -81,7 +88,7 @@
...
@@ -81,7 +88,7 @@
</div>
</div>
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
,
toRefs
,
watch
}
from
"vue"
;
import
{
ref
,
toRefs
,
watch
,
computed
}
from
"vue"
;
import
{
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
RESOURCE_FILTER_ALL_TIME
,
...
@@ -126,6 +133,10 @@ const props = defineProps({
...
@@ -126,6 +133,10 @@ const props = defineProps({
searchKeyword
:
{
searchKeyword
:
{
type
:
String
,
type
:
String
,
default
:
""
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
}
});
});
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
// 解构 props,保持模板里变量名不变
// 解构 props,保持模板里变量名不变
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
}
=
toRefs
(
props
);
const
{
researchTypeList
,
researchTimeList
,
curFooterList
,
total
,
currentPage
}
=
toRefs
(
props
);
const
hasData
=
computed
(()
=>
Array
.
isArray
(
curFooterList
.
value
)
&&
curFooterList
.
value
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
12
;
const
t
=
Number
(
total
.
value
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
t
/
size
));
});
const
selectedResearchIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedResearchIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
const
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
...
@@ -360,15 +377,28 @@ const handleToReportDetail = item => {
...
@@ -360,15 +377,28 @@ const handleToReportDetail = item => {
}
}
.right
{
.right
{
width
:
1284px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
flex
:
1
;
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
13px
;
gap
:
13px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
.footer-card
{
width
:
398px
;
width
:
398px
;
height
:
300px
;
height
:
300px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/index.vue
浏览文件 @
84861ffe
...
@@ -53,18 +53,20 @@
...
@@ -53,18 +53,20 @@
<ThinkTankReport
v-if=
"isThinkTankReport"
:research-type-list=
"researchTypeList"
<ThinkTankReport
v-if=
"isThinkTankReport"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
:key=
"`智库报告-${tabResetKey}`"
:selected-filters=
"selectedFilters"
:research-time-list=
"researchTimeList"
:key=
"`智库报告-${tabResetKey}`"
:selected-filters=
"selectedFilters"
:cur-footer-list=
"curFooterList"
:total=
"total"
:current-page=
"currentPage"
:search-keyword=
"searchReport"
:cur-footer-list=
"curFooterList"
:total=
"total"
:current-page=
"currentPage"
:search-keyword=
"searchReport"
:loading=
"isThinkTankReportLoading"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
report-click=
"handleToReportDetail"
/>
@
report-click=
"handleToReportDetail"
/>
<CongressHearing
v-else-if=
"isCongressHearing"
:research-type-list=
"researchTypeList"
<CongressHearing
v-else-if=
"isCongressHearing"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
:key=
"`国会听证会-${tabResetKey}`"
:research-hearing-list=
"researchHearingList"
:research-time-list=
"researchTimeList"
:key=
"`国会听证会-${tabResetKey}`"
:research-hearing-list=
"researchHearingList"
:selected-filters=
"selectedFilters"
:selected-year=
"selectedYear"
:total=
"total"
:current-page=
"currentPage"
:selected-filters=
"selectedFilters"
:selected-year=
"selectedYear"
:total=
"total"
:current-page=
"currentPage"
:search-keyword=
"searchReport"
:hearing-data=
"hearingData"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
:search-keyword=
"searchReport"
:hearing-data=
"hearingData"
:loading=
"isCongressHearingLoading"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
report-click=
"handleToHearingDetail"
/>
@
report-click=
"handleToHearingDetail"
/>
<SurveyForm
v-else-if=
"isSurveyForm"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
<SurveyForm
v-else-if=
"isSurveyForm"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
:key=
"`调查项目-${tabResetKey}`"
:selected-filters=
"selectedFilters"
:cur-footer-list=
"curFooterProjectList"
:key=
"`调查项目-${tabResetKey}`"
:selected-filters=
"selectedFilters"
:cur-footer-list=
"curFooterProjectList"
:total=
"total"
:current-page=
"currentPage"
:search-keyword=
"searchReport"
:total=
"total"
:current-page=
"currentPage"
:search-keyword=
"searchReport"
:loading=
"isSurveyFormLoading"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
filter-change=
"(payload) => handleGetThinkDynamicsReport(payload)"
@
page-change=
"handleCurrentChange"
@
report-click=
"handleToProjectDetail"
/>
@
report-click=
"handleToProjectDetail"
/>
...
@@ -108,6 +110,10 @@ const isThinkTankReport = ref(true);
...
@@ -108,6 +110,10 @@ const isThinkTankReport = ref(true);
const
isSurveyForm
=
ref
(
false
);
const
isSurveyForm
=
ref
(
false
);
const
isCongressHearing
=
ref
(
false
);
const
isCongressHearing
=
ref
(
false
);
const
searchReport
=
ref
(
''
)
const
searchReport
=
ref
(
''
)
// 智库详情-智库动态:右侧列表 loading(居中显示)
const
isThinkTankReportLoading
=
ref
(
false
);
const
isSurveyFormLoading
=
ref
(
false
);
const
isCongressHearingLoading
=
ref
(
false
);
const
handleToReportDetail
=
(
item
)
=>
{
const
handleToReportDetail
=
(
item
)
=>
{
window
.
sessionStorage
.
setItem
(
'curTabName'
,
item
.
name
)
window
.
sessionStorage
.
setItem
(
'curTabName'
,
item
.
name
)
...
@@ -248,6 +254,10 @@ const handleChooseType = async (type) => {
...
@@ -248,6 +254,10 @@ const handleChooseType = async (type) => {
await
handleGetThinkDynamicsReport
()
await
handleGetThinkDynamicsReport
()
}
}
const
researchTimeList
=
ref
([
const
researchTimeList
=
ref
([
{
id
:
'2026年'
,
name
:
'2026年'
,
},
{
{
id
:
'2025年'
,
id
:
'2025年'
,
name
:
'2025年'
,
name
:
'2025年'
,
...
@@ -395,6 +405,11 @@ const handleGetThinkDynamicsReport = async (payload) => {
...
@@ -395,6 +405,11 @@ const handleGetThinkDynamicsReport = async (payload) => {
return
;
return
;
}
}
try
{
try
{
// 仅当前 tab 显示 loading,其它置 false,避免切换后残留
isThinkTankReportLoading
.
value
=
isThinkTankReport
.
value
;
isSurveyFormLoading
.
value
=
isSurveyForm
.
value
;
isCongressHearingLoading
.
value
=
isCongressHearing
.
value
;
const
strippedTime
=
stripAllTimeForRequest
(
nextFilters
.
researchTimeIds
||
[]);
const
strippedTime
=
stripAllTimeForRequest
(
nextFilters
.
researchTimeIds
||
[]);
const
allTimeIds
=
(
researchTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
);
const
allTimeIds
=
(
researchTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
);
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
...
@@ -415,6 +430,9 @@ const handleGetThinkDynamicsReport = async (payload) => {
...
@@ -415,6 +430,9 @@ const handleGetThinkDynamicsReport = async (payload) => {
thinkTankId
,
thinkTankId
,
pageNum
:
Math
.
max
(
0
,
(
currentPage
.
value
||
1
)),
pageNum
:
Math
.
max
(
0
,
(
currentPage
.
value
||
1
)),
pageSize
:
10
,
pageSize
:
10
,
// 国会听证会:排序语义与其它资源库相反(正序→desc,倒序→asc)
sortField
:
"createTime"
,
sortOrder
:
sort
.
value
===
true
?
"desc"
:
"asc"
,
domainIds
,
domainIds
,
startDate
,
startDate
,
...
@@ -481,6 +499,10 @@ const handleGetThinkDynamicsReport = async (payload) => {
...
@@ -481,6 +499,10 @@ const handleGetThinkDynamicsReport = async (payload) => {
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"获取智库动态报告error"
,
error
);
console
.
error
(
"获取智库动态报告error"
,
error
);
}
finally
{
isThinkTankReportLoading
.
value
=
false
;
isSurveyFormLoading
.
value
=
false
;
isCongressHearingLoading
.
value
=
false
;
}
}
};
};
...
...
src/views/thinkTank/ThinkTankDetail/thinkInfo/utils/piechart.js
浏览文件 @
84861ffe
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getPieChart
=
(
data
)
=>
{
const
getPieChart
=
(
data
)
=>
{
let
option
=
{
let
option
=
{
series
:
[
series
:
[
...
@@ -53,7 +55,10 @@ const getPieChart = (data) => {
...
@@ -53,7 +55,10 @@ const getPieChart = (data) => {
labelLinePoints
:
points
labelLinePoints
:
points
};
};
},
},
data
:
data
data
:
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
,
index
)
=>
({
...
d
,
itemStyle
:
{
...(
d
?.
itemStyle
||
{}),
color
:
d
?.
color
||
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
}
}))
}]
}]
}
}
return
option
return
option
...
...
src/views/thinkTank/ThinkTankDetail/thinkInfo/utils/treeMapChart.js
浏览文件 @
84861ffe
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getTreeMapChart
=
(
treemapData
)
=>
{
const
getTreeMapChart
=
(
treemapData
)
=>
{
const
list
=
Array
.
isArray
(
treemapData
)
?
treemapData
:
[]
const
dataWithColors
=
list
.
map
((
node
,
idx
)
=>
({
...
node
,
itemStyle
:
{
...(
node
?.
itemStyle
||
{}),
color
:
node
?.
itemStyle
?.
color
||
MUTICHARTCOLORS
[
idx
%
MUTICHARTCOLORS
.
length
]
}
}))
const
option
=
{
const
option
=
{
tooltip
:
{
tooltip
:
{
trigger
:
'item'
,
trigger
:
'item'
,
...
@@ -14,7 +25,7 @@ const getTreeMapChart = (treemapData) => {
...
@@ -14,7 +25,7 @@ const getTreeMapChart = (treemapData) => {
series
:
[
series
:
[
{
{
type
:
'treemap'
,
type
:
'treemap'
,
data
:
treemapData
,
data
:
dataWithColors
,
roam
:
false
,
roam
:
false
,
nodeClick
:
false
,
nodeClick
:
false
,
breadcrumb
:
{
breadcrumb
:
{
...
...
src/views/thinkTank/allThinkTank/index.vue
浏览文件 @
84861ffe
...
@@ -474,7 +474,7 @@ onMounted(async () => {
...
@@ -474,7 +474,7 @@ onMounted(async () => {
display
:
inline-flex
;
display
:
inline-flex
;
position
:
absolute
;
position
:
absolute
;
left
:
277
px
;
right
:
-8
px
;
bottom
:
208px
;
bottom
:
208px
;
background-color
:
rgba
(
255
,
77
,
79
,
1
);
background-color
:
rgba
(
255
,
77
,
79
,
1
);
align-items
:
center
;
align-items
:
center
;
...
...
src/views/thinkTank/components/HomeMainFooterMain.vue
浏览文件 @
84861ffe
...
@@ -36,8 +36,9 @@
...
@@ -36,8 +36,9 @@
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<div
class=
"footer-card-top"
>
...
@@ -51,11 +52,17 @@
...
@@ -51,11 +52,17 @@
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇智库报告
</div>
<div
class=
"info"
>
共 {{ total }} 篇智库报告
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"12"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"12"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"emit('page-change', $event)"
:current-page=
"currentPage"
/>
@
current-change=
"emit('page-change', $event)"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -64,20 +71,22 @@
...
@@ -64,20 +71,22 @@
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
computed
}
from
"vue"
;
import
{
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
RESOURCE_FILTER_ALL_TIME
,
normalizeExclusiveAllOption
normalizeExclusiveAllOption
}
from
"../utils/resourceLibraryFilters"
;
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
});
const
emit
=
defineEmits
([
const
emit
=
defineEmits
([
...
@@ -102,6 +111,13 @@ const formatDate = (str) => {
...
@@ -102,6 +111,13 @@ const formatDate = (str) => {
const
[
y
,
m
,
d
]
=
str
.
split
(
'T'
)[
0
].
split
(
'-'
)
const
[
y
,
m
,
d
]
=
str
.
split
(
'T'
)[
0
].
split
(
'-'
)
return
`
${
y
}
年
${
+
m
}
月
${
+
d
}
日`
return
`
${
y
}
年
${
+
m
}
月
${
+
d
}
日`
};
};
const
hasData
=
computed
(()
=>
Array
.
isArray
(
props
.
curFooterList
)
&&
props
.
curFooterList
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
12
;
const
total
=
Number
(
props
.
total
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
total
/
size
));
});
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
@@ -195,14 +211,25 @@ const formatDate = (str) => {
...
@@ -195,14 +211,25 @@ const formatDate = (str) => {
.right
{
.right
{
width
:
1284px
;
width
:
1284px
;
max-height
:
1377px
;
max-height
:
1377px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
width
:
1226px
;
width
:
1226px
;
flex
:
1
;
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
16px
16px
;
gap
:
16px
16px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
.footer-card
{
width
:
398px
;
width
:
398px
;
height
:
300px
;
height
:
300px
;
...
...
src/views/thinkTank/components/HomeMainFooterSurvey.vue
浏览文件 @
84861ffe
...
@@ -36,8 +36,9 @@
...
@@ -36,8 +36,9 @@
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<div
class=
"footer-card-top"
>
...
@@ -51,11 +52,17 @@
...
@@ -51,11 +52,17 @@
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇调查项目
</div>
<div
class=
"info"
>
共 {{ total }} 篇调查项目
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"12"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"12"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"emit('page-change', $event)"
:current-page=
"currentPage"
/>
@
current-change=
"emit('page-change', $event)"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -64,20 +71,22 @@
...
@@ -64,20 +71,22 @@
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
computed
}
from
"vue"
;
import
{
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
RESOURCE_FILTER_ALL_TIME
,
normalizeExclusiveAllOption
normalizeExclusiveAllOption
}
from
"../utils/resourceLibraryFilters"
;
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
});
const
formatDate
=
(
str
)
=>
{
const
formatDate
=
(
str
)
=>
{
if
(
!
str
)
return
''
if
(
!
str
)
return
''
...
@@ -101,6 +110,13 @@ const handleTimeGroupChange = (val) => {
...
@@ -101,6 +110,13 @@ const handleTimeGroupChange = (val) => {
emit
(
"update:selectedPubTimeList"
,
normalizeExclusiveAllOption
(
val
,
RESOURCE_FILTER_ALL_TIME
));
emit
(
"update:selectedPubTimeList"
,
normalizeExclusiveAllOption
(
val
,
RESOURCE_FILTER_ALL_TIME
));
emit
(
"filter-change"
);
emit
(
"filter-change"
);
};
};
const
hasData
=
computed
(()
=>
Array
.
isArray
(
props
.
curFooterList
)
&&
props
.
curFooterList
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
12
;
const
total
=
Number
(
props
.
total
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
total
/
size
));
});
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
@@ -170,14 +186,25 @@ const handleTimeGroupChange = (val) => {
...
@@ -170,14 +186,25 @@ const handleTimeGroupChange = (val) => {
.right
{
.right
{
width
:
1284px
;
width
:
1284px
;
max-height
:
1377px
;
max-height
:
1377px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
width
:
1226px
;
width
:
1226px
;
flex
:
1
;
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
16px
16px
;
gap
:
16px
16px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
.footer-card
{
width
:
398px
;
width
:
398px
;
...
...
src/views/thinkTank/components/ThinkTankCongressHearingOverview.vue
浏览文件 @
84861ffe
...
@@ -55,10 +55,10 @@
...
@@ -55,10 +55,10 @@
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"card-content"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id ?? index"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
>
<div
class=
"card-item"
>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
...
@@ -72,7 +72,8 @@
...
@@ -72,7 +72,8 @@
class=
"card-open-image"
/>
class=
"card-open-image"
/>
</div>
</div>
<div
class=
"card-item-category"
v-if=
"item.domains"
>
<div
class=
"card-item-category"
v-if=
"Array.isArray(item.domains) && item.domains.some(v => String(v || '').trim())"
>
<div
v-for=
"(value, index) in item.domains"
:key=
"`domain-$
{index}`">
<div
v-for=
"(value, index) in item.domains"
:key=
"`domain-$
{index}`">
<AreaTag
:key=
"`cat-$
{item.id}`" :tagName="value" />
<AreaTag
:key=
"`cat-$
{item.id}`" :tagName="value" />
</div>
</div>
...
@@ -81,16 +82,20 @@
...
@@ -81,16 +82,20 @@
</div>
</div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
<div
class=
"info"
>
共
{{
hearingData
.
length
}}
篇国会听证会
共 {{
total
}} 篇国会听证会
</div>
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"pageSize"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"pageSize"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"handlePageChange"
:current-page=
"currentPage"
/>
@
current-change=
"handlePageChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -121,7 +126,8 @@ const props = defineProps({
...
@@ -121,7 +126,8 @@ const props = defineProps({
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
hearingData
:
{
type
:
Array
,
default
:
()
=>
[]
},
hearingData
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
});
const
emit
=
defineEmits
([
const
emit
=
defineEmits
([
...
@@ -133,6 +139,12 @@ const emit = defineEmits([
...
@@ -133,6 +139,12 @@ const emit = defineEmits([
]);
]);
const
pageSize
=
10
;
const
pageSize
=
10
;
const
hasData
=
computed
(()
=>
Array
.
isArray
(
props
.
hearingData
)
&&
props
.
hearingData
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
Number
(
pageSize
||
10
)
||
10
;
const
total
=
Number
(
props
.
total
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
total
/
size
));
});
const
selectedResearchIds
=
computed
(()
=>
(
const
selectedResearchIds
=
computed
(()
=>
(
Array
.
isArray
(
props
.
selectedAreaList
)
&&
props
.
selectedAreaList
.
length
Array
.
isArray
(
props
.
selectedAreaList
)
&&
props
.
selectedAreaList
.
length
...
@@ -274,11 +286,13 @@ const handlePageChange = page => {
...
@@ -274,11 +286,13 @@ const handlePageChange = page => {
.right
{
.right
{
width
:
1224px
;
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
width
:
100%
;
width
:
100%
;
flex
:
1
;
display
:
flex
;
display
:
flex
;
background
:
rgba
(
255
,
255
,
255
,
1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
box-sizing
:
border-box
;
box-sizing
:
border-box
;
...
@@ -287,6 +301,15 @@ const handlePageChange = page => {
...
@@ -287,6 +301,15 @@ const handlePageChange = page => {
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
padding-right
:
36px
;
padding-right
:
36px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.card-content
{
.card-content
{
width
:
1211px
;
width
:
1211px
;
...
...
src/views/thinkTank/components/ThinkTankPolicyAdviceOverview.vue
浏览文件 @
84861ffe
...
@@ -36,8 +36,9 @@
...
@@ -36,8 +36,9 @@
</div>
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-box"
>
<template
v-if=
"hasData"
>
<div
class=
"card-content"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in list"
:key=
"item.id ?? index"
>
<div
v-for=
"(item, index) in list"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
@
click=
"emit('item-click', item)"
>
<div
class=
"card-item"
@
click=
"emit('item-click', item)"
>
...
@@ -79,11 +80,17 @@
...
@@ -79,11 +80,17 @@
<div
class=
"divider"
v-if=
"index !== list.length - 1"
></div>
<div
class=
"divider"
v-if=
"index !== list.length - 1"
></div>
</div>
</div>
</div>
</div>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
template
>
</div>
</div>
<div
class=
"right-footer"
>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共{{ total }}篇政策建议
</div>
<div
class=
"info"
>
共{{ total }}篇政策建议
</div>
<div
class=
"page-box"
>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"pageSize"
background
layout=
"prev, pager, next"
:total=
"total
"
<el-pagination
:page-size=
"pageSize"
:page-count=
"pageCount"
background
layout=
"prev, pager, next
"
@
current-change=
"p => emit('page-change', p)"
:current-page=
"currentPage"
/>
@
current-change=
"p => emit('page-change', p)"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
</div>
...
@@ -92,7 +99,7 @@
...
@@ -92,7 +99,7 @@
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
{
ref
,
computed
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
useRouter
}
from
"vue-router"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
import
{
import
{
...
@@ -103,13 +110,14 @@ import {
...
@@ -103,13 +110,14 @@ import {
stripAllTimeForRequest
stripAllTimeForRequest
}
from
"../utils/resourceLibraryFilters"
;
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
researchTypeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
researchTypeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
researchTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
researchTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
list
:
{
type
:
Array
,
default
:
()
=>
[]
},
list
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
},
currentPage
:
{
type
:
Number
,
default
:
1
},
pageSize
:
{
type
:
Number
,
default
:
7
},
pageSize
:
{
type
:
Number
,
default
:
7
},
loading
:
{
type
:
Boolean
,
default
:
false
},
});
});
const
emit
=
defineEmits
([
"filter-change"
,
"page-change"
,
"item-click"
]);
const
emit
=
defineEmits
([
"filter-change"
,
"page-change"
,
"item-click"
]);
...
@@ -158,6 +166,13 @@ const handleAdministrativeMoreClick = (ad) => {
...
@@ -158,6 +166,13 @@ const handleAdministrativeMoreClick = (ad) => {
const
selectedTypeIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedTypeIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
const
selectedYearIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
const
selectedYearIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
const
hasData
=
computed
(()
=>
Array
.
isArray
(
props
.
list
)
&&
props
.
list
.
length
>
0
);
const
pageCount
=
computed
(()
=>
{
const
size
=
Number
(
props
.
pageSize
||
7
)
||
7
;
const
total
=
Number
(
props
.
total
||
0
)
||
0
;
return
Math
.
max
(
1
,
Math
.
ceil
(
total
/
size
));
});
const
emitFilterToParent
=
()
=>
{
const
emitFilterToParent
=
()
=>
{
emit
(
"filter-change"
,
{
emit
(
"filter-change"
,
{
researchTypeIds
:
stripAllAreaForRequest
(
selectedTypeIds
.
value
),
researchTypeIds
:
stripAllAreaForRequest
(
selectedTypeIds
.
value
),
...
@@ -298,6 +313,8 @@ const handleYearGroupChange = (val) => {
...
@@ -298,6 +313,8 @@ const handleYearGroupChange = (val) => {
.right
{
.right
{
width
:
1224px
;
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
.card-box
{
...
@@ -311,6 +328,15 @@ const handleYearGroupChange = (val) => {
...
@@ -311,6 +328,15 @@ const handleYearGroupChange = (val) => {
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
padding-right
:
36px
;
padding-right
:
36px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.card-content
{
.card-content
{
width
:
1211px
;
width
:
1211px
;
height
:
1067px
;
height
:
1067px
;
...
@@ -332,7 +358,7 @@ const handleYearGroupChange = (val) => {
...
@@ -332,7 +358,7 @@ const handleYearGroupChange = (val) => {
.card-item-img
{
.card-item-img
{
width
:
56px
;
width
:
1
56px
;
height
:
77px
;
height
:
77px
;
margin-right
:
22px
;
margin-right
:
22px
;
flex-shrink
:
0
;
flex-shrink
:
0
;
...
...
src/views/thinkTank/index.vue
浏览文件 @
84861ffe
...
@@ -209,14 +209,10 @@
...
@@ -209,14 +209,10 @@
</el-carousel>
</el-carousel>
</OverviewMainBox>
</OverviewMainBox>
<RiskSignal
:list=
"warningList"
@
more-click=
"handleToMoreRiskSignal"
postDate=
"time"
name=
"title"
<RiskSignal
:list=
"warningList"
@
more-click=
"handleToMoreRiskSignal"
postDate=
"time"
name=
"title"
@
item-click=
"handleRiskSignalItemToManage"
/>
riskLevel=
"status"
@
item-click=
"handleRiskSignalItemToManage"
/>
<RiskSignalOverviewDetailDialog
<RiskSignalOverviewDetailDialog
v-model=
"isRiskDetailVisible"
:row=
"riskOverviewDetailRow"
name-field=
"title"
v-model=
"isRiskDetailVisible"
post-date-field=
"time"
risk-level-field=
"status"
/>
:row=
"riskOverviewDetailRow"
name-field=
"title"
post-date-field=
"time"
/>
</div>
</div>
<DivideHeader
id=
"position2"
class=
"divide-header"
:titleText=
"'资讯要闻'"
></DivideHeader>
<DivideHeader
id=
"position2"
class=
"divide-header"
:titleText=
"'资讯要闻'"
></DivideHeader>
<div
class=
"center-center"
>
<div
class=
"center-center"
>
...
@@ -353,7 +349,11 @@
...
@@ -353,7 +349,11 @@
</div>
</div>
</div>
</div>
<div
class=
"box8-main"
>
<div
class=
"box8-main"
:class=
"{ 'box8-main--empty': !hasBox8Data }"
>
<
template
v-if=
"!hasBox8Data"
>
<el-empty
class=
"box8-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</
template
>
<
template
v-else
>
<div
class=
"box8-main-item"
>
<div
class=
"box8-main-item"
>
<div
class=
"box8-item"
v-for=
"(item, index) in box8Data"
:key=
"index"
<div
class=
"box8-item"
v-for=
"(item, index) in box8Data"
:key=
"index"
@
click=
"handleBox8ToDataLibrary(item)"
>
@
click=
"handleBox8ToDataLibrary(item)"
>
...
@@ -361,21 +361,14 @@
...
@@ -361,21 +361,14 @@
:class=
"
{ itemBold1: index === 0, itemBold2: index === 1, itemBold3: index === 2 }">
:class=
"
{ itemBold1: index === 0, itemBold2: index === 1, itemBold3: index === 2 }">
{{
index
+
1
}}
{{
index
+
1
}}
</div>
</div>
<!-- <el-popover effect="dark" :content="item.clause" placement="top-start">
<template #reference> -->
<div
class=
"item-center"
<div
class=
"item-center"
:class=
"
{ itemBold1: index === 0, itemBold2: index === 1, itemBold3: index === 2 }">
:class=
"
{ itemBold1: index === 0, itemBold2: index === 1, itemBold3: index === 2 }">
{{
item
.
clause
}}
{{
item
.
clause
}}
</div>
</div>
<!-- </template>
</el-popover> -->
<!-- <div class="item-right">{{ `${item.count}份报告 >` }}</div> -->
<div
class=
"item-count"
>
{{
item
.
count
+
"份报告 >"
}}
</div>
<div
class=
"item-count"
>
{{
item
.
count
+
"份报告 >"
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -416,12 +409,14 @@
...
@@ -416,12 +409,14 @@
v-model:selectedAreaList=
"selectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedAreaList=
"selectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedPubTimeList=
"selectedPubTimeList"
@
filter-change=
"handleThinkTankReportFilterChange"
v-model:selectedPubTimeList=
"selectedPubTimeList"
@
filter-change=
"handleThinkTankReportFilterChange"
:cur-footer-list=
"curFooterList"
:total=
"total"
:current-page=
"currentPage"
:cur-footer-list=
"curFooterList"
:total=
"total"
:current-page=
"currentPage"
:loading=
"isResourceReportLoading"
@
report-click=
"handleToReportDetail"
@
page-change=
"handleCurrentChange"
/>
@
report-click=
"handleToReportDetail"
@
page-change=
"handleCurrentChange"
/>
<HomeMainFooterSurvey
v-else-if=
"activeCate === '调查项目'"
:area-list=
"areaList"
<HomeMainFooterSurvey
v-else-if=
"activeCate === '调查项目'"
:area-list=
"areaList"
v-model:selectedAreaList=
"surveySelectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedAreaList=
"surveySelectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedPubTimeList=
"surveySelectedPubTimeList"
@
filter-change=
"handleSurveyFilterChange"
v-model:selectedPubTimeList=
"surveySelectedPubTimeList"
@
filter-change=
"handleSurveyFilterChange"
:cur-footer-list=
"surveyFooterList"
:total=
"surveyTotal"
:current-page=
"surveyCurrentPage"
:cur-footer-list=
"surveyFooterList"
:total=
"surveyTotal"
:current-page=
"surveyCurrentPage"
:loading=
"isResourceSurveyLoading"
@
report-click=
"handleToSurveyProjectView"
@
page-change=
"handleSurveyCurrentChange"
/>
@
report-click=
"handleToSurveyProjectView"
@
page-change=
"handleSurveyCurrentChange"
/>
<ThinkTankCongressHearingOverview
v-else-if=
"activeCate === '国会听证会'"
:key=
"`congress-${resourceTabResetKey}`"
<ThinkTankCongressHearingOverview
v-else-if=
"activeCate === '国会听证会'"
:key=
"`congress-${resourceTabResetKey}`"
...
@@ -429,11 +424,13 @@
...
@@ -429,11 +424,13 @@
v-model:selectedAreaList=
"congressSelectedAreaList"
v-model:selectedAreaList=
"congressSelectedAreaList"
v-model:selectedPubTimeList=
"congressSelectedPubTimeList"
:total=
"congressTotal"
v-model:selectedPubTimeList=
"congressSelectedPubTimeList"
:total=
"congressTotal"
:current-page=
"congressCurrentPage"
@
filter-change=
"handleCongressFilterChange"
:current-page=
"congressCurrentPage"
@
filter-change=
"handleCongressFilterChange"
:loading=
"isResourceHearingLoading"
@
page-change=
"handleCongressCurrentChange"
@
report-click=
"handleToHearingDetail"
/>
@
page-change=
"handleCongressCurrentChange"
@
report-click=
"handleToHearingDetail"
/>
<ThinkTankPolicyAdviceOverview
v-else
:key=
"`policy-${resourceTabResetKey}`"
:research-type-list=
"areaList"
<ThinkTankPolicyAdviceOverview
v-else
:key=
"`policy-${resourceTabResetKey}`"
:research-type-list=
"areaList"
:research-time-list=
"pubTimeList"
:list=
"policyFooterList"
:total=
"policyTotal"
:research-time-list=
"pubTimeList"
:list=
"policyFooterList"
:total=
"policyTotal"
:current-page=
"policyCurrentPage"
:page-size=
"7"
@
filter-change=
"handlePolicyFilterChange"
:current-page=
"policyCurrentPage"
:page-size=
"7"
@
filter-change=
"handlePolicyFilterChange"
:loading=
"isResourcePolicyLoading"
@
page-change=
"handlePolicyCurrentChange"
/>
@
page-change=
"handlePolicyCurrentChange"
/>
</div>
</div>
...
@@ -462,7 +459,6 @@ import {
...
@@ -462,7 +459,6 @@ import {
getThinkTankList
,
getThinkTankList
,
getThinkTankRiskSignal
,
getThinkTankRiskSignal
,
getThinkTankReportDomainStats
,
getThinkTankReportDomainStats
,
getThinkTankPolicyIndustry
,
getThinkTankDonation
,
getThinkTankDonation
,
getAllThinkTankList
,
getAllThinkTankList
,
getThinkTankHot
,
getThinkTankHot
,
...
@@ -478,6 +474,7 @@ import {
...
@@ -478,6 +474,7 @@ import {
import
{
getPersonSummaryInfo
}
from
"@/api/common/index"
;
import
{
getPersonSummaryInfo
}
from
"@/api/common/index"
;
import
getMultiLineChart
from
"./utils/multiLineChart"
;
import
getMultiLineChart
from
"./utils/multiLineChart"
;
import
getPieChart
from
"./utils/piechart"
;
import
getPieChart
from
"./utils/piechart"
;
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
import
getSankeyChart
from
"./utils/sankey"
;
import
getSankeyChart
from
"./utils/sankey"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
defaultNewsIcon
from
"@/assets/icons/default-icon-news.png"
;
import
defaultNewsIcon
from
"@/assets/icons/default-icon-news.png"
;
...
@@ -524,33 +521,59 @@ const isRiskDetailVisible = ref(false);
...
@@ -524,33 +521,59 @@ const isRiskDetailVisible = ref(false);
const
statCountInfo
=
ref
([]);
const
statCountInfo
=
ref
([]);
const
pageSize
=
ref
(
15
)
const
pageSize
=
ref
(
15
)
const
totalAllItem
=
ref
(
0
)
const
totalAllItem
=
ref
(
0
)
const
isShowAiContentBox5
=
ref
(
true
);
// 资源库四个 tab 的列表 loading(右侧居中显示)
const
isResourceReportLoading
=
ref
(
false
);
const
isResourceSurveyLoading
=
ref
(
false
);
const
isResourceHearingLoading
=
ref
(
false
);
const
isResourcePolicyLoading
=
ref
(
false
);
const
isShowAiContentBox5
=
ref
(
false
);
const
aiContentBox5
=
ref
(
""
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox5ChartInterpretation
();
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
}
};
};
// 刷新后默认展示「领域分布情况」AI 总结
// 刷新后默认展示「领域分布情况」AI 总结
const
isShowAiContentBox6
=
ref
(
tru
e
);
const
isShowAiContentBox6
=
ref
(
fals
e
);
const
aiContentBox6
=
ref
(
""
);
const
aiContentBox6
=
ref
(
""
);
const
isBox6InterpretLoading
=
ref
(
false
);
const
isBox6InterpretLoading
=
ref
(
false
);
const
box6AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox6
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox6
=
(
val
)
=>
{
isShowAiContentBox6
.
value
=
val
;
isShowAiContentBox6
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox6ChartInterpretation
();
fetchBox6ChartInterpretation
();
}
else
{
if
(
box6AiAbortController
.
value
)
{
box6AiAbortController
.
value
.
abort
();
box6AiAbortController
.
value
=
null
;
}
isBox6InterpretLoading
.
value
=
false
;
}
}
};
};
// 刷新后默认展示「智库资金流向」AI 总结
// 刷新后默认展示「智库资金流向」AI 总结
const
isShowAiContentBox7
=
ref
(
tru
e
);
const
isShowAiContentBox7
=
ref
(
fals
e
);
const
aiContentBox7
=
ref
(
""
);
const
aiContentBox7
=
ref
(
""
);
const
isBox7InterpretLoading
=
ref
(
false
);
const
isBox7InterpretLoading
=
ref
(
false
);
const
box7AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox7
=
(
val
)
=>
{
const
handleSwitchAiContentShowBox7
=
(
val
)
=>
{
isShowAiContentBox7
.
value
=
val
;
isShowAiContentBox7
.
value
=
val
;
if
(
val
)
{
if
(
val
)
{
fetchBox7ChartInterpretation
();
fetchBox7ChartInterpretation
();
}
else
{
if
(
box7AiAbortController
.
value
)
{
box7AiAbortController
.
value
.
abort
();
box7AiAbortController
.
value
=
null
;
}
isBox7InterpretLoading
.
value
=
false
;
}
}
};
};
const
handleGetAllThinkTankList
=
async
()
=>
{
const
handleGetAllThinkTankList
=
async
()
=>
{
...
@@ -985,9 +1008,17 @@ function getDateMonthsAgo(months) {
...
@@ -985,9 +1008,17 @@ function getDateMonthsAgo(months) {
/** 自然年日期范围(传给 policyIndustryChange) */
/** 自然年日期范围(传给 policyIndustryChange) */
const
getBox5YearDateRange
=
year
=>
{
const
getBox5YearDateRange
=
year
=>
{
const
y
=
Number
(
year
);
const
y
=
Number
(
year
);
const
getYesterdayYmd
=
()
=>
{
const
d
=
new
Date
();
d
.
setDate
(
d
.
getDate
()
-
1
);
const
yy
=
d
.
getFullYear
();
const
mm
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
dd
=
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
yy
}
-
${
mm
}
-
${
dd
}
`
;
};
return
{
return
{
startDate
:
`
${
y
}
-01-01`
,
startDate
:
`
${
y
}
-01-01`
,
endDate
:
`
${
y
}
-12-31`
endDate
:
y
===
new
Date
().
getFullYear
()
?
getYesterdayYmd
()
:
`
${
y
}
-12-31`
};
};
};
};
...
@@ -1161,6 +1192,10 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -1161,6 +1192,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -1178,6 +1213,7 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -1178,6 +1213,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
}
...
@@ -1186,10 +1222,13 @@ const fetchBox5ChartInterpretation = async () => {
...
@@ -1186,10 +1222,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"图表解读请求失败"
,
error
);
console
.
error
(
"图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -1301,6 +1340,10 @@ const fetchBox6ChartInterpretation = async () => {
...
@@ -1301,6 +1340,10 @@ const fetchBox6ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox6InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox6InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box6AiAbortController
.
value
)
{
box6AiAbortController
.
value
.
abort
();
}
box6AiAbortController
.
value
=
new
AbortController
();
isBox6InterpretLoading
.
value
=
true
;
isBox6InterpretLoading
.
value
=
true
;
aiContentBox6
.
value
=
"解读生成中…"
;
aiContentBox6
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -1316,6 +1359,7 @@ const fetchBox6ChartInterpretation = async () => {
...
@@ -1316,6 +1359,7 @@ const fetchBox6ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box6AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox6
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox6
,
chunk
);
}
}
...
@@ -1324,10 +1368,13 @@ const fetchBox6ChartInterpretation = async () => {
...
@@ -1324,10 +1368,13 @@ const fetchBox6ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox6
.
value
=
text
||
aiContentBox6
.
value
||
"未返回有效解读内容"
;
aiContentBox6
.
value
=
text
||
aiContentBox6
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"领域分布图表解读请求失败"
,
error
);
console
.
error
(
"领域分布图表解读请求失败"
,
error
);
aiContentBox6
.
value
=
"解读加载失败"
;
aiContentBox6
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox6InterpretLoading
.
value
=
false
;
isBox6InterpretLoading
.
value
=
false
;
box6AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -1342,64 +1389,82 @@ const box6TankList = ref([
...
@@ -1342,64 +1389,82 @@ const box6TankList = ref([
}
}
]);
]);
function
transformToChartFormat
(
data
)
{
function
transformToChartFormat
(
data
)
{
// 按 AreaTag 的颜色规则映射到饼图配色(取 tag 的文字色)
// 兼容 /thinkTankReport/domainStats 返回:[{ year:'2025-Q1', areaList:[{industry,amount}...] }]
const
areaTagColorByName
=
{
const
rawList
=
Array
.
isArray
(
data
)
?
data
:
[];
"人工智能"
:
"rgba(245, 34, 45, 1)"
,
// tag1
const
amountByIndustry
=
new
Map
();
"生物科技"
:
"rgba(19, 168, 168, 1)"
,
// tag2
rawList
.
forEach
((
row
)
=>
{
"新一代通信网络"
:
"rgba(5, 95, 194, 1)"
,
// tag3
const
list
=
Array
.
isArray
(
row
?.
areaList
)
// 兼容常见写法
?
row
.
areaList
"通信网络"
:
"rgba(5, 95, 194, 1)"
,
:
Array
.
isArray
(
row
?.
industryList
)
"量子科技"
:
"rgba(114, 46, 209, 1)"
,
// tag4
?
row
.
industryList
"新能源"
:
"rgba(82, 196, 26, 1)"
,
// tag5
:
[];
"集成电路"
:
"rgba(22, 119, 255, 1)"
,
// tag6
list
.
forEach
((
it
)
=>
{
"海洋"
:
"rgba(15, 120, 199, 1)"
,
// tag7
const
name
=
String
(
it
?.
industry
||
""
).
trim
();
"先进制造"
:
"rgba(250, 173, 20, 1)"
,
// tag8
if
(
!
name
)
return
;
"新材料"
:
"rgba(250, 140, 22, 1)"
,
// tag9
const
amt
=
Number
(
it
?.
amount
??
0
)
||
0
;
"航空航天"
:
"rgba(47, 84, 235, 1)"
,
// tag10
amountByIndustry
.
set
(
name
,
(
amountByIndustry
.
get
(
name
)
||
0
)
+
amt
);
"太空"
:
"rgba(47, 84, 235, 1)"
,
// tag11
});
"深海"
:
"rgba(73, 104, 161, 1)"
,
// tag12
});
"极地"
:
"rgba(133, 165, 255, 1)"
,
// tag13
const
all
=
[...
amountByIndustry
.
entries
()]
"核"
:
"rgba(250, 84, 28, 1)"
,
// tag14
.
map
(([
name
,
value
])
=>
({
name
,
value
}))
"其他"
:
"rgba(82, 196, 26, 1)"
// tag15
.
sort
((
a
,
b
)
=>
(
Number
(
b
.
value
)
||
0
)
-
(
Number
(
a
.
value
)
||
0
));
};
const
total
=
all
.
reduce
((
sum
,
it
)
=>
sum
+
(
Number
(
it
.
value
)
||
0
),
0
);
const
top
=
all
.
slice
(
0
,
7
);
// 未命中 AreaTag 映射时的兜底色板
const
rest
=
all
.
slice
(
7
);
const
fallbackColorPalette
=
[
const
topSum
=
top
.
reduce
((
sum
,
it
)
=>
sum
+
(
Number
(
it
.
value
)
||
0
),
0
);
"rgba(5, 95, 194, 1)"
,
"rgba(245, 34, 45, 1)"
,
const
out
=
top
.
map
((
item
,
index
)
=>
({
"rgba(19, 168, 168, 1)"
,
name
:
item
.
name
,
"rgba(250, 140, 22, 1)"
,
value
:
item
.
value
,
"rgba(114, 46, 209, 1)"
,
percent
:
total
>
0
?
((
Number
(
item
.
value
)
||
0
)
/
total
*
100
).
toFixed
(
2
)
:
"0.00"
,
"rgba(82, 196, 26, 1)"
,
color
:
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
"rgba(22, 119, 255, 1)"
,
"rgba(250, 84, 28, 1)"
,
"rgba(47, 84, 235, 1)"
];
const
list
=
Array
.
isArray
(
data
)
?
data
.
slice
(
0
,
7
)
:
[];
return
list
.
map
((
item
,
index
)
=>
({
name
:
item
.
industry
,
value
:
item
.
amount
,
color
:
areaTagColorByName
[
item
.
industry
]
||
fallbackColorPalette
[
index
%
fallbackColorPalette
.
length
]
}));
}));
// 超过 7 个领域时,追加「其他」汇总;
<=
7
则不展示
if
(
rest
.
length
>
0
&&
top
.
length
===
7
)
{
const
otherValue
=
Math
.
max
(
0
,
total
-
topSum
);
if
(
otherValue
>
0
)
{
out
.
push
({
name
:
"其他"
,
value
:
otherValue
,
percent
:
total
>
0
?
(
otherValue
/
total
*
100
).
toFixed
(
2
)
:
"0.00"
,
color
:
MUTICHARTCOLORS
[
out
.
length
%
MUTICHARTCOLORS
.
length
]
});
}
}
return
out
;
}
}
// 政策建议领域分布
const
handleGetThinkTankPolicyIndustry
=
async
()
=>
{
const
getYesterdayYmd
=
()
=>
{
const
params
=
{
const
d
=
new
Date
();
year
:
box6selectetedYear
.
value
d
.
setDate
(
d
.
getDate
()
-
1
);
const
y
=
d
.
getFullYear
();
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
y
}
-
${
m
}
-
${
day
}
`
;
};
const
buildBox6DateRange
=
()
=>
{
const
y
=
Number
(
box6selectetedYear
.
value
);
const
currentY
=
new
Date
().
getFullYear
();
return
{
startDate
:
`
${
y
}
-01-01`
,
endDate
:
y
===
currentY
?
getYesterdayYmd
()
:
`
${
y
}
-12-31`
};
};
try
{
};
const
res
=
await
getThinkTankPolicyIndustry
(
params
);
console
.
log
(
"政策建议领域分布"
,
res
);
// 领域分布情况(/thinkTankReport/domainStats):前端汇总项数与占比,展示前 7 + 其他
const
handleGetThinkTankDomainStatsForBox6
=
async
()
=>
{
try
{
const
range
=
buildBox6DateRange
();
const
res
=
await
getThinkTankReportDomainStats
(
range
);
console
.
log
(
"领域分布情况(domainStats)"
,
res
);
if
(
res
.
code
===
200
&&
res
.
data
)
{
if
(
res
.
code
===
200
&&
res
.
data
)
{
box6Data
.
value
=
transformToChartFormat
(
res
.
data
);
box6Data
.
value
=
transformToChartFormat
(
res
.
data
);
console
.
log
(
transformToChartFormat
(
res
.
data
),
"datadatadata"
);
}
else
{
}
else
{
box6Data
.
value
=
[];
box6Data
.
value
=
[];
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"获取
政策建议领域分布
error"
,
error
);
console
.
error
(
"获取
领域分布情况(domainStats)
error"
,
error
);
box6Data
.
value
=
[];
box6Data
.
value
=
[];
}
}
};
};
...
@@ -1415,7 +1480,19 @@ const renderBox6Chart = () => {
...
@@ -1415,7 +1480,19 @@ const renderBox6Chart = () => {
const
selectParam
=
{
const
selectParam
=
{
moduleType
:
'科技智库报告'
,
moduleType
:
'科技智库报告'
,
key
:
2
,
key
:
2
,
selectedDate
:
JSON
.
stringify
([
box6selectetedYear
.
value
+
'-01-01'
,
box6selectetedYear
.
value
+
'-12-31'
]),
selectedDate
:
JSON
.
stringify
([
box6selectetedYear
.
value
+
"-01-01"
,
Number
(
box6selectetedYear
.
value
)
===
new
Date
().
getFullYear
()
?
(()
=>
{
const
d
=
new
Date
();
d
.
setDate
(
d
.
getDate
()
-
1
);
const
yy
=
d
.
getFullYear
();
const
mm
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
dd
=
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
yy
}
-
${
mm
}
-
${
dd
}
`
;
})()
:
box6selectetedYear
.
value
+
"-12-31"
]),
orgnizationName
:
box6selectetedTank
.
value
,
orgnizationName
:
box6selectetedTank
.
value
,
}
}
const
box6Chart
=
getPieChart
(
pieData
);
const
box6Chart
=
getPieChart
(
pieData
);
...
@@ -1430,7 +1507,7 @@ const handleBox6AreaChange = () => {
...
@@ -1430,7 +1507,7 @@ const handleBox6AreaChange = () => {
const
handleBox6
=
async
()
=>
{
const
handleBox6
=
async
()
=>
{
box6selectetedArea
.
value
=
"全部领域"
;
box6selectetedArea
.
value
=
"全部领域"
;
aiContentBox6
.
value
=
""
;
aiContentBox6
.
value
=
""
;
await
handleGetThinkTank
PolicyIndustry
();
await
handleGetThinkTank
DomainStatsForBox6
();
renderBox6Chart
();
renderBox6Chart
();
// 若 AI 面板已打开,让解读在首次加载时自动生成
// 若 AI 面板已打开,让解读在首次加载时自动生成
if
(
isShowAiContentBox6
.
value
)
{
if
(
isShowAiContentBox6
.
value
)
{
...
@@ -1459,6 +1536,21 @@ const hasBox7ChartData = computed(() => {
...
@@ -1459,6 +1536,21 @@ const hasBox7ChartData = computed(() => {
function
transformDataToSankey
(
inputData
)
{
function
transformDataToSankey
(
inputData
)
{
const
nodes
=
[];
const
nodes
=
[];
const
links
=
[];
const
links
=
[];
const
colorByName
=
new
Map
();
let
colorCursor
=
0
;
const
getNodeColor
=
(
name
)
=>
{
const
key
=
String
(
name
||
""
).
trim
();
if
(
!
key
)
{
return
MUTICHARTCOLORS
[
0
];
}
if
(
colorByName
.
has
(
key
))
{
return
colorByName
.
get
(
key
);
}
const
c
=
MUTICHARTCOLORS
[
colorCursor
%
MUTICHARTCOLORS
.
length
];
colorCursor
+=
1
;
colorByName
.
set
(
key
,
c
);
return
c
;
};
// 遍历输入数据
// 遍历输入数据
inputData
.
forEach
(
item
=>
{
inputData
.
forEach
(
item
=>
{
...
@@ -1466,7 +1558,7 @@ function transformDataToSankey(inputData) {
...
@@ -1466,7 +1558,7 @@ function transformDataToSankey(inputData) {
// 添加智库节点(如果尚未添加)
// 添加智库节点(如果尚未添加)
if
(
!
nodes
.
some
(
node
=>
node
.
name
===
thinkTankName
))
{
if
(
!
nodes
.
some
(
node
=>
node
.
name
===
thinkTankName
))
{
nodes
.
push
({
name
:
thinkTankName
});
nodes
.
push
({
name
:
thinkTankName
,
itemStyle
:
{
color
:
getNodeColor
(
thinkTankName
)
}
});
}
}
// 遍历捐赠来源
// 遍历捐赠来源
...
@@ -1477,12 +1569,12 @@ function transformDataToSankey(inputData) {
...
@@ -1477,12 +1569,12 @@ function transformDataToSankey(inputData) {
// 添加捐赠机构节点(如果尚未添加)
// 添加捐赠机构节点(如果尚未添加)
if
(
!
nodes
.
some
(
node
=>
node
.
name
===
institution
))
{
if
(
!
nodes
.
some
(
node
=>
node
.
name
===
institution
))
{
nodes
.
push
({
name
:
institution
});
nodes
.
push
({
name
:
institution
,
itemStyle
:
{
color
:
getNodeColor
(
institution
)
}
});
}
}
// 如果存在二级机构,也添加二级机构节点(如果尚未添加)
// 如果存在二级机构,也添加二级机构节点(如果尚未添加)
if
(
secondInstitution
&&
!
nodes
.
some
(
node
=>
node
.
name
===
secondInstitution
))
{
if
(
secondInstitution
&&
!
nodes
.
some
(
node
=>
node
.
name
===
secondInstitution
))
{
nodes
.
push
({
name
:
secondInstitution
});
nodes
.
push
({
name
:
secondInstitution
,
itemStyle
:
{
color
:
getNodeColor
(
secondInstitution
)
}
});
}
}
// 添加链接
// 添加链接
...
@@ -1570,6 +1662,10 @@ const fetchBox7ChartInterpretation = async () => {
...
@@ -1570,6 +1662,10 @@ const fetchBox7ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox7InterpretLoading
.
value
)
{
if
(
hasValidContent
||
isBox7InterpretLoading
.
value
)
{
return
;
return
;
}
}
if
(
box7AiAbortController
.
value
)
{
box7AiAbortController
.
value
.
abort
();
}
box7AiAbortController
.
value
=
new
AbortController
();
isBox7InterpretLoading
.
value
=
true
;
isBox7InterpretLoading
.
value
=
true
;
aiContentBox7
.
value
=
"解读生成中…"
;
aiContentBox7
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
const
chartPayload
=
{
...
@@ -1586,6 +1682,7 @@ const fetchBox7ChartInterpretation = async () => {
...
@@ -1586,6 +1682,7 @@ const fetchBox7ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
{
signal
:
box7AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox7
,
chunk
);
appendAiInterpretationChunk
(
aiContentBox7
,
chunk
);
}
}
...
@@ -1594,10 +1691,13 @@ const fetchBox7ChartInterpretation = async () => {
...
@@ -1594,10 +1691,13 @@ const fetchBox7ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox7
.
value
=
text
||
aiContentBox7
.
value
||
"未返回有效解读内容"
;
aiContentBox7
.
value
=
text
||
aiContentBox7
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
}
catch
(
error
)
{
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"智库资金流向图表解读请求失败"
,
error
);
console
.
error
(
"智库资金流向图表解读请求失败"
,
error
);
aiContentBox7
.
value
=
"解读加载失败"
;
aiContentBox7
.
value
=
"解读加载失败"
;
}
}
finally
{
}
finally
{
isBox7InterpretLoading
.
value
=
false
;
isBox7InterpretLoading
.
value
=
false
;
box7AiAbortController
.
value
=
null
;
}
}
};
};
...
@@ -1645,6 +1745,11 @@ const box8Data = ref([
...
@@ -1645,6 +1745,11 @@ const box8Data = ref([
// }
// }
]);
]);
const
hasBox8Data
=
computed
(()
=>
{
const
list
=
box8Data
.
value
;
return
Array
.
isArray
(
list
)
&&
list
.
length
>
0
;
});
const
box8selectetedYear
=
ref
(
1
);
const
box8selectetedYear
=
ref
(
1
);
const
box8YearList
=
ref
([
const
box8YearList
=
ref
([
{
{
...
@@ -1795,6 +1900,10 @@ const handleThinkTankReportFilterChange = () => {
...
@@ -1795,6 +1900,10 @@ const handleThinkTankReportFilterChange = () => {
};
};
const
pubTimeList
=
ref
([
const
pubTimeList
=
ref
([
{
id
:
2026
,
name
:
"2026"
},
{
{
id
:
2025
,
id
:
2025
,
name
:
"2025"
name
:
"2025"
...
@@ -1866,7 +1975,7 @@ const handleResourceLibrarySortChange = () => {
...
@@ -1866,7 +1975,7 @@ const handleResourceLibrarySortChange = () => {
}
else
if
(
activeCate
.
value
===
"政策建议"
)
{
}
else
if
(
activeCate
.
value
===
"政策建议"
)
{
handleGetThinkTankPolicyAdvice
();
handleGetThinkTankPolicyAdvice
();
}
else
if
(
activeCate
.
value
===
"国会听证会"
)
{
}
else
if
(
activeCate
.
value
===
"国会听证会"
)
{
return
;
handleGetThinkTankHearings
()
;
}
else
{
}
else
{
handleGetetThinkTankReport
();
handleGetetThinkTankReport
();
}
}
...
@@ -1881,6 +1990,7 @@ const toggleResourceLibrarySortPrefix = () => {
...
@@ -1881,6 +1990,7 @@ const toggleResourceLibrarySortPrefix = () => {
handleGetThinkTankPolicyAdvice
();
handleGetThinkTankPolicyAdvice
();
}
else
if
(
activeCate
.
value
===
"国会听证会"
)
{
}
else
if
(
activeCate
.
value
===
"国会听证会"
)
{
congressResourceSort
.
value
=
congressResourceSort
.
value
===
true
?
false
:
true
;
congressResourceSort
.
value
=
congressResourceSort
.
value
===
true
?
false
:
true
;
handleGetThinkTankHearings
();
}
else
{
}
else
{
sort
.
value
=
sort
.
value
===
true
?
false
:
true
;
sort
.
value
=
sort
.
value
===
true
?
false
:
true
;
handleGetetThinkTankReport
();
handleGetetThinkTankReport
();
...
@@ -1908,6 +2018,26 @@ const congressSelectedPubTimeList = ref([RESOURCE_FILTER_ALL_TIME]);
...
@@ -1908,6 +2018,26 @@ const congressSelectedPubTimeList = ref([RESOURCE_FILTER_ALL_TIME]);
const
congressCurrentPage
=
ref
(
1
);
const
congressCurrentPage
=
ref
(
1
);
const
congressTotal
=
ref
(
0
);
const
congressTotal
=
ref
(
0
);
const
scrollToResourceLibraryHeader
=
()
=>
{
const
header
=
document
.
getElementById
(
"position4"
);
const
wrapper
=
containerRef
.
value
;
if
(
!
header
||
!
wrapper
)
return
;
const
wrapperRect
=
wrapper
.
getBoundingClientRect
();
const
headerRect
=
header
.
getBoundingClientRect
();
// 滚动到「资源库」标题上方一点点(在 home-wrapper 这个滚动容器内)
const
delta
=
headerRect
.
top
-
wrapperRect
.
top
;
const
targetTop
=
(
wrapper
.
scrollTop
||
0
)
+
delta
-
20
;
wrapper
.
scrollTo
({
top
:
Math
.
max
(
0
,
targetTop
),
behavior
:
"smooth"
});
};
const
scrollToResourceLibraryHeaderAfterRender
=
async
()
=>
{
// 等待分页数据渲染完成(对齐风险信号管理:nextTick 后再滚动)
await
nextTick
();
await
new
Promise
((
resolve
)
=>
requestAnimationFrame
(
resolve
));
scrollToResourceLibraryHeader
();
};
const
handleCongressFilterChange
=
()
=>
{
const
handleCongressFilterChange
=
()
=>
{
congressCurrentPage
.
value
=
1
;
congressCurrentPage
.
value
=
1
;
handleGetThinkTankHearings
();
handleGetThinkTankHearings
();
...
@@ -1916,6 +2046,8 @@ const handleCongressFilterChange = () => {
...
@@ -1916,6 +2046,8 @@ const handleCongressFilterChange = () => {
const
handleCongressCurrentChange
=
(
page
)
=>
{
const
handleCongressCurrentChange
=
(
page
)
=>
{
congressCurrentPage
.
value
=
page
;
congressCurrentPage
.
value
=
page
;
handleGetThinkTankHearings
();
handleGetThinkTankHearings
();
// 分页后让滚动条回到资源库标题上方一点点
scrollToResourceLibraryHeaderAfterRender
();
};
};
const
handleSurveyFilterChange
=
()
=>
{
const
handleSurveyFilterChange
=
()
=>
{
...
@@ -1926,9 +2058,11 @@ const handleSurveyFilterChange = () => {
...
@@ -1926,9 +2058,11 @@ const handleSurveyFilterChange = () => {
const
handleSurveyCurrentChange
=
page
=>
{
const
handleSurveyCurrentChange
=
page
=>
{
surveyCurrentPage
.
value
=
page
;
surveyCurrentPage
.
value
=
page
;
handleGetThinkTankSurvey
();
handleGetThinkTankSurvey
();
scrollToResourceLibraryHeaderAfterRender
();
};
};
const
handleGetThinkTankSurvey
=
async
()
=>
{
const
handleGetThinkTankSurvey
=
async
()
=>
{
isResourceSurveyLoading
.
value
=
true
;
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
stripAllTimeForRequest
(
surveySelectedPubTimeList
.
value
),
stripAllTimeForRequest
(
surveySelectedPubTimeList
.
value
),
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
)
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
)
...
@@ -1961,9 +2095,12 @@ const handleGetThinkTankSurvey = async () => {
...
@@ -1961,9 +2095,12 @@ const handleGetThinkTankSurvey = async () => {
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"获取调查项目 error"
,
error
);
console
.
error
(
"获取调查项目 error"
,
error
);
}
finally
{
isResourceSurveyLoading
.
value
=
false
;
}
}
};
};
const
handleGetThinkTankHearings
=
async
()
=>
{
const
handleGetThinkTankHearings
=
async
()
=>
{
isResourceHearingLoading
.
value
=
true
;
try
{
try
{
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
stripAllTimeForRequest
(
congressSelectedPubTimeList
.
value
),
stripAllTimeForRequest
(
congressSelectedPubTimeList
.
value
),
...
@@ -1973,6 +2110,9 @@ const handleGetThinkTankHearings = async () => {
...
@@ -1973,6 +2110,9 @@ const handleGetThinkTankHearings = async () => {
pageNum
:
congressCurrentPage
.
value
,
pageNum
:
congressCurrentPage
.
value
,
pageSize
:
10
,
pageSize
:
10
,
sortFun
:
congressResourceSort
.
value
===
true
,
sortFun
:
congressResourceSort
.
value
===
true
,
// 国会听证会:排序语义与其它资源库相反(正序→desc,倒序→asc)
sortField
:
"createTime"
,
sortOrder
:
congressResourceSort
.
value
===
true
?
"desc"
:
"asc"
,
domainIds
:
(()
=>
{
domainIds
:
(()
=>
{
const
areas
=
stripAllAreaForRequest
(
congressSelectedAreaList
.
value
);
const
areas
=
stripAllAreaForRequest
(
congressSelectedAreaList
.
value
);
const
allAreaIds
=
(
areaList
.
value
||
[]).
map
((
a
)
=>
a
.
id
);
const
allAreaIds
=
(
areaList
.
value
||
[]).
map
((
a
)
=>
a
.
id
);
...
@@ -1994,6 +2134,8 @@ const handleGetThinkTankHearings = async () => {
...
@@ -1994,6 +2134,8 @@ const handleGetThinkTankHearings = async () => {
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"获取调查项目 error"
,
error
);
console
.
error
(
"获取调查项目 error"
,
error
);
}
finally
{
isResourceHearingLoading
.
value
=
false
;
}
}
};
};
// ===== 政策建议:独立状态(不影响智库报告/调查项目)=====
// ===== 政策建议:独立状态(不影响智库报告/调查项目)=====
...
@@ -2016,9 +2158,11 @@ const handlePolicyFilterChange = payload => {
...
@@ -2016,9 +2158,11 @@ const handlePolicyFilterChange = payload => {
const
handlePolicyCurrentChange
=
page
=>
{
const
handlePolicyCurrentChange
=
page
=>
{
policyCurrentPage
.
value
=
page
;
policyCurrentPage
.
value
=
page
;
handleGetThinkTankPolicyAdvice
();
handleGetThinkTankPolicyAdvice
();
scrollToResourceLibraryHeaderAfterRender
();
};
};
const
handleGetThinkTankPolicyAdvice
=
async
()
=>
{
const
handleGetThinkTankPolicyAdvice
=
async
()
=>
{
isResourcePolicyLoading
.
value
=
true
;
const
strippedPolicyYears
=
stripAllTimeForRequest
(
policySelectedYearIds
.
value
);
const
strippedPolicyYears
=
stripAllTimeForRequest
(
policySelectedYearIds
.
value
);
const
allPubTimeIds
=
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
);
const
allPubTimeIds
=
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
);
/** 与智库报告一致:仅「全部时间」或选满所有具体年份 → 不按 years 狭义过滤 */
/** 与智库报告一致:仅「全部时间」或选满所有具体年份 → 不按 years 狭义过滤 */
...
@@ -2056,7 +2200,8 @@ const handleGetThinkTankPolicyAdvice = async () => {
...
@@ -2056,7 +2200,8 @@ const handleGetThinkTankPolicyAdvice = async () => {
name
:
item
.
title
,
name
:
item
.
title
,
reportName
:
item
.
reportName
,
reportName
:
item
.
reportName
,
times
:
item
.
date
,
times
:
item
.
date
,
imageUrl
:
item
.
coverImage
,
// 列表图片:优先 coverImageUrl,兼容旧字段 coverImage
imageUrl
:
item
.
coverImageUrl
||
item
.
coverImage
,
tagList
:
item
.
domains
||
[],
tagList
:
item
.
domains
||
[],
thinkTankName
:
item
.
thinkTankName
??
null
,
thinkTankName
:
item
.
thinkTankName
??
null
,
reportId
:
item
.
reportId
??
item
.
id
,
reportId
:
item
.
reportId
??
item
.
id
,
...
@@ -2079,6 +2224,8 @@ const handleGetThinkTankPolicyAdvice = async () => {
...
@@ -2079,6 +2224,8 @@ const handleGetThinkTankPolicyAdvice = async () => {
policyTotal
.
value
=
0
;
policyTotal
.
value
=
0
;
console
.
error
(
"获取政策建议 error"
,
error
);
console
.
error
(
"获取政策建议 error"
,
error
);
ElMessage
.
warning
(
"获取政策建议失败"
);
ElMessage
.
warning
(
"获取政策建议失败"
);
}
finally
{
isResourcePolicyLoading
.
value
=
false
;
}
}
};
};
// 处理页码改变事件
// 处理页码改变事件
...
@@ -2086,6 +2233,7 @@ const handleCurrentChange = page => {
...
@@ -2086,6 +2233,7 @@ const handleCurrentChange = page => {
console
.
log
(
page
,
"pagepagepage"
);
console
.
log
(
page
,
"pagepagepage"
);
currentPage
.
value
=
page
;
currentPage
.
value
=
page
;
handleGetetThinkTankReport
();
handleGetetThinkTankReport
();
scrollToResourceLibraryHeaderAfterRender
();
};
};
function
arrayToString
(
arr
)
{
function
arrayToString
(
arr
)
{
return
arr
.
reduce
((
acc
,
item
)
=>
{
return
arr
.
reduce
((
acc
,
item
)
=>
{
...
@@ -2098,6 +2246,7 @@ function arrayToString(arr) {
...
@@ -2098,6 +2246,7 @@ function arrayToString(arr) {
//获取智库报告
//获取智库报告
const
handleGetetThinkTankReport
=
async
()
=>
{
const
handleGetetThinkTankReport
=
async
()
=>
{
isResourceReportLoading
.
value
=
true
;
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
stripAllTimeForRequest
(
selectedPubTimeList
.
value
),
stripAllTimeForRequest
(
selectedPubTimeList
.
value
),
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
)
(
pubTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
)
...
@@ -2128,6 +2277,8 @@ const handleGetetThinkTankReport = async () => {
...
@@ -2128,6 +2277,8 @@ const handleGetetThinkTankReport = async () => {
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"获取智库报告error"
,
error
);
console
.
error
(
"获取智库报告error"
,
error
);
}
finally
{
isResourceReportLoading
.
value
=
false
;
}
}
};
};
...
@@ -2297,9 +2448,8 @@ onMounted(async () => {
...
@@ -2297,9 +2448,8 @@ onMounted(async () => {
handleGetNewReport
();
handleGetNewReport
();
handleGetThinkTankRiskSignal
();
handleGetThinkTankRiskSignal
();
// 先
拉到图表数据,再打开 AI 面板并触发解读,避免初始为空导致“无内容”
// 先
把图表数据准备好;AI 解读仅在用户悬停打开面板时触发
await
handleBox5
(
box5selectetedYear
.
value
);
await
handleBox5
(
box5selectetedYear
.
value
);
handleSwitchAiContentShowBox5
(
true
);
// 先把图表数据准备好,避免用户悬浮太快触发解读但数据未就绪
// 先把图表数据准备好,避免用户悬浮太快触发解读但数据未就绪
await
handleBox6
();
await
handleBox6
();
await
handleBox7
();
await
handleBox7
();
...
@@ -2639,7 +2789,7 @@ onBeforeUnmount(() => {
...
@@ -2639,7 +2789,7 @@ onBeforeUnmount(() => {
display
:
inline-flex
;
display
:
inline-flex
;
position
:
absolute
;
position
:
absolute
;
left
:
233
px
;
right
:
-8
px
;
bottom
:
208px
;
bottom
:
208px
;
background-color
:
rgba
(
255
,
77
,
79
,
1
);
background-color
:
rgba
(
255
,
77
,
79
,
1
);
align-items
:
center
;
align-items
:
center
;
...
@@ -4165,6 +4315,22 @@ onBeforeUnmount(() => {
...
@@ -4165,6 +4315,22 @@ onBeforeUnmount(() => {
height
:
412px
;
height
:
412px
;
&
.box8-main--empty
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
:deep
(
.el-empty__image
)
{
margin-bottom
:
0
;
}
}
.box8-el-empty
{
padding
:
0
;
margin
:
0
;
}
.box8-main-item
{
.box8-main-item
{
margin
:
0
auto
;
margin
:
0
auto
;
margin-top
:
5px
;
margin-top
:
5px
;
...
@@ -4612,6 +4778,7 @@ onBeforeUnmount(() => {
...
@@ -4612,6 +4778,7 @@ onBeforeUnmount(() => {
<
style
lang=
"scss"
>
<
style
lang=
"scss"
>
/* 弹窗打开时禁用轮播箭头穿透(样式仅作用于智库页结构) */
/* 弹窗打开时禁用轮播箭头穿透(样式仅作用于智库页结构) */
.home-main.is-risk-detail-open
{
.home-main.is-risk-detail-open
{
.box1-left
,
.box1-left
,
.box1-right
{
.box1-right
{
pointer-events
:
none
;
pointer-events
:
none
;
...
...
src/views/thinkTank/reportOriginal/index.vue
浏览文件 @
84861ffe
...
@@ -49,7 +49,7 @@
...
@@ -49,7 +49,7 @@
<img
class=
"translate-icon"
src=
"../ReportDetail/images/image-translate.png"
alt=
""
<img
class=
"translate-icon"
src=
"../ReportDetail/images/image-translate.png"
alt=
""
style=
"width: 16px; height: 16px; max-width: 16px; max-height: 16px; display: block; object-fit: contain;"
/>
style=
"width: 16px; height: 16px; max-width: 16px; max-height: 16px; display: block; object-fit: contain;"
/>
</div>
</div>
<div
class=
"translate-text"
>
{{
"显示
原
文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示
译
文"
}}
</div>
</div>
</div>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"icon"
>
<div
class=
"icon"
>
...
@@ -61,13 +61,15 @@
...
@@ -61,13 +61,15 @@
</div>
</div>
</div>
</div>
<div
class=
"report-box"
>
<div
class=
"report-box"
>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<!-- 英文原文:始终展示;关闭「显示译文」时占满宽度 -->
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlEnWithPage">
</div>
<pdf
:key=
"`left-pdf-$
{valueSwitch ? 'split' : 'full'}`" ref="leftPdfRef" :pdfUrl="reportUrlEnWithPage"
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`" ref="rightPdfRef" :pdfUrl="reportUrlWithPage"
class="pdf-pane-inner" />
class="pdf-pane-inner" />
</div>
</div>
<!-- 中文译文:仅在开关打开时展示 -->
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlWithPage"
>
<pdf
ref=
"rightPdfRef"
:pdfUrl=
"reportUrlWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
src/views/thinkTank/utils/multiLineChart.js
浏览文件 @
84861ffe
import
*
as
echarts
from
'echarts'
;
import
*
as
echarts
from
'echarts'
;
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
/**
/**
* @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} data
* @param {{ title: unknown[], data: Array<{ name: string, value: unknown[], color?: string }> }} data
...
@@ -9,70 +10,21 @@ const getMultiLineChart = (data) => {
...
@@ -9,70 +10,21 @@ const getMultiLineChart = (data) => {
const
series
=
data
.
data
const
series
=
data
.
data
const
allNames
=
series
.
map
((
item
)
=>
item
.
name
)
const
allNames
=
series
.
map
((
item
)
=>
item
.
name
)
const
legendSplitAt
=
Math
.
ceil
(
allNames
.
length
/
2
)
const
legendFirstLine
=
allNames
.
slice
(
0
,
legendSplitAt
)
const
legendSecondLine
=
allNames
.
slice
(
legendSplitAt
)
// 按 AreaTag 的颜色规则映射到折线图配色(取 tag 的文字色)
const
parseHexToRgb
=
(
hex
)
=>
{
const
AREA_TAG_COLOR_BY_NAME
=
{
const
h
=
String
(
hex
||
''
).
replace
(
'#'
,
''
).
trim
()
'人工智能'
:
'rgba(245, 34, 45, 1)'
,
// tag1
if
(
h
.
length
!==
6
)
return
{
r
:
0
,
g
:
0
,
b
:
0
}
'生物科技'
:
'rgba(19, 168, 168, 1)'
,
// tag2
'新一代通信网络'
:
'rgba(5, 95, 194, 1)'
,
// tag3
// 兼容后端/页面常见写法
'通信网络'
:
'rgba(5, 95, 194, 1)'
,
'量子科技'
:
'rgba(114, 46, 209, 1)'
,
// tag4
'新能源'
:
'rgba(82, 196, 26, 1)'
,
// tag5
'集成电路'
:
'rgba(22, 119, 255, 1)'
,
// tag6
'海洋'
:
'rgba(15, 120, 199, 1)'
,
// tag7
'先进制造'
:
'rgba(250, 173, 20, 1)'
,
// tag8
'新材料'
:
'rgba(250, 140, 22, 1)'
,
// tag9
'航空航天'
:
'rgba(47, 84, 235, 1)'
,
// tag10
'太空'
:
'rgba(47, 84, 235, 1)'
,
// tag11
'深海'
:
'rgba(73, 104, 161, 1)'
,
// tag12
'极地'
:
'rgba(133, 165, 255, 1)'
,
// tag13
'核'
:
'rgba(250, 84, 28, 1)'
,
// tag14
'其他'
:
'rgba(82, 196, 26, 1)'
// tag15
}
// 兜底颜色池(未命中 AreaTag 映射时使用)
const
fallbackColorList
=
[
'rgba(5, 95, 194, 1)'
,
'rgba(19, 168, 168, 1)'
,
'rgba(250, 140, 22, 1)'
,
'rgba(114, 46, 209, 1)'
,
'rgba(82, 196, 26, 1)'
,
'rgba(250, 84, 28, 1)'
,
'rgba(22, 119, 255, 1)'
,
'rgba(95, 101, 108, 1)'
,
'rgba(47, 84, 235, 1)'
,
'rgba(133, 165, 255, 1)'
,
]
// 解析 RGBA 颜色的辅助函数
const
parseRgba
=
(
colorStr
)
=>
{
// 匹配 rgba(r, g, b, a) 格式
const
match
=
colorStr
.
match
(
/rgba
\((\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
)
,
\s
*
(\d
+
(\.\d
+
)?)\)
/
);
if
(
match
)
{
return
{
return
{
r
:
parseInt
(
match
[
1
]),
r
:
parseInt
(
h
.
slice
(
0
,
2
),
16
),
g
:
parseInt
(
match
[
2
]),
g
:
parseInt
(
h
.
slice
(
2
,
4
),
16
),
b
:
parseInt
(
match
[
3
]),
b
:
parseInt
(
h
.
slice
(
4
,
6
),
16
),
a
:
parseFloat
(
match
[
4
])
}
};
}
}
// 默认返回黑色
return
{
r
:
0
,
g
:
0
,
b
:
0
,
a
:
1
};
};
// 动态生成 series 配置
// 动态生成 series 配置
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
// 获取当前系列的颜色(优先使用item.color,否则用预设颜色,再否则随机)
const
baseColor
=
item
.
color
||
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
||
'#055FC2'
const
baseColor
=
const
{
r
,
g
,
b
}
=
parseHexToRgb
(
baseColor
)
item
.
color
||
AREA_TAG_COLOR_BY_NAME
[
item
.
name
]
||
fallbackColorList
[
index
%
fallbackColorList
.
length
]
||
`rgba(
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
,
${
Math
.
floor
(
Math
.
random
()
*
256
)}
, 1)`
;
const
{
r
,
g
,
b
}
=
parseRgba
(
baseColor
);
return
({
return
({
name
:
item
.
name
,
name
:
item
.
name
,
...
@@ -89,11 +41,11 @@ const getMultiLineChart = (data) => {
...
@@ -89,11 +41,11 @@ const getMultiLineChart = (data) => {
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
{
offset
:
0
,
// 顶部
offset
:
0
,
// 顶部
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0.1)`
// 按需求:0.1 -> 0
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0.1)`
},
},
{
{
offset
:
1
,
// 底部
offset
:
1
,
// 底部
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0)`
// 0 透明度
color
:
`rgba(
${
r
}
,
${
g
}
,
${
b
}
, 0)`
}
}
])
])
},
},
...
@@ -118,20 +70,24 @@ const getMultiLineChart = (data) => {
...
@@ -118,20 +70,24 @@ const getMultiLineChart = (data) => {
},
},
/* 顶部预留足够空间:多行图例 + Y 轴标题「数量」在纵轴顶端,避免与图例重合 */
/* 顶部预留足够空间:多行图例 + Y 轴标题「数量」在纵轴顶端,避免与图例重合 */
grid
:
{
grid
:
{
top
:
'34%'
,
top
:
68
,
right
:
'3%'
,
right
:
'3%'
,
bottom
:
'5%'
,
bottom
:
'5%'
,
left
:
'2%'
,
left
:
'2%'
,
containLabel
:
true
containLabel
:
true
},
},
legend
:
[
legend
:
{
{
show
:
true
,
show
:
true
,
type
:
'plain'
,
type
:
'plain'
,
data
:
legendFirstLine
,
orient
:
'horizontal'
,
top
:
8
,
left
:
'center'
,
left
:
'center'
,
top
:
8
,
width
:
'90%'
,
height
:
24
,
icon
:
'circle'
,
icon
:
'circle'
,
itemWidth
:
12
,
itemHeight
:
12
,
data
:
allNames
,
textStyle
:
{
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontWeight
:
400
,
...
@@ -141,23 +97,6 @@ const getMultiLineChart = (data) => {
...
@@ -141,23 +97,6 @@ const getMultiLineChart = (data) => {
align
:
'left'
align
:
'left'
}
}
},
},
{
show
:
legendSecondLine
.
length
>
0
,
type
:
'plain'
,
data
:
legendSecondLine
,
top
:
32
,
left
:
'center'
,
icon
:
'circle'
,
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontSize
:
14
,
lineHeight
:
24
,
letterSpacing
:
0
,
align
:
'left'
}
}
],
// 不使用全局 color,改为每条 series 自己定色(与 AreaTag 一致)
// 不使用全局 color,改为每条 series 自己定色(与 AreaTag 一致)
xAxis
:
[
xAxis
:
[
{
{
...
...
src/views/thinkTank/utils/piechart.js
浏览文件 @
84861ffe
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getPieChart
=
(
data
)
=>
{
const
getPieChart
=
(
data
)
=>
{
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
)
=>
{
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
,
index
)
=>
{
const
color
=
d
?.
color
const
color
=
d
?.
color
||
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
if
(
!
color
)
return
d
return
{
return
{
...
d
,
...
d
,
itemStyle
:
{
...(
d
.
itemStyle
||
{}),
color
},
itemStyle
:
{
...(
d
?
.
itemStyle
||
{}),
color
},
// “飞线”(labelLine)跟随领域色
// “飞线”(labelLine)跟随领域色
labelLine
:
{
labelLine
:
{
...(
d
.
labelLine
||
{}),
...(
d
?
.
labelLine
||
{}),
lineStyle
:
{
...(
d
.
labelLine
?.
lineStyle
||
{}),
color
}
lineStyle
:
{
...(
d
?
.
labelLine
?.
lineStyle
||
{}),
color
}
}
}
}
}
})
})
...
@@ -37,10 +38,15 @@ const getPieChart = (data) => {
...
@@ -37,10 +38,15 @@ const getPieChart = (data) => {
alignTo
:
'edge'
,
alignTo
:
'edge'
,
formatter
:
params
=>
{
formatter
:
params
=>
{
const
name
=
params
.
name
||
""
;
const
name
=
params
.
name
||
""
;
const
value
=
params
.
value
??
""
;
const
value
=
Number
(
params
.
value
??
0
)
||
0
;
const
percent
=
params
.
percent
!=
null
?
Math
.
round
(
params
.
percent
)
:
0
;
const
rawPercent
=
params
?.
data
?.
percent
!=
null
?
params
.
data
.
percent
:
params
.
percent
;
const
percent
=
rawPercent
!=
null
?
Number
(
rawPercent
).
toFixed
(
2
)
:
"0.00"
;
return
`{name|
${
name
}
}\n{time|
${
percent
}
%}`
;
// 第二行:数值 + 百分比(同一行、同一文字样式),保持与旧版一致的两行结构
return
`{name|
${
name
}
}\n{time|
${
value
}
项
${
percent
}
%}`
;
},
},
minMargin
:
5
,
minMargin
:
5
,
edgeDistance
:
10
,
edgeDistance
:
10
,
...
...
src/views/thinkTank/utils/resourceLibraryFilters.js
浏览文件 @
84861ffe
...
@@ -124,7 +124,20 @@ export function matchesEarlierChineseDate(timeStr) {
...
@@ -124,7 +124,20 @@ export function matchesEarlierChineseDate(timeStr) {
/** 与政策追踪「仅全部时间」一致:固定起止(结束日按产品要求) */
/** 与政策追踪「仅全部时间」一致:固定起止(结束日按产品要求) */
export
const
RESOURCE_REPORT_ALL_TIME_START
=
"2000-01-01"
;
export
const
RESOURCE_REPORT_ALL_TIME_START
=
"2000-01-01"
;
export
const
RESOURCE_REPORT_ALL_TIME_END
=
"2025-12-31"
;
function
getYesterdayYmd
()
{
const
d
=
new
Date
();
// JS Date 自动处理跨月/跨年
d
.
setDate
(
d
.
getDate
()
-
1
);
const
y
=
d
.
getFullYear
();
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
y
}
-
${
m
}
-
${
day
}
`
;
}
export
function
getResourceReportAllTimeEndYmd
()
{
return
getYesterdayYmd
();
}
function
getResourceReportDateYearsAgo
(
years
)
{
function
getResourceReportDateYearsAgo
(
years
)
{
const
currentDate
=
new
Date
();
const
currentDate
=
new
Date
();
...
@@ -145,7 +158,7 @@ function getResourceReportTodayYmd() {
...
@@ -145,7 +158,7 @@ function getResourceReportTodayYmd() {
/**
/**
* 资源库 /thinkTankOverview/report:由发布时间多选(数字年 +「更早」)推导 startDate/endDate,语义对齐政策追踪 getPolicyListDateRangeFromYearList。
* 资源库 /thinkTankOverview/report:由发布时间多选(数字年 +「更早」)推导 startDate/endDate,语义对齐政策追踪 getPolicyListDateRangeFromYearList。
* - 仅「全部时间」或选满全部具体年份 → 2000-01-01 ~
2025-12-31
* - 仅「全部时间」或选满全部具体年份 → 2000-01-01 ~
昨天
* - 单选/多选自然年 +「更早」→ 取最小年 01-01 与最大年 12-31 包络(「更早」为 2000~2020)
* - 单选/多选自然年 +「更早」→ 取最小年 01-01 与最大年 12-31 包络(「更早」为 2000~2020)
* - 无法解析时的兜底:近 relativeYearsAgo 年至今天
* - 无法解析时的兜底:近 relativeYearsAgo 年至今天
*
*
...
@@ -169,7 +182,7 @@ export function getResourceLibraryReportDateRangeFromTimeSelection(
...
@@ -169,7 +182,7 @@ export function getResourceLibraryReportDateRangeFromTimeSelection(
if
(
isAllTime
)
{
if
(
isAllTime
)
{
return
{
return
{
startDate
:
RESOURCE_REPORT_ALL_TIME_START
,
startDate
:
RESOURCE_REPORT_ALL_TIME_START
,
endDate
:
RESOURCE_REPORT_ALL_TIME_END
,
endDate
:
getResourceReportAllTimeEndYmd
()
,
};
};
}
}
...
@@ -206,6 +219,6 @@ export function getResourceLibraryReportDateRangeFromTimeSelection(
...
@@ -206,6 +219,6 @@ export function getResourceLibraryReportDateRangeFromTimeSelection(
}
}
return
{
return
{
startDate
:
`
${
minY
}
-01-01`
,
startDate
:
`
${
minY
}
-01-01`
,
endDate
:
`
${
maxY
}
-12-31`
,
endDate
:
maxY
===
new
Date
().
getFullYear
()
?
getYesterdayYmd
()
:
`
${
maxY
}
-12-31`
,
};
};
}
}
src/views/thinkTank/utils/sankey.js
浏览文件 @
84861ffe
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getSankeyChart
=
(
nodes
,
links
)
=>
{
const
getSankeyChart
=
(
nodes
,
links
)
=>
{
const
formatAmountWan
=
(
v
)
=>
{
const
n
=
Number
(
v
)
if
(
!
Number
.
isFinite
(
n
))
return
'0.00万'
return
`
${
n
.
toFixed
(
2
)}
万`
}
const
option
=
{
const
option
=
{
tooltip
:
{
trigger
:
'item'
,
backgroundColor
:
'rgba(255, 255, 255, 0.95)'
,
borderColor
:
'rgba(234, 236, 238, 1)'
,
borderWidth
:
1
,
textStyle
:
{
color
:
'rgb(59, 65, 75)'
,
fontFamily
:
'Microsoft YaHei'
,
fontSize
:
14
,
lineHeight
:
22
},
formatter
:
function
(
params
)
{
// 仅对连线展示资金金额;节点保持默认名称提示
if
(
params
?.
dataType
===
'edge'
)
{
const
amount
=
formatAmountWan
(
params
?.
data
?.
value
)
return
`资金金额:
${
amount
}
`
}
return
params
?.
name
??
''
}
},
series
:
{
series
:
{
type
:
'sankey'
,
type
:
'sankey'
,
layout
:
'none'
,
layout
:
'none'
,
...
@@ -9,6 +37,11 @@ const getSankeyChart = (nodes, links) => {
...
@@ -9,6 +37,11 @@ const getSankeyChart = (nodes, links) => {
right
:
'15%'
,
right
:
'15%'
,
top
:
'5%'
,
top
:
'5%'
,
bottom
:
'5%'
,
bottom
:
'5%'
,
color
:
MUTICHARTCOLORS
,
lineStyle
:
{
color
:
'rgb(230, 231, 232)'
,
opacity
:
1
},
emphasis
:
{
emphasis
:
{
focus
:
'adjacency'
focus
:
'adjacency'
},
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论