Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
d454ba89
提交
d454ba89
authored
4月 22, 2026
作者:
张烨
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'origin/pre' into zy-dev
上级
b86e5c64
bb55d9a0
流水线
#599
已通过 于阶段
in 1 分 36 秒
变更
46
流水线
1
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
46 个修改的文件
包含
1134 行增加
和
518 行删除
+1134
-518
index.vue
src/components/base/RiskSignalOverviewDetailDialog/index.vue
+28
-43
index.vue
src/components/base/TimePickerBox/index.vue
+221
-0
index.js
src/router/index.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
+32
-86
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
+2
-2
index.vue
src/views/coopRestriction/components/resLib/index.vue
+18
-34
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
ChartAiAnalysis.vue
src/views/exportControl/components/ChartAiAnalysis.vue
+128
-0
title.vue
src/views/exportControl/components/title.vue
+2
-2
index.vue
src/views/exportControl/index.vue
+0
-0
index.vue
...ol/v2.0SingleSanction/components/dataStatistics/index.vue
+0
-0
index.vue
...v2.0SingleSanction/components/sanctionsOverview/index.vue
+71
-34
index.vue
...ews/exportControl/v2.0SingleSanction/originPage/index.vue
+4
-4
title.vue
src/views/finance/components/title.vue
+2
-2
index.vue
src/views/finance/index.vue
+169
-31
index.vue
...nce/singleSanction/components/sanctionsOverview/index.vue
+78
-36
index.vue
src/views/finance/singleSanction/index.vue
+1
-1
index.vue
src/views/finance/singleSanction/originPage/index.vue
+4
-4
index.vue
src/views/thinkTank/CongressHearingView/index.vue
+25
-6
index.vue
src/views/thinkTank/ReportDetail/index.vue
+0
-3
index.vue
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
+25
-3
index.vue
src/views/thinkTank/SurveyProjectView/index.vue
+1
-4
index.vue
src/views/thinkTank/ThinkTankDetail/PolicyTracking/index.vue
+22
-29
index.vue
src/views/thinkTank/ThinkTankDetail/index.vue
+6
-4
index.vue
src/views/thinkTank/ThinkTankDetail/thinkDynamics/index.vue
+30
-40
index.vue
src/views/thinkTank/ThinkTankDetail/thinkInfo/index.vue
+2
-2
index.vue
src/views/thinkTank/TipTab/index.vue
+1
-1
ThinkTankPolicyAdviceOverview.vue
...ws/thinkTank/components/ThinkTankPolicyAdviceOverview.vue
+3
-2
index.vue
src/views/thinkTank/index.vue
+30
-29
index.vue
src/views/thinkTank/reportOriginal/index.vue
+2
-5
pdf.vue
src/views/thinkTank/reportOriginal/pdf.vue
+82
-50
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
+25
-6
没有找到文件。
src/components/base/RiskSignalOverviewDetailDialog/index.vue
浏览文件 @
d454ba89
<
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
浏览文件 @
d454ba89
<
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/index.js
浏览文件 @
d454ba89
...
...
@@ -147,7 +147,7 @@ const router = createRouter({
// 2)登录成功回跳带 ?token=:先 setToken 并同步 bootId,再去掉 URL 中的 token(须先于 clearTokenIfNewDevBoot,避免误清刚写入的登录态)
// 3)已有本地 token:正常走前端路由
router
.
beforeEach
((
to
,
from
,
next
)
=>
{
//
【新增】
在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 这能防止旧页面的数据回来覆盖新页面,也能减少服务器压力
cancelAllRequests
();
if
(
import
.
meta
.
env
.
DEV
)
{
...
...
src/styles/components/TimePickerBox/index.vue
0 → 100644
浏览文件 @
d454ba89
<
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
浏览文件 @
d454ba89
...
...
@@ -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
浏览文件 @
d454ba89
差异被折叠。
点击展开。
src/utils/setChart.js
浏览文件 @
d454ba89
...
...
@@ -4,69 +4,7 @@ import getQuarterRange from './getQuarterRange';
import
*
as
echarts
from
'echarts'
import
'echarts-wordcloud'
;
import
router
from
'@/router/index'
const
LEGEND_ARROW_NAMES
=
new
Set
([
'__legend_prev__'
,
'__legend_next__'
])
const
shouldShowLegendPagingArrows
=
(
chart
,
option
)
=>
{
const
w
=
typeof
chart
?.
getWidth
===
'function'
?
chart
.
getWidth
()
:
0
const
legendOpt
=
option
?.
legend
const
legend
=
Array
.
isArray
(
legendOpt
)
?
legendOpt
[
0
]
:
legendOpt
if
(
!
legend
||
legend
.
type
!==
'scroll'
)
return
false
const
names
=
Array
.
isArray
(
legend
.
data
)
?
legend
.
data
:
[]
if
(
!
names
.
length
)
return
false
const
resolveSize
=
(
val
,
base
)
=>
{
if
(
val
==
null
)
return
0
if
(
typeof
val
===
'number'
&&
Number
.
isFinite
(
val
))
return
val
const
s
=
String
(
val
).
trim
()
if
(
!
s
)
return
0
if
(
s
.
endsWith
(
'%'
))
{
const
n
=
parseFloat
(
s
.
slice
(
0
,
-
1
))
return
Number
.
isFinite
(
n
)
?
(
base
*
n
)
/
100
:
0
}
const
n
=
Number
(
s
)
return
Number
.
isFinite
(
n
)
?
n
:
0
}
const
available
=
resolveSize
(
legend
.
width
,
w
)
||
Math
.
floor
(
w
*
0.82
)
if
(
!
available
||
available
<=
0
)
return
true
const
fontSize
=
Number
(
legend
?.
textStyle
?.
fontSize
||
14
)
const
fontFamily
=
legend
?.
textStyle
?.
fontFamily
||
'Source Han Sans CN'
const
fontWeight
=
legend
?.
textStyle
?.
fontWeight
||
400
const
iconW
=
Number
(
legend
?.
itemWidth
||
12
)
const
gap
=
10
const
paddingPerItem
=
iconW
+
gap
+
10
const
canvas
=
document
.
createElement
(
'canvas'
)
const
ctx
=
canvas
.
getContext
(
'2d'
)
if
(
!
ctx
)
return
true
ctx
.
font
=
`
${
fontWeight
}
${
fontSize
}
px
${
fontFamily
}
`
const
total
=
names
.
reduce
((
sum
,
n
)
=>
{
const
text
=
String
(
n
??
''
)
const
textW
=
ctx
.
measureText
(
text
).
width
||
0
return
sum
+
textW
+
paddingPerItem
},
0
)
return
total
>
available
}
const
applyLegendPagingArrowVisibility
=
(
chart
,
option
)
=>
{
const
shouldShow
=
shouldShowLegendPagingArrows
(
chart
,
option
)
const
graphic
=
option
?.
graphic
if
(
!
Array
.
isArray
(
graphic
)
||
graphic
.
length
===
0
)
return
const
nextGraphic
=
graphic
.
map
((
g
)
=>
{
if
(
!
g
||
!
LEGEND_ARROW_NAMES
.
has
(
g
.
name
))
return
g
return
{
...
g
,
invisible
:
!
shouldShow
,
silent
:
!
shouldShow
}
})
chart
.
setOption
({
graphic
:
nextGraphic
},
false
,
true
)
}
import
{
goToDataCountryBill
}
from
'./goToPage'
;
const
setChart
=
(
option
,
chartId
,
allowClick
,
selectParam
)
=>
{
let
chartDom
=
document
.
getElementById
(
chartId
);
if
(
!
chartDom
)
{
...
...
@@ -114,20 +52,22 @@ const setChart = (option, chartId, allowClick, selectParam) => {
// 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
...
...
@@ -142,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
'政令'
:
...
...
src/views/bill/billHome/index.vue
浏览文件 @
d454ba89
...
...
@@ -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
浏览文件 @
d454ba89
<
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
浏览文件 @
d454ba89
...
...
@@ -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
浏览文件 @
d454ba89
...
...
@@ -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 即可恢复走接口
...
...
src/views/coopRestriction/components/resLib/index.vue
浏览文件 @
d454ba89
...
...
@@ -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>
...
...
@@ -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
([
{
...
...
src/views/coopRestriction/detail/index.vue
浏览文件 @
d454ba89
...
...
@@ -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
浏览文件 @
d454ba89
...
...
@@ -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
浏览文件 @
d454ba89
...
...
@@ -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/components/ChartAiAnalysis.vue
0 → 100644
浏览文件 @
d454ba89
<
template
>
<div
class=
"chart-ai-analysis"
@
mouseenter=
"handleMouseEnter"
@
mouseleave=
"handleMouseLeave"
>
<!-- 触发按钮 -->
<div
class=
"ai-button-wrapper"
>
<AiButton
/>
</div>
<!-- AI 内容面板 -->
<transition
name=
"ai-fade"
>
<div
v-if=
"isVisible"
class=
"ai-pane-wrapper"
>
<div
class=
"ai-pane-content"
>
<!-- 1. Loading 状态 -->
<div
v-if=
"hookInstance.loading"
class=
"ai-loading-text"
>
智能总结生成中...
</div>
<!-- 2. 错误状态 -->
<div
v-else-if=
"hookInstance.error"
class=
"ai-error-text"
>
{{
hookInstance
.
error
}}
</div>
<!-- 3. 成功状态 -->
<div
v-else-if=
"hookInstance.interpretation"
class=
"ai-success-content"
>
<AiPane
:aiContent=
"hookInstance.interpretation"
/>
</div>
<!-- 4. 初始空状态 (可选,防止闪烁) -->
<div
v-else
class=
"ai-empty-text"
>
暂无数据
</div>
</div>
</div>
</transition>
</div>
</
template
>
<
script
setup
>
import
{
ref
}
from
"vue"
;
import
AiButton
from
"@/components/base/Ai/AiButton/index.vue"
;
import
AiPane
from
"@/components/base/Ai/AiPane/index.vue"
;
const
props
=
defineProps
({
// 接收由 useChartInterpretation() 创建的实例对象
hookInstance
:
{
type
:
Object
,
required
:
true
},
// 图表数据 Payload,用于首次请求
payload
:
{
type
:
Object
,
required
:
true
}
});
const
isVisible
=
ref
(
false
);
const
hasRequested
=
ref
(
false
);
const
handleMouseEnter
=
()
=>
{
isVisible
.
value
=
true
;
// 如果还没有请求过数据,则发起请求
if
(
!
hasRequested
.
value
&&
!
props
.
hookInstance
.
loading
)
{
hasRequested
.
value
=
true
;
// 调用 Hook 中的 interpret 方法
props
.
hookInstance
.
interpret
(
props
.
payload
);
}
};
const
handleMouseLeave
=
()
=>
{
isVisible
.
value
=
false
;
};
</
script
>
<
style
lang=
"scss"
scoped
>
.chart-ai-analysis
{
position
:
absolute
;
right
:
0px
;
bottom
:
15px
;
z-index
:
999
;
width
:
auto
;
height
:
auto
;
.ai-button-wrapper
{
display
:
flex
;
justify-content
:
flex-end
;
}
.ai-pane-wrapper
{
position
:
absolute
;
bottom
:
0
;
right
:
0
;
width
:
100%
;
// 确保面板出现在按钮上方或覆盖区域,根据原有 CSS 调整
// 原有 .ai-pane:hover 逻辑是宽度变宽,这里我们直接显示完整面板
background
:
#fff
;
border-radius
:
4px
;
box-shadow
:
0
-2px
12px
rgba
(
0
,
0
,
0
,
0
.1
);
padding
:
10px
;
box-sizing
:
border-box
;
.ai-pane-content
{
min-height
:
40px
;
.ai-loading-text
,
.ai-error-text
,
.ai-empty-text
{
font-size
:
14px
;
color
:
var
(
--
text-primary-50-color
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
min-height
:
40px
;
}
.ai-error-text
{
color
:
var
(
--
color-red-100
);
}
}
}
}
// 复用原有的淡入淡出动画
.ai-fade-enter-active
,
.ai-fade-leave-active
{
transition
:
opacity
0
.3s
ease
;
}
.ai-fade-enter-from
,
.ai-fade-leave-to
{
opacity
:
0
;
}
</
style
>
src/views/exportControl/components/title.vue
浏览文件 @
d454ba89
...
...
@@ -43,8 +43,8 @@ defineProps({
.title-text
{
color
:
rgba
(
10
,
18
,
30
,
1
);
font-size
:
32px
;
font-family
:
$base-font-family
;
font-weight
:
bold
;
font-family
:
"Microsoft YaHei"
;
font-weight
:
700
;
margin-left
:
20px
;
white-space
:
nowrap
;
}
...
...
src/views/exportControl/index.vue
浏览文件 @
d454ba89
差异被折叠。
点击展开。
src/views/exportControl/v2.0SingleSanction/components/dataStatistics/index.vue
浏览文件 @
d454ba89
差异被折叠。
点击展开。
src/views/exportControl/v2.0SingleSanction/components/sanctionsOverview/index.vue
浏览文件 @
d454ba89
...
...
@@ -8,7 +8,7 @@
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
/>
<img
:src=
"
formattedData.postOrgLogoUrl ||
title"
alt=
""
class=
"icon"
/>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
...
...
@@ -45,15 +45,22 @@
<div
class=
"left-top-content"
>
<div
class=
"content-title"
>
制裁实体分布:
</div>
<div
class=
"distribution-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<img
:src=
"item.imageUrl || flag"
alt=
""
class=
"flag"
/>
<div
class=
"country-name"
>
{{
item
.
name
}}
</div>
<div
class=
"progress-bar-container"
>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}">
</div>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}"
>
</div>
</div>
<div
class=
"count"
:class=
"
{ highlight: index === 0 }">
{{
item
.
count
}}
家
</div>
</div>
...
...
@@ -96,13 +103,25 @@
</div>
<div
class=
"filter-right"
>
<el-checkbox
v-model=
"onlyChina"
label=
"只看中国实体"
/>
<el-select
v-model=
"filterField"
placeholder=
"选择领域"
style=
"width: 150px; margin: 0 12px 0 16px"
>
<el-select
v-model=
"filterField"
placeholder=
"选择领域"
style=
"width: 150px; margin: 0 12px 0 16px"
>
<!--
<el-option
label=
"全部领域"
value=
""
/>
-->
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon=
"Search"
/>
:suffix-icon=
"Search"
/>
</div>
</div>
<div
class=
"stats-row"
>
...
...
@@ -117,14 +136,21 @@
<div
class=
"stats-info"
>
<div
class=
"stat-item"
>
<span
class=
"dot red"
></span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span>
家)
</span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span
>
家)
</span
>
</div>
<div
class=
"stat-item"
>
<span
class=
"dot green"
></span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span>
家)
</span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span
>
家)
</span
>
</div>
</div>
</div>
...
...
@@ -156,7 +182,11 @@
>
{{
item
}}
</span
>
-->
<div
class=
"domain-box"
>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
</div>
</
template
>
</el-table-column>
...
...
@@ -165,8 +195,11 @@
<el-table-column
prop=
"revenue"
label=
"营收(亿元)"
width=
"110"
align=
"center"
/>
<el-table-column
label=
"50%规则子企业"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
{{
scope
.
row
.
subsidiaryText
}}
<span
class=
"blue-text"
>
{{
scope
.
row
.
subsidiaryCount
}}
家 >
</span>
</span>
...
...
@@ -184,8 +217,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</div>
</template>
...
...
@@ -310,8 +347,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText
:
org
.
ruleOrgList
&&
org
.
ruleOrgList
.
length
>
0
?
(
org
.
ruleOrgList
[
0
].
orgName
.
length
>
10
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
:
""
}))
}));
...
...
@@ -445,7 +482,8 @@ const formattedData = computed(() => {
administrativeOrderId
:
info
.
administrativeOrderId
?
`No.
${
info
.
administrativeOrderId
}
`
:
""
,
postPersonName
:
info
.
postPersonName
,
domains
:
info
.
domainNames
,
avartar
:
info
.
postPersonAvatarUrl
avartar
:
info
.
postPersonAvatarUrl
,
postOrgLogoUrl
:
info
.
postOrgLogoUrl
};
});
...
...
@@ -525,23 +563,22 @@ const entityDistribution = ref([
const
sanTypeId
=
ref
(
""
);
// 跳转到数据资源库
const
handleToDataLibrary
=
(
item
)
=>
{
console
.
log
(
'item'
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
'0'
)}
-
${
d
.
padStart
(
2
,
'0'
)}
`
const
handleToDataLibrary
=
item
=>
{
console
.
log
(
"item"
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
"0"
)}
-
${
d
.
padStart
(
2
,
"0"
)}
`
);
const
route
=
router
.
resolve
({
const
route
=
router
.
resolve
({
path
:
"/dataLibrary/dataEntityList"
,
query
:{
selectedDate
:
JSON
.
stringify
([
dateStr
,
dateStr
]),
query
:
{
selectedDate
:
JSON
.
stringify
([
dateStr
,
dateStr
]),
selectedCountryId
:
item
.
id
}
});
window
.
open
(
route
.
href
,
"_blank"
);
}
};
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
...
...
src/views/exportControl/v2.0SingleSanction/originPage/index.vue
浏览文件 @
d454ba89
...
...
@@ -51,7 +51,7 @@
object-fit: contain;
"
/>
</div>
<div
class=
"translate-text"
>
{{
"显示
原
文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示
译
文"
}}
</div>
</div>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"icon"
>
...
...
@@ -62,13 +62,13 @@
</div>
</div>
<div
class=
"report-box"
>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
<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" />
</div>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
...
...
src/views/finance/components/title.vue
浏览文件 @
d454ba89
...
...
@@ -43,8 +43,8 @@ defineProps({
.title-text
{
color
:
rgba
(
10
,
18
,
30
,
1
);
font-size
:
32px
;
font-family
:
$base-font-family
;
font-weight
:
bold
;
font-family
:
"Microsoft YaHei"
;
font-weight
:
700
;
margin-left
:
20px
;
white-space
:
nowrap
;
}
...
...
src/views/finance/index.vue
浏览文件 @
d454ba89
...
...
@@ -234,13 +234,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布实体清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"entityListReleaseFreqChart.interpretation"
/>
<!-- <AiButton />
<AiPane :aiContent="entityListReleaseFreqChart.interpretation" /> -->
<AiButton
@
mouseenter=
"handleShowAiPane('entityListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.entityListReleaseFreqChart"
:aiContent=
"overviewAiContent.entityListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('entityListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
>
...
...
@@ -280,13 +284,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
美国商务部发布商业管制清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"commerceControlListReleaseFreqChart.interpretation"
/>
<!-- <AiButton />
<AiPane :aiContent="commerceControlListReleaseFreqChart.interpretation" /> -->
<AiButton
@
mouseenter=
"handleShowAiPane('commerceControlListReleaseFreqChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.commerceControlListReleaseFreqChart"
:aiContent=
"overviewAiContent.commerceControlListReleaseFreqChart"
@
mouseleave=
"handleHideAiPane('commerceControlListReleaseFreqChart')"
/>
</div>
</div>
<div
class=
"box3-content"
style=
"display: none"
>
...
...
@@ -339,13 +347,13 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入SDN清单的中国实体领域分布情况,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"radarChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"radarChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('radarChart')"
/>
<AiPane
:aiContent=
"overviewAiContent.radarChart"
@
mouseleave=
"handleHideAiPane('radarChart')"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -371,13 +379,17 @@
<div
class=
"data-origin-icon"
>
<img
:src=
"tipsIcon"
alt=
""
/>
</div>
<div
class=
"data-origin-text"
>
进入SDN清单的中国实体数量变化趋势,数据来源:美国财政部海外资产管理办公室官网
</div>
<div
class=
"data-origin-text"
>
数据来源:美国财政部海外资产管理办公室官网
</div>
</div>
<div
class=
"ai-pane"
>
<AiButton
/>
<AiPane
:aiContent=
"trendChart.interpretation"
/>
<!--
<AiButton
/>
<AiPane
:aiContent=
"trendChart.interpretation"
/>
-->
<AiButton
@
mouseenter=
"handleShowAiPane('trendChart')"
/>
<AiPane
v-if=
"aiPaneVisible?.trendChart"
:aiContent=
"overviewAiContent.trendChart"
@
mouseleave=
"handleHideAiPane('trendChart')"
/>
</div>
</
template
>
</custom-container>
...
...
@@ -705,6 +717,7 @@ import RiskSignal from "@/components/base/riskSignal/index.vue";
import
RiskSignalOverviewDetailDialog
from
"@/components/base/RiskSignalOverviewDetailDialog/index.vue"
;
import
{
onMounted
,
ref
,
computed
,
reactive
,
shallowRef
,
watch
,
nextTick
}
from
"vue"
;
import
{
useContainerScroll
}
from
"@/hooks/useScrollShow"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
const
homeMainRef
=
ref
(
null
);
const
{
isShow
}
=
useContainerScroll
(
homeMainRef
);
import
*
as
echarts
from
"echarts"
;
...
...
@@ -1265,7 +1278,8 @@ const fetchRadarData = async checked => {
};
});
console
.
log
(
"图例 =>"
,
radarOption
.
value
);
radarChart
.
interpret
({
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
data
});
radarChartData
.
value
=
data
;
// radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
}
}
catch
(
error
)
{
console
.
error
(
"获取雷达图数据失败:"
,
error
);
...
...
@@ -1824,6 +1838,128 @@ const handleToDataLibrary = item => {
window
.
open
(
route
.
href
,
"_blank"
);
};
const
requestAiPaneContent
=
async
key
=>
{
if
(
!
key
||
aiPaneLoading
.
value
[
key
]
||
aiPaneFetched
.
value
[
key
])
return
;
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
true
};
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成中..."
};
try
{
const
payload
=
buildAiChartPayload
(
key
);
const
res
=
await
getChartAnalysis
(
{
text
:
JSON
.
stringify
(
payload
)
},
{
onChunk
:
chunk
=>
{
const
current
=
overviewAiContent
.
value
[
key
];
const
base
=
current
===
"智能总结生成中..."
?
""
:
current
;
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
base
+
chunk
};
}
}
);
const
list
=
res
?.
data
;
const
first
=
Array
.
isArray
(
list
)
?
list
[
0
]
:
null
;
const
interpretation
=
first
?.
解读
||
first
?.[
"解读"
];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if
(
interpretation
)
{
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
interpretation
};
}
aiPaneFetched
.
value
=
{
...
aiPaneFetched
.
value
,
[
key
]:
true
};
}
catch
(
error
)
{
console
.
error
(
"获取图表解读失败"
,
error
);
overviewAiContent
.
value
=
{
...
overviewAiContent
.
value
,
[
key
]:
"智能总结生成失败"
};
}
finally
{
aiPaneLoading
.
value
=
{
...
aiPaneLoading
.
value
,
[
key
]:
false
};
}
};
const
trendChartData
=
ref
([]);
const
radarChartData
=
ref
([]);
const
entityListReleaseFreqChartData
=
ref
([]);
const
commerceControlListReleaseFreqChartData
=
ref
([]);
const
aiPaneVisible
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
overviewAiContent
=
ref
({
trendChart
:
"智能总结生成中..."
,
radarChart
:
"智能总结生成中..."
,
entityListReleaseFreqChart
:
"智能总结生成中..."
,
commerceControlListReleaseFreqChart
:
"智能总结生成中..."
});
const
aiPaneFetched
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
aiPaneLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
chartLoading
=
ref
({
trendChart
:
false
,
radarChart
:
false
,
entityListReleaseFreqChart
:
false
,
commerceControlListReleaseFreqChart
:
false
});
const
buildAiChartPayload
=
key
=>
{
if
(
key
===
"trendChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"制裁清单数量增长趋势"
,
data
:
trendChartData
.
value
};
}
if
(
key
===
"radarChart"
)
{
return
{
type
:
"雷达图"
,
name
:
"实体清单领域分布情况"
,
data
:
radarChartData
.
value
};
}
if
(
key
===
"entityListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreqChartData
.
value
};
}
if
(
key
===
"commerceControlListReleaseFreqChart"
)
{
return
{
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreqChartData
.
value
};
}
return
{
type
:
""
,
name
:
""
,
data
:
[]
};
};
const
handleShowAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
true
};
requestAiPaneContent
(
key
);
};
const
handleHideAiPane
=
key
=>
{
aiPaneVisible
.
value
=
{
...
aiPaneVisible
.
value
,
[
key
]:
false
};
};
onMounted
(
async
()
=>
{
console
.
log
(
"finance 页面 mounted"
);
try
{
...
...
@@ -1886,11 +2022,12 @@ onMounted(async () => {
await
fetchRadarData
(
domainChecked
.
value
);
// 获取出口管制制裁措施
await
fetchSanctionList
();
entityListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布实体清单的频次"
,
data
:
entityListReleaseFreq
.
value
});
entityListReleaseFreqChartData
.
value
=
entityListReleaseFreq
.
value
;
// entityListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布实体清单的频次",
// data: entityListReleaseFreq.value
// });
commerceControlListReleaseFreq
.
value
=
_
.
map
(
cclList1
,
item
=>
{
return
{
year
:
item
.
year
,
...
...
@@ -1899,11 +2036,12 @@ onMounted(async () => {
tags
:
item
.
domain
};
});
commerceControlListReleaseFreqChart
.
interpret
({
type
:
"柱状图"
,
name
:
"美国商务部发布商业管制清单的频次"
,
data
:
commerceControlListReleaseFreq
.
value
});
commerceControlListReleaseFreqChartData
.
value
=
commerceControlListReleaseFreq
.
value
;
// commerceControlListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布商业管制清单的频次",
// data: commerceControlListReleaseFreq.value
// });
}
catch
(
err
)
{
console
.
log
(
"此处报错?"
);
console
.
log
(
err
);
...
...
src/views/finance/singleSanction/components/sanctionsOverview/index.vue
浏览文件 @
d454ba89
...
...
@@ -8,7 +8,7 @@
<div
class=
"info-row"
>
<div
class=
"label"
>
发布机构:
</div>
<div
class=
"value link"
>
<img
:src=
"title"
alt=
""
class=
"icon"
/>
<img
:src=
"
formattedData.postOrgLogoUrl ||
title"
alt=
""
class=
"icon"
/>
<span
@
click=
"handleClickDp"
>
{{
formattedData
.
postOrgName
}}
>
</span>
</div>
</div>
...
...
@@ -46,17 +46,24 @@
<div
class=
"left-top-content"
>
<div
class=
"content-title"
>
制裁实体分布:
</div>
<div
class=
"distribution-list"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<div
class=
"list-item"
v-for=
"(item, index) in entityDistribution"
:key=
"index"
@
click=
"handleToDataLibrary(item)"
>
<img
:src=
"item.imageUrl || flag"
alt=
""
class=
"flag"
/>
<div
class=
"country-name"
>
{{
item
.
name
}}
</div>
<div
class=
"progress-bar-container"
>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}">
</div>
<div
class=
"progress-bar"
:style=
"
{
width: item.width,
background: item.gradient
}"
>
</div>
</div>
<div
class=
"count"
:class=
"
{ highlight: i
ndex === 0
}">
{{
item
.
count
}}
家
</div>
<div
class=
"count"
:class=
"
{ highlight: i
tem.name === '中国'
}">
{{
item
.
count
}}
家
</div>
</div>
</div>
</div>
...
...
@@ -97,13 +104,25 @@
</div>
<div
class=
"filter-right"
>
<el-checkbox
v-model=
"onlyChina"
label=
"只看中国实体"
/>
<el-select
v-model=
"filterField"
placeholder=
"全部领域"
style=
"width: 150px; margin: 0 12px 0 16px"
>
<el-select
v-model=
"filterField"
placeholder=
"全部领域"
style=
"width: 150px; margin: 0 12px 0 16px"
>
<el-option
label=
"全部领域"
value=
""
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
<el-input
v-model=
"searchKeyword"
placeholder=
"搜索实体"
style=
"width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon=
"Search"
/>
:suffix-icon=
"Search"
/>
</div>
</div>
<div
class=
"stats-row"
>
...
...
@@ -118,14 +137,21 @@
<div
class=
"stats-info"
>
<div
class=
"stat-item"
>
<span
class=
"dot red"
></span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span>
家)
</span>
<span
class=
"text"
>
新增
<span
class=
"num red"
>
{{
addCount
}}
</span>
家 (50%规则涉及
<span
class=
"num red"
>
{{
addRuleCount
}}
</span
>
家)
</span
>
</div>
<div
class=
"stat-item"
>
<span
class=
"dot green"
></span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span>
家)
</span>
<span
class=
"text"
>
移除
<span
class=
"num green"
>
{{
removeCount
}}
</span>
家 (50%规则涉及
<span
class=
"num green"
>
{{
removeRuleCount
}}
</span
>
家)
</span
>
</div>
</div>
</div>
...
...
@@ -157,7 +183,11 @@
>
{{
item
}}
</span
>
-->
<div
class=
"domain-box"
>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
<AreaTag
v-for=
"(domain, index) in scope.row.fields"
:key=
"index"
:tagName=
"domain"
/>
</div>
</
template
>
</el-table-column>
...
...
@@ -166,20 +196,26 @@
<el-table-column
prop=
"entityTypeId"
label=
"类型"
width=
"120"
align=
"center"
>
<
template
#
default=
"scope"
>
<div
style=
"display: flex; gap: 4px; justify-content: center"
>
<AreaTag
:tagName=
"scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
"
/>
<AreaTag
:tagName=
"
scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
"
/>
</div>
</
template
>
</el-table-column>
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="110" align="center" /> -->
<el-table-column
label=
"50%规则子企业"
width=
"180"
align=
"center"
>
<
template
#
default=
"scope"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
<span
v-if=
"scope.row.subsidiaryCount"
class=
"subsidiary-link"
@
click=
"handleSubsidiaryClick(scope.row)"
>
{{
scope
.
row
.
subsidiaryText
}}
<span
class=
"blue-text"
>
{{
scope
.
row
.
subsidiaryCount
}}
家 >
</span>
</span>
...
...
@@ -223,8 +259,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
<RuleSubsidiaryDialog
v-model=
"subsidiaryDialogVisible"
:company-name=
"currentSubsidiaryCompanyName"
:total-count=
"currentSubsidiaryCount"
:data-list=
"currentSubsidiaryList"
/>
</div>
</template>
...
...
@@ -333,8 +373,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText
:
org
.
ruleOrgList
&&
org
.
ruleOrgList
.
length
>
0
?
(
org
.
ruleOrgList
[
0
].
orgName
.
length
>
10
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
?
org
.
ruleOrgList
[
0
].
orgName
.
slice
(
0
,
10
)
+
"..."
:
org
.
ruleOrgList
[
0
].
orgName
)
+
"...等"
:
""
}))
}));
...
...
@@ -481,7 +521,8 @@ const formattedData = computed(() => {
administrativeOrderId
:
info
.
administrativeOrderId
?
`No.
${
info
.
administrativeOrderId
}
`
:
""
,
postPersonName
:
info
.
postPersonName
,
domains
:
info
.
domainNames
,
avartar
:
info
.
postPersonAvatarUrl
avartar
:
info
.
postPersonAvatarUrl
,
postOrgLogoUrl
:
info
.
postOrgLogoUrl
};
});
...
...
@@ -590,10 +631,11 @@ const getReasonHistoryList = async () => {
const
sanTypeId
=
ref
(
""
);
// 跳转到数据资源库
const
handleToDataLibrary
=
(
item
)
=>
{
console
.
log
(
'item'
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
'0'
)}
-
${
d
.
padStart
(
2
,
'0'
)}
`
const
handleToDataLibrary
=
item
=>
{
console
.
log
(
"item"
,
item
);
const
dateStr
=
formattedData
.
value
.
postDate
.
replace
(
/
(\d{4})
年
(\d{1,2})
月
(\d{1,2})
日/
,
(
_
,
y
,
m
,
d
)
=>
`
${
y
}
-
${
m
.
padStart
(
2
,
"0"
)}
-
${
d
.
padStart
(
2
,
"0"
)}
`
);
const
route
=
router
.
resolve
({
...
...
@@ -604,7 +646,7 @@ const handleToDataLibrary = (item) => {
}
});
window
.
open
(
route
.
href
,
"_blank"
);
}
}
;
onMounted
(()
=>
{
// 获取路由参数中的sanTypeId
...
...
src/views/finance/singleSanction/index.vue
浏览文件 @
d454ba89
...
...
@@ -28,7 +28,7 @@
</div>
<div
class=
"original-text-btn"
@
click=
"handleClickOriginalText"
>
<img
:src=
"icon1"
alt=
""
/>
<span>
实体
清单原文
</span>
<span>
SDN
清单原文
</span>
</div>
<div
class=
"btn3"
@
click=
"handleAnalysisClick"
>
<div
class=
"icon"
>
...
...
src/views/finance/singleSanction/originPage/index.vue
浏览文件 @
d454ba89
...
...
@@ -68,7 +68,7 @@
"
/>
</div>
<div
class=
"translate-text"
>
{{
"显示
原
文"
}}
</div>
<div
class=
"translate-text"
>
{{
"显示
译
文"
}}
</div>
</div>
<div
class=
"btn"
@
click=
"handleDownload"
>
<div
class=
"icon"
>
...
...
@@ -79,9 +79,6 @@
</div>
</div>
<div
class=
"report-box"
>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
<div
class=
"pdf-pane-wrap"
:class=
"
{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key=
"`right-pdf-$
{valueSwitch ? 'split' : 'full'}`"
...
...
@@ -90,6 +87,9 @@
class="pdf-pane-inner"
/>
</div>
<div
class=
"pdf-pane-wrap"
v-if=
"valueSwitch && reportUrlEnWithPage"
>
<pdf
ref=
"leftPdfRef"
:pdfUrl=
"reportUrlEnWithPage"
class=
"pdf-pane-inner"
/>
</div>
</div>
</div>
</div>
...
...
src/views/thinkTank/CongressHearingView/index.vue
浏览文件 @
d454ba89
<
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"
>
...
...
@@ -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,7 +304,7 @@ const handleGetThinkTankHearingInfo = async () => {
}
};
const
REPORT_ANALYSIS_TIP_BOX5
=
"
国会听证会关键词云,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
// 默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
false
);
const
aiContentBox5
=
ref
(
""
);
...
...
@@ -592,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
();
};
...
...
@@ -749,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
浏览文件 @
d454ba89
...
...
@@ -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
{
...
...
src/views/thinkTank/ReportDetail/reportAnalysis/index.vue
浏览文件 @
d454ba89
<
template
>
<div
class=
"wrap"
>
<div
class=
"wrap"
ref=
"pageScrollRef"
>
<div
class=
"top"
>
<WarningPane
:warnningLevel=
"riskSignal?.level"
:warnningContent=
"riskSignal?.content"
v-if=
"riskSignal?.level"
>
</WarningPane>
...
...
@@ -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,7 +258,7 @@ const props = defineProps({
}
});
const
REPORT_ANALYSIS_TIP_BOX5
=
"
智库报告关键词云,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
// 默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
false
);
const
aiContentBox5
=
ref
(
""
);
...
...
@@ -545,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
();
};
...
...
src/views/thinkTank/SurveyProjectView/index.vue
浏览文件 @
d454ba89
...
...
@@ -184,7 +184,7 @@ const applySurveyProjectDocumentTitle = (title) => {
document
.
title
=
text
;
};
const
REPORT_ANALYSIS_TIP_BOX5
=
"
调查项目关键词云,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
// 默认仅展示 AiButton,悬停后再请求 AI
const
isShowAiContentBox5
=
ref
(
false
);
const
aiContentBox5
=
ref
(
""
);
...
...
@@ -636,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
浏览文件 @
d454ba89
...
...
@@ -149,27 +149,8 @@
</div>
<div
class=
"select-box"
>
<el-select
class=
"select-box-sort"
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=
"../thinkDynamics/images/image down.png"
class=
"select-prefix-img"
alt=
""
/>
<img
v-else
src=
"../thinkDynamics/images/image up.png"
class=
"select-prefix-img"
alt=
""
/>
</
template
>
<el-option
@
click=
"handleGetThinkPolicy()"
:key=
"true"
label=
"正序"
:value=
"true"
/>
<el-option
@
click=
"handleGetThinkPolicy()"
:key=
"false"
label=
"倒序"
:value=
"false"
/>
</el-select>
<TimeSortSelectBox
:key=
"`policy-tracking-sort-${router.currentRoute.value?.params?.id || ''}`"
:sort-demension=
"1"
@
handle-px-change=
"handlePolicyTrackingPxChange"
/>
</div>
</div>
<div
class=
"bottom-main"
>
...
...
@@ -279,8 +260,8 @@
<div
class=
"right-footer"
>
<div
class=
"info"
>
共{{ total }}条政策建议
</div>
<div
class=
"page-box"
>
<el-pagination
:page-size=
"10"
:page-count=
"pageCount"
background
layout=
"prev, pager, next"
:total=
"total"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
<el-pagination
:page-size=
"10"
:page-count=
"pageCount"
background
layout=
"prev, pager, next"
:total=
"total"
@
current-change=
"handleCurrentChange"
:current-page=
"currentPage"
/>
</div>
</div>
</div>
...
...
@@ -306,6 +287,7 @@ import {
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
AiButton
from
"@/components/base/Ai/AiButton/index.vue"
;
import
AiPane
from
"@/components/base/Ai/AiPane/index.vue"
;
import
TimeSortSelectBox
from
"@/components/base/TimeSortSelectBox/index.vue"
;
import
TipTab
from
"@/views/thinkTank/TipTab/index.vue"
;
import
defaultNewsIcon
from
"@/assets/icons/default-icon-news.png"
;
import
AreaTag
from
"@/components/base/AreaTag/index.vue"
;
...
...
@@ -353,11 +335,11 @@ const getAreaTagColor = (name, idx = 0) =>
/** 与智库概览 TipTab 文案格式一致(政策追踪-美国国会) */
const
POLICY_TRACKING_TIP_BOX1
=
"
智库报告中政策建议的领域分布情况,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
const
POLICY_TRACKING_TIP_BOX2
=
"
智库报告中政策建议部门分布情况,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
const
POLICY_TRACKING_TIP_BOX3
=
"
智库报告热门研究领域变化趋势,
数据来源:美国兰德公司官网"
;
"数据来源:美国兰德公司官网"
;
/** 筛选「全部」项文案,与市场准入概览-资源库复选逻辑一致 */
const
POLICY_FILTER_ALL_AREA
=
"全部领域"
;
...
...
@@ -1319,6 +1301,12 @@ const handleSwithSort = () => {
handleGetThinkPolicy
();
};
/** 政策追踪排序公共组件回调:1=时间倒序,2=时间正序(映射到现有 sort(true/false/null)) */
const
handlePolicyTrackingPxChange
=
(
val
)
=>
{
sort
.
value
=
Number
(
val
)
===
2
?
true
:
false
;
handleGetThinkPolicy
();
};
const
currentPage
=
ref
(
1
);
const
pageCount
=
computed
(()
=>
{
const
size
=
10
;
...
...
@@ -1610,6 +1598,8 @@ onMounted(async () => {
width
:
420px
;
height
:
22px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-start
;
}
.chart-box
{
...
...
@@ -1669,10 +1659,13 @@ onMounted(async () => {
.source
{
position
:
absolute
;
bottom
:
24px
;
left
:
24px
;
width
:
420px
;
height
:
22px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-start
;
}
.chart-box
{
...
...
@@ -2150,7 +2143,7 @@ onMounted(async () => {
.right
{
width
:
1224px
;
min-height
:
1670px
;
margin-bottom
:
20px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
...
...
@@ -2167,7 +2160,7 @@ onMounted(async () => {
box-sizing
:
border-box
;
padding-left
:
37px
;
padding-right
:
0
;
max-height
:
1540px
;
flex
:
1
;
.right-empty
{
...
...
@@ -2187,7 +2180,7 @@ onMounted(async () => {
padding-left
:
37px
;
padding-right
:
36px
;
width
:
calc
(
100%
+
37px
-
36px
);
height
:
153px
;
border-bottom
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
display
:
flex
;
...
...
src/views/thinkTank/ThinkTankDetail/index.vue
浏览文件 @
d454ba89
...
...
@@ -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/index.vue
浏览文件 @
d454ba89
<
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,8 +28,7 @@
<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"
:loading=
"isThinkTankReportLoading"
@
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"
...
...
@@ -76,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";
...
...
@@ -330,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
()
};
...
...
@@ -615,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
浏览文件 @
d454ba89
...
...
@@ -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/TipTab/index.vue
浏览文件 @
d454ba89
...
...
@@ -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/components/ThinkTankPolicyAdviceOverview.vue
浏览文件 @
d454ba89
...
...
@@ -319,7 +319,8 @@ const handleYearGroupChange = (val) => {
.card-box
{
width
:
100%
;
height
:
1134px
;
padding-bottom
:
32px
;
;
display
:
flex
;
background
:
rgba
(
255
,
255
,
255
,
1
);
box-sizing
:
border-box
;
...
...
@@ -339,7 +340,7 @@ const handleYearGroupChange = (val) => {
.card-content
{
width
:
1211px
;
height
:
1067px
;
margin-top
:
33px
;
margin-left
:
37px
;
...
...
src/views/thinkTank/index.vue
浏览文件 @
d454ba89
...
...
@@ -221,7 +221,7 @@
<MessageBubble
:messageList=
"messageList"
imageUrl=
"personImage"
@
more-click=
"handleToSocialDetail"
@
person-click=
"handleClickPerson"
name=
"personName"
content=
"remarks"
source=
"orgName"
/>
</div>
<DivideHeader
id=
"position3"
class=
"divide-header"
:titleText=
"'
数据总
览'"
></DivideHeader>
<DivideHeader
id=
"position3"
class=
"divide-header"
:titleText=
"'
全景概
览'"
></DivideHeader>
<div
class=
"center-footer"
>
<div
class=
"box5"
>
<div
class=
"box5-header"
>
...
...
@@ -253,7 +253,7 @@
<div
id=
"box5Chart"
class=
"box5-chart-canvas"
></div>
</div>
<div
class=
"source"
>
<TipTab
:text=
"'
智库报告数量变化趋势,
数据来源:美国各智库官网'"
/>
<TipTab
:text=
"'数据来源:美国各智库官网'"
/>
</div>
<div
class=
"chart-box"
>
<div
class=
"btn-box"
v-if=
"!isShowAiContentBox5"
@
mouseenter=
"handleSwitchAiContentShowBox5(true)"
>
...
...
@@ -293,7 +293,7 @@
<
template
v-else
>
<div
id=
"box6Chart"
></div>
<div
class=
"source"
>
<TipTab
:text=
"'
智库报告领域分布情况,
数据来源:美国各智库官网'"
/>
<TipTab
:text=
"'数据来源:美国各智库官网'"
/>
</div>
<div
class=
"chart-box"
>
<div
class=
"btn-box"
v-if=
"!isShowAiContentBox6"
@
mouseenter=
"handleSwitchAiContentShowBox6(true)"
>
...
...
@@ -325,7 +325,7 @@
<
template
v-else
>
<div
id=
"box7Chart"
></div>
<div
class=
"source"
>
<TipTab
:text=
"'
美国科技智库与主要政府机构之间的资金往来,
数据来源:美国各智库官网'"
/>
<TipTab
:text=
"'数据来源:美国各智库官网'"
/>
</div>
<div
class=
"chart-box"
>
<div
class=
"btn-box"
v-if=
"!isShowAiContentBox7"
@
mouseenter=
"handleSwitchAiContentShowBox7(true)"
>
...
...
@@ -375,7 +375,7 @@
</div>
<div
class=
"home-main-footer"
>
<DivideHeader
id=
"position4"
class=
"divide-header"
:titleText=
"'
资源
库'"
></DivideHeader>
<DivideHeader
id=
"position4"
class=
"divide-header"
:titleText=
"'
美国科技智库数据
库'"
></DivideHeader>
<div
class=
"home-main-footer-header"
>
<div
class=
"btn-box"
>
...
...
@@ -385,19 +385,8 @@
</div>
</div>
<div
class=
"select-box"
>
<el-select
v-model=
"resourceLibrarySortModel"
class=
"resource-library-sort-select"
placeholder=
"发布时间"
style=
"width: 120px"
:teleported=
"true"
placement=
"bottom-start"
:popper-options=
"resourceLibrarySortPopperOptions"
@
change=
"handleResourceLibrarySortChange"
>
<
template
#
prefix
>
<img
v-if=
"resourceLibrarySortModel !== true"
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png"
class=
"resource-library-sort-prefix-img"
alt=
""
@
click
.
stop=
"toggleResourceLibrarySortPrefix"
/>
<img
v-else
src=
"@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png"
class=
"resource-library-sort-prefix-img"
alt=
""
@
click
.
stop=
"toggleResourceLibrarySortPrefix"
/>
</
template
>
<el-option
:key=
"'resource-lib-sort-asc'"
label=
"正序"
:value=
"true"
/>
<el-option
:key=
"'resource-lib-sort-desc'"
label=
"倒序"
:value=
"false"
/>
</el-select>
<TimeSortSelectBox
:key=
"`reslib-sort-${resourceTabResetKey}`"
:sort-demension=
"1"
@
handle-px-change=
"handleResourceLibraryPxChange"
/>
</div>
<!-- <el-select v-model="sort" placeholder="发布时间" style="width: 120px; margin-left: 8px">
<el-option @click="handleGetetThinkTankReport()" :key="true" label="正序" :value="true" />
...
...
@@ -409,29 +398,28 @@
v-model:selectedAreaList=
"selectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedPubTimeList=
"selectedPubTimeList"
@
filter-change=
"handleThinkTankReportFilterChange"
:cur-footer-list=
"curFooterList"
:total=
"total"
:current-page=
"currentPage"
:loading=
"isResourceReportLoading"
@
report-click=
"handleToReportDetail"
@
page-change=
"handleCurrentChange"
/>
:loading=
"isResourceReportLoading"
@
report-click=
"handleToReportDetail"
@
page-change=
"handleCurrentChange"
/>
<HomeMainFooterSurvey
v-else-if=
"activeCate === '调查项目'"
:area-list=
"areaList"
v-model:selectedAreaList=
"surveySelectedAreaList"
:pub-time-list=
"pubTimeList"
v-model:selectedPubTimeList=
"surveySelectedPubTimeList"
@
filter-change=
"handleSurveyFilterChange"
:cur-footer-list=
"surveyFooterList"
:total=
"surveyTotal"
:current-page=
"surveyCurrentPage"
:loading=
"isResourceSurveyLoading"
@
report-click=
"handleToSurveyProjectView"
@
page-change=
"handleSurveyCurrentChange"
/>
:loading=
"isResourceSurveyLoading"
@
report-click=
"handleToSurveyProjectView"
@
page-change=
"handleSurveyCurrentChange"
/>
<ThinkTankCongressHearingOverview
v-else-if=
"activeCate === '国会听证会'"
:key=
"`congress-${resourceTabResetKey}`"
:hearing-data=
"hearingData"
:research-type-list=
"areaList"
:research-time-list=
"pubTimeList"
v-model:selectedAreaList=
"congressSelectedAreaList"
v-model:selectedPubTimeList=
"congressSelectedPubTimeList"
:total=
"congressTotal"
:current-page=
"congressCurrentPage"
@
filter-change=
"handleCongressFilterChange"
:loading=
"isResourceHearingLoading"
@
page-change=
"handleCongressCurrentChange"
@
report-click=
"handleToHearingDetail"
/>
:loading=
"isResourceHearingLoading"
@
page-change=
"handleCongressCurrentChange"
@
report-click=
"handleToHearingDetail"
/>
<ThinkTankPolicyAdviceOverview
v-else
:key=
"`policy-${resourceTabResetKey}`"
:research-type-list=
"areaList"
:research-time-list=
"pubTimeList"
:list=
"policyFooterList"
:total=
"policyTotal"
:current-page=
"policyCurrentPage"
:page-size=
"7"
@
filter-change=
"handlePolicyFilterChange"
:loading=
"isResourcePolicyLoading"
@
page-change=
"handlePolicyCurrentChange"
/>
:loading=
"isResourcePolicyLoading"
@
page-change=
"handlePolicyCurrentChange"
/>
</div>
</div>
...
...
@@ -477,6 +465,7 @@ import getPieChart from "./utils/piechart";
import
{
MUTICHARTCOLORS
}
from
"@/common/constant.js"
;
import
getSankeyChart
from
"./utils/sankey"
;
import
{
getChartAnalysis
}
from
"@/api/aiAnalysis/index"
;
import
TimeSortSelectBox
from
"@/components/base/TimeSortSelectBox/index.vue"
;
import
defaultNewsIcon
from
"@/assets/icons/default-icon-news.png"
;
import
defaultHeaderIcin
from
"@/assets/icons/default-icon1.png"
;
import
News1
from
"./assets/images/news1.png"
;
...
...
@@ -515,6 +504,7 @@ import { setCanvasCreator } from "echarts/core";
import
{
ElMessage
}
from
"element-plus"
;
import
{
useRouter
}
from
'vue-router'
;
import
{
useGotoNewsDetail
}
from
'@/router/modules/news'
;
import
{
goToThinkTank
}
from
"@/utils/goToPage"
;
const
gotoNewsDetail
=
useGotoNewsDetail
()
const
containerRef
=
ref
(
null
);
const
isRiskDetailVisible
=
ref
(
false
);
...
...
@@ -1997,6 +1987,14 @@ const toggleResourceLibrarySortPrefix = () => {
}
};
/** 资源库排序公共组件回调:1=时间倒序,2=时间正序(与现有 sort(true/false/null) 映射) */
const
handleResourceLibraryPxChange
=
(
val
)
=>
{
// 组件默认值为 1(时间倒序),这里保持与旧逻辑一致:倒序用 false,正序用 true
const
mapped
=
Number
(
val
)
===
2
?
true
:
false
;
resourceLibrarySortModel
.
value
=
mapped
;
handleResourceLibrarySortChange
();
};
/** 与调查项目 surveyFooterList 一致:初始空列表,由接口填充;失败或非 200 时清空 */
const
curFooterList
=
ref
([]);
const
currentPage
=
ref
(
1
);
...
...
@@ -2284,14 +2282,17 @@ const handleGetetThinkTankReport = async () => {
const
handleClick
=
tank
=>
{
console
.
log
(
tank
);
window
.
sessionStorage
.
setItem
(
"curTabName"
,
tank
.
name
+
"概览"
);
// router.push({ name: "ThinkTankDetail", params: { id: tank.id } });
if
(
!
tank
.
id
)
{
ElMessage
.
warning
(
"当前智库id为空,无法进入详情页"
);
return
;
}
const
curRoute
=
router
.
resolve
({
name
:
"ThinkTankDetail"
,
params
:
{
id
:
tank
.
id
,
name
:
tank
.
name
}
});
window
.
open
(
curRoute
.
href
,
"_blank"
);
goToThinkTank
(
tank
.
id
,
tank
.
name
+
"概览"
)
// window.sessionStorage.setItem("curTabName", tank.name + "概览");
// const curRoute = router.resolve({ name: "ThinkTankDetail", params: { id: tank.id, name: tank.name } });
// window.open(curRoute.href, "_blank");
// router.push({ name: "ThinkTankDetail", params: { id: tank.id, name: tank.name } })
};
...
...
src/views/thinkTank/reportOriginal/index.vue
浏览文件 @
d454ba89
...
...
@@ -309,8 +309,9 @@ onMounted(async () => {
<
style
lang=
"scss"
scoped
>
.wrap
{
overflow-y
:
auto
;
height
:
100vh
;
overflow-y
:
auto
;
overflow-x
:
hidden
;
.header
{
...
...
@@ -320,9 +321,6 @@ onMounted(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
{
...
...
@@ -703,7 +701,6 @@ onMounted(async () => {
height
:
881px
;
display
:
flex
;
overflow-y
:
auto
;
/* 右侧统一滚动条,控制两侧原文+译文一起滚动 */
overflow-x
:
hidden
;
}
...
...
src/views/thinkTank/reportOriginal/pdf.vue
浏览文件 @
d454ba89
...
...
@@ -326,7 +326,79 @@ export default {
}
}
if
(
matchList
.
value
.
length
>
0
)
jumpTo
(
0
);
if
(
matchList
.
value
.
length
>
0
)
{
// 先把所有命中都标黄,再把“当前命中”改成蓝底
renderAllHighlights
();
jumpTo
(
0
);
}
};
const
setActiveHighlight
=
(
idx
)
=>
{
const
all
=
document
.
querySelectorAll
(
'.highlight-rect[data-match-idx]'
);
all
.
forEach
((
el
)
=>
{
const
isActive
=
Number
(
el
.
getAttribute
(
'data-match-idx'
))
===
Number
(
idx
);
if
(
isActive
)
{
el
.
classList
.
add
(
'highlight-rect--active'
);
}
else
{
el
.
classList
.
remove
(
'highlight-rect--active'
);
}
});
};
const
renderAllHighlights
=
()
=>
{
clearHighlights
();
const
list
=
Array
.
isArray
(
matchList
.
value
)
?
matchList
.
value
:
[];
list
.
forEach
((
m
,
idx
)
=>
{
if
(
!
m
||
m
.
fallback
)
return
;
const
layer
=
overlayMap
[
m
.
pageNum
];
if
(
!
layer
)
return
;
const
pageWrap
=
layer
.
closest
(
'.page-wrap'
);
const
container
=
(
pageWrap
||
layer
);
const
containerRect
=
container
.
getBoundingClientRect
();
const
segs
=
Array
.
isArray
(
m
?.
segments
)
?
m
.
segments
:
[];
for
(
const
seg
of
segs
)
{
const
segEl
=
seg
?.
el
;
if
(
!
segEl
)
continue
;
const
textNode
=
segEl
.
firstChild
;
if
(
!
textNode
||
textNode
.
nodeType
!==
Node
.
TEXT_NODE
)
continue
;
try
{
const
range
=
document
.
createRange
();
range
.
setStart
(
textNode
,
Math
.
max
(
0
,
seg
.
startIdx
??
0
));
range
.
setEnd
(
textNode
,
Math
.
max
(
0
,
seg
.
endIdx
??
0
));
const
rectList
=
Array
.
from
(
range
.
getClientRects
());
if
(
rectList
.
length
)
{
rectList
.
forEach
(
r
=>
{
const
mark
=
document
.
createElement
(
'div'
);
mark
.
className
=
'highlight-rect'
;
mark
.
setAttribute
(
'data-match-idx'
,
String
(
idx
));
mark
.
style
.
zIndex
=
'5'
;
mark
.
style
.
left
=
(
r
.
left
-
containerRect
.
left
)
+
'px'
;
mark
.
style
.
top
=
(
r
.
top
-
containerRect
.
top
)
+
'px'
;
mark
.
style
.
width
=
r
.
width
+
'px'
;
mark
.
style
.
height
=
r
.
height
+
'px'
;
container
.
appendChild
(
mark
);
});
}
else
{
const
r
=
segEl
.
getBoundingClientRect
();
if
(
r
.
width
>
0
&&
r
.
height
>
0
)
{
const
mark
=
document
.
createElement
(
'div'
);
mark
.
className
=
'highlight-rect'
;
mark
.
setAttribute
(
'data-match-idx'
,
String
(
idx
));
mark
.
style
.
zIndex
=
'5'
;
mark
.
style
.
left
=
(
r
.
left
-
containerRect
.
left
)
+
'px'
;
mark
.
style
.
top
=
(
r
.
top
-
containerRect
.
top
)
+
'px'
;
mark
.
style
.
width
=
r
.
width
+
'px'
;
mark
.
style
.
height
=
r
.
height
+
'px'
;
container
.
appendChild
(
mark
);
}
}
range
.
detach
?.();
}
catch
(
e
)
{
// ignore
}
}
});
setActiveHighlight
(
matchIdx
.
value
);
};
// 跳转到第 N 个匹配项
...
...
@@ -342,55 +414,7 @@ export default {
const
firstSeg
=
m
?.
segments
?.[
0
];
const
el
=
firstSeg
?.
el
;
if
(
!
el
)
return
;
clearHighlights
();
const
layer
=
overlayMap
[
m
.
pageNum
];
if
(
!
layer
)
return
;
const
pageWrap
=
layer
.
closest
(
'.page-wrap'
);
// 用 Range 精确计算“子串”在页面上的矩形位置,再画黄色块(支持跨 span)
const
containerRect
=
(
pageWrap
||
layer
).
getBoundingClientRect
();
const
segs
=
Array
.
isArray
(
m
?.
segments
)
?
m
.
segments
:
[];
for
(
const
seg
of
segs
)
{
const
segEl
=
seg
?.
el
;
if
(
!
segEl
)
continue
;
const
textNode
=
segEl
.
firstChild
;
if
(
!
textNode
||
textNode
.
nodeType
!==
Node
.
TEXT_NODE
)
continue
;
try
{
const
range
=
document
.
createRange
();
range
.
setStart
(
textNode
,
Math
.
max
(
0
,
seg
.
startIdx
??
0
));
range
.
setEnd
(
textNode
,
Math
.
max
(
0
,
seg
.
endIdx
??
0
));
const
rectList
=
Array
.
from
(
range
.
getClientRects
());
if
(
rectList
.
length
)
{
rectList
.
forEach
(
r
=>
{
const
mark
=
document
.
createElement
(
'div'
);
mark
.
className
=
'highlight-rect'
;
mark
.
style
.
zIndex
=
'5'
;
mark
.
style
.
left
=
(
r
.
left
-
containerRect
.
left
)
+
'px'
;
mark
.
style
.
top
=
(
r
.
top
-
containerRect
.
top
)
+
'px'
;
mark
.
style
.
width
=
r
.
width
+
'px'
;
mark
.
style
.
height
=
r
.
height
+
'px'
;
(
pageWrap
||
layer
).
appendChild
(
mark
);
});
}
else
{
// Range 兜底为空时:用 span 自身的矩形画块(精度低,但尽量可见)
const
r
=
segEl
.
getBoundingClientRect
();
if
(
r
.
width
>
0
&&
r
.
height
>
0
)
{
const
mark
=
document
.
createElement
(
'div'
);
mark
.
className
=
'highlight-rect'
;
mark
.
style
.
zIndex
=
'5'
;
mark
.
style
.
left
=
(
r
.
left
-
containerRect
.
left
)
+
'px'
;
mark
.
style
.
top
=
(
r
.
top
-
containerRect
.
top
)
+
'px'
;
mark
.
style
.
width
=
r
.
width
+
'px'
;
mark
.
style
.
height
=
r
.
height
+
'px'
;
(
pageWrap
||
layer
).
appendChild
(
mark
);
}
}
range
.
detach
?.();
}
catch
(
e
)
{
// ignore
}
}
setActiveHighlight
(
idx
);
// 优先只滚动右侧 report-box,避免触发整页滚动导致 header 遮挡
const
container
=
el
.
closest
(
'.report-box'
);
...
...
@@ -510,6 +534,10 @@ canvas {
z-index
:
5
;
}
.textLayer
:deep
(
.highlight-rect--active
)
{
background
:
rgb
(
184
,
222
,
254
);
}
.page-wrap
:deep
(
.highlight-rect
)
{
position
:
absolute
;
background
:
#ff0
;
...
...
@@ -519,6 +547,10 @@ canvas {
z-index
:
3
;
}
.page-wrap
:deep
(
.highlight-rect--active
)
{
background
:
rgb
(
184
,
222
,
254
);
}
.loading
{
position
:
absolute
;
top
:
50%
;
...
...
src/views/viewRiskSignal/assets/images/risk-icon-blue.png
0 → 100644
浏览文件 @
d454ba89
14.9 KB
src/views/viewRiskSignal/assets/images/risk-icon-green.png
0 → 100644
浏览文件 @
d454ba89
15.2 KB
src/views/viewRiskSignal/assets/images/risk-icon-orange.png
0 → 100644
浏览文件 @
d454ba89
14.0 KB
src/views/viewRiskSignal/assets/images/risk-icon.png
→
src/views/viewRiskSignal/assets/images/risk-icon
-red
.png
浏览文件 @
d454ba89
File moved
src/views/viewRiskSignal/assets/images/risk-icon-yellow.png
0 → 100644
浏览文件 @
d454ba89
14.7 KB
src/views/viewRiskSignal/assets/images/warning.svg
→
src/views/viewRiskSignal/assets/images/warning
-red
.svg
浏览文件 @
d454ba89
File moved
src/views/viewRiskSignal/index.vue
浏览文件 @
d454ba89
...
...
@@ -5,7 +5,7 @@
<div
class=
"center-center"
>
<div
class=
"center-header"
>
<div
class=
"center-header-left"
>
<img
class=
"iconstyle"
src=
"./assets/images/warning.svg"
/>
<img
class=
"iconstyle"
src=
"./assets/images/warning
-red
.svg"
/>
<div
class=
"center-header-title"
>
风险信号管理
</div>
</div>
<div
class=
"center-header-right"
>
...
...
@@ -16,7 +16,7 @@
<div
class=
"center-middle"
>
<div
class=
"center-middle-left"
>
<div
class=
"lineitem"
>
<div
class=
"item"
>
<div
class=
"item
item--shift-left
"
>
<div
class=
"top"
>
<div
class=
"dot"
style=
"background-color: rgba(95, 101, 108, 1)"
></div>
<div
class=
"text1"
>
本年新增风险
</div>
...
...
@@ -36,7 +36,7 @@
</div>
</div>
<div
class=
"lineitem"
>
<div
class=
"item"
>
<div
class=
"item
item--shift-left
"
>
<div
class=
"top"
>
<div
class=
"dot"
style=
"background-color: rgba(5, 95, 194, 1)"
></div>
<div
class=
"text1"
>
已处理风险
</div>
...
...
@@ -50,7 +50,7 @@
<div
class=
"text1"
>
待处理风险
</div>
</div>
<div
class=
"text2"
style=
"color: rgba(206, 79, 81, 1)"
>
<span
class=
"text2-inner"
>
{{
basicInfo
.
pendingCount
+
" 项"
}}
</span>
<span
class=
"text2-inner"
>
{{
formatThousands
(
basicInfo
.
pendingCount
)
+
" 项"
}}
</span>
</div>
</div>
</div>
...
...
@@ -213,7 +213,7 @@
</div>
<div
class=
"right-footer"
>
<div
class=
"footer-left"
>
{{
`共 ${
totalNum
}
项调查`
}}
{{
`共 ${
formatThousands(totalNum)
}
项调查`
}}
<
/div
>
<
div
class
=
"footer-right"
>
<
el
-
pagination
@
current
-
change
=
"handleCurrentChange"
:
pageSize
=
"pageSize"
:
current
-
page
=
"currentPage"
...
...
@@ -233,7 +233,7 @@
<
el
-
dialog
v
-
model
=
"isRiskDetailVisible"
class
=
"risk-signal-detail-dialog"
modal
-
class
=
"risk-signal-detail-modal"
width
=
"1280px"
align
-
center
:
show
-
close
=
"true"
destroy
-
on
-
close
@
closed
=
"handleCloseRiskDetail"
>
<
template
#
header
>
<
img
class
=
"header-icon"
src
=
"./assets/images/risk-icon.png
"
alt
=
""
/>
<
img
class
=
"header-icon"
:
src
=
"riskDetailHeaderIconSrc
"
alt
=
""
/>
<
span
v
-
if
=
"riskDetailListLevelText"
class
=
"risk-signal-detail-dialog__level"
:
class
=
"riskDetailListLevelModifierClass"
>
{{
riskDetailListLevelText
}}
<
/span
>
<
div
v
-
if
=
"riskDetailBodyFromApi"
class
=
"risk-signal-detail-dialog__read-indicator"
>
...
...
@@ -597,6 +597,15 @@ const riskDetailListLevelModifierClass = computed(
()
=>
`risk-signal-detail-dialog__level--${getRiskDetailLevelModifier(riskDetailListLevelText.value)
}
`
);
const
riskDetailHeaderIconSrc
=
computed
(()
=>
{
const
key
=
getRiskListItemLevelKey
(
riskDetailListLevelText
.
value
);
if
(
key
===
"lv1"
)
return
new
URL
(
"./assets/images/risk-icon-red.png"
,
import
.
meta
.
url
).
href
;
if
(
key
===
"lv2"
)
return
new
URL
(
"./assets/images/risk-icon-orange.png"
,
import
.
meta
.
url
).
href
;
if
(
key
===
"lv3"
)
return
new
URL
(
"./assets/images/risk-icon-yellow.png"
,
import
.
meta
.
url
).
href
;
if
(
key
===
"lv4"
)
return
new
URL
(
"./assets/images/risk-icon-green.png"
,
import
.
meta
.
url
).
href
;
return
new
URL
(
"./assets/images/risk-icon-blue.png"
,
import
.
meta
.
url
).
href
;
}
);
/** 列表项风险等级样式键:与 `@/components/base/riskSignal` itemLeftStatus1~5 一致 */
const
getRiskListItemLevelKey
=
(
level
)
=>
{
const
t
=
String
(
level
??
""
).
trim
();
...
...
@@ -623,6 +632,12 @@ const isRiskLevelNoData = (level) => {
const
route
=
useRoute
();
const
router
=
useRouter
();
const
formatThousands
=
(
val
)
=>
{
const
n
=
Number
(
val
??
0
);
if
(
!
Number
.
isFinite
(
n
))
return
"0"
;
return
new
Intl
.
NumberFormat
(
"en-US"
).
format
(
n
);
}
;
const
consumeOpenFirstDetailFromQuery
=
async
()
=>
{
if
(
route
.
query
[
OPEN_FIRST_RISK_DETAIL_QUERY_KEY
]
!==
"1"
)
{
return
;
...
...
@@ -1095,6 +1110,10 @@ onMounted(async () => {
unicode
-
bidi
:
isolate
;
}
}
.
item
--
shift
-
left
{
transform
:
translateX
(
-
20
px
);
}
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论