Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
c71276c1
提交
c71276c1
authored
3月 20, 2026
作者:
刘宇琪
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix:刘宇琪 修复法案页面样式
上级
d37759f1
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
459 行增加
和
49 行删除
+459
-49
billHome.js
src/api/bill/billHome.js
+280
-2
bill.js
src/router/modules/bill.js
+1
-1
index.ts
src/views/bill/influence/ProgressForecast/api/index.ts
+3
-3
Step1FilterCondition.vue
...ence/ProgressForecast/components/Step1FilterCondition.vue
+73
-15
Step2FilterBills.vue
...nfluence/ProgressForecast/components/Step2FilterBills.vue
+49
-11
Step3PredictionAnalysis.vue
...e/ProgressForecast/components/Step3PredictionAnalysis.vue
+1
-1
index.vue
src/views/bill/influence/ProgressForecast/index.vue
+41
-5
bindEcharts.js
...views/innovationSubject/InnovationAnalysis/bindEcharts.js
+1
-1
index.vue
src/views/innovationSubject/innovativeInstitutions/index.vue
+2
-2
ResearchStrength.vue
...nSubject/innovativeInstitutions/tabs/ResearchStrength.vue
+1
-1
TimelineMap.vue
src/views/technologyFigures/component/TimelineMap.vue
+1
-1
vite.config.js
vite.config.js
+6
-6
没有找到文件。
src/api/bill/billHome.js
浏览文件 @
c71276c1
...
...
@@ -264,5 +264,284 @@ export async function getPotentialNewsKeywords(personId) {
method
:
'GET'
,
})
}
/**
* 获取法案进展预测基础数据
* @param {string} billId - 法案ID,如 "119_HR_1"
* @returns {Promise<Object>} 预测基础数据
*/
export
function
getProgressPrediction
(
billId
)
{
return
request
(
'/api/BillProgressPrediction/progressPrediction'
,
{
method
:
'GET'
,
params
:
{
billId
}
})
}
/**
* 获取相似法案列表
* @param {Object} params - 查询参数
* @param {string} params.patternType - 政府结构类型,如 "统一政府"
* @param {string} params.proposalType - 提案类型,默认 "两党共同提案"
* @returns {Promise<Object>} 相似法案列表
*/
export
function
getSimiBills
(
params
=
{})
{
return
request
(
'/api/BillProgressPrediction/simiBills'
,
{
method
:
'GET'
,
params
:
{
patternType
:
params
.
patternType
||
'统一政府'
,
proposalType
:
params
.
proposalType
||
'两党共同提案'
,
...
params
}
})
}
\ No newline at end of file
/**
* 转换相似法案 API 返回的数据为组件所需格式
* @param {Object} apiData - API 返回的原始数据
* @returns {Object} 转换后的统计数据和法案列表
*/
export
function
transformSimiBillsData
(
apiData
)
{
if
(
!
apiData
||
!
apiData
.
data
||
!
Array
.
isArray
(
apiData
.
data
))
{
return
{
stats
:
null
,
bills
:
[]
}
}
const
bills
=
apiData
.
data
// 计算统计数据
let
becameLaw
=
0
let
notPassedOrShelved
=
0
let
totalDays
=
0
let
completedBills
=
0
bills
.
forEach
(
bill
=>
{
const
actions
=
bill
.
bill_actions
||
[]
const
hasBecameLaw
=
actions
.
some
(
a
=>
a
.
action_type
===
'BecameLaw'
||
a
.
action_type
===
'President'
&&
a
.
action_desc
?.
includes
(
'签署'
)
)
if
(
hasBecameLaw
)
{
becameLaw
++
// 计算耗时
if
(
actions
.
length
>=
2
)
{
const
firstDate
=
new
Date
(
actions
[
0
].
action_date
)
const
lastDate
=
new
Date
(
actions
[
actions
.
length
-
1
].
action_date
)
const
days
=
Math
.
ceil
((
lastDate
-
firstDate
)
/
(
1000
*
60
*
60
*
24
))
if
(
days
>
0
)
{
totalDays
+=
days
completedBills
++
}
}
}
else
{
notPassedOrShelved
++
}
})
const
medianDays
=
completedBills
>
0
?
Math
.
round
(
totalDays
/
completedBills
)
:
223
const
passRate
=
bills
.
length
>
0
?
((
becameLaw
/
bills
.
length
)
*
100
).
toFixed
(
1
)
:
'0'
const
stats
=
{
totalBills
:
bills
.
length
,
becameLaw
,
notPassedOrShelved
,
medianDays
,
passRate
}
// 转换法案列表格式
const
transformedBills
=
bills
.
map
(
bill
=>
({
id
:
bill
.
bill_id
,
title
:
bill
.
bill_name
||
bill
.
bill_id
,
tags
:
[
bill
.
poli_pattern_type
,
bill
.
bill_proposal_type
].
filter
(
Boolean
),
passTime
:
extractPassTime
(
bill
.
bill_actions
),
totalDays
:
calculateTotalDays
(
bill
.
bill_actions
),
selected
:
true
,
// 默认全选
_raw
:
bill
}))
return
{
stats
,
bills
:
transformedBills
}
}
/**
* 从法案动作中提取通过时间
* @param {Array} actions - 法案动作列表
* @returns {string} 通过时间
*/
function
extractPassTime
(
actions
)
{
if
(
!
actions
||
actions
.
length
===
0
)
return
''
// 找到最后一个动作的日期
const
lastAction
=
actions
[
actions
.
length
-
1
]
if
(
lastAction
&&
lastAction
.
action_date
)
{
const
date
=
new
Date
(
lastAction
.
action_date
)
return
`
${
date
.
getFullYear
()}
年
${
date
.
getMonth
()
+
1
}
月
${
date
.
getDate
()}
日`
}
return
''
}
/**
* 计算法案总耗时
* @param {Array} actions - 法案动作列表
* @returns {number} 总天数
*/
function
calculateTotalDays
(
actions
)
{
if
(
!
actions
||
actions
.
length
<
2
)
return
0
const
firstDate
=
new
Date
(
actions
[
0
].
action_date
)
const
lastDate
=
new
Date
(
actions
[
actions
.
length
-
1
].
action_date
)
return
Math
.
ceil
((
lastDate
-
firstDate
)
/
(
1000
*
60
*
60
*
24
))
}
/**
* 解析 billId 从路由参数
* 路由格式: ProgressForecast/:id
* billId 格式: 119_HR_1 (国会届次_法案类型_法案编号)
* @param {string} id - 路由中的 id 参数
* @returns {string} 完整的 billId
*/
export
function
parseBillId
(
id
)
{
// 如果 id 已经包含完整格式,直接返回
if
(
id
&&
id
.
includes
(
'_'
))
{
return
id
}
// 否则根据业务规则构建 billId
// 这里假设默认是 119 届国会的 HR 类型法案
return
`119_HR_
${
id
}
`
}
/**
* 转换 API 返回的数据为组件所需格式
* @param {Object} apiData - API 返回的原始数据
* @returns {Object} 转换后的提案信息
*/
export
function
transformProposalInfo
(
apiData
)
{
if
(
!
apiData
||
!
apiData
.
data
)
{
return
null
}
const
data
=
apiData
.
data
// 解析提案人信息
const
sponsors
=
data
.
bill_sponsors
||
[]
const
mainProposer
=
sponsors
.
find
(
s
=>
s
.
sponsor_type
===
'提案人'
)
const
coProposers
=
sponsors
.
filter
(
s
=>
s
.
sponsor_type
!==
'提案人'
)
// 构建共同提案人描述
let
coProposersDesc
=
''
if
(
coProposers
.
length
>
0
)
{
const
partyGroups
=
{}
coProposers
.
forEach
(
s
=>
{
const
party
=
s
.
party_name
||
'未知'
partyGroups
[
party
]
=
(
partyGroups
[
party
]
||
0
)
+
1
})
coProposersDesc
=
Object
.
entries
(
partyGroups
)
.
map
(([
party
,
count
])
=>
`
${
party
}${
count
}
人`
)
.
join
(
';'
)
}
else
{
coProposersDesc
=
data
.
proposal_desc
||
'无'
}
return
{
// 提案标题 - 需要从其他 API 获取或使用默认值
title
:
data
.
bill_title
||
'H.R.1-大而美法案'
,
// 提案时间 - 从政治格局描述中提取或使用默认值
date
:
extractDateFromDesc
(
data
.
poli_pattern_desc
)
||
'2025年5月20日'
,
// 涉及领域
areas
:
data
.
domain_name
||
[],
// 选举周期阶段 - 从政治格局描述推断
electionPhase
:
inferElectionPhase
(
data
.
poli_pattern_desc
)
||
'执政初期/蜜月期'
,
// 提案人
proposer
:
mainProposer
?
`
${
mainProposer
.
person_name
}
`
:
''
,
// 共同提案人
coProposers
:
coProposersDesc
,
// 提案人职务 - 需要从其他 API 获取
proposerPosition
:
data
.
proposer_position
||
'委员会主席'
,
// 政府结构类型
governmentType
:
formatGovernmentType
(
data
.
poli_pattern_type
,
data
.
poli_pattern_desc
),
// 法案预算规模 - 需要从其他 API 获取
budgetScale
:
data
.
budget_scale
||
'4万亿美元'
,
// 原始数据,供筛选条件使用
_raw
:
data
}
}
/**
* 从政治格局描述中提取日期
* @param {string} desc - 政治格局描述
* @returns {string|null} 提取的日期
*/
function
extractDateFromDesc
(
desc
)
{
if
(
!
desc
)
return
null
// 尝试匹配 YYYY-MM-DD 或 YYYY年M月D日 格式
const
match
=
desc
.
match
(
/
(\d{4})
-
(\d{2})
-
(\d{2})
/
)
if
(
match
)
{
return
`
${
match
[
1
]}
年
${
parseInt
(
match
[
2
])}
月
${
parseInt
(
match
[
3
])}
日`
}
return
null
}
/**
* 从政治格局描述推断选举周期阶段
* @param {string} desc - 政治格局描述
* @returns {string} 选举周期阶段
*/
function
inferElectionPhase
(
desc
)
{
if
(
!
desc
)
return
'执政初期/蜜月期'
// 根据描述中的关键词推断
if
(
desc
.
includes
(
'就职'
))
{
return
'执政初期/蜜月期'
}
return
'执政初期/蜜月期'
}
/**
* 格式化政府结构类型
* @param {string} type - 政府类型
* @param {string} desc - 详细描述
* @returns {string} 格式化后的政府结构类型
*/
function
formatGovernmentType
(
type
,
desc
)
{
if
(
type
===
'统一政府'
)
{
return
'一致政府(总统与两院同党)'
}
if
(
type
===
'分立政府'
)
{
return
'分立政府'
}
return
type
||
'一致政府(总统与两院同党)'
}
/**
* 根据提案信息生成默认筛选条件
* @param {Object} proposalInfo - 转换后的提案信息
* @returns {Object} 默认筛选条件
*/
export
function
generateDefaultFilters
(
proposalInfo
)
{
if
(
!
proposalInfo
)
return
{}
return
{
// 政策领域 - 使用提案的涉及领域
policyArea
:
proposalInfo
.
areas
.
map
(
area
=>
{
const
areaMap
=
{
'能源'
:
'energy'
,
'集成电路'
:
'ic'
,
'人工智能'
:
'ai'
,
'生物技术'
:
'biotech'
}
return
areaMap
[
area
]
||
area
.
toLowerCase
()
}),
// 提案人职务
proposerPosition
:
proposalInfo
.
proposerPosition
===
'委员会主席'
?
[
'chairman'
]
:
[],
// 政府结构类型
governmentType
:
proposalInfo
.
governmentType
.
includes
(
'一致'
)
?
[
'unified'
]
:
[
'divided'
],
// 选举周期阶段
electionPhase
:
proposalInfo
.
electionPhase
.
includes
(
'蜜月'
)
?
[
'honeymoon'
]
:
[],
// 法案预算规模
budgetScale
:
[
'trillion_plus'
],
// 对方党派提案人
oppositionProposer
:
[
'none'
],
// 提案时间
proposalTime
:
[
'recent_5'
]
}
}
src/router/modules/bill.js
浏览文件 @
c71276c1
...
...
@@ -138,7 +138,7 @@ const billRoutes = [
// }
]
},
{
path
:
"ProgressForecast"
,
path
:
"ProgressForecast
/:id
"
,
name
:
"BillProgressForecast"
,
component
:
BillProgressForecast
,
// meta: { title: "对华科研影响" }
...
...
src/views/bill/influence/ProgressForecast/api/index.ts
浏览文件 @
c71276c1
...
...
@@ -240,7 +240,7 @@ export async function fetchPredictionAnalysis(): Promise<PredictionAnalysis> {
overallRisk
:
'high'
,
overallProgressLevel
:
3
,
overallEstimatedDays
:
'预计耗时120~150天'
,
highlightText
:
'《大而美法案》的通过概率非常高。
\
n
\
n该法案由众议院共和党领导层在5月正式提出,作为特朗普第二任期核心经济议程,旨在通过一揽子税收、贸易改革重塑经济。到6月初,法案已快速通过关键的筹款委员会和预算委员会审议,进入众议院
��院
辩论阶段。共和党当时控制众议院,党内团结支持;白宫已明确表示强烈支持。虽然民主党普遍反对,但共和党凭借微弱优势足以在众议院通过。主要不确定在于参议院,随着中期选举临近民主党议员可能支持,或通过预算和解程序(只需简单多数)规避阻挠议事。因此,该法案在6月初已势在必行,最终成法几无悬念。'
,
highlightText
:
'《大而美法案》的通过概率非常高。
\
n
\
n该法案由众议院共和党领导层在5月正式提出,作为特朗普第二任期核心经济议程,旨在通过一揽子税收、贸易改革重塑经济。到6月初,法案已快速通过关键的筹款委员会和预算委员会审议,进入众议院辩论阶段。共和党当时控制众议院,党内团结支持;白宫已明确表示强烈支持。虽然民主党普遍反对,但共和党凭借微弱优势足以在众议院通过。主要不确定在于参议院,随着中期选举临近民主党议员可能支持,或通过预算和解程序(只需简单多数)规避阻挠议事。因此,该法案在6月初已势在必行,最终成法几无悬念。'
,
phases
:
[
{
id
:
1
,
...
...
@@ -251,7 +251,7 @@ export async function fetchPredictionAnalysis(): Promise<PredictionAnalysis> {
estimatedDays
:
'已通过'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正
������
外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
...
...
@@ -383,7 +383,7 @@ export async function fetchBillList(): Promise<BillInfo[]> {
return
[
{
id
:
'bill-1'
,
title
:
'激励创新的交通走廊
��减
少碳和温室气体排放,提供允许对公共交通系统投资的税收结构,并帮助化石燃料劳动力向可持续工作领域转型'
,
title
:
'激励创新的交通走廊
以
少碳和温室气体排放,提供允许对公共交通系统投资的税收结构,并帮助化石燃料劳动力向可持续工作领域转型'
,
proposalDate
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
...
...
src/views/bill/influence/ProgressForecast/components/Step1FilterCondition.vue
浏览文件 @
c71276c1
...
...
@@ -5,7 +5,7 @@
</div>
<template
v-else
>
<div
class=
"content-wrapper"
>
<ProposalInfoSection
v-if=
"
proposalInfo"
:info=
"p
roposalInfo"
/>
<ProposalInfoSection
v-if=
"
currentProposalInfo"
:info=
"currentP
roposalInfo"
/>
<FilterSection
:fields=
"filterFields"
@
update:fields=
"filterFields = $event"
...
...
@@ -21,33 +21,61 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
from
'vue'
import
{
ref
,
onMounted
,
watch
}
from
'vue'
import
type
{
ProposalInfo
,
FilterField
}
from
'../api'
import
{
fetchProposalInfo
,
fetchFilterFields
}
from
'../api'
import
ProposalInfoSection
from
'./ProposalInfoSection.vue'
import
FilterSection
from
'./FilterSection.vue'
import
ActionButtons
from
'./ActionButtons.vue'
const
props
=
defineProps
<
{
proposalInfo
?:
ProposalInfo
|
null
defaultFilters
?:
Record
<
string
,
string
[]
>
}
>
()
const
emit
=
defineEmits
<
{
next
:
[]
}
>
()
// 提案信息
const
p
roposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
// 提案信息
(优先使用 props,否则从 API 获取)
const
currentP
roposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
// 筛选字段列表
const
filterFields
=
ref
<
FilterField
[]
>
([])
// 加载状态
const
loading
=
ref
(
true
)
// 初始筛选字段(用于重置)
const
initialFilterFields
=
ref
<
FilterField
[]
>
([])
// 监听 props.proposalInfo 变化
watch
(()
=>
props
.
proposalInfo
,
(
newInfo
)
=>
{
if
(
newInfo
)
{
currentProposalInfo
.
value
=
newInfo
}
},
{
immediate
:
true
})
// 页面初始化时加载数据
onMounted
(
async
()
=>
{
try
{
const
[
info
,
fields
]
=
await
Promise
.
all
([
fetchProposalInfo
(),
fetchFilterFields
()
])
proposalInfo
.
value
=
info
filterFields
.
value
=
fields
// 如果没有从 props 获取到提案信息,则从 API 获取
if
(
!
props
.
proposalInfo
)
{
currentProposalInfo
.
value
=
await
fetchProposalInfo
()
}
// 获取筛选字段配置
const
fields
=
await
fetchFilterFields
()
// 如果有默认筛选条件,应用到筛选字段
if
(
props
.
defaultFilters
&&
Object
.
keys
(
props
.
defaultFilters
).
length
>
0
)
{
filterFields
.
value
=
fields
.
map
(
field
=>
({
...
field
,
selectedValues
:
props
.
defaultFilters
?.[
field
.
id
]
||
field
.
selectedValues
}))
}
else
{
filterFields
.
value
=
fields
}
// 保存初始状态用于重置
initialFilterFields
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
filterFields
.
value
))
}
finally
{
loading
.
value
=
false
}
...
...
@@ -55,15 +83,45 @@ onMounted(async () => {
// 重置所有筛选条件
function
handleReset
()
{
filterFields
.
value
=
filterFields
.
value
.
map
(
field
=>
({
...
field
,
selectedValues
:
[]
}))
filterFields
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
initialFilterFields
.
value
))
}
// 设置为当前提案
function
handleSetAsCurrent
()
{
console
.
log
(
'设置为当前提案'
)
// 根据当前提案信息重新设置筛选条件
if
(
currentProposalInfo
.
value
)
{
filterFields
.
value
=
filterFields
.
value
.
map
(
field
=>
{
// 根据提案信息设置默认值
const
defaultValues
=
getDefaultValuesForField
(
field
.
id
,
currentProposalInfo
.
value
!
)
return
{
...
field
,
selectedValues
:
defaultValues
.
length
>
0
?
defaultValues
:
field
.
selectedValues
}
})
}
}
// 根据字段 ID 和提案信息获取默认值
function
getDefaultValuesForField
(
fieldId
:
string
,
info
:
ProposalInfo
):
string
[]
{
const
areaMap
:
Record
<
string
,
string
>
=
{
'能源'
:
'energy'
,
'集成电路'
:
'ic'
,
'人工智能'
:
'ai'
,
'生物技术'
:
'biotech'
}
switch
(
fieldId
)
{
case
'policyArea'
:
return
info
.
areas
.
map
(
area
=>
areaMap
[
area
]
||
area
.
toLowerCase
())
case
'proposerPosition'
:
return
info
.
proposerPosition
===
'委员会主席'
?
[
'chairman'
]
:
[]
case
'governmentType'
:
return
info
.
governmentType
.
includes
(
'一致'
)
?
[
'unified'
]
:
[
'divided'
]
case
'electionPhase'
:
return
info
.
electionPhase
.
includes
(
'蜜月'
)
?
[
'honeymoon'
]
:
[]
default
:
return
[]
}
}
// 下一步
...
...
src/views/bill/influence/ProgressForecast/components/Step2FilterBills.vue
浏览文件 @
c71276c1
<
template
>
<div
class=
"step-container"
>
<div
v-if=
"loading"
class=
"loading-wrapper flex-display-center"
>
<span
class=
"text-tip-2
text-tip-1"
>
加载中...
</span>
<span
class=
"text-tip-2 text-tip-1"
>
加载中...
</span>
</div>
<template
v-else-if=
"stats"
>
<div
class=
"content-wrapper"
>
...
...
@@ -12,7 +12,7 @@
<img
src=
"../assets/document.svg"
>
<span
class=
"text-title-3-bold text-primary-80-clor"
>
筛选结果统计
</span>
</div>
<p
class=
"text-tip-2
stats-subtitle text-tip-1"
style=
"margin-left: 21px;"
>
基于筛选条件匹配的历史相似法案
</p>
<p
class=
"text-tip-2 stats-subtitle text-tip-1"
style=
"margin-left: 21px;"
>
基于筛选条件匹配的历史相似法案
</p>
</div>
<div
class=
"header-right"
>
<div
class=
"total-number-row"
>
...
...
@@ -28,8 +28,8 @@
:key=
"stat.label"
class=
"stat-card"
>
<span
class=
"text-title-2-bold
text-primary-80-clor"
>
{{
stat
.
label
}}
</span>
<span
class=
"main-color stat-value
"
>
{{
stat
.
value
}}
</span>
<span
class=
"text-title-2-bold text-primary-80-clor"
>
{{
stat
.
label
}}
</span>
<span
class=
"main-color stat-value"
>
{{
stat
.
value
}}
</span>
</div>
</div>
<div
class=
"select-row"
>
...
...
@@ -42,7 +42,7 @@
/>
<span
class=
"text-compact"
>
全选
</span>
</label>
<span
class=
"
text-tip-1"
>
已选择
{{
selectedCount
}}
项法案
</span>
<span
class=
"text-tip-1"
>
已选择
{{
selectedCount
}}
项法案
</span>
</div>
<div
class=
"bills-list"
>
<BillCard
...
...
@@ -75,9 +75,10 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
onMounted
}
from
'vue'
import
{
ref
,
computed
,
onMounted
,
inject
,
watch
}
from
'vue'
import
type
{
FilterStats
,
BillInfo
}
from
'../api'
import
{
fetchFilterStats
,
fetchBillList
}
from
'../api'
import
{
getSimiBills
,
transformSimiBillsData
}
from
'@/api/bill/billHome'
import
BillCard
from
'./BillCard.vue'
const
emit
=
defineEmits
<
{
...
...
@@ -85,6 +86,9 @@ const emit = defineEmits<{
next
:
[]
}
>
()
// 从父组件注入筛选参数
const
filterParams
=
inject
<
any
>
(
'filterParams'
,
null
)
// 筛选统计数据
const
stats
=
ref
<
FilterStats
|
null
>
(
null
)
// 法案列表
...
...
@@ -114,8 +118,33 @@ const selectedCount = computed(() => {
})
// 加载数据
onMounted
(
async
()
=>
{
async
function
loadData
()
{
loading
.
value
=
true
try
{
// 优先使用真实 API
const
params
=
{
patternType
:
filterParams
?.
governmentType
||
'统一政府'
,
proposalType
:
'两党共同提案'
}
const
response
=
await
getSimiBills
(
params
)
if
(
response
&&
response
.
success
&&
response
.
data
)
{
const
{
stats
:
apiStats
,
bills
:
apiBills
}
=
transformSimiBillsData
(
response
)
stats
.
value
=
apiStats
bills
.
value
=
apiBills
}
else
{
// 如果 API 失败,使用模拟数据
const
[
statsData
,
billsData
]
=
await
Promise
.
all
([
fetchFilterStats
(),
fetchBillList
()
])
stats
.
value
=
statsData
bills
.
value
=
billsData
}
}
catch
(
error
)
{
console
.
error
(
'获取相似法案失败:'
,
error
)
// 使用模拟数据作为 fallback
const
[
statsData
,
billsData
]
=
await
Promise
.
all
([
fetchFilterStats
(),
fetchBillList
()
...
...
@@ -125,8 +154,18 @@ onMounted(async () => {
}
finally
{
loading
.
value
=
false
}
}
// 加载数据
onMounted
(()
=>
{
loadData
()
})
// 监听筛选参数变化,重新加载数据
watch
(()
=>
filterParams
,
()
=>
{
loadData
()
},
{
deep
:
true
})
// 切换全选
function
toggleSelectAll
()
{
const
newValue
=
!
isAllSelected
.
value
...
...
@@ -155,9 +194,10 @@ function handleStartAnalysis() {
</
script
>
<
style
scoped
>
.text-tip-1
{
.text-tip-1
{
color
:
#5f656c
;
}
.step-container
{
display
:
flex
;
flex-direction
:
column
;
...
...
@@ -242,7 +282,6 @@ function handleStartAnalysis() {
background
:
white
;
border-radius
:
10px
;
border
:
1px
solid
rgba
(
234
,
236
,
238
,
1
);
/* box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); */
overflow
:
hidden
;
}
...
...
@@ -261,13 +300,12 @@ function handleStartAnalysis() {
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
}
.stat-value
{
font-size
:
30px
;
font-weight
:
700
;
color
:
var
(
--color-red-100
);
color
:
var
(
--color-red-100
);
}
.select-row
{
...
...
src/views/bill/influence/ProgressForecast/components/Step3PredictionAnalysis.vue
浏览文件 @
c71276c1
...
...
@@ -160,7 +160,7 @@ function handleRepredict() {
.header-section
{
justify-content
:
space-between
;
align-items
:
flex-start
;
margin-bottom
:
24px
;
/* margin-bottom: 24px; */
}
.header-left
{
...
...
src/views/bill/influence/ProgressForecast/index.vue
浏览文件 @
c71276c1
...
...
@@ -12,6 +12,8 @@
<template
v-if=
"!loading"
>
<Step1FilterCondition
v-if=
"currentStep === 1"
:proposal-info=
"proposalInfo"
:default-filters=
"defaultFilters"
@
next=
"goToNextStep"
/>
<Step2FilterBills
...
...
@@ -35,26 +37,62 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
from
'vue'
import
type
{
Step
}
from
'./api'
import
{
ref
,
onMounted
,
provide
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
import
type
{
Step
,
ProposalInfo
}
from
'./api'
import
{
fetchSteps
}
from
'./api'
import
{
getProgressPrediction
,
parseBillId
,
transformProposalInfo
,
generateDefaultFilters
}
from
'@/api/bill/billHome.js'
import
PageHeader
from
'./components/PageHeader.vue'
import
StepSidebar
from
'./components/StepSidebar.vue'
import
Step1FilterCondition
from
'./components/Step1FilterCondition.vue'
import
Step2FilterBills
from
'./components/Step2FilterBills.vue'
import
Step3PredictionAnalysis
from
'./components/Step3PredictionAnalysis.vue'
const
route
=
useRoute
()
// 步骤列表数据
const
steps
=
ref
<
Step
[]
>
([])
// 当前步骤
const
currentStep
=
ref
(
1
)
// 加载状态
const
loading
=
ref
(
true
)
// 提案信息
const
proposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
// 默认筛选条件
const
defaultFilters
=
ref
<
Record
<
string
,
string
[]
>>
({})
// 法案ID
const
billId
=
ref
(
''
)
// 向子组件提供提案信息
provide
(
'proposalInfo'
,
proposalInfo
)
provide
(
'defaultFilters'
,
defaultFilters
)
// 页面初始化时加载数据
onMounted
(
async
()
=>
{
try
{
steps
.
value
=
await
fetchSteps
()
// 获取路由参数中的 id
const
routeId
=
route
.
params
.
id
as
string
||
'1'
billId
.
value
=
parseBillId
(
routeId
)
// 并行加载步骤数据和提案信息
const
[
stepsData
,
predictionData
]
=
await
Promise
.
all
([
fetchSteps
(),
getProgressPrediction
(
billId
.
value
).
catch
(
err
=>
{
console
.
error
(
'[v0] 获取预测数据失败:'
,
err
)
return
null
})
])
steps
.
value
=
stepsData
if
(
predictionData
)
{
// 转换 API 数据为组件所需格式
proposalInfo
.
value
=
transformProposalInfo
(
predictionData
)
// 根据提案信息生成默认筛选条件
if
(
proposalInfo
.
value
)
{
defaultFilters
.
value
=
generateDefaultFilters
(
proposalInfo
.
value
)
}
}
}
finally
{
loading
.
value
=
false
}
...
...
@@ -92,13 +130,11 @@ function handleRepredict() {
.layout-main-center
{
width
:
1600px
;
flex
:
1
;
/* margin: 0 auto; */
padding-bottom
:
32px
;
box-sizing
:
border-box
;
}
.content-card
{
/* height: 903px; */
display
:
flex
;
overflow
:
hidden
;
border-radius
:
var
(
--radius-10
);
...
...
src/views/innovationSubject/InnovationAnalysis/bindEcharts.js
浏览文件 @
c71276c1
...
...
@@ -608,7 +608,7 @@ export const raderOption1 = (data) => {
const
indicatorNames
=
data
.
map
(
item
=>
item
.
areaName
);
const
indicatorValues
=
data
.
map
(
item
=>
item
.
areaValue
);
//
动态生成雷���图的
indicator 配置
//
indicator 配置
const
indicators
=
indicatorNames
.
map
((
name
,
index
)
=>
({
name
,
max
:
Math
.
max
(...
indicatorValues
)
*
1.2
// 设置最大值为所有值的最大值的1.2倍
...
...
src/views/innovationSubject/innovativeInstitutions/index.vue
浏览文件 @
c71276c1
...
...
@@ -221,8 +221,8 @@ const latestDynamics = ref([
year
:
'2025'
,
date
:
'9月15日'
,
title
:
'艾伦·M·加伯接任临时校长'
,
content
:
'原教务长艾伦·加伯(Alan M. Garber)出任哈佛第30任校长(临时),并完整复学术诚信与校园团
�����
。'
,
tags
:
[{
name
:
'集成
��
路'
,
type
:
'tag6'
}],
content
:
'原教务长艾伦·加伯(Alan M. Garber)出任哈佛第30任校长(临时),并完整复学术诚信与校园团
结
。'
,
tags
:
[{
name
:
'集成
电
路'
,
type
:
'tag6'
}],
isHighlight
:
true
},
{
...
...
src/views/innovationSubject/innovativeInstitutions/tabs/ResearchStrength.vue
浏览文件 @
c71276c1
...
...
@@ -288,7 +288,7 @@ const paperPoints = computed(() => getDataPoints(paperData))
const
fundingPoints
=
computed
(()
=>
getDataPoints
(
fundingData
))
// 雷达图数据和计算
const
radarValues
=
[
0.7
,
0.9
,
0.85
,
0.6
,
0.75
,
0.65
]
// 各领域
相对��
const
radarValues
=
[
0.7
,
0.9
,
0.85
,
0.6
,
0.75
,
0.65
]
// 各领域
const
getHexagonPoints
=
(
cx
,
cy
,
r
)
=>
{
const
points
=
[]
...
...
src/views/technologyFigures/component/TimelineMap.vue
浏览文件 @
c71276c1
...
...
@@ -440,7 +440,7 @@ export default {
};
chart
.
setOption
(
option
);
// 地图拖拽/缩放时重新计算 DOM 卡片位置(加防抖避免频繁���发)
let
georoamTimer
=
null
;
chart
.
on
(
"georoam"
,
()
=>
{
if
(
georoamTimer
)
clearTimeout
(
georoamTimer
);
...
...
vite.config.js
浏览文件 @
c71276c1
...
...
@@ -51,15 +51,15 @@ export default defineConfig({
rewrite
:
(
path
)
=>
path
.
replace
(
/^
\/
reportData/
,
''
)
},
// '/api': {
// target: 'http://8.140.26.4:9085/',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '')
'/api'
:
{
target
:
'http://
10.119.133.162:28080
/'
,
target
:
'http://
8.140.26.4:9085
/'
,
changeOrigin
:
true
,
rewrite
:
(
path
)
=>
path
.
replace
(
/^
\/
api/
,
''
)
// '/api': {
// target: 'http://10.119.133.162:28080/',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '')
},
'/sseChat'
:
{
// target: 'http://192.168.26.115:8000',
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论