Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
a9c00d6f
提交
a9c00d6f
authored
4月 22, 2026
作者:
yanpeng
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'pre' of
http://8.140.26.4:10003/caijian/risk-monitor
into yp-dev
上级
f78815c8
bb55d9a0
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
55 个修改的文件
包含
1221 行增加
和
598 行删除
+1221
-598
overview.js
src/api/thinkTank/overview.js
+3
-2
index.vue
src/components/base/RiskSignalOverviewDetailDialog/index.vue
+28
-43
index.vue
src/components/base/TimePickerBox/index.vue
+221
-0
thinktank.js
src/router/modules/thinktank.js
+1
-1
index.vue
src/styles/components/TimePickerBox/index.vue
+40
-0
index.vue
src/styles/components/index.vue
+6
-1
goToPage.js
src/utils/goToPage.js
+0
-0
setChart.js
src/utils/setChart.js
+64
-23
index.vue
src/views/bill/billHome/index.vue
+6
-4
comTitle.vue
src/views/coopRestriction/common/comTitle.vue
+21
-14
index.vue
src/views/coopRestriction/components/dataNew/index.vue
+23
-15
index.vue
src/views/coopRestriction/components/dataSub/index.vue
+38
-8
index.vue
src/views/coopRestriction/components/resLib/index.vue
+32
-40
index.vue
src/views/coopRestriction/detail/index.vue
+4
-5
index.vue
src/views/coopRestriction/index.vue
+2
-2
index.vue
src/views/dataLibrary/bill/countryBill/index.vue
+17
-13
index.vue
...ews/exportControl/v2.0SingleSanction/originPage/index.vue
+13
-33
index.vue
src/views/scientificFunding/components/dataSub/index.vue
+74
-27
index.vue
src/views/scientificFunding/components/resLib/index.vue
+7
-3
index.vue
src/views/thinkTank/CongressHearingView/index.vue
+46
-15
index.vue
src/views/thinkTank/ReportDetail/index.vue
+1
-4
index.vue
src/views/thinkTank/ReportDetail/policyTracking/index.vue
+10
-4
index.vue
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
+47
-13
index.vue
src/views/thinkTank/SurveyProjectView/index.vue
+22
-13
index.vue
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
+0
-0
multiLineChart.js
...nk/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
+61
-109
piechart.js
...hinkTank/ThinkTankDetail/PolicyTracking/utils/piechart.js
+14
-9
index.vue
src/views/thinkTank/ThinkTankDetail/index.vue
+6
-4
index.vue
...k/ThinkTankDetail/thinkDynamics/CongressHearing/index.vue
+51
-21
index.vue
...nkTank/ThinkTankDetail/thinkDynamics/SurveyForm/index.vue
+43
-14
index.vue
...k/ThinkTankDetail/thinkDynamics/ThinkTankReport/index.vue
+44
-14
index.vue
src/views/thinkTank/ThinkTankDetail/thinkDynamics/index.vue
+53
-41
index.vue
src/views/thinkTank/ThinkTankDetail/thinkInfo/index.vue
+2
-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/TipTab/index.vue
+1
-1
index.vue
src/views/thinkTank/allThinkTank/index.vue
+1
-1
HomeMainFooterMain.vue
src/views/thinkTank/components/HomeMainFooterMain.vue
+43
-16
HomeMainFooterSurvey.vue
src/views/thinkTank/components/HomeMainFooterSurvey.vue
+43
-16
ThinkTankCongressHearingOverview.vue
...thinkTank/components/ThinkTankCongressHearingOverview.vue
+49
-26
ThinkTankPolicyAdviceOverview.vue
...ws/thinkTank/components/ThinkTankPolicyAdviceOverview.vue
+66
-39
index.vue
src/views/thinkTank/index.vue
+0
-0
index.vue
src/views/thinkTank/reportOriginal/index.vue
+0
-0
pdf.vue
src/views/thinkTank/reportOriginal/pdf.vue
+0
-0
multiLineChart.js
src/views/thinkTank/utils/multiLineChart.js
+0
-0
piechart.js
src/views/thinkTank/utils/piechart.js
+0
-0
resourceLibraryFilters.js
src/views/thinkTank/utils/resourceLibraryFilters.js
+0
-0
sankey.js
src/views/thinkTank/utils/sankey.js
+0
-0
risk-icon-blue.png
src/views/viewRiskSignal/assets/images/risk-icon-blue.png
+0
-0
risk-icon-green.png
src/views/viewRiskSignal/assets/images/risk-icon-green.png
+0
-0
risk-icon-orange.png
src/views/viewRiskSignal/assets/images/risk-icon-orange.png
+0
-0
risk-icon-red.png
src/views/viewRiskSignal/assets/images/risk-icon-red.png
+0
-0
risk-icon-yellow.png
src/views/viewRiskSignal/assets/images/risk-icon-yellow.png
+0
-0
warning-red.svg
src/views/viewRiskSignal/assets/images/warning-red.svg
+0
-0
index.vue
src/views/viewRiskSignal/index.vue
+0
-0
没有找到文件。
src/api/thinkTank/overview.js
浏览文件 @
a9c00d6f
...
...
@@ -75,7 +75,7 @@ export function getThinkTankReportDomainStats(params) {
export
function
getThinkTankPolicyIndustry
(
params
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/thinkTankOverview/policyIndustry
/
${
params
.
year
}
`
,
url
:
`/api/thinkTankOverview/policyIndustry`
,
params
})
}
...
...
@@ -180,7 +180,8 @@ export function getThinkTankTestimoniesByThinkTankId(params) {
params
:
{
pageNum
:
params
.
pageNum
,
pageSize
:
params
.
pageSize
,
sortField
:
params
.
sortField
,
sortOrder
:
params
.
sortOrder
,
domainIds
:
params
.
domainIds
,
startDate
:
params
.
startDate
,
endDate
:
params
.
endDate
,
...
...
src/components/base/RiskSignalOverviewDetailDialog/index.vue
浏览文件 @
a9c00d6f
<
template
>
<el-dialog
v-model=
"visible"
class=
"risk-signal-detail-dialog"
modal-class=
"risk-signal-detail-modal"
width=
"1280px"
align-center
:z-index=
"zIndex"
:show-close=
"true"
destroy-on-close
@
closed=
"handleClosed"
>
<el-dialog
v-model=
"visible"
class=
"risk-signal-detail-dialog"
modal-class=
"risk-signal-detail-modal"
width=
"1280px"
align-center
:z-index=
"zIndex"
:show-close=
"true"
destroy-on-close
@
closed=
"handleClosed"
>
<template
#
header
>
<img
class=
"header-icon"
src=
"@/views/viewRiskSignal/assets/images/risk-icon.png"
alt=
""
/>
<span
v-if=
"listLevelText"
class=
"risk-signal-detail-dialog__level"
:class=
"listLevelModifierClass"
>
{{
listLevelText
}}
</span>
<img
class=
"header-icon"
:src=
"headerIconSrc"
alt=
""
/>
<span
v-if=
"listLevelText"
class=
"risk-signal-detail-dialog__level"
:class=
"listLevelModifierClass"
>
{{
listLevelText
}}
</span>
<div
v-if=
"bodyFromApi"
class=
"risk-signal-detail-dialog__read-indicator"
>
<el-icon
v-if=
"riskDetailStatus === false"
class=
"risk-signal-detail-dialog__header-badge-close"
>
<Close
/>
</el-icon>
<img
v-else-if=
"riskDetailStatus === true"
class=
"risk-signal-detail-dialog__header-badge-read"
:src=
"greenRightImg"
alt=
""
/>
<img
v-else-if=
"riskDetailStatus === true"
class=
"risk-signal-detail-dialog__header-badge-read"
:src=
"greenRightImg"
alt=
""
/>
<span
v-if=
"riskDetailStatus != null"
class=
"read"
>
{{
riskDetailStatus
?
"已读"
:
"未读"
}}
</span>
</div>
</
template
>
...
...
@@ -35,28 +19,19 @@
<div
v-if=
"riskDetailItem.title"
class=
"risk-signal-detail-dialog__body"
>
<span
class=
"risk-signal-detail-dialog__title"
>
{{ riskDetailItem.title }}
</span>
<div
v-if=
"riskDetailItem.directionLabels.length"
class=
"risk-signal-detail-dialog__directions"
>
<div
v-for=
"(dirLabel, dirIndex) in riskDetailItem.directionLabels"
<div
v-for=
"(dirLabel, dirIndex) in riskDetailItem.directionLabels"
:key=
"'overview-risk-detail-direction-' + dirIndex + '-' + dirLabel"
class=
"risk-signal-detail-dialog__origin"
>
{{ dirLabel }}
</div>
class=
"risk-signal-detail-dialog__origin"
>
{{ dirLabel }}
</div>
</div>
<div
class=
"risk-signal-detail-dialog__meta"
>
<span>
{{ metaLine }}
</span>
<div
v-if=
"riskDetailItem.tag.length"
class=
"risk-signal-detail-dialog__tags"
>
<AreaTag
v-for=
"(tag, index) in riskDetailItem.tag"
:key=
"'overview-risk-detail-tag-' + index + '-' + tag"
:tag-name=
"tag"
>
{{ tag }}
</AreaTag>
<AreaTag
v-for=
"(tag, index) in riskDetailItem.tag"
:key=
"'overview-risk-detail-tag-' + index + '-' + tag"
:tag-name=
"tag"
>
{{ tag }}
</AreaTag>
</div>
</div>
</div>
<div
v-if=
"showRelationBar"
class=
"risk-signal-detail-dialog_relation"
@
click=
"handleRelationClick"
>
<div
v-if=
"showRelationBar"
class=
"risk-signal-detail-dialog_relation"
@
click=
"handleRelationClick"
>
<div
class=
"relation"
>
<div
class=
"logo"
>
<img
src=
"@/views/viewRiskSignal/assets/images/logo.png"
alt=
""
/>
...
...
@@ -72,12 +47,8 @@
</div>
</div>
<
template
#
footer
>
<el-button
type=
"primary"
class=
"risk-signal-detail-dialog__action-btn"
:loading=
"confirmLoading"
@
click=
"handleConfirm"
>
<el-button
type=
"primary"
class=
"risk-signal-detail-dialog__action-btn"
:loading=
"confirmLoading"
@
click=
"handleConfirm"
>
确定
</el-button>
</
template
>
...
...
@@ -91,6 +62,11 @@ import AreaTag from "@/components/base/AreaTag/index.vue";
import
{
ElMessage
}
from
"element-plus"
;
import
{
Close
}
from
"@element-plus/icons-vue"
;
import
greenRightImg
from
"@/views/viewRiskSignal/assets/images/green-right.png"
;
import
riskIconRed
from
"@/views/viewRiskSignal/assets/images/risk-icon-red.png"
;
import
riskIconOrange
from
"@/views/viewRiskSignal/assets/images/risk-icon-orange.png"
;
import
riskIconYellow
from
"@/views/viewRiskSignal/assets/images/risk-icon-yellow.png"
;
import
riskIconGreen
from
"@/views/viewRiskSignal/assets/images/risk-icon-green.png"
;
import
riskIconBlue
from
"@/views/viewRiskSignal/assets/images/risk-icon-blue.png"
;
import
{
getRiskSignalInfoById
,
updateRiskSignalStatus
}
from
"@/api/riskSignal/index.js"
;
import
{
buildListRowFallbackFromRawRow
,
...
...
@@ -180,6 +156,15 @@ const listLevelModifierClass = computed(
()
=>
`risk-signal-detail-dialog__level--
${
getRiskDetailLevelModifier
(
listLevelText
.
value
)}
`
);
const
headerIconSrc
=
computed
(()
=>
{
const
lv
=
getRiskDetailLevelModifier
(
listLevelText
.
value
);
if
(
lv
===
"lv1"
)
return
riskIconRed
;
if
(
lv
===
"lv2"
)
return
riskIconOrange
;
if
(
lv
===
"lv3"
)
return
riskIconYellow
;
if
(
lv
===
"lv4"
)
return
riskIconGreen
;
return
riskIconBlue
;
});
const
fieldMap
=
computed
(()
=>
({
nameField
:
props
.
nameField
,
postDateField
:
props
.
postDateField
,
...
...
src/components/base/TimePickerBox/index.vue
0 → 100644
浏览文件 @
a9c00d6f
<
template
>
<div
class=
"time-filter"
>
<el-checkbox-group
class=
"checkbox-group"
v-model=
"selectedValues"
@
change=
"handleChange"
>
<el-checkbox
class=
"filter-checkbox"
v-for=
"option in options"
:key=
"option.value"
:label=
"option.value"
:disabled=
"option.disabled"
>
{{
option
.
label
}}
</el-checkbox>
</el-checkbox-group>
<!-- 自定义日期选择器,仅在选中“自定义”时显示 -->
<div
v-if=
"showCustomPicker"
class=
"custom-date-picker"
>
<el-date-picker
v-model=
"customDateRange"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
format=
"YYYY-MM-DD"
value-format=
"YYYY-MM-DD"
@
change=
"handleCustomDateChange"
style=
"width: 100%;"
/>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
,
computed
,
watch
,
onMounted
}
from
'vue'
// Props 定义
const
props
=
defineProps
({
// 外部控制选中值(支持 v-model)
modelValue
:
{
type
:
Array
,
default
:
()
=>
[]
},
// 外部控制自定义日期范围
customDate
:
{
type
:
Array
,
default
:
()
=>
[]
}
})
// Emits 定义
const
emit
=
defineEmits
([
'update:modelValue'
,
'update:customDate'
,
'change'
])
// 响应式数据
const
selectedValues
=
ref
([])
// 当前选中的选项值数组
const
customDateRange
=
ref
([])
// 自定义日期范围 [start, end]
const
options
=
ref
([])
// 动态生成的选项列表
// 是否显示自定义日期选择器
const
showCustomPicker
=
computed
(()
=>
{
return
selectedValues
.
value
.
includes
(
'custom'
)
})
// 生成选项列表:全部时间 + 最近5个年份 + 自定义
const
initOptions
=
()
=>
{
const
currentYear
=
new
Date
().
getFullYear
()
// 生成最近5个年份(包含当前年份)
const
recentYears
=
[]
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
recentYears
.
push
(
currentYear
-
i
)
}
// 年份选项(倒序展示,从大到小)
const
yearOptions
=
recentYears
.
map
(
year
=>
({
label
:
`
${
year
}
年`
,
value
:
String
(
year
)
}))
options
.
value
=
[
{
label
:
'全部时间'
,
value
:
'all'
},
...
yearOptions
,
{
label
:
'自定义'
,
value
:
'custom'
}
]
}
// 向上层派发事件
const
emitChange
=
()
=>
{
const
eventData
=
{
type
:
'time'
,
value
:
selectedValues
.
value
[
0
],
// 单选,只有一个值
customRange
:
selectedValues
.
value
.
includes
(
'custom'
)
?
customDateRange
.
value
:
[]
}
emit
(
'update:modelValue'
,
selectedValues
.
value
)
emit
(
'update:customDate'
,
customDateRange
.
value
)
emit
(
'change'
,
eventData
)
}
// 选项变化处理(单选逻辑)
const
handleChange
=
(
selected
)
=>
{
// 实现单选逻辑
let
lastSelected
=
null
if
(
selected
.
length
>
selectedValues
.
value
.
length
)
{
// 新增了选项:找到新增的那个
const
added
=
selected
.
find
(
v
=>
!
selectedValues
.
value
.
includes
(
v
))
lastSelected
=
added
}
else
if
(
selected
.
length
<
selectedValues
.
value
.
length
)
{
// 移除了选项:选中“全部时间”
lastSelected
=
'all'
}
else
if
(
selected
.
length
===
1
&&
selectedValues
.
value
.
length
===
1
&&
selected
[
0
]
!==
selectedValues
.
value
[
0
])
{
// 直接替换场景
lastSelected
=
selected
[
0
]
}
else
if
(
selected
.
length
>
1
)
{
// 多选时,只保留最后一个选中的
lastSelected
=
selected
[
selected
.
length
-
1
]
}
if
(
lastSelected
)
{
selectedValues
.
value
=
[
lastSelected
]
}
else
if
(
selected
.
length
===
0
)
{
// 全部取消,默认给“全部时间”
selectedValues
.
value
=
[
'all'
]
}
else
{
// 确保只有一个
selectedValues
.
value
=
selected
.
slice
(
0
,
1
)
}
// 如果选中的不是“自定义”,清空自定义日期范围
if
(
!
selectedValues
.
value
.
includes
(
'custom'
))
{
customDateRange
.
value
=
[]
}
emitChange
()
}
// 自定义日期范围变化处理
const
handleCustomDateChange
=
(
val
)
=>
{
if
(
val
&&
val
.
length
===
2
)
{
customDateRange
.
value
=
[...
val
]
}
else
{
customDateRange
.
value
=
[]
}
emitChange
()
}
// 重置方法(供外部调用)
const
reset
=
()
=>
{
selectedValues
.
value
=
[
'all'
]
customDateRange
.
value
=
[]
emitChange
()
}
// 获取当前筛选条件描述(供外部调用)
const
getFilterDescription
=
()
=>
{
const
selectedVal
=
selectedValues
.
value
[
0
]
if
(
selectedVal
===
'all'
)
{
return
'全部时间'
}
else
if
(
selectedVal
===
'custom'
)
{
if
(
customDateRange
.
value
&&
customDateRange
.
value
.
length
===
2
)
{
return
`
${
customDateRange
.
value
[
0
]}
至
${
customDateRange
.
value
[
1
]}
`
}
else
{
return
'自定义(未选择日期)'
}
}
else
{
return
`
${
selectedVal
}
年`
}
}
// 暴露方法给父组件
defineExpose
({
reset
,
getFilterDescription
})
// 监听外部传入的 modelValue 变化
watch
(()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
JSON
.
stringify
(
newVal
)
!==
JSON
.
stringify
(
selectedValues
.
value
))
{
selectedValues
.
value
=
[...
newVal
]
}
},
{
immediate
:
true
})
// 监听外部传入的自定义日期变化
watch
(()
=>
props
.
customDate
,
(
newVal
)
=>
{
if
(
JSON
.
stringify
(
newVal
)
!==
JSON
.
stringify
(
customDateRange
.
value
))
{
customDateRange
.
value
=
[...
newVal
]
}
},
{
immediate
:
true
})
// 初始化
onMounted
(()
=>
{
initOptions
()
// 初始化选中值:如果外部没有传入,默认选中“全部时间”
if
(
!
props
.
modelValue
||
props
.
modelValue
.
length
===
0
)
{
selectedValues
.
value
=
[
'all'
]
}
else
{
selectedValues
.
value
=
[...
props
.
modelValue
]
}
})
</
script
>
<
style
lang=
"scss"
scoped
>
.time-filter
{
// display: flex;
// flex-direction: column;
// gap: 12px;
width
:
100%
;
.checkbox-group
{
display
:
flex
;
flex-wrap
:
wrap
;
width
:
100%
;
.filter-checkbox
{
width
:
50%
;
margin-right
:
0
;
margin-bottom
:
4px
;
// background: orange;
:deep
(
.el-checkbox__label
)
{
color
:
var
(
--
text-primary-65-color
);
font-size
:
var
(
--
font-size-base
);
font-weight
:
400
;
font-family
:
"Microsoft YaHei"
;
line-height
:
24px
;
}
}
}
}
.custom-date-picker
{
width
:
100%
;
margin-top
:
8px
;
}
</
style
>
\ No newline at end of file
src/router/modules/thinktank.js
浏览文件 @
a9c00d6f
...
...
@@ -15,7 +15,7 @@ const thinktankRoutes = [
name
:
"thinkTank"
,
component
:
thinkTank
,
meta
:
{
title
:
"科技智库概览"
,
title
:
"
美国
科技智库概览"
,
isShowHeader
:
true
}
},
...
...
src/styles/components/TimePickerBox/index.vue
0 → 100644
浏览文件 @
a9c00d6f
<
template
>
<el-row
class=
"wrapper layout-grid-line"
>
<el-col
:span=
"span"
>
<pre>
{{
`
`
}}
</pre>
<div
class=
"time-box"
>
<TimePickerBox
/>
</div>
</el-col>
</el-row>
</
template
>
<
script
setup
>
import
{
ref
}
from
'vue'
import
'@/styles/common.scss'
import
TimePickerBox
from
'@/components/base/TimePickerBox/index.vue'
const
span
=
12
</
script
>
<
style
lang=
"scss"
scoped
>
.time-box
{
width
:
300px
;
height
:
700px
;
background
:
#F2F8FF
;
border
:
1px
solid
var
(
--
bg-black-5
);
display
:
flex
;
gap
:
8px
;
}
</
style
>
\ No newline at end of file
src/styles/components/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -9,7 +9,8 @@
<TextStyle
/>
<div
class=
"text-title-1-show"
>
通用样式/组件
</div>
<div
style=
"position: relative; height: 800px;"
>
<el-tabs
tabPosition=
"left"
style=
"position: relative; height: 700px;"
class=
"tabs-nav-no-wrap left-float-nav-tabs dev-style-tabs"
>
<el-tabs
tabPosition=
"left"
style=
"position: relative; height: 700px;"
class=
"tabs-nav-no-wrap left-float-nav-tabs dev-style-tabs"
>
<el-tab-pane
label=
"通用"
lazy
>
<common-page
/>
</el-tab-pane>
...
...
@@ -76,6 +77,9 @@
<el-tab-pane
label=
"自定义排序"
lazy
>
<TimeSortSelectBox
/>
</el-tab-pane>
<!--
<el-tab-pane
label=
"时间筛选框"
lazy
>
<TimePickerBox
/>
</el-tab-pane>
-->
</el-tabs>
</div>
</el-space>
...
...
@@ -111,6 +115,7 @@ import RelationCenterChart from './RelationCenterChart/index.vue'
import
RelationForceChart
from
'./RelationForceChart/index.vue'
import
WorkingBox
from
'./WorkingBox/index.vue'
import
TimeSortSelectBox
from
'./TimeSortSelectBox/index.vue'
import
TimePickerBox
from
'./TimePickerBox/index.vue'
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
src/utils/goToPage.js
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/utils/setChart.js
浏览文件 @
a9c00d6f
...
...
@@ -4,6 +4,7 @@ import getQuarterRange from './getQuarterRange';
import
*
as
echarts
from
'echarts'
import
'echarts-wordcloud'
;
import
router
from
'@/router/index'
import
{
goToDataCountryBill
}
from
'./goToPage'
;
const
setChart
=
(
option
,
chartId
,
allowClick
,
selectParam
)
=>
{
let
chartDom
=
document
.
getElementById
(
chartId
);
if
(
!
chartDom
)
{
...
...
@@ -12,28 +13,61 @@ const setChart = (option, chartId, allowClick, selectParam) => {
chartDom
.
removeAttribute
(
"_echarts_instance_"
);
let
chart
=
echarts
.
init
(
chartDom
);
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
)
{
chart
.
on
(
'click'
,
function
(
params
)
{
// 图例分页箭头只负责翻页,不走任何跳转
if
(
params
?.
componentType
===
'graphic'
&&
LEGEND_ARROW_NAMES
.
has
(
params
?.
name
))
{
return
}
switch
(
selectParam
.
moduleType
)
{
case
'国会法案'
:
if
(
selectParam
.
key
===
1
)
{
// console.log('当前点击', selectParam, params.seriesName, params.name);
selectParam
.
selectedStatus
=
params
.
seriesName
selectParam
.
selectedDate
=
JSON
.
stringify
(
getMonthRange
(
params
.
name
))
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/countryBill"
,
query
:
selectParam
});
window
.
open
(
route
.
href
,
"_blank"
);
return
// const route = router.resolve({
// path: "/dataLibrary/countryBill",
// query: selectParam
// });
// window.open(route.href, "_blank");
// goToDataCountryBill(selectParam)
// return
}
else
if
(
selectParam
.
key
===
2
)
{
selectParam
.
domains
=
params
.
name
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/countryBill"
,
query
:
selectParam
});
window
.
open
(
route
.
href
,
"_blank"
);
return
// const route = router.resolve({
// path: "/dataLibrary/countryBill",
// query: selectParam
// });
// window.open(route.href, "_blank");
// goToDataCountryBill(selectParam)
// return
}
else
if
(
selectParam
.
key
===
3
)
{
if
(
params
.
name
===
'众议院'
||
params
.
name
===
'参议院'
)
{
selectParam
.
selectedCongress
=
params
.
name
...
...
@@ -48,20 +82,26 @@ const setChart = (option, chartId, allowClick, selectParam) => {
selectParam
.
selectedDate
=
JSON
.
stringify
([
selectParam
.
selectedDate
+
'-01-01'
,
selectParam
.
selectedDate
+
'-12-31'
])
}
}
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/countryBill"
,
query
:
selectParam
});
window
.
open
(
route
.
href
,
"_blank"
);
return
// const route = router.resolve({
// path: "/dataLibrary/countryBill",
// query: selectParam
// });
// window.open(route.href, "_blank");
// goToDataCountryBill(selectParam)
// return
}
else
{
selectParam
.
selectedStatus
=
params
.
name
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/countryBill"
,
query
:
selectParam
});
window
.
open
(
route
.
href
,
"_blank"
);
// const route = router.resolve({
// path: "/dataLibrary/countryBill",
// query: selectParam
// });
// window.open(route.href, "_blank");
// goToDataCountryBill(selectParam)
}
goToDataCountryBill
(
selectParam
)
break
case
'政令'
:
...
...
@@ -112,6 +152,7 @@ const setChart = (option, chartId, allowClick, selectParam) => {
// 容器可能受布局/异步渲染影响,强制一次 resize 保证 canvas 与容器一致
setTimeout
(()
=>
{
chart
.
resize
();
applyLegendPagingArrowVisibility
(
chart
,
option
)
},
0
);
return
chart
;
};
...
...
src/views/bill/billHome/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -322,6 +322,7 @@ import iconILetter from "./assets/icons/icon-iLetter.png";
import
{
ElMessage
}
from
"element-plus"
;
import
{
useGotoNewsDetail
}
from
"@/router/modules/news"
;
import
{
goToBill
}
from
"@/utils/goToPage"
;
// 跳转人物主页(MessageBubble 的 person-click 传入整条列表项,需取 personId)
const
handleClickToCharacter
=
async
item
=>
{
...
...
@@ -518,11 +519,12 @@ const handleClickToDetail = () => {
};
// 查看详情 传递参数
const
handleClickToDetailO
=
item
=>
{
window
.
sessionStorage
.
setItem
(
"billId"
,
item
.
billId
);
window
.
sessionStorage
.
setItem
(
"curTabName"
,
item
.
name
||
item
.
signalTitle
);
const
route
=
router
.
resolve
(
"/billLayout?billId="
+
item
.
billId
);
window
.
open
(
route
.
href
,
"_blank"
);
//
window.sessionStorage.setItem("billId", item.billId);
//
window.sessionStorage.setItem("curTabName", item.name || item.signalTitle);
//
const route = router.resolve("/billLayout?billId=" + item.billId);
//
window.open(route.href, "_blank");
// router.push("/billLayout?billId=" + item.billId)
goToBill
(
item
.
billId
,
item
.
signalTitle
)
};
const
isRiskOverviewDetailOpen
=
ref
(
false
);
...
...
src/views/coopRestriction/common/comTitle.vue
浏览文件 @
a9c00d6f
<
template
>
<div
class=
"com-title"
>
<div
class=
"cl1"
></div>
<div
class=
"com-title"
>
<div
class=
"cl1"
></div>
<div
class=
"cl2"
></div>
<div
class=
"title"
>
{{
title
}}
</div>
<div
class=
"cl3"
></div>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
}
from
"vue"
;
// 传入的title数据
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
""
}
title
:
{
type
:
String
,
default
:
""
}
});
</
script
>
<
style
scoped
lang=
"scss"
>
*
{
margin
:
0
;
padding
:
0
;
margin
:
0
;
padding
:
0
;
}
.com-title
{
width
:
1575
px
;
height
:
42px
;
width
:
1600
px
;
height
:
42px
;
display
:
flex
;
align-items
:
center
;
.cl1
{
width
:
24px
;
height
:
30px
;
background-color
:
rgba
(
174
,
214
,
255
,
1
);
margin-right
:
8px
;
}
.cl2
{
width
:
8px
;
height
:
30px
;
background-color
:
rgba
(
174
,
214
,
255
,
1
);
margin-right
:
8px
;
}
.title
{
width
:
152px
;
height
:
42px
;
text-align
:
center
;
font-size
:
32px
;
...
...
@@ -50,13 +54,16 @@ const props = defineProps({
font-family
:
'Microsoft YaHei'
;
line-height
:
42px
;
margin-right
:
8px
;
width
:
fit-content
;
/* 核心:强制不换行 */
white-space
:
nowrap
;
}
.cl3
{
width
:
1
367px
;
width
:
1
00%
;
height
:
1px
;
background-color
:
rgba
(
174
,
214
,
255
,
1
);
box-sizing
:
border-box
;
}
}
</
style
>
src/views/coopRestriction/components/dataNew/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -33,12 +33,9 @@
</li>
<li>
<span
class=
"ul-title"
>
涉及领域:
</span>
<div
class=
"ul-tags"
v-if=
"item.AREA"
>
<span
v-for=
"(field, fIndex) in typeof item.AREA === 'string'
? item.AREA.split(',')
: item.AREA"
:key=
"fIndex"
class=
"ul-pie"
:class=
"'cl' + ((fIndex % 3) + 1)"
>
{{
field
}}
</span>
<div
class=
"ul-tags"
v-if=
"getAreaTagList(item).length"
>
<AreaTag
v-for=
"(field, fIndex) in getAreaTagList(item)"
:key=
"`$
{field}-${fIndex}`"
:tagName="field" />
</div>
<span
v-else
class=
"ul-content"
>
未知
</span>
</li>
...
...
@@ -113,13 +110,8 @@
<RiskSignal
:list=
"riskSignals"
@
more-click=
"handleToMoreRiskSignal"
postDate=
"time"
name=
"content"
riskLevel=
"title"
@
item-click=
"handleRiskSignalItemToManage"
/>
<RiskSignalOverviewDetailDialog
v-model=
"isRiskOverviewDetailOpen"
:row=
"riskOverviewDetailRow"
name-field=
"content"
post-date-field=
"time"
risk-level-field=
"title"
/>
<RiskSignalOverviewDetailDialog
v-model=
"isRiskOverviewDetailOpen"
:row=
"riskOverviewDetailRow"
name-field=
"content"
post-date-field=
"time"
risk-level-field=
"title"
/>
</div>
</
template
>
...
...
@@ -132,6 +124,7 @@ import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import
{
getCoopRestrictionTrends
,
getCoopRestrictionSignals
}
from
"@/api/coopRestriction/coopRestriction.js"
;
import
defaultImg
from
"./assets/usImg.png"
;
import
CommonPrompt
from
"../../commonPrompt/index.vue"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
// 合作限制-查询风险信号数据
const
getCoopRestrictionSignalsData
=
async
()
=>
{
...
...
@@ -168,6 +161,20 @@ const getCoopRestrictionTrendsData = async () => {
}
};
const
getAreaTagList
=
(
item
)
=>
{
const
raw
=
item
?.
AREA
;
if
(
Array
.
isArray
(
raw
))
{
return
raw
.
map
(
v
=>
String
(
v
||
""
).
trim
()).
filter
(
Boolean
);
}
if
(
typeof
raw
===
"string"
)
{
return
raw
.
split
(
","
)
.
map
(
v
=>
String
(
v
||
""
).
trim
())
.
filter
(
Boolean
);
}
return
[];
};
// 轮播图手动切换
const
handlePrev
=
()
=>
{
if
(
carouselRef
.
value
)
{
...
...
@@ -350,7 +357,7 @@ onMounted(() => {
display
:
flex
;
.left-center-main
{
width
:
439px
;
width
:
1000dvb
;
height
:
175px
;
position
:
relative
;
...
...
@@ -364,7 +371,7 @@ onMounted(() => {
}
.left-center-main-ul
{
width
:
439
px
;
width
:
1000
px
;
height
:
132px
;
ul
{
...
...
@@ -410,6 +417,7 @@ onMounted(() => {
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
8px
;
width
:
600px
;
}
.ul-pie
{
...
...
src/views/coopRestriction/components/dataSub/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -74,8 +74,8 @@ import TipTab from "@/views/thinkTank/TipTab/index.vue";
import
AiButton
from
"@/components/base/Ai/AiButton/index.vue"
;
import
AiPane
from
"@/components/base/Ai/AiPane/index.vue"
;
const
COOP_LEFT_TIP_TEXT
=
"
各类型合作限制政策对比,
数据来源:美对华科技合作限制信息平台"
;
const
COOP_RIGHT_TIP_TEXT
=
"
各领域规则分布情况,
数据来源:美对华科技合作限制信息平台"
;
const
COOP_LEFT_TIP_TEXT
=
"数据来源:美对华科技合作限制信息平台"
;
const
COOP_RIGHT_TIP_TEXT
=
"数据来源:美对华科技合作限制信息平台"
;
// 临时展示 mock(不改样式):右侧“各领域规则分布情况”
// 用完把这个开关改回 false 即可恢复走接口
...
...
@@ -186,18 +186,26 @@ let leftChart;
const
rightChartRef
=
ref
(
null
);
let
rightChart
;
const
isShowAiLeft
=
ref
(
tru
e
);
const
isShowAiLeft
=
ref
(
fals
e
);
const
aiContentLeft
=
ref
(
""
);
const
isLeftInterpretLoading
=
ref
(
false
);
const
leftAiAbortController
=
ref
(
null
);
const
isShowAiRight
=
ref
(
tru
e
);
const
isShowAiRight
=
ref
(
fals
e
);
const
aiContentRight
=
ref
(
""
);
const
isRightInterpretLoading
=
ref
(
false
);
const
rightAiAbortController
=
ref
(
null
);
const
handleSwitchAiLeft
=
(
val
)
=>
{
isShowAiLeft
.
value
=
val
;
if
(
val
)
{
fetchLeftInterpretation
();
}
else
{
if
(
leftAiAbortController
.
value
)
{
leftAiAbortController
.
value
.
abort
();
leftAiAbortController
.
value
=
null
;
}
isLeftInterpretLoading
.
value
=
false
;
}
};
...
...
@@ -205,6 +213,12 @@ const handleSwitchAiRight = (val) => {
isShowAiRight
.
value
=
val
;
if
(
val
)
{
fetchRightInterpretation
();
}
else
{
if
(
rightAiAbortController
.
value
)
{
rightAiAbortController
.
value
.
abort
();
rightAiAbortController
.
value
=
null
;
}
isRightInterpretLoading
.
value
=
false
;
}
};
...
...
@@ -276,12 +290,17 @@ const fetchLeftInterpretation = async () => {
if
(
hasValidContent
||
isLeftInterpretLoading
.
value
)
{
return
;
}
if
(
leftAiAbortController
.
value
)
{
leftAiAbortController
.
value
.
abort
();
}
leftAiAbortController
.
value
=
new
AbortController
();
isLeftInterpretLoading
.
value
=
true
;
aiContentLeft
.
value
=
"解读生成中…"
;
try
{
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
signal
:
leftAiAbortController
.
value
.
signal
,
onChunk
:
(
chunk
)
=>
{
// 与智库概览「数量变化趋势」一致:按 chunk 增量拼接展示
appendAiInterpretationChunk
(
aiContentLeft
,
chunk
);
...
...
@@ -292,10 +311,13 @@ const fetchLeftInterpretation = async () => {
// 与智库概览一致:优先用最终「解读」收口;否则保留已拼接内容
aiContentLeft
.
value
=
text
||
aiContentLeft
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
console
.
error
(
"合作限制政策对比图表解读请求失败"
,
error
);
aiContentLeft
.
value
=
"解读加载失败"
;
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"合作限制政策对比图表解读请求失败"
,
error
);
aiContentLeft
.
value
=
"解读加载失败"
;
}
}
finally
{
isLeftInterpretLoading
.
value
=
false
;
leftAiAbortController
.
value
=
null
;
}
};
...
...
@@ -348,12 +370,17 @@ const fetchRightInterpretation = async () => {
if
(
hasValidContent
||
isRightInterpretLoading
.
value
)
{
return
;
}
if
(
rightAiAbortController
.
value
)
{
rightAiAbortController
.
value
.
abort
();
}
rightAiAbortController
.
value
=
new
AbortController
();
isRightInterpretLoading
.
value
=
true
;
aiContentRight
.
value
=
"解读生成中…"
;
try
{
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
signal
:
rightAiAbortController
.
value
.
signal
,
onChunk
:
(
chunk
)
=>
{
appendAiInterpretationChunk
(
aiContentRight
,
chunk
);
}
...
...
@@ -362,10 +389,13 @@ const fetchRightInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentRight
.
value
=
text
||
aiContentRight
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
console
.
error
(
"合作限制领域分布图表解读请求失败"
,
error
);
aiContentRight
.
value
=
"解读加载失败"
;
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"合作限制领域分布图表解读请求失败"
,
error
);
aiContentRight
.
value
=
"解读加载失败"
;
}
}
finally
{
isRightInterpretLoading
.
value
=
false
;
rightAiAbortController
.
value
=
null
;
}
};
...
...
src/views/coopRestriction/components/resLib/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -2,21 +2,14 @@
<div
class=
"reslib-page"
ref=
"reslibContainer"
>
<div
class=
"nav"
>
<div
v-for=
"item in navList"
:key=
"item.id"
class=
"nav-item"
:class=
"
{ active: item.id === activeItem }"
@click="
activeItem = item.id
">
@click="
handleNavItemClick(item.id)
">
{{
item
.
name
}}
</div>
</div>
<el-select
v-model=
"sortModel"
placeholder=
"发布时间"
class=
"select"
popper-class=
"coop-select-dropdown"
:teleported=
"true"
placement=
"bottom-start"
:popper-options=
"sortPopperOptions"
@
change=
"handleSortChange"
>
<template
#
prefix
>
<img
v-if=
"sortModel !== true"
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png"
class=
"select-prefix-img"
alt=
""
@
click
.
stop=
"toggleSortPrefix"
/>
<img
v-else
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png"
class=
"select-prefix-img"
alt=
""
@
click
.
stop=
"toggleSortPrefix"
/>
</
template
>
<el-option
:key=
"true"
label=
"正序"
:value=
"true"
/>
<el-option
:key=
"false"
label=
"倒序"
:value=
"false"
/>
</el-select>
<div
class=
"select"
>
<TimeSortSelectBox
:key=
"`coop-reslib-sort-$
{activeItem}`" :sort-demension="1"
@handle-px-change="handleCoopReslibPxChange" />
</div>
<div
class=
"main"
>
<div
class=
"left"
>
<div
class=
"left-ti1"
></div>
...
...
@@ -61,17 +54,17 @@
</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
v-else
>
<div
class=
"right-main-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
</
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>
...
...
@@ -82,6 +75,7 @@
import
{
ref
,
onMounted
,
watch
,
computed
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
getCoopRestrictionList
}
from
"@/api/coopRestriction/coopRestriction"
;
import
TimeSortSelectBox
from
"@/components/base/TimeSortSelectBox/index.vue"
;
import
defaultImg
from
"../../assets/images/default-icon2.png"
;
...
...
@@ -172,21 +166,15 @@ const activeItem = ref("0");
/** null:占位「发布时间」且默认倒序;true 正序;false 倒序(显式),与智库概览一致 */
const
sort
=
ref
(
null
);
const
sortPopperOptions
=
{
modifiers
:
[
{
name
:
"preventOverflow"
,
options
:
{
mainAxis
:
false
,
altAxis
:
false
}
},
{
name
:
"flip"
,
enabled
:
false
}
]
};
const
sortModel
=
computed
({
get
()
{
return
sort
.
value
;
},
set
(
v
)
{
sort
.
value
=
v
;
const
handleNavItemClick
=
(
id
)
=>
{
if
(
activeItem
.
value
===
id
)
{
return
;
}
});
// 切换 tab:默认回到倒序,并从第一页开始
sort
.
value
=
null
;
currentPage
.
value
=
1
;
activeItem
.
value
=
id
;
};
const
handleSortChange
=
()
=>
{
// 改变排序后从第一页开始
...
...
@@ -197,14 +185,10 @@ const handleSortChange = () => {
}
};
const
toggleSortPrefix
=
()
=>
{
sort
.
value
=
sort
.
value
===
true
?
false
:
true
;
// 切换排序后从第一页开始
if
(
currentPage
.
value
===
1
)
{
getMainDataList
();
}
else
{
currentPage
.
value
=
1
;
}
/** 合作限制数据库排序公共组件回调:1=时间倒序,2=时间正序(映射到现有 sort(true/false/null)) */
const
handleCoopReslibPxChange
=
(
val
)
=>
{
sort
.
value
=
Number
(
val
)
===
2
?
true
:
false
;
handleSortChange
();
};
const
dataList
=
ref
([
{
...
...
@@ -293,6 +277,11 @@ const total = ref(0);
const
pageSize
=
ref
(
10
);
const
currentPage
=
ref
(
1
);
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
map
=
{
...
...
@@ -658,13 +647,16 @@ watch(currentPage, () => {
.title
{
font-size
:
20px
;
width
:
950px
;
max-
width
:
950px
;
font-weight
:
700
;
font-family
:
"Microsoft YaHei"
;
line-height
:
26px
;
color
:
rgb
(
59
,
65
,
75
);
margin-bottom
:
9px
;
cursor
:
pointer
;
white-space
:
normal
;
overflow-wrap
:
anywhere
;
word-break
:
break-word
;
}
.content
{
...
...
src/views/coopRestriction/detail/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -432,9 +432,6 @@ const dataList3 = ref([
padding
:
19px
0
20px
;
background
:
rgba
(
255
,
255
,
255
,
1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
position
:
sticky
;
top
:
0
;
z-index
:
9
;
.nav-main
{
width
:
1600px
;
...
...
@@ -442,6 +439,7 @@ const dataList3 = ref([
margin
:
0
auto
;
display
:
flex
;
align-items
:
center
;
position
:
relative
;
img
{
width
:
72px
;
...
...
@@ -488,8 +486,9 @@ const dataList3 = ref([
display
:
flex
;
justify-content
:
right
;
position
:
absolute
;
bottom
:
0
;
margin-left
:
1224px
;
right
:
0
;
bottom
:
-20px
;
margin-left
:
0
;
.btn1
{
...
...
src/views/coopRestriction/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -34,14 +34,14 @@
</div>
<!-- 数据总览 -->
<div
class=
"datasub"
id=
"position3"
>
<com-title
title=
"
数据总
览"
/>
<com-title
title=
"
全景概
览"
/>
<div
class=
"datasub-main"
>
<dataSub
/>
</div>
</div>
<!-- 资源库 -->
<div
class=
"reslib"
id=
"position4"
>
<com-title
title=
"
资源
库"
/>
<com-title
title=
"
合作限制数据
库"
/>
<div
class=
"reslib-main"
>
<resLib
/>
</div>
...
...
src/views/dataLibrary/bill/countryBill/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -173,6 +173,7 @@ import { getPostOrgList, getPostMemberList } from '@/api/bill/billHome'
import
{
search
,
getStatusList
}
from
'@/api/comprehensiveSearch'
import
{
ElMessage
}
from
'element-plus'
import
getDateRange
from
'@/utils/getDateRange'
import
{
getDecodedParams
}
from
'@/utils/goToPage'
const
route
=
useRoute
();
...
...
@@ -1151,33 +1152,36 @@ const handleDownloadCurChartData = () => {
// 跳转到当前页 初始化筛选条件
const
initParam
=
()
=>
{
const
hasQuery
=
Object
.
keys
(
route
.
query
).
length
>
0
;
const
routeQuery
=
getDecodedParams
()
console
.
log
(
'routeQuery'
,
routeQuery
);
const
hasQuery
=
Object
.
keys
(
routeQuery
).
length
>
0
;
if
(
hasQuery
)
{
if
(
route
.
q
uery
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
route
.
q
uery
.
selectedAreaList
)
if
(
route
Q
uery
.
selectedAreaList
)
{
selectedArea
.
value
=
JSON
.
parse
(
route
Q
uery
.
selectedAreaList
)
}
else
{
selectedArea
.
value
=
route
.
query
.
domains
?
[
route
.
q
uery
.
domains
]
:
[
'全部领域'
]
selectedArea
.
value
=
route
Query
.
domains
?
[
routeQ
uery
.
domains
]
:
[
'全部领域'
]
}
if
(
route
.
query
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
route
.
query
.
selectedDate
))
&&
JSON
.
parse
(
route
.
q
uery
.
selectedDate
).
length
)
{
if
(
route
Query
.
selectedDate
&&
Array
.
isArray
(
JSON
.
parse
(
routeQuery
.
selectedDate
))
&&
JSON
.
parse
(
routeQ
uery
.
selectedDate
).
length
)
{
selectedDate
.
value
=
'自定义'
customTime
.
value
=
JSON
.
parse
(
route
.
q
uery
.
selectedDate
)
customTime
.
value
=
JSON
.
parse
(
route
Q
uery
.
selectedDate
)
}
isInvolveCn
.
value
=
route
.
q
uery
.
isInvolveCn
?
true
:
false
if
(
route
.
query
.
selectedStatus
&&
route
.
q
uery
.
selectedStatus
!==
'全部阶段'
)
{
isInvolveCn
.
value
=
route
Q
uery
.
isInvolveCn
?
true
:
false
if
(
route
Query
.
selectedStatus
&&
routeQ
uery
.
selectedStatus
!==
'全部阶段'
)
{
selectedStatus
.
value
=
statusList
.
value
.
filter
(
item
=>
{
return
item
.
name
===
route
.
q
uery
.
selectedStatus
return
item
.
name
===
route
Q
uery
.
selectedStatus
}
)[
0
].
id
}
else
{
selectedStatus
.
value
=
'全部阶段'
}
selectedCongress
.
value
=
route
.
query
.
selectedCongress
?
route
.
q
uery
.
selectedCongress
:
'全部议院'
selectedOrg
.
value
=
route
.
query
.
selectedOrg
?
[
route
.
q
uery
.
selectedOrg
]
:
[
'全部委员会'
]
selectedmember
.
value
=
route
.
query
.
selectedmember
?
JSON
.
parse
(
route
.
q
uery
.
selectedmember
)
:
[
'全部议员'
]
selectedCongress
.
value
=
route
Query
.
selectedCongress
?
routeQ
uery
.
selectedCongress
:
'全部议院'
selectedOrg
.
value
=
route
Query
.
selectedOrg
?
[
routeQ
uery
.
selectedOrg
]
:
[
'全部委员会'
]
selectedmember
.
value
=
route
Query
.
selectedmember
?
JSON
.
parse
(
routeQ
uery
.
selectedmember
)
:
[
'全部议员'
]
const
query
=
route
.
q
uery
;
const
query
=
route
Q
uery
;
if
(
Object
.
keys
(
query
).
length
>
0
)
{
sessionStorage
.
setItem
(
'countryBillRouteQuery'
,
JSON
.
stringify
(
query
));
}
...
...
src/views/exportControl/v2.0SingleSanction/originPage/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -26,27 +26,15 @@
<div
class=
"btn-box"
>
<div
class=
"translate"
>
<div
class=
"search-input-wrap"
v-if=
"showSearchInput"
>
<input
v-model=
"searchKeywordText"
class=
"search-input"
placeholder=
"回车查询"
@
keyup
.
enter=
"handleSearchInPdf"
/>
<input
v-model=
"searchKeywordText"
class=
"search-input"
placeholder=
"回车查询"
@
keyup
.
enter=
"handleSearchInPdf"
/>
<div
class=
"search-match-count"
>
{{
matchInfo
.
current
}}
/
{{
matchInfo
.
total
}}
</div>
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handlePrevMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current
<
=
1
"
>
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handlePrevMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current
<
=
1
"
>
上一个
</button>
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handleNextMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current >= matchInfo.total"
>
<button
class=
"search-nav-btn"
type=
"button"
@
click=
"handleNextMatch"
:disabled=
"matchInfo.total === 0 || matchInfo.current >= matchInfo.total"
>
下一个
</button>
</div>
...
...
@@ -54,19 +42,14 @@
<el-switch
v-model=
"valueSwitch"
/>
</div>
<div
class=
"translate-image"
>
<img
class=
"translate-icon"
src=
"../assets/icon-translation.png"
alt=
""
style=
"
<img
class=
"translate-icon"
src=
"../assets/icon-translation.png"
alt=
""
style=
"
width: 16px;
height: 16px;
max-width: 16px;
max-height: 16px;
display: block;
object-fit: contain;
"
/>
"
/>
</div>
<div
class=
"translate-text"
>
{{
"显示译文"
}}
</div>
</div>
...
...
@@ -80,12 +63,8 @@
</div>
<div
class=
"report-box"
>
<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"
/>
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`" ref="rightPdfRef" :pdfUrl="reportUrlWithPage"
class="pdf-pane-inner" />
</div>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
...
...
@@ -203,7 +182,7 @@ const handleSearchInPdf = async () => {
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
ElMessage
.
warning
(
"未找到包含该关键词的页面"
);
}
catch
(
_
)
{}
}
catch
(
_
)
{
}
}
};
...
...
@@ -246,7 +225,7 @@ const handleDownload = async () => {
try
{
const
{
ElMessage
}
=
await
import
(
"element-plus"
);
ElMessage
.
warning
(
"暂无下载链接"
);
}
catch
(
_
)
{}
}
catch
(
_
)
{
}
return
;
}
const
baseName
=
(
thinkInfo
.
value
?.
name
||
"报告原文"
).
replace
(
/
[/\\
?%*:|"<>
]
/g
,
"-"
);
...
...
@@ -659,6 +638,7 @@ onMounted(async () => {
display
:
flex
;
gap
:
8px
;
cursor
:
pointer
;
.icon
{
width
:
16px
;
height
:
16px
;
...
...
src/views/scientificFunding/components/dataSub/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -171,6 +171,10 @@ const options = [
];
/** value 须与 v-model 类型一致(数字),否则 el-select 无法匹配 label,会显示成「2025」而非「2025年」 */
const
options1
=
[
{
value
:
2026
,
label
:
"2026年"
},
{
value
:
2025
,
label
:
"2025年"
...
...
@@ -719,11 +723,11 @@ let rightChart1;
let
leftSankey
;
let
boxplotChart
;
// ------- AI 解读(
刷新后默认展开,行为对齐智库概览
) -------
const
isShowAiContentLeft1
=
ref
(
tru
e
);
const
isShowAiContentLeft2
=
ref
(
tru
e
);
const
isShowAiContentRight1
=
ref
(
tru
e
);
const
isShowAiContentRight2
=
ref
(
tru
e
);
// ------- AI 解读(
默认仅展示 AiButton,悬停后再请求 AI
) -------
const
isShowAiContentLeft1
=
ref
(
fals
e
);
const
isShowAiContentLeft2
=
ref
(
fals
e
);
const
isShowAiContentRight1
=
ref
(
fals
e
);
const
isShowAiContentRight2
=
ref
(
fals
e
);
const
aiContentLeft1
=
ref
(
""
);
const
aiContentLeft2
=
ref
(
""
);
...
...
@@ -735,6 +739,11 @@ const isAiLoadingLeft2 = ref(false);
const
isAiLoadingRight1
=
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
left1AiSeq
=
ref
(
0
);
...
...
@@ -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
;
const
hasValidContent
=
...
...
@@ -778,12 +793,19 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
if
(
hasValidContent
)
return
;
const
localSeq
=
aiSeqRef
.
value
;
if
(
abortControllerRef
?.
value
)
{
abortControllerRef
.
value
.
abort
();
}
if
(
abortControllerRef
)
{
abortControllerRef
.
value
=
new
AbortController
();
}
loadingRef
.
value
=
true
;
targetRef
.
value
=
AI_LOADING_TEXT
;
try
{
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
...(
abortControllerRef
?.
value
?.
signal
?
{
signal
:
abortControllerRef
.
value
.
signal
}
:
{}),
onChunk
:
(
chunk
)
=>
{
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
appendAiInterpretationChunk
(
targetRef
,
chunk
,
AI_LOADING_TEXT
);
...
...
@@ -796,10 +818,13 @@ const fetchChartInterpretationOnce = async (payload, targetRef, loadingRef, aiSe
targetRef
.
value
=
text
||
targetRef
.
value
||
"未返回有效解读内容"
;
}
catch
(
e
)
{
if
(
aiSeqRef
.
value
!==
localSeq
)
return
;
console
.
error
(
"图表解读请求失败"
,
e
);
targetRef
.
value
=
"解读加载失败"
;
if
(
e
?.
name
!==
"AbortError"
)
{
console
.
error
(
"图表解读请求失败"
,
e
);
targetRef
.
value
=
"解读加载失败"
;
}
}
finally
{
if
(
aiSeqRef
.
value
===
localSeq
)
loadingRef
.
value
=
false
;
if
(
abortControllerRef
)
abortControllerRef
.
value
=
null
;
}
};
...
...
@@ -842,43 +867,71 @@ const buildPayloadRight2 = () => {
const
handleSwitchAiLeft1
=
async
(
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
();
if
(
!
payload
)
{
aiContentLeft1
.
value
=
"暂无图表数据"
;
return
;
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft1
,
isAiLoadingLeft1
,
left1AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft1
,
isAiLoadingLeft1
,
left1AiSeq
,
left1AiAbortController
);
};
const
handleSwitchAiLeft2
=
async
(
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
();
if
(
!
payload
)
{
aiContentLeft2
.
value
=
"暂无图表数据"
;
return
;
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft2
,
isAiLoadingLeft2
,
left2AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentLeft2
,
isAiLoadingLeft2
,
left2AiSeq
,
left2AiAbortController
);
};
const
handleSwitchAiRight1
=
async
(
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
();
if
(
!
payload
)
{
aiContentRight1
.
value
=
"暂无图表数据"
;
return
;
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight1
,
isAiLoadingRight1
,
right1AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight1
,
isAiLoadingRight1
,
right1AiSeq
,
right1AiAbortController
);
};
const
handleSwitchAiRight2
=
async
(
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
();
if
(
!
payload
)
{
aiContentRight2
.
value
=
"暂无图表数据"
;
return
;
}
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight2
,
isAiLoadingRight2
,
right2AiSeq
);
await
fetchChartInterpretationOnce
(
payload
,
aiContentRight2
,
isAiLoadingRight2
,
right2AiSeq
,
right2AiAbortController
);
};
...
...
@@ -1136,21 +1189,15 @@ const initLeftSankey = (data) => {
// };
onMounted
(()
=>
{
// 刷新后 AiPane 默认展开:先给出“解读生成中…”占位,再在数据到位后触发解读请求
aiContentLeft1
.
value
=
"解读生成中…"
;
aiContentLeft2
.
value
=
"解读生成中…"
;
aiContentRight1
.
value
=
"解读生成中…"
;
aiContentRight2
.
value
=
"解读生成中…"
;
// 先拉数据;每块数据到位后立即触发一次 AI 解读(不必等其它块完成)
void
handleGetFundField
().
then
(()
=>
handleSwitchAiLeft1
(
true
));
// 仅拉取图表数据;AI 解读仅在用户悬停打开面板时触发
void
handleGetFundField
();
void
handleFindCountryProjectAreaList
();
void
handlegetCountryFundingChange
()
.
then
(()
=>
handleSwitchAiRight1
(
true
))
;
void
handlegetCountryFundingChange
();
void
handlegetCountryFundProjectChange
();
void
handleGetOrgFundsArea
()
.
then
(()
=>
handleSwitchAiLeft2
(
true
))
;
void
handlegetOrgFundStrength
()
.
then
(()
=>
handleSwitchAiRight2
(
true
))
;
void
handleGetOrgFundsArea
();
void
handlegetOrgFundStrength
();
});
// onBeforeUnmount(() => {
// window.removeEventListener("resize", handleResize);
...
...
src/views/scientificFunding/components/resLib/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -166,6 +166,10 @@ const handleTimeGroupChange = (val) => {
};
const
pubTimeList
=
ref
([
{
id
:
2026
,
name
:
"2026年"
},
{
id
:
2025
,
name
:
"2025年"
...
...
@@ -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_END
=
202
5
;
const
YEAR_ALL_RANGE_END
=
202
6
;
const
buildYearlistForRequest
=
(
selectedTimeModel
)
=>
{
const
strippedTime
=
stripAllTimeForRequest
(
selectedTimeModel
);
// 仅勾选「全部时间」、未选具体年份时,传 2000~202
5
逐年
// 仅勾选「全部时间」、未选具体年份时,传 2000~202
6
逐年
if
(
strippedTime
.
length
===
0
)
{
const
out
=
[];
for
(
let
y
=
YEAR_ALL_RANGE_START
;
y
<=
YEAR_ALL_RANGE_END
;
y
+=
1
)
{
...
...
src/views/thinkTank/CongressHearingView/index.vue
浏览文件 @
a9c00d6f
<
template
>
<div
class=
"wrap"
>
<div
class=
"scroll-inner"
>
<div
class=
"scroll-inner"
ref=
"pageScrollRef"
>
<div
class=
"header"
>
<div
class=
"header-top"
>
<div
class=
"header-top-left"
>
...
...
@@ -136,7 +136,7 @@
<div
class=
"box5-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
</div>
...
...
@@ -231,7 +231,7 @@ import DefaultIcon1 from '@/assets/icons/default-icon1.png'
import
WarningPane
from
"@/components/base/WarningPane/index.vue"
import
WordCloudChart
from
"@/components/base/WordCloundChart/index.vue"
import
SearchContainer
from
"@/components/SearchContainer.vue"
;
import
{
ref
,
onMounted
,
computed
,
defineProps
}
from
"vue"
;
import
{
ref
,
onMounted
,
computed
,
defineProps
,
nextTick
}
from
"vue"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
getThinkTankReportAbstract
,
...
...
@@ -304,15 +304,22 @@ const handleGetThinkTankHearingInfo = async () => {
}
};
const
REPORT_ANALYSIS_TIP_BOX5
=
"
国会听证会关键词云,
数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
const
isShowAiContentBox5
=
ref
(
tru
e
);
"数据来源:美国兰德公司官网"
;
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
};
...
...
@@ -510,10 +517,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
}
// 刷新后默认展开 AI:数据就绪即触发解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
// 仅在用户打开 AI 面板时才请求解读
}
else
{
box5Data
.
value
=
[];
}
...
...
@@ -588,8 +592,30 @@ const switchTab = name => {
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
total
=
ref
(
0
);
const
pageScrollRef
=
ref
(
null
);
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
=>
{
currentPage
.
value
=
page
;
scrollToTop
();
handleGetThinkTankReportViewpoint
();
};
...
...
@@ -663,6 +689,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
...
...
@@ -677,6 +707,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
...
...
@@ -685,10 +716,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
};
...
...
@@ -737,9 +771,6 @@ onMounted(() => {
border-bottom
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
position
:
sticky
;
top
:
0
;
z-index
:
99999
;
overflow
:
hidden
;
.header-top
{
...
...
src/views/thinkTank/ReportDetail/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -232,9 +232,6 @@ const handleDownloadDocument = async () => {
border-bottom
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
position
:
sticky
;
top
:
0
;
z-index
:
99999
;
overflow
:
hidden
;
.header-top
{
...
...
@@ -248,7 +245,7 @@ const handleDownloadDocument = async () => {
display
:
flex
;
img
{
width
:
72
px
;
width
:
178
px
;
height
:
88px
;
}
...
...
src/views/thinkTank/ReportDetail/policyTracking/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -68,11 +68,11 @@
<div
class=
"info-content"
>
<div
class=
"info-item"
>
<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"
v-for=
"(
item, index) in box1DataItem.domains"
:key=
"index"
v-show=
"item"
>
{{
item
.
industryName
}}
</div>
<div
class=
"tag"
v-for=
"(
name, index) in domainTags"
:key=
"name + '-' + index"
>
{{
name
}}
</div>
</div>
</div>
</div>
...
...
@@ -155,6 +155,12 @@ const activeItemIndex = ref(0);
const
pageSize
=
ref
(
10
);
// 当前选中 item 的数据
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 顶部
const
wrapRef
=
ref
(
null
);
const
reportUrl
=
ref
(
""
);
...
...
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
浏览文件 @
a9c00d6f
<
template
>
<div
class=
"wrap"
>
<div
class=
"wrap"
ref=
"pageScrollRef"
>
<div
class=
"top"
>
<WarningPane
:warnningLevel=
"riskSignal?.level"
:warnningContent=
"riskSignal?.content"
v-if=
"riskSignal?.level"
>
</WarningPane>
...
...
@@ -79,7 +79,7 @@
<div
class=
"box5-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
</div>
...
...
@@ -217,7 +217,7 @@ import DefaultIcon1 from '@/assets/icons/default-icon1.png'
import
WarningPane
from
"@/components/base/WarningPane/index.vue"
import
WordCloudChart
from
"@/components/base/WordCloundChart/index.vue"
import
SearchContainer
from
"@/components/SearchContainer.vue"
;
import
{
ref
,
onMounted
,
computed
,
defineProps
}
from
"vue"
;
import
{
ref
,
onMounted
,
computed
,
defineProps
,
nextTick
}
from
"vue"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
getThinkTankReportAbstract
,
...
...
@@ -258,15 +258,22 @@ const props = defineProps({
}
});
const
REPORT_ANALYSIS_TIP_BOX5
=
"
智库报告关键词云,
数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
const
isShowAiContentBox5
=
ref
(
tru
e
);
"数据来源:美国兰德公司官网"
;
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
};
...
...
@@ -463,10 +470,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
}
// 刷新后默认展开 AI:数据就绪即触发解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
// 仅在用户打开 AI 面板时才请求解读
}
else
{
box5Data
.
value
=
[];
}
...
...
@@ -541,8 +545,30 @@ const switchTab = name => {
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
total
=
ref
(
0
);
const
pageScrollRef
=
ref
(
null
);
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
=>
{
currentPage
.
value
=
page
;
scrollToTop
();
handleGetThinkTankReportViewpoint
();
};
...
...
@@ -617,6 +643,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
...
...
@@ -631,6 +661,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
...
...
@@ -639,10 +670,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
};
...
...
@@ -963,7 +997,7 @@ onMounted(() => {
display
:
flex
;
.left
{
width
:
56
px
;
width
:
150
px
;
height
:
74px
;
margin-top
:
8px
;
...
...
src/views/thinkTank/SurveyProjectView/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -43,7 +43,7 @@
<div
class=
"box1-footer"
>
<TipTab
:text=
"REPORT_ANALYSIS_TIP_BOX5"
/>
</div>
<div
class=
"ai-wrap"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<div
class=
"ai-wrap"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
<AiButton
/>
</div>
...
...
@@ -184,15 +184,22 @@ const applySurveyProjectDocumentTitle = (title) => {
document
.
title
=
text
;
};
const
REPORT_ANALYSIS_TIP_BOX5
=
"
调查项目关键词云,
数据来源:美国兰德公司官网"
;
//
刷新后默认展示「报告关键词云」AI 总结
const
isShowAiContentBox5
=
ref
(
tru
e
);
"数据来源:美国兰德公司官网"
;
//
默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
fals
e
);
const
aiContentBox5
=
ref
(
""
);
const
isBox5InterpretLoading
=
ref
(
false
);
const
box5AiAbortController
=
ref
(
null
);
const
handleSwitchAiContentShowBox5
=
(
val
)
=>
{
isShowAiContentBox5
.
value
=
val
;
if
(
val
)
{
fetchBox5ChartInterpretation
();
}
else
{
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
box5AiAbortController
.
value
=
null
;
}
isBox5InterpretLoading
.
value
=
false
;
}
};
const
searchOpinions
=
ref
(
''
);
...
...
@@ -403,10 +410,7 @@ const handleGetThinkTankReportIndustryCloud = async () => {
if
(
data
.
length
)
{
box5WordCloudKey
.
value
+=
1
;
}
// 刷新后默认展开 AI:数据就绪即触发解读
if
(
isShowAiContentBox5
.
value
)
{
fetchBox5ChartInterpretation
();
}
// 仅在用户打开 AI 面板时才请求解读
}
else
{
box5Data
.
value
=
[];
}
...
...
@@ -548,6 +552,10 @@ const fetchBox5ChartInterpretation = async () => {
if
(
hasValidContent
||
isBox5InterpretLoading
.
value
)
{
return
;
}
if
(
box5AiAbortController
.
value
)
{
box5AiAbortController
.
value
.
abort
();
}
box5AiAbortController
.
value
=
new
AbortController
();
isBox5InterpretLoading
.
value
=
true
;
aiContentBox5
.
value
=
"解读生成中…"
;
const
chartPayload
=
{
...
...
@@ -562,6 +570,7 @@ const fetchBox5ChartInterpretation = async () => {
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
chartPayload
)
},
{
signal
:
box5AiAbortController
.
value
.
signal
,
onChunk
:
chunk
=>
{
appendAiInterpretationChunk
(
aiContentBox5
,
chunk
);
}
...
...
@@ -570,10 +579,13 @@ const fetchBox5ChartInterpretation = async () => {
const
text
=
getInterpretationTextFromChartResponse
(
res
);
aiContentBox5
.
value
=
text
||
aiContentBox5
.
value
||
"未返回有效解读内容"
;
}
catch
(
error
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
if
(
error
?.
name
!==
"AbortError"
)
{
console
.
error
(
"报告关键词云图表解读请求失败"
,
error
);
aiContentBox5
.
value
=
"解读加载失败"
;
}
}
finally
{
isBox5InterpretLoading
.
value
=
false
;
box5AiAbortController
.
value
=
null
;
}
};
...
...
@@ -624,9 +636,6 @@ onMounted(() => {
border-bottom
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
position
:
sticky
;
top
:
0
;
z-index
:
99999
;
overflow
:
hidden
;
.header-top
{
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/multiLineChart.js
浏览文件 @
a9c00d6f
import
*
as
echarts
from
'echarts'
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
// 按 AreaTag 的颜色规则映射到折线图配色(取 tag 的文字色)
const
AREA_TAG_COLOR_BY_NAME
=
{
'人工智能'
:
'rgba(245, 34, 45, 1)'
,
// tag1
'生物科技'
:
'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
{
r
:
parseInt
(
match
[
1
],
10
),
g
:
parseInt
(
match
[
2
],
10
),
b
:
parseInt
(
match
[
3
],
10
),
a
:
parseFloat
(
match
[
4
])
}
const
parseHexToRgb
=
(
hex
)
=>
{
const
h
=
String
(
hex
||
''
).
replace
(
'#'
,
''
).
trim
()
if
(
h
.
length
!==
6
)
return
{
r
:
0
,
g
:
0
,
b
:
0
}
return
{
r
:
parseInt
(
h
.
slice
(
0
,
2
),
16
),
g
:
parseInt
(
h
.
slice
(
2
,
4
),
16
),
b
:
parseInt
(
h
.
slice
(
4
,
6
),
16
),
}
return
{
r
:
0
,
g
:
0
,
b
:
0
,
a
:
1
}
}
/**
...
...
@@ -55,22 +19,14 @@ const getMultiLineChart = (chartInput) => {
const
series
=
chartInput
.
data
||
[]
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
labelFontSize
=
xCount
>
8
?
10
:
xCount
>
5
?
11
:
12
const
labelRotate
=
xCount
>
6
?
28
:
0
const
echartsSeries
=
series
.
map
((
item
,
index
)
=>
{
const
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
)
const
baseColor
=
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
||
'#055FC2'
const
{
r
,
g
,
b
}
=
parseHexToRgb
(
baseColor
)
return
{
name
:
item
.
name
,
...
...
@@ -102,69 +58,65 @@ const getMultiLineChart = (chartInput) => {
},
/* 贴满 #box3Chart:四边 0,由 containLabel 在网格内为轴文字留位,避免左侧/底部大块留白 */
grid
:
{
top
:
92
,
top
:
60
,
right
:
10
,
bottom
:
0
,
left
:
20
,
containLabel
:
true
},
legend
:
[
{
show
:
true
,
type
:
'plain'
,
data
:
legendLine1
,
top
:
4
,
left
:
'center'
,
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
,
itemHeight
:
12
},
legend
:
{
show
:
true
,
type
:
'scroll'
,
orient
:
'horizontal'
,
left
:
8
,
top
:
6
,
width
:
'95%'
,
height
:
24
,
padding
:
[
0
,
24
,
0
,
24
],
icon
:
'circle'
,
itemWidth
:
12
,
itemHeight
:
12
,
data
:
allNames
,
// 隐藏内置分页按钮与页码(使用 graphic 自定义左右箭头)
pageButtonPosition
:
'end'
,
pageIconSize
:
0
,
pageButtonGap
:
0
,
pageFormatter
:
()
=>
''
,
pageTextStyle
:
{
fontSize
:
0
,
color
:
'transparent'
},
textStyle
:
{
fontFamily
:
'Source Han Sans CN'
,
fontWeight
:
400
,
fontSize
:
14
,
lineHeight
:
24
,
letterSpacing
:
0
,
align
:
'left'
,
color
:
'rgb(95, 101, 108)'
}
},
graphic
:
[
{
show
:
legendLine2
.
length
>
0
,
type
:
'plain'
,
data
:
legendLine2
,
top
:
30
,
left
:
'center'
,
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
,
itemHeight
:
12
type
:
'polygon'
,
name
:
'__legend_prev__'
,
left
:
10
,
top
:
14
,
shape
:
{
points
:
[[
8
,
0
],
[
0
,
6
],
[
8
,
12
]]
},
style
:
{
fill
:
'rgb(95, 101, 108)'
},
cursor
:
'pointer'
,
tooltip
:
{
show
:
false
},
silent
:
false
,
z
:
100
},
{
show
:
legendLine3
.
length
>
0
,
type
:
'plain'
,
data
:
legendLine3
,
top
:
56
,
left
:
'center'
,
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
,
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 一致)
...
...
src/views/thinkTank/ThinkTankDetail/PolicyTracking/utils/piechart.js
浏览文件 @
a9c00d6f
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getPieChart
=
(
data
)
=>
{
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
)
=>
{
const
color
=
d
?.
color
if
(
!
color
)
return
d
const
seriesData
=
(
Array
.
isArray
(
data
)
?
data
:
[]).
map
((
d
,
index
)
=>
{
const
color
=
MUTICHARTCOLORS
[
index
%
MUTICHARTCOLORS
.
length
]
return
{
...
d
,
itemStyle
:
{
...(
d
.
itemStyle
||
{}),
color
},
itemStyle
:
{
...(
d
?
.
itemStyle
||
{}),
color
},
// “飞线”(labelLine)跟随领域色
labelLine
:
{
...(
d
.
labelLine
||
{}),
lineStyle
:
{
...(
d
.
labelLine
?.
lineStyle
||
{}),
color
}
...(
d
?
.
labelLine
||
{}),
lineStyle
:
{
...(
d
?
.
labelLine
?.
lineStyle
||
{}),
color
}
}
}
})
...
...
@@ -29,10 +30,14 @@ const getPieChart = (data) => {
alignTo
:
'edge'
,
formatter
:
params
=>
{
const
name
=
params
.
name
||
""
;
const
value
=
params
.
value
??
""
;
const
percent
=
params
.
percent
!=
null
?
Math
.
round
(
params
.
percent
)
:
0
;
const
value
=
Number
(
params
.
value
??
0
)
||
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
,
edgeDistance
:
20
,
...
...
src/views/thinkTank/ThinkTankDetail/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -64,6 +64,7 @@ import PolicyTracking from "./PolicyTracking/index.vue";
import
ThinkInfo
from
"./thinkInfo/index.vue"
;
import
{
getThinkTankSummary
}
from
"@/api/thinkTank/overview"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
getDecodedParams
}
from
"@/utils/goToPage"
;
const
router
=
useRouter
();
const
tabActiveName
=
ref
(
"智库动态"
);
...
...
@@ -74,9 +75,13 @@ const switchTab = name => {
const
thinkTank
=
ref
({});
// 获取智库基本信息
const
handleGetThinkTankSummary
=
async
()
=>
{
const
id
=
getDecodedParams
()
try
{
const
parmas
=
{
id
:
router
.
currentRoute
.
_value
.
params
.
id
id
:
id
};
const
res
=
await
getThinkTankSummary
(
parmas
);
console
.
log
(
"智库信息"
,
res
);
...
...
@@ -120,9 +125,6 @@ onMounted(async () => {
box-shadow
:
0
0
20px
0
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
position
:
relative
;
position
:
sticky
;
top
:
0
;
z-index
:
99999
;
overflow
:
visible
;
.header-top
{
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/CongressHearing/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -72,38 +72,46 @@
</div>
-->
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id"
>
<div
class=
"card-item"
>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
@
click=
"emit('report-click', item)"
>
<span
v-html=
"highlightText(item.title)"
></span>
<template
v-if=
"hasData"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id"
>
<div
class=
"card-item"
>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
@
click=
"emit('report-click', item)"
>
<span
v-html=
"highlightText(item.title)"
></span>
</div>
<div
class=
"card-item-time"
>
<span
v-html=
"highlightText(item.time + ' · ' + item.content)"
></span>
<img
src=
"../images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
</div>
<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"
/>
</div>
</div>
<div
class=
"card-item-time"
>
<span
v-html=
"highlightText(item.time + ' · ' + item.content)"
></span>
<img
src=
"../images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
</div>
<div
class=
"card-item-category"
>
<AreaTag
v-for=
"(val, idx) in item.category"
:key=
"idx"
:tagName=
"val"
/>
</div>
</div>
</div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></div>
</div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></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
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇国会听证会
</div>
<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"
/>
</div>
</div>
...
...
@@ -169,6 +177,10 @@ const props = defineProps({
searchKeyword
:
{
type
:
String
,
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
...
...
@@ -181,6 +193,12 @@ const emit = defineEmits([
// 解构 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
;
function
getDateYearsAgo
(
years
)
{
...
...
@@ -488,8 +506,11 @@ const handleToReportDetail = item => {
.right
{
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
flex
:
1
;
...
...
@@ -498,6 +519,15 @@ const handleToReportDetail = item => {
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
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
border-radius
:
10px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/SurveyForm/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -52,28 +52,35 @@
</div>
-->
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
item.projectCoverImgUrl
alt=
""
/>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
item.projectCoverImgUrl
alt=
""
/>
</div>
<div
class=
"footer-card-title"
>
<span
v-html=
"highlightText(item.projectNameZh)"
></span>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
startDate
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
<div
class=
"footer-card-title"
>
<span
v-html=
"highlightText(item.projectNameZh)"
></span>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
startDate
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
</
template
>
</div>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇调查项目
</div>
<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"
/>
</div>
</div>
...
...
@@ -81,7 +88,7 @@
</div>
</template>
<
script
setup
>
import
{
ref
,
toRefs
,
watch
}
from
"vue"
;
import
{
ref
,
toRefs
,
watch
,
computed
}
from
"vue"
;
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
...
...
@@ -126,6 +133,10 @@ const props = defineProps({
searchKeyword
:
{
type
:
String
,
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
...
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
// 解构 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
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
...
...
@@ -360,15 +377,27 @@ const handleToReportDetail = item => {
.right
{
width
:
1284px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
flex
:
1
;
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
13px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
width
:
398px
;
height
:
300px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/ThinkTankReport/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -52,28 +52,35 @@
</div>
-->
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"handleToReportDetail(item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
</div>
<div
class=
"footer-card-title"
>
<span
v-html=
"highlightText(item.name)"
></span>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
times
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
<div
class=
"footer-card-title"
>
<span
v-html=
"highlightText(item.name)"
></span>
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
times
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
</
template
>
</div>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇智库报告
</div>
<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"
/>
</div>
</div>
...
...
@@ -81,7 +88,7 @@
</div>
</template>
<
script
setup
>
import
{
ref
,
toRefs
,
watch
}
from
"vue"
;
import
{
ref
,
toRefs
,
watch
,
computed
}
from
"vue"
;
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
...
...
@@ -126,6 +133,10 @@ const props = defineProps({
searchKeyword
:
{
type
:
String
,
default
:
""
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
...
...
@@ -138,6 +149,12 @@ const emit = defineEmits([
// 解构 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
selectedResearchTimeIds
=
ref
([
RESOURCE_FILTER_ALL_TIME
]);
...
...
@@ -360,15 +377,28 @@ const handleToReportDetail = item => {
}
.right
{
width
:
1284px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
flex
:
1
;
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
13px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
width
:
398px
;
height
:
300px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkDynamics/index.vue
浏览文件 @
a9c00d6f
<
template
>
<div
class=
"wrap"
>
<div
class=
"wrap"
ref=
"pageScrollRef"
>
<div
class=
"main-header"
>
<div
class=
"search-box"
>
...
...
@@ -19,33 +19,8 @@
<div
class=
"select-box"
>
<div
class=
"select-box-sort"
>
<el-select
v-model=
"sort"
placeholder=
"发布时间"
style=
"width: 120px"
:teleported=
"true"
:placement=
"'bottom-start'"
:popper-options=
"
{
modifiers: [
{
name: 'preventOverflow', // 禁用自动翻转逻辑
options: {
mainAxis: false, // 禁用垂直方向的自动调整
altAxis: false, // 禁用水平方向的自动调整
}
},
{
name: 'flip', // 完全禁用翻转功能
enabled: false
}
]
}">
<template
#
prefix
>
<img
v-if=
"sort !== true"
src=
"./images/image down.png"
class=
"select-prefix-img"
alt=
""
@
click
.
stop=
"toggleSortAndFetch()"
/>
<img
v-else
src=
"./images/image up.png"
class=
"select-prefix-img"
alt=
""
@
click
.
stop=
"toggleSortAndFetch()"
/>
</
template
>
<el-option
:key=
"'think-dynamics-sort-asc'"
label=
"正序"
:value=
"true"
@
click=
"handleGetThinkDynamicsReport()"
/>
<el-option
:key=
"'think-dynamics-sort-desc'"
label=
"倒序"
:value=
"false"
@
click=
"handleGetThinkDynamicsReport()"
/>
</el-select>
<TimeSortSelectBox
:key=
"`dynamics-sort-$
{tabResetKey}`" :sort-demension="1"
@handle-px-change="handleDynamicsPxChange" />
</div>
</div>
</div>
...
...
@@ -53,18 +28,19 @@
<ThinkTankReport
v-if=
"isThinkTankReport"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
:key=
"`智库报告-$
{tabResetKey}`" :selected-filters="selectedFilters"
:cur-footer-list="curFooterList" :total="total" :current-page="currentPage" :search-keyword="searchReport"
@
update:selected-filters=
"handleSelectedFiltersUpdate"
:loading="isThinkTankReportLoading"
@update:selected-filters="handleSelectedFiltersUpdate"
@filter-change="(payload) => handleGetThinkDynamicsReport(payload)" @page-change="handleCurrentChange"
@report-click="handleToReportDetail" />
<CongressHearing
v-else-if=
"isCongressHearing"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
:key=
"`国会听证会-$
{tabResetKey}`" :research-hearing-list="researchHearingList"
: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"
@report-click="handleToHearingDetail" />
<SurveyForm
v-else-if=
"isSurveyForm"
:research-type-list=
"researchTypeList"
:research-time-list=
"researchTimeList"
: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"
@filter-change="(payload) => handleGetThinkDynamicsReport(payload)" @page-change="handleCurrentChange"
@report-click="handleToProjectDetail" />
...
...
@@ -74,6 +50,7 @@
<
script
setup
>
import
{
ref
,
reactive
,
onMounted
,
nextTick
}
from
"vue"
;
import
SurveyForm
from
"./SurveyForm/index.vue"
import
TimeSortSelectBox
from
"@/components/base/TimeSortSelectBox/index.vue"
;
// import Img1 from "./images/img1.png";
// import Img2 from "./images/img2.png";
// import Img3 from "./images/img3.png";
...
...
@@ -108,6 +85,10 @@ const isThinkTankReport = ref(true);
const
isSurveyForm
=
ref
(
false
);
const
isCongressHearing
=
ref
(
false
);
const
searchReport
=
ref
(
''
)
// 智库详情-智库动态:右侧列表 loading(居中显示)
const
isThinkTankReportLoading
=
ref
(
false
);
const
isSurveyFormLoading
=
ref
(
false
);
const
isCongressHearingLoading
=
ref
(
false
);
const
handleToReportDetail
=
(
item
)
=>
{
window
.
sessionStorage
.
setItem
(
'curTabName'
,
item
.
name
)
...
...
@@ -248,6 +229,10 @@ const handleChooseType = async (type) => {
await
handleGetThinkDynamicsReport
()
}
const
researchTimeList
=
ref
([
{
id
:
'2026年'
,
name
:
'2026年'
,
},
{
id
:
'2025年'
,
name
:
'2025年'
,
...
...
@@ -320,14 +305,37 @@ const handleGetHylyList = async () => {
}
};
const
toggleSortAndFetch
=
async
()
=>
{
sort
.
value
=
sort
.
value
===
true
?
false
:
true
;
/** 智库动态排序公共组件回调:1=时间倒序,2=时间正序(映射到现有 sort(true/false/null)) */
const
handleDynamicsPxChange
=
async
(
val
)
=>
{
sort
.
value
=
Number
(
val
)
===
2
?
true
:
false
;
await
handleGetThinkDynamicsReport
();
};
const
currentPage
=
ref
(
1
);
const
pageScrollRef
=
ref
(
null
);
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
=>
{
currentPage
.
value
=
page
;
scrollToTop
();
handleGetThinkDynamicsReport
()
};
...
...
@@ -395,6 +403,11 @@ const handleGetThinkDynamicsReport = async (payload) => {
return
;
}
try
{
// 仅当前 tab 显示 loading,其它置 false,避免切换后残留
isThinkTankReportLoading
.
value
=
isThinkTankReport
.
value
;
isSurveyFormLoading
.
value
=
isSurveyForm
.
value
;
isCongressHearingLoading
.
value
=
isCongressHearing
.
value
;
const
strippedTime
=
stripAllTimeForRequest
(
nextFilters
.
researchTimeIds
||
[]);
const
allTimeIds
=
(
researchTimeList
.
value
||
[]).
map
((
x
)
=>
x
.
id
);
const
{
startDate
,
endDate
}
=
getResourceLibraryReportDateRangeFromTimeSelection
(
...
...
@@ -415,6 +428,9 @@ const handleGetThinkDynamicsReport = async (payload) => {
thinkTankId
,
pageNum
:
Math
.
max
(
0
,
(
currentPage
.
value
||
1
)),
pageSize
:
10
,
// 国会听证会:排序语义与其它资源库相反(正序→desc,倒序→asc)
sortField
:
"createTime"
,
sortOrder
:
sort
.
value
===
true
?
"desc"
:
"asc"
,
domainIds
,
startDate
,
...
...
@@ -481,6 +497,10 @@ const handleGetThinkDynamicsReport = async (payload) => {
}
}
catch
(
error
)
{
console
.
error
(
"获取智库动态报告error"
,
error
);
}
finally
{
isThinkTankReportLoading
.
value
=
false
;
isSurveyFormLoading
.
value
=
false
;
isCongressHearingLoading
.
value
=
false
;
}
};
...
...
@@ -593,14 +613,6 @@ onMounted(async () => {
margin-top
:
16px
;
display
:
flex
;
.select-box-time
,
.select-box-sort
{
background
:
rgb
(
255
,
255
,
255
);
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
border
:
1px
solid
rgb
(
230
,
231
,
232
);
border-radius
:
4px
;
height
:
32px
;
}
.select-prefix-img
{
width
:
8px
;
...
...
src/views/thinkTank/ThinkTankDetail/thinkInfo/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -115,7 +115,7 @@
</div>
<div
class=
"source"
>
<div
class=
"info"
><img
src=
"./images/image-exclamation.png"
></div>
<div
class=
"text"
>
数据来源:美国国会官网
,数据时间:2015.1至2025.12
</div>
<div
class=
"text"
>
数据来源:美国国会官网
</div>
</div>
</AnalysisBox>
</div>
...
...
@@ -222,7 +222,7 @@
</div>
<div
class=
"source"
>
<div
class=
"info"
><img
src=
"./images/image-exclamation.png"
></div>
<div
class=
"text"
>
数据来源:美国国会官网
,数据时间:2015.1至2025.12
</div>
<div
class=
"text"
>
数据来源:美国国会官网
</div>
</div>
<div
class=
"middle"
>
<div
class=
"middle-text"
>
{{
"共"
}}{{
personTotal
}}{{
"名核心研究人员"
}}
</div>
...
...
src/views/thinkTank/ThinkTankDetail/thinkInfo/utils/piechart.js
浏览文件 @
a9c00d6f
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
const
getPieChart
=
(
data
)
=>
{
let
option
=
{
series
:
[
...
...
@@ -53,7 +55,10 @@ const getPieChart = (data) => {
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
...
...
src/views/thinkTank/ThinkTankDetail/thinkInfo/utils/treeMapChart.js
浏览文件 @
a9c00d6f
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
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
=
{
tooltip
:
{
trigger
:
'item'
,
...
...
@@ -14,7 +25,7 @@ const getTreeMapChart = (treemapData) => {
series
:
[
{
type
:
'treemap'
,
data
:
treemapData
,
data
:
dataWithColors
,
roam
:
false
,
nodeClick
:
false
,
breadcrumb
:
{
...
...
src/views/thinkTank/TipTab/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -32,7 +32,7 @@ const props = defineProps({
width
:
100
%
;
display
:
flex
;
gap
:
8
px
;
justify
-
content
:
center
;
justify
-
content
:
flex
-
start
;
align
-
items
:
center
;
height
:
22
px
;
...
...
src/views/thinkTank/allThinkTank/index.vue
浏览文件 @
a9c00d6f
...
...
@@ -474,7 +474,7 @@ onMounted(async () => {
display
:
inline-flex
;
position
:
absolute
;
left
:
277
px
;
right
:
-8
px
;
bottom
:
208px
;
background-color
:
rgba
(
255
,
77
,
79
,
1
);
align-items
:
center
;
...
...
src/views/thinkTank/components/HomeMainFooterMain.vue
浏览文件 @
a9c00d6f
...
...
@@ -36,26 +36,33 @@
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
</div>
<div
class=
"footer-card-title"
>
{{
item
.
name
}}
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
times
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
<div
class=
"footer-card-title"
>
{{
item
.
name
}}
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
times
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinkTankName
}}
</div>
</div>
</div>
</
template
>
</div>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇智库报告
</div>
<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"
/>
</div>
</div>
...
...
@@ -64,20 +71,22 @@
</template>
<
script
setup
>
import
{
computed
}
from
"vue"
;
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
normalizeExclusiveAllOption
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
const
emit
=
defineEmits
([
...
...
@@ -102,6 +111,13 @@ const formatDate = (str) => {
const
[
y
,
m
,
d
]
=
str
.
split
(
'T'
)[
0
].
split
(
'-'
)
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
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -195,14 +211,25 @@ const formatDate = (str) => {
.right
{
width
:
1284px
;
max-height
:
1377px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
width
:
1226px
;
flex
:
1
;
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
16px
16px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
width
:
398px
;
height
:
300px
;
...
...
src/views/thinkTank/components/HomeMainFooterSurvey.vue
浏览文件 @
a9c00d6f
...
...
@@ -36,26 +36,33 @@
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.projectCoverImgUrl"
alt=
""
/>
<template
v-if=
"hasData"
>
<div
class=
"footer-card"
v-for=
"(item, index) in curFooterList"
:key=
"index"
@
click=
"emit('report-click', item)"
>
<div
class=
"footer-card-top"
>
<img
:src=
"item.projectCoverImgUrl"
alt=
""
/>
</div>
<div
class=
"footer-card-title"
>
{{
item
.
projectNameZh
}}
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
startDate
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
<div
class=
"footer-card-title"
>
{{
item
.
projectNameZh
}}
</
template
>
<
template
v-else-if=
"!loading"
>
<div
class=
"right-empty"
>
<el-empty
class=
"right-el-empty"
description=
"暂无数据"
:image-size=
"100"
/>
</div>
<div
class=
"footer-card-footer"
>
<div
class=
"time"
>
{{
formatDate
(
item
.
startDate
)
}}
</div>
<div
class=
"from"
>
{{
item
.
thinktankName
}}
</div>
</div>
</div>
</
template
>
</div>
<div
class=
"right-footer"
>
<div
class=
"info"
>
共 {{ total }} 篇调查项目
</div>
<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"
/>
</div>
</div>
...
...
@@ -64,20 +71,22 @@
</template>
<
script
setup
>
import
{
computed
}
from
"vue"
;
import
{
RESOURCE_FILTER_ALL_AREA
,
RESOURCE_FILTER_ALL_TIME
,
normalizeExclusiveAllOption
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
areaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedAreaList
:
{
type
:
Array
,
default
:
()
=>
[]
},
pubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
curFooterList
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
const
formatDate
=
(
str
)
=>
{
if
(
!
str
)
return
''
...
...
@@ -101,6 +110,13 @@ const handleTimeGroupChange = (val) => {
emit
(
"update:selectedPubTimeList"
,
normalizeExclusiveAllOption
(
val
,
RESOURCE_FILTER_ALL_TIME
));
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
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -170,14 +186,25 @@ const handleTimeGroupChange = (val) => {
.right
{
width
:
1284px
;
max-height
:
1377px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
width
:
1226px
;
flex
:
1
;
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
16px
16px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.footer-card
{
width
:
398px
;
...
...
src/views/thinkTank/components/ThinkTankCongressHearingOverview.vue
浏览文件 @
a9c00d6f
...
...
@@ -55,42 +55,47 @@
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<
div
class=
"card-content
"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
@
click=
"emit('report-click', item)"
>
{{
item
.
titleZh
}}
</div>
<div
class=
"card-item-time"
>
{{
item
.
testimonyDate
+
' · '
+
item
.
thinkTankName
+
' · '
+
item
.
committeeZh
}}
<img
src=
"../ThinkTankDetail/thinkDynamics/images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
</div>
<
template
v-if=
"hasData
"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in hearingData"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
>
<img
class=
"card-item-img"
:src=
"item.coverImgUrl"
alt=
"report image"
/>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
@
click=
"emit('report-click', item)"
>
{{
item
.
titleZh
}}
</div>
<div
class=
"card-item-time"
>
{{
item
.
testimonyDate
+
' · '
+
item
.
thinkTankName
+
' · '
+
item
.
committeeZh
}}
<img
src=
"../ThinkTankDetail/thinkDynamics/images/image open.png"
alt=
"open icon"
class=
"card-open-image"
/>
</div>
<div
class=
"card-item-category"
v-if=
"item.domains"
>
<div
v-for=
"(value, index) in item.domains"
:key=
"`domain-$
{index}`">
<AreaTag
:key=
"`cat-$
{item.id}`" :tagName="value" />
<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}`">
<AreaTag
:key=
"`cat-$
{item.id}`" :tagName="value" />
</div>
</div>
</div>
</div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></div>
</div>
<div
class=
"divider"
v-if=
"index !== hearingData.length - 1"
></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
class=
"right-footer"
>
<div
class=
"info"
>
共
{{
hearingData
.
length
}}
篇国会听证会
共 {{
total
}} 篇国会听证会
</div>
<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"
/>
</div>
</div>
...
...
@@ -121,7 +126,8 @@ const props = defineProps({
selectedPubTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
hearingData
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
}
currentPage
:
{
type
:
Number
,
default
:
1
},
loading
:
{
type
:
Boolean
,
default
:
false
}
});
const
emit
=
defineEmits
([
...
...
@@ -133,6 +139,12 @@ const emit = defineEmits([
]);
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
(()
=>
(
Array
.
isArray
(
props
.
selectedAreaList
)
&&
props
.
selectedAreaList
.
length
...
...
@@ -274,11 +286,13 @@ const handlePageChange = page => {
.right
{
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
background
:
rgba
(
255
,
255
,
255
,
1
);
box-sizing
:
border-box
;
...
...
@@ -287,6 +301,15 @@ const handlePageChange = page => {
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
padding-right
:
36px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.card-content
{
width
:
1211px
;
...
...
src/views/thinkTank/components/ThinkTankPolicyAdviceOverview.vue
浏览文件 @
a9c00d6f
...
...
@@ -36,54 +36,61 @@
</div>
</div>
<div
class=
"right"
>
<div
class=
"right"
v-loading=
"loading"
>
<div
class=
"card-box"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in list"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
@
click=
"emit('item-click', item)"
>
<div
class=
"card-item-img"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
</div>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
>
{{
item
.
name
}}
</div>
<div
class=
"card-item-time"
>
<span
class=
"info-text"
>
{{
formatDate
(
item
.
times
)
}}
·
{{
item
.
thinkTankName
}}
·
{{
item
.
reportName
}}
</span>
<div
class=
"card-open-image"
@
click
.
stop=
"handleOpenReportOriginal(item)"
>
<img
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open.png"
alt=
""
/>
</div>
</div>
<div
class=
"card-item-category"
v-if=
"item.tagList && item.tagList.length"
>
<AreaTag
v-for=
"d in item.tagList"
:key=
"d"
:tagName=
"d"
/>
<template
v-if=
"hasData"
>
<div
class=
"card-content"
>
<div
v-for=
"(item, index) in list"
:key=
"item.id ?? index"
>
<div
class=
"card-item"
@
click=
"emit('item-click', item)"
>
<div
class=
"card-item-img"
>
<img
:src=
"item.imageUrl"
alt=
""
/>
</div>
<div
class=
"file-box"
>
<div
class=
"
file"
v-for=
"sv in item.billInfoList"
:key=
"sv.id || sv.na
me"
>
<
div
class=
"type"
>
法案
</div>
<div
class=
"title"
>
{{
sv
.
name
}}
</div
>
<div
class=
"
more"
@
click
.
stop=
"handleBillMoreClick(sv
)"
>
<img
src=
"
../assets/images/image-right
.png"
alt=
""
/>
<div
class=
"card-item-text"
>
<div
class=
"card-item-title"
>
{{
item
.
name
}}
</div
>
<div
class=
"
card-item-ti
me"
>
<
span
class=
"info-text"
>
{{
formatDate
(
item
.
times
)
}}
·
{{
item
.
thinkTankName
}}
·
{{
item
.
reportName
}}
</span
>
<div
class=
"
card-open-image"
@
click
.
stop=
"handleOpenReportOriginal(item
)"
>
<img
src=
"
@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open
.png"
alt=
""
/>
</div>
</div>
<div
class=
"file"
v-for=
"(sv, index) in item.administrativeOrderInfoVOList"
:key=
"index"
>
<div
class=
"type"
>
政令
</div>
<div
class=
"title"
>
{{
sv
.
content
}}
</div>
<div
class=
"more"
@
click
.
stop=
"handleAdministrativeMoreClick(sv)"
>
<img
src=
"../assets/images/image-right.png"
alt=
""
/>
</div>
<div
class=
"card-item-category"
v-if=
"item.tagList && item.tagList.length"
>
<AreaTag
v-for=
"d in item.tagList"
:key=
"d"
:tagName=
"d"
/>
</div>
<div
class=
"file-box"
>
<div
class=
"file"
v-for=
"sv in item.billInfoList"
:key=
"sv.id || sv.name"
>
<div
class=
"type"
>
法案
</div>
<div
class=
"title"
>
{{
sv
.
name
}}
</div>
<div
class=
"more"
@
click
.
stop=
"handleBillMoreClick(sv)"
>
<img
src=
"../assets/images/image-right.png"
alt=
""
/>
</div>
</div>
<div
class=
"file"
v-for=
"(sv, index) in item.administrativeOrderInfoVOList"
:key=
"index"
>
<div
class=
"type"
>
政令
</div>
<div
class=
"title"
>
{{
sv
.
content
}}
</div>
<div
class=
"more"
@
click
.
stop=
"handleAdministrativeMoreClick(sv)"
>
<img
src=
"../assets/images/image-right.png"
alt=
""
/>
</div>
</div>
</div>
</div>
</div>
<div
class=
"divider"
v-if=
"index !== list.length - 1"
></div>
</div>
<div
class=
"divider"
v-if=
"index !== list.length - 1"
></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
class=
"right-footer"
>
<div
class=
"info"
>
共{{ total }}篇政策建议
</div>
<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"
/>
</div>
</div>
...
...
@@ -92,7 +99,7 @@
</template>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
{
ref
,
computed
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
import
{
...
...
@@ -103,13 +110,14 @@ import {
stripAllTimeForRequest
}
from
"../utils/resourceLibraryFilters"
;
defineProps
({
const
props
=
defineProps
({
researchTypeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
researchTimeList
:
{
type
:
Array
,
default
:
()
=>
[]
},
list
:
{
type
:
Array
,
default
:
()
=>
[]
},
total
:
{
type
:
Number
,
default
:
0
},
currentPage
:
{
type
:
Number
,
default
:
1
},
pageSize
:
{
type
:
Number
,
default
:
7
},
loading
:
{
type
:
Boolean
,
default
:
false
},
});
const
emit
=
defineEmits
([
"filter-change"
,
"page-change"
,
"item-click"
]);
...
...
@@ -158,6 +166,13 @@ const handleAdministrativeMoreClick = (ad) => {
const
selectedTypeIds
=
ref
([
RESOURCE_FILTER_ALL_AREA
]);
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
=
()
=>
{
emit
(
"filter-change"
,
{
researchTypeIds
:
stripAllAreaForRequest
(
selectedTypeIds
.
value
),
...
...
@@ -298,11 +313,14 @@ const handleYearGroupChange = (val) => {
.right
{
width
:
1224px
;
display
:
flex
;
flex-direction
:
column
;
.card-box
{
width
:
100%
;
height
:
1134px
;
padding-bottom
:
32px
;
;
display
:
flex
;
background
:
rgba
(
255
,
255
,
255
,
1
);
box-sizing
:
border-box
;
...
...
@@ -311,9 +329,18 @@ const handleYearGroupChange = (val) => {
box-shadow
:
0px
0px
20px
0px
rgba
(
94
,
95
,
95
,
0
.1
);
padding-right
:
36px
;
.right-empty
{
width
:
100%
;
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
24px
;
}
.card-content
{
width
:
1211px
;
height
:
1067px
;
margin-top
:
33px
;
margin-left
:
37px
;
...
...
@@ -332,7 +359,7 @@ const handleYearGroupChange = (val) => {
.card-item-img
{
width
:
56px
;
width
:
1
56px
;
height
:
77px
;
margin-right
:
22px
;
flex-shrink
:
0
;
...
...
src/views/thinkTank/index.vue
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/reportOriginal/index.vue
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/reportOriginal/pdf.vue
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/utils/multiLineChart.js
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/utils/piechart.js
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/utils/resourceLibraryFilters.js
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/thinkTank/utils/sankey.js
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/viewRiskSignal/assets/images/risk-icon-blue.png
0 → 100644
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/viewRiskSignal/assets/images/risk-icon-green.png
0 → 100644
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/viewRiskSignal/assets/images/risk-icon-orange.png
0 → 100644
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/viewRiskSignal/assets/images/risk-icon.png
→
src/views/viewRiskSignal/assets/images/risk-icon
-red
.png
浏览文件 @
a9c00d6f
File moved
src/views/viewRiskSignal/assets/images/risk-icon-yellow.png
0 → 100644
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
src/views/viewRiskSignal/assets/images/warning.svg
→
src/views/viewRiskSignal/assets/images/warning
-red
.svg
浏览文件 @
a9c00d6f
File moved
src/views/viewRiskSignal/index.vue
浏览文件 @
a9c00d6f
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论