Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
d37759f1
提交
d37759f1
authored
3月 17, 2026
作者:
刘宇琪
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
lyq 法案预测 功能第一版
上级
8b87711d
隐藏空白字符变更
内嵌
并排
正在显示
29 个修改的文件
包含
2733 行增加
和
25 行删除
+2733
-25
bill.js
src/router/modules/bill.js
+15
-2
index.ts
src/views/bill/influence/ProgressForecast/api/index.ts
+441
-0
collection.svg
...ews/bill/influence/ProgressForecast/assets/collection.svg
+3
-0
document.svg
...views/bill/influence/ProgressForecast/assets/document.svg
+4
-0
down.svg
src/views/bill/influence/ProgressForecast/assets/down.svg
+17
-0
fitller.svg
src/views/bill/influence/ProgressForecast/assets/fitller.svg
+4
-0
forecast.svg
...views/bill/influence/ProgressForecast/assets/forecast.svg
+4
-0
icon1.svg
src/views/bill/influence/ProgressForecast/assets/icon1.svg
+4
-0
importent.svg
...iews/bill/influence/ProgressForecast/assets/importent.svg
+6
-0
input.svg
src/views/bill/influence/ProgressForecast/assets/input.svg
+4
-0
source.svg
src/views/bill/influence/ProgressForecast/assets/source.svg
+15
-0
ActionButtons.vue
...l/influence/ProgressForecast/components/ActionButtons.vue
+113
-0
BillCard.vue
...s/bill/influence/ProgressForecast/components/BillCard.vue
+123
-0
FilterSection.vue
...l/influence/ProgressForecast/components/FilterSection.vue
+145
-0
FilterSelect.vue
...ll/influence/ProgressForecast/components/FilterSelect.vue
+195
-0
FilterTag.vue
.../bill/influence/ProgressForecast/components/FilterTag.vue
+51
-0
PageHeader.vue
...bill/influence/ProgressForecast/components/PageHeader.vue
+94
-0
PredictionPhaseCard.vue
...uence/ProgressForecast/components/PredictionPhaseCard.vue
+288
-0
ProposalInfoSection.vue
...uence/ProgressForecast/components/ProposalInfoSection.vue
+167
-0
Step1FilterCondition.vue
...ence/ProgressForecast/components/Step1FilterCondition.vue
+90
-0
Step2FilterBills.vue
...nfluence/ProgressForecast/components/Step2FilterBills.vue
+357
-0
Step3PredictionAnalysis.vue
...e/ProgressForecast/components/Step3PredictionAnalysis.vue
+311
-0
StepSidebar.vue
...ill/influence/ProgressForecast/components/StepSidebar.vue
+126
-0
index.vue
src/views/bill/influence/ProgressForecast/index.vue
+120
-0
main.ts
src/views/bill/influence/ProgressForecast/main.ts
+5
-0
shims-vue.d.ts
src/views/bill/influence/ProgressForecast/shims-vue.d.ts
+5
-0
style.css
src/views/bill/influence/ProgressForecast/style.css
+12
-0
index.vue
...inkTankPerson/components/characterRelationships/index.vue
+8
-17
vite.config.js
vite.config.js
+6
-6
没有找到文件。
src/router/modules/bill.js
浏览文件 @
d37759f1
...
...
@@ -11,6 +11,7 @@ const BillDeepDigProcessAnalysis = () => import('@/views/bill/deepDig/processAna
const
BillDeepDigPoliContribution
=
()
=>
import
(
'@/views/bill/deepDig/poliContribution/index.vue'
)
const
BillInfluenceLayout
=
()
=>
import
(
'@/views/bill/influence/index.vue'
)
const
BillInfluenceIndustry
=
()
=>
import
(
'@/views/bill/influence/industry/index.vue'
)
const
BillProgressForecast
=
()
=>
import
(
'@/views/bill/influence/ProgressForecast/index.vue'
)
const
BillInfluenceScientificResearch
=
()
=>
import
(
'@/views/bill/influence/scientificResearch/index.vue'
)
const
BillRelevantCircumstance
=
()
=>
import
(
'@/views/bill/relevantCircumstance/index.vue'
)
const
BillOriginalText
=
()
=>
import
(
'@/views/bill/billOriginalText/index.vue'
)
...
...
@@ -60,7 +61,7 @@ const billRoutes = [
component
:
BillIntroduction
,
// meta: {
// title: "法案简介"
// }
},
{
...
...
@@ -128,9 +129,21 @@ const billRoutes = [
name
:
"BillInfluenceScientificResearch"
,
component
:
BillInfluenceScientificResearch
,
// meta: { title: "对华科研影响" }
}
},
// {
// path: "ProgressForecast",
// name: "BillProgressForecast",
// component: BillProgressForecast,
// // meta: { title: "对华科研影响" }
// }
]
},
{
path
:
"ProgressForecast"
,
name
:
"BillProgressForecast"
,
component
:
BillProgressForecast
,
// meta: { title: "对华科研影响" }
},
{
path
:
"relevantCircumstance"
,
name
:
"BillRelevantCircumstance"
,
...
...
src/views/bill/influence/ProgressForecast/api/index.ts
0 → 100644
浏览文件 @
d37759f1
// 模拟 API 延迟
const
delay
=
(
ms
:
number
)
=>
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
// 步骤数据类型
export
interface
Step
{
id
:
number
title
:
string
description
:
string
}
// 当前提案信息类型
export
interface
ProposalInfo
{
title
:
string
date
:
string
areas
:
string
[]
electionPhase
:
string
proposer
:
string
coProposers
:
string
proposerPosition
:
string
governmentType
:
string
budgetScale
:
string
}
// 筛选条件类型
export
interface
FilterOption
{
value
:
string
label
:
string
hint
?:
string
}
export
interface
FilterField
{
id
:
string
label
:
string
options
:
FilterOption
[]
selectedValues
:
string
[]
hint
?:
string
}
// 获取步骤数据
export
async
function
fetchSteps
():
Promise
<
Step
[]
>
{
await
delay
(
100
)
return
[
{
id
:
1
,
title
:
'设置筛选条件'
,
description
:
'基于核心相似度维度构建预测基准'
},
{
id
:
2
,
title
:
'筛选同类法案'
,
description
:
'形成历史相似同类法案预测基准集'
},
{
id
:
3
,
title
:
'阶段预测分析'
,
description
:
'基于相似法案的历史数据预测模型'
}
]
}
// 获取当前提案信息
export
async
function
fetchProposalInfo
():
Promise
<
ProposalInfo
>
{
await
delay
(
100
)
return
{
title
:
'H.R.1-大而美法案'
,
date
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
proposer
:
'乔迪·阿灵顿(Jodey Arrington)'
,
coProposers
:
'共和党4人'
,
proposerPosition
:
'委员会主席'
,
governmentType
:
'一致政府(总统与两院同党)'
,
budgetScale
:
'4万亿美元'
}
}
// 获取筛选字段配置
export
async
function
fetchFilterFields
():
Promise
<
FilterField
[]
>
{
await
delay
(
100
)
return
[
{
id
:
'policyArea'
,
label
:
'政策领域:'
,
options
:
[
{
value
:
'energy'
,
label
:
'能源'
},
{
value
:
'ic'
,
label
:
'集成电路'
},
{
value
:
'ai'
,
label
:
'人工智能'
},
{
value
:
'biotech'
,
label
:
'生物技术'
}
],
selectedValues
:
[
'energy'
,
'ic'
]
},
{
id
:
'proposerPosition'
,
label
:
'提案人职务:'
,
options
:
[
{
value
:
'chairman'
,
label
:
'委员会主席'
},
{
value
:
'vice_chairman'
,
label
:
'委员会副主席'
},
{
value
:
'member'
,
label
:
'普通委员'
}
],
selectedValues
:
[
'chairman'
]
},
{
id
:
'governmentType'
,
label
:
'政府结构类型:'
,
options
:
[
{
value
:
'unified'
,
label
:
'一致政府'
},
{
value
:
'divided'
,
label
:
'分裂政府'
}
],
selectedValues
:
[
'unified'
],
hint
:
'总统所属政党同时控制国会参众两院'
},
{
id
:
'electionPhase'
,
label
:
'选举周期阶段:'
,
options
:
[
{
value
:
'honeymoon'
,
label
:
'执政初期/蜜月期'
},
{
value
:
'midterm'
,
label
:
'中期选举期'
},
{
value
:
'election'
,
label
:
'大选年'
}
],
selectedValues
:
[
'honeymoon'
],
hint
:
'新总统上任初期,上任后约100天至1年。'
},
{
id
:
'budgetScale'
,
label
:
'法案预算规模:'
,
options
:
[
{
value
:
'trillion_plus'
,
label
:
'1万亿元以上'
},
{
value
:
'billion_plus'
,
label
:
'100亿-1万亿'
},
{
value
:
'below_billion'
,
label
:
'100亿以下'
}
],
selectedValues
:
[
'trillion_plus'
]
},
{
id
:
'oppositionProposer'
,
label
:
'对方党派提案人:'
,
options
:
[
{
value
:
'none'
,
label
:
'无对方党派提案人'
},
{
value
:
'has'
,
label
:
'有对方党派提案人'
}
],
selectedValues
:
[
'none'
]
},
{
id
:
'proposalTime'
,
label
:
'提案时间:'
,
options
:
[
{
value
:
'recent_5'
,
label
:
'近五年'
},
{
value
:
'recent_10'
,
label
:
'近十年'
},
{
value
:
'all'
,
label
:
'全部'
}
],
selectedValues
:
[
'recent_5'
]
}
]
}
// 提交筛选条件
export
async
function
submitFilters
(
filters
:
Record
<
string
,
string
[]
>
):
Promise
<
{
success
:
boolean
}
>
{
await
delay
(
300
)
console
.
log
(
'提交筛选条件:'
,
filters
)
return
{
success
:
true
}
}
// 筛选结果统计类型
export
interface
FilterStats
{
totalBills
:
number
becameLaw
:
number
notPassedOrShelved
:
number
medianDays
:
number
passRate
:
number
selectedCount
:
number
}
// 法案信息类型
export
interface
BillInfo
{
id
:
string
title
:
string
proposalDate
:
string
areas
:
string
[]
electionPhase
:
string
proposer
:
string
coProposers
:
string
proposerPosition
:
string
governmentType
:
string
budgetScale
:
string
passDays
:
number
selected
:
boolean
}
// 获取筛选结果统计
export
async
function
fetchFilterStats
():
Promise
<
FilterStats
>
{
await
delay
(
100
)
return
{
totalBills
:
50
,
becameLaw
:
24
,
notPassedOrShelved
:
26
,
medianDays
:
223
,
passRate
:
48.5
,
selectedCount
:
50
}
}
// 阶段预测分析类型
export
type
RiskLevel
=
'passed'
|
'high'
|
'medium'
|
'low'
export
interface
StatItem
{
value
:
string
label
:
string
}
export
interface
PredictionPhase
{
id
:
number
title
:
string
description
:
string
riskLevel
:
RiskLevel
progressLevel
:
number
estimatedDays
:
string
modelInputs
:
string
[]
supportingFacts
:
{
title
:
string
basedOn
:
string
stats
:
StatItem
[]
}
}
export
interface
PredictionAnalysis
{
title
:
string
subtitle
:
string
overallRisk
:
RiskLevel
overallProgressLevel
:
number
overallEstimatedDays
:
string
highlightText
:
string
phases
:
PredictionPhase
[]
}
// 获取阶段预测分析数据
export
async
function
fetchPredictionAnalysis
():
Promise
<
PredictionAnalysis
>
{
await
delay
(
200
)
return
{
title
:
'立法进展阶段预测分析'
,
subtitle
:
'基于50个相似法案的历史数据与预测模型'
,
overallRisk
:
'high'
,
overallProgressLevel
:
3
,
overallEstimatedDays
:
'预计耗时120~150天'
,
highlightText
:
'《大而美法案》的通过概率非常高。
\
n
\
n该法案由众议院共和党领导层在5月正式提出,作为特朗普第二任期核心经济议程,旨在通过一揽子税收、贸易改革重塑经济。到6月初,法案已快速通过关键的筹款委员会和预算委员会审议,进入众议院��院辩论阶段。共和党当时控制众议院,党内团结支持;白宫已明确表示强烈支持。虽然民主党普遍反对,但共和党凭借微弱优势足以在众议院通过。主要不确定在于参议院,随着中期选举临近民主党议员可能支持,或通过预算和解程序(只需简单多数)规避阻挠议事。因此,该法案在6月初已势在必行,最终成法几无悬念。'
,
phases
:
[
{
id
:
1
,
title
:
'阶段一:法案提出'
,
description
:
'提案提交至参议院/众议院,此阶段为流程起点'
,
riskLevel
:
'passed'
,
progressLevel
:
3
,
estimatedDays
:
'已通过'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正������外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
title
:
'支撑预测的客观事实'
,
basedOn
:
'基于142项历史提案'
,
stats
:
[
{
value
:
'100%'
,
label
:
'提案提交率'
},
{
value
:
'142'
,
label
:
'匹配法案数'
},
{
value
:
'62.7%'
,
label
:
'进入委员会比例'
},
{
value
:
'85天'
,
label
:
'平均等待分配时间'
}
]
}
},
{
id
:
2
,
title
:
'阶段二:参议院通过'
,
description
:
'包括委员会审议、听证、修正和全院表决'
,
riskLevel
:
'high'
,
progressLevel
:
3
,
estimatedDays
:
'预计耗时60~120天'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正案;外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
title
:
'支撑预测的客观事实'
,
basedOn
:
'基于98项历史提案'
,
stats
:
[
{
value
:
'62.7%'
,
label
:
'历史通过率'
},
{
value
:
'85天'
,
label
:
'平均耗时'
},
{
value
:
'78%'
,
label
:
'委员会主席支持率'
},
{
value
:
'31%'
,
label
:
'对方党派平均支持'
},
{
value
:
'8.5次'
,
label
:
'平均听证会次数'
},
{
value
:
'14.2个'
,
label
:
'平均修正案数'
},
{
value
:
'92.3%'
,
label
:
'本党派支持率'
},
{
value
:
'68天'
,
label
:
'中位数耗时'
}
]
}
},
{
id
:
3
,
title
:
'阶段三:众议院通过'
,
description
:
'众议院审议、委员会分配和全院表决'
,
riskLevel
:
'medium'
,
progressLevel
:
2
,
estimatedDays
:
'预计耗时20~45天'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正案;外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
title
:
'支撑预测的客观事实'
,
basedOn
:
'基于98项历史提案'
,
stats
:
[
{
value
:
'62.7%'
,
label
:
'历史通过率'
},
{
value
:
'85天'
,
label
:
'平均耗时'
},
{
value
:
'78%'
,
label
:
'本党派支持率'
},
{
value
:
'31%'
,
label
:
'对方党派平均支持'
},
{
value
:
'45%'
,
label
:
'规则委员会支持率'
},
{
value
:
'14.2个'
,
label
:
'平均修正案数'
},
{
value
:
'49.5天'
,
label
:
'中位数耗时'
},
{
value
:
'32%'
,
label
:
'快速通道使用率'
}
]
}
},
{
id
:
4
,
title
:
'阶段四:两院协商'
,
description
:
'协商委员会解决两院版本差异,形成统一文本'
,
riskLevel
:
'medium'
,
progressLevel
:
2
,
estimatedDays
:
'预计耗时7~14天'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正案;外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
title
:
'支撑预测的客观事实'
,
basedOn
:
'基于96项历史提案'
,
stats
:
[
{
value
:
'62.7%'
,
label
:
'历史通过率'
},
{
value
:
'85天'
,
label
:
'平均耗时'
},
{
value
:
'8.5个'
,
label
:
'平均差异条款数'
},
{
value
:
'3.2轮'
,
label
:
'平均协商轮次'
},
{
value
:
'78%'
,
label
:
'协商成功法案比例'
},
{
value
:
'42%'
,
label
:
'需要领导层介入比例'
},
{
value
:
'55天'
,
label
:
'中位数耗时'
},
{
value
:
'15%'
,
label
:
'协商失败后重启比例'
}
]
}
},
{
id
:
5
,
title
:
'阶段五:总统签署'
,
description
:
'总统审议、可能否决或签署成为法律'
,
riskLevel
:
'low'
,
progressLevel
:
1
,
estimatedDays
:
'预计耗时1~5天'
,
modelInputs
:
[
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
,
'确定法案的动态特征:随着流程推进才产生的特征,这是分阶段预测的核心。包括流程特征:在当前节点已停留时间、距国会会休会剩余时间;投票特征:上一节点投票结果(如委员会投票的赞成比例、党派支持度)。修正案特征:法案是否被附加了有争议的修正案;外部环境特征:当前新闻情绪指数、公众舆论压力、关键利益集团的态度。'
,
'确定法案的固有特征:法案提出时确定的特征,如提案人党派、资历、共同提案人网络强度、法案主题分类、文本复杂度。这些特征在所有节点预测中都可使用。'
],
supportingFacts
:
{
title
:
'支撑预测的客观事实'
,
basedOn
:
'基于33次送法总统法案'
,
stats
:
[
{
value
:
'62.7%'
,
label
:
'历史签署率'
},
{
value
:
'12天'
,
label
:
'平均审议时间'
},
{
value
:
'24.2%'
,
label
:
'否决率'
},
{
value
:
'3.2轮'
,
label
:
'口袋否决率'
},
{
value
:
'78%'
,
label
:
'一致政府时签署率'
},
{
value
:
'42%'
,
label
:
'分立政府时签署率'
},
{
value
:
'9天'
,
label
:
'中位数审议时间'
},
{
value
:
'15%'
,
label
:
'附带声明签署比例'
}
]
}
}
]
}
}
// 获取法案列表
export
async
function
fetchBillList
():
Promise
<
BillInfo
[]
>
{
await
delay
(
200
)
return
[
{
id
:
'bill-1'
,
title
:
'激励创新的交通走廊��减少碳和温室气体排放,提供允许对公共交通系统投资的税收结构,并帮助化石燃料劳动力向可持续工作领域转型'
,
proposalDate
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
proposer
:
'乔迪·阿灵顿(Jodey Arrington)'
,
coProposers
:
'共和党4人;民主党2人'
,
proposerPosition
:
'委员会主席'
,
governmentType
:
'一致政府(总统与两院同党)'
,
budgetScale
:
'7500亿美元'
,
passDays
:
245
,
selected
:
true
},
{
id
:
'bill-2'
,
title
:
'重新授权国家海洋和大气管理局切萨皮克湾办公室及其他用途'
,
proposalDate
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
proposer
:
'乔迪·阿灵顿(Jodey Arrington)'
,
coProposers
:
'共和党4人;民主党2人'
,
proposerPosition
:
'委员会主席'
,
governmentType
:
'一致政府(总统与两院同党)'
,
budgetScale
:
'7500亿美元'
,
passDays
:
245
,
selected
:
true
},
{
id
:
'bill-3'
,
title
:
'特朗普关税透明法案'
,
proposalDate
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
proposer
:
'乔迪·阿灵顿(Jodey Arrington)'
,
coProposers
:
'共和党4人;民主党2人'
,
proposerPosition
:
'委员会主席'
,
governmentType
:
'一致政府(总统与两院同党)'
,
budgetScale
:
'7500亿美元'
,
passDays
:
245
,
selected
:
true
},
{
id
:
'bill-4'
,
title
:
'禁止向关注国家销售最先进的人工智能芯片,并促进芯片安全高效地向海外获批美国人员转让'
,
proposalDate
:
'2025年5月20日'
,
areas
:
[
'能源'
,
'集成电路'
],
electionPhase
:
'执政初期/蜜月期'
,
proposer
:
'乔迪·阿灵顿(Jodey Arrington)'
,
coProposers
:
'共和党4人;民主党2人'
,
proposerPosition
:
'委员会主席'
,
governmentType
:
'一致政府(总统与两院同党)'
,
budgetScale
:
'7500亿美元'
,
passDays
:
245
,
selected
:
true
}
]
}
src/views/bill/influence/ProgressForecast/assets/collection.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 18 18"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"18.000000"
height=
"18.000000"
fill=
"none"
customFrame=
"#000000"
>
<path
id=
"星形 2"
d=
"M10.9534 6.01133C10.9936 6.13492 11.1088 6.2186 11.2387 6.2186L16.6363 6.21885C16.9269 6.21885 17.0478 6.59069 16.8127 6.76151L12.446 9.93429C12.3409 10.0107 12.2969 10.1461 12.3371 10.2697L14.0048 15.4031C14.0946 15.6795 13.7783 15.9094 13.5432 15.7385L9.17633 12.5661C9.07118 12.4897 8.92882 12.4897 8.82367 12.5661L4.45683 15.7385C4.22171 15.9094 3.90539 15.6795 3.99518 15.4031L5.66293 10.2697C5.70308 10.1461 5.65909 10.0107 5.55395 9.93429L1.18734 6.76151C0.952245 6.59069 1.07307 6.21885 1.36368 6.21885L6.76125 6.2186C6.89121 6.2186 7.00639 6.13492 7.04655 6.01132L8.71469 0.877992C8.8045 0.601613 9.1955 0.601613 9.28531 0.877992L10.9534 6.01133Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/document.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 1925"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"合并"
d=
"M2.45616 3.00599L2.47971 13.9809C2.48128 14.542 2.78116 14.998 3.34324 14.998L2.54408 14.998C1.982 14.998 1.52512 14.542 1.52355 13.9809L1.5 2.01867C1.5 1.45605 1.95688 1 2.52053 1L10.9705 1C11.5342 1 11.9911 1.45605 11.9911 2.01867L11.9911 2.62987C11.9079 2.14874 11.4886 1.98732 10.9847 1.98732L3.31969 1.98732C2.75604 1.98732 2.45616 2.44337 2.45616 3.00599ZM4.24592 2.72586L11.0034 2.72586C11.5576 2.72586 12.0051 3.1819 12.0051 3.74452L12.0051 8.14359C11.8481 8.12479 11.6895 8.11538 11.5278 8.11538C9.35171 8.11538 7.58541 9.85182 7.58541 11.9926C7.58541 13.2056 8.1522 14.2885 9.04084 15L4.26947 15C3.71838 15 3.26935 14.544 3.26778 13.9829L3.24423 3.74452C3.24423 3.1819 3.69169 2.72586 4.24592 2.72586ZM10.2419 5.08759C10.5151 5.08759 10.7365 4.86192 10.7365 4.58453C10.7365 4.30714 10.5151 4.08147 10.2419 4.08147L5.00739 4.08147C4.7342 4.08147 4.51282 4.30714 4.51282 4.58453C4.51282 4.86192 4.7342 5.08759 5.00739 5.08759L10.2419 5.08759ZM8.65461 7.92732C8.9278 7.92732 9.14918 7.70165 9.14918 7.42426C9.14918 7.14687 8.92623 6.92119 8.65304 6.92119L5.00739 6.92119C4.7342 6.92119 4.51282 7.14687 4.51282 7.42426C4.51282 7.70165 4.7342 7.92732 5.00739 7.92732L8.65461 7.92732ZM8.47101 11.9891C8.47101 10.3278 9.82126 8.98007 11.4855 8.98007C13.1498 8.98007 14.5 10.3278 14.5 11.9891C14.5 13.6503 13.1498 14.998 11.4855 14.998C9.82126 14.998 8.47101 13.6503 8.47101 11.9891ZM6.14096 10.7968C6.41415 10.7968 6.63553 10.5712 6.63553 10.2938C6.63553 10.0164 6.41258 9.7907 6.14096 9.7907L5.00896 9.7907C4.73577 9.7907 4.51439 10.0164 4.51439 10.2938C4.51439 10.5712 4.73577 10.7968 5.00896 10.7968L6.14096 10.7968ZM12.0036 13.5186L12.0036 12.0141L13.0132 12.0141L11.5012 10.3592L9.98925 12.0141L10.9941 12.0141L10.9941 13.5186L12.0036 13.5186ZM6.14096 13.6444C6.41415 13.6444 6.63553 13.4187 6.63553 13.1413C6.63553 12.8639 6.41258 12.6383 6.14096 12.6383L5.00896 12.6383C4.73577 12.6383 4.51439 12.8639 4.51439 13.1413C4.51439 13.4187 4.73577 13.6444 5.00896 13.6444L6.14096 13.6444Z"
fill=
"rgb(59,65,75)"
fill-rule=
"evenodd"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/down.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 28 28"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"28.000000"
height=
"28.000000"
fill=
"none"
clip-path=
"url(#clipPath_2)"
customFrame=
"url(#clipPath_2)"
>
<defs>
<clipPath
id=
"clipPath_2"
>
<rect
width=
"28.000000"
height=
"28.000000"
x=
"0.000000"
y=
"0.000000"
rx=
"14.000000"
fill=
"rgb(255,255,255)"
/>
</clipPath>
<clipPath
id=
"clipPath_3"
>
<rect
width=
"18.000000"
height=
"15.000000"
x=
"5.000000"
y=
"5.000000"
fill=
"rgb(255,255,255)"
/>
</clipPath>
</defs>
<rect
id=
"导出数据"
width=
"28.000000"
height=
"28.000000"
x=
"0.000000"
y=
"0.000000"
rx=
"14.000000"
/>
<g
id=
"容器 742"
customFrame=
"url(#clipPath_3)"
>
<rect
id=
"容器 742"
width=
"18.000000"
height=
"15.000000"
x=
"5.000000"
y=
"5.000000"
/>
<rect
id=
"矩形 347"
width=
"2.000000"
height=
"6.000000"
x=
"13.000000"
y=
"5.000000"
fill=
"rgb(132,136,142)"
/>
<path
id=
"矢量 600"
d=
"M18 11L10 11L14 16L18 11Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
<path
id=
"矢量 601"
d=
"M22 19.9996L22.9999 15.0012L19.9999 12.0011L18.9999 12L21.0003 15.001L17.9999 15.0015L16.9998 17.9987L14 17.9996L11.0001 17.9997L9.99998 15.002L7.00017 15.0028L8.99996 12.0008L8 12.0004L5 15.0023L6.00016 20L22 19.9996Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
</g>
</svg>
src/views/bill/influence/ProgressForecast/assets/fitller.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 1926"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"合并"
d=
"M1.35139 1.5L14.6461 1.5C14.946 1.5 15.1091 1.78803 14.9173 1.97461L10.1219 6.60295L10.1219 14.2076C10.1219 14.4249 9.84414 14.5662 9.60823 14.4684L6.0762 13.0174C5.95494 12.9685 5.87998 12.8671 5.87998 12.7566L5.87998 6.60295L1.08461 1.97461C0.890592 1.78803 1.05154 1.5 1.35139 1.5ZM11.1956 6.63044L14.0243 6.63044C14.2183 6.63044 14.3771 6.76087 14.3749 6.92028L14.3749 7.6177C14.3749 7.77711 14.2161 7.90754 14.0221 7.90754L11.1956 7.90754C11.0016 7.90754 10.8428 7.77711 10.8428 7.6177L10.8428 6.92028C10.8428 6.76087 11.0016 6.63044 11.1956 6.63044ZM11.1956 9.18464L14.0243 9.18464C14.2183 9.18464 14.3771 9.31506 14.3749 9.47447L14.3749 10.1719C14.3749 10.3313 14.2161 10.4617 14.0221 10.4617L11.1956 10.4617C11.0016 10.4617 10.8428 10.3313 10.8428 10.1719L10.8428 9.47447C10.8428 9.31506 11.0016 9.18464 11.1956 9.18464ZM11.1956 11.7406L14.0243 11.7406C14.2183 11.7406 14.3771 11.8693 14.3749 12.0305L14.3749 12.7279C14.3749 12.8873 14.2161 13.0177 14.0221 13.0177L11.1956 13.0177C11.0016 13.0177 10.8428 12.8873 10.8428 12.7279L10.8428 12.0305C10.8428 11.8711 11.0016 11.7406 11.1956 11.7406Z"
fill=
"rgb(59,65,75)"
fill-rule=
"evenodd"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/forecast.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 1438"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"矢量 1656"
d=
"M9.67676 9.0769C9.55421 9.41452 9.32715 9.70684 9.02686 9.9136C8.72656 10.1204 8.3678 10.2314 8 10.2314C7.6322 10.2314 7.27344 10.1204 6.97314 9.9136C6.67285 9.70684 6.44579 9.41452 6.32324 9.0769L3.55562 9.0769C1.59838 9.0769 0 7.59858 0 5.75958C0 4.20884 1.13686 2.91448 2.66667 2.54703L2.66667 1.5769C2.66667 1.47563 2.69405 1.37615 2.74606 1.28845C2.79806 1.20075 2.87287 1.12792 2.96295 1.07729C3.05303 1.02666 3.15522 1 3.25924 1L11.5555 1C11.6596 0.999984 11.7618 1.02663 11.8518 1.07726C11.9419 1.12789 12.0168 1.20072 12.0688 1.28842C12.1208 1.37613 12.1482 1.47562 12.1482 1.5769L12.1482 4.4615C12.1482 4.56279 12.1208 4.6623 12.0688 4.75002C12.0168 4.83774 11.942 4.91058 11.8519 4.96122C11.7618 5.01186 11.6596 5.03851 11.5555 5.0385L3.25933 5.0385C3.15529 5.03851 3.05308 5.01186 2.96298 4.96122C2.87288 4.91058 2.79806 4.83774 2.74604 4.75002C2.69403 4.6623 2.66665 4.56279 2.66667 4.4615L2.66667 3.7534C1.79552 4.07533 1.18514 4.85584 1.18514 5.75958C1.18514 6.94778 2.24 7.9231 3.55562 7.9231L6.32324 7.9231C6.44579 7.58548 6.67285 7.29316 6.97314 7.0864C7.27344 6.87964 7.6322 6.76862 8 6.76862C8.3678 6.76862 8.72656 6.87964 9.02686 7.0864C9.32715 7.29316 9.55421 7.58548 9.67676 7.9231L12.4444 7.9231C14.4015 7.9231 16 9.40142 16 11.2403C16 13.0793 14.4015 14.5577 12.4445 14.5577L12.1482 14.5577L12.1482 15.4231C12.1482 15.5244 12.1208 15.6239 12.0688 15.7116C12.0168 15.7993 11.9419 15.8721 11.8518 15.9227C11.7618 15.9734 11.6596 16 11.5555 16L3.25933 16C3.1553 16 3.0531 15.9734 2.96301 15.9227C2.87291 15.8721 2.79809 15.7993 2.74607 15.7116C2.69405 15.6239 2.66667 15.5244 2.66667 15.4231L2.66667 12.5384C2.66667 12.4371 2.69405 12.3377 2.74606 12.25C2.79806 12.1623 2.87287 12.0894 2.96295 12.0388C3.05303 11.9882 3.15522 11.9615 3.25924 11.9615L11.5555 11.9615C11.6596 11.9615 11.7618 11.9881 11.8518 12.0388C11.9419 12.0894 12.0168 12.1622 12.0688 12.2499C12.1208 12.3376 12.1482 12.4371 12.1482 12.5384L12.1482 13.4038L12.4444 13.4038C13.76 13.4038 14.8149 12.4285 14.8149 11.2403C14.8149 10.0522 13.76 9.0769 12.4444 9.0769L9.67676 9.0769Z"
fill=
"rgb(59,65,75)"
fill-rule=
"nonzero"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/icon1.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
>
<rect
id=
"统计 2"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"合并"
d=
"M8.09474 1.99497L8.09474 6.91078C8.09474 7.08635 8.141 7.25882 8.22886 7.41086C8.31671 7.56291 8.44308 7.68917 8.59526 7.77695C8.74744 7.86473 8.92006 7.91095 9.09578 7.91095L14.0159 7.91095C14.2102 7.9107 14.4003 7.85392 14.5629 7.74754C14.7255 7.64116 14.8536 7.48979 14.9316 7.3119C15.0095 7.13401 15.034 6.93729 15.0019 6.74575C14.7594 5.31252 14.0763 3.99025 13.0476 2.96241C12.0189 1.93458 10.6954 1.25214 9.26095 1.0098C9.06925 0.977761 8.87236 1.00217 8.69431 1.08005C8.51626 1.15793 8.36476 1.28592 8.25829 1.44838C8.15182 1.61085 8.09499 1.80077 8.09474 1.99497ZM12.4164 8.74814L7.75904 8.74814C7.67118 8.74814 7.58487 8.72504 7.50878 8.68115C7.43269 8.63725 7.36951 8.57413 7.32558 8.4981C7.28165 8.42208 7.25852 8.33584 7.25852 8.24806L7.25852 3.60729C7.26139 3.41073 7.20622 3.21768 7.0999 3.05227C6.99358 2.88686 6.84083 2.75642 6.66074 2.67725C6.48065 2.59808 6.2812 2.57369 6.08731 2.60713C4.80952 2.85335 3.64037 3.49154 2.74265 4.43285C1.84493 5.37415 1.26334 6.57171 1.07883 7.85879C0.894329 9.14586 1.11611 10.4584 1.71328 11.6136C2.31046 12.7688 3.25331 13.7093 4.41056 14.3039C5.56781 14.8986 6.88186 15.118 8.16974 14.9314C9.45762 14.7449 10.6552 14.1617 11.5958 13.2632C12.5364 12.3647 13.1732 11.1954 13.4174 9.91834C13.4509 9.72462 13.4265 9.52534 13.3472 9.34541C13.268 9.16547 13.1374 9.01285 12.9719 8.90663C12.8063 8.8004 12.6131 8.74528 12.4164 8.74814Z"
fill=
"rgb(59,65,75)"
fill-rule=
"evenodd"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/importent.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 704"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<circle
id=
"椭圆 96"
cx=
"8"
cy=
"8"
r=
"7"
fill=
"rgb(230,231,232)"
/>
<circle
id=
"椭圆 97"
cx=
"8"
cy=
"4"
r=
"1"
fill=
"rgb(132,136,142)"
/>
<path
id=
"矩形 241"
d=
"M6.49996 6L8.00028 6.0004C8.55256 6.0004 9.00028 6.44811 9.00028 7.00039L9.00028 10.4992C9.00028 10.7754 9.22408 10.9989 9.50033 10.9992L9.50033 10.9997C9.77657 10.9998 10.0005 11.2236 10.0005 11.4998L10.0003 11.5001C10.0002 11.7765 9.77622 12.0006 9.49978 12.0006L8.00028 12.0004L6.50033 12.0004C6.22423 12.0004 6.00064 11.7767 6.00049 11.5006L6.00021 11.5005C6.00021 11.2243 6.22418 11.0003 6.50037 11.0003L6.50037 11.0006C6.77649 11.0007 7.00042 10.7766 7.00042 10.5005L7.00017 7.50005C7.00017 7.22376 6.77644 7.00047 6.50015 7.00002L6.49946 6.99922C6.22357 6.999 6 6.77565 6 6.49976C6.00011 6.22373 6.22393 6 6.49996 6Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/input.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"16.000000"
height=
"16.000000"
fill=
"none"
customFrame=
"#000000"
>
<rect
id=
"容器 1928"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"矢量 1657"
d=
"M4.99213 1.2695C5.91426 1.091 6.91689 1 8 1C9.08311 1 10.0857 1.091 11.0079 1.2695C11.93 1.448 12.6597 1.693 13.1951 2.001C13.7305 2.309 14 2.6415 14 3.002L14 4.003C14 4.3635 13.7323 4.696 13.1951 5.004C12.6579 5.312 11.93 5.55525 11.0079 5.7355C10.0857 5.91225 9.08311 6.0015 8 6.0015C6.91689 6.0015 5.91426 5.9105 4.99213 5.732C4.06999 5.5535 3.34033 5.3085 2.8049 5.0005C2.26772 4.6925 2 4.36 2 3.9995L2 2.9985C2 2.638 2.26772 2.3055 2.8049 1.9975C3.34208 1.693 4.06999 1.44975 4.99213 1.2695L4.99213 1.2695ZM4.54068 6.663C5.6133 6.887 6.7664 6.999 8 6.999C9.23359 6.999 10.3867 6.887 11.4593 6.663C12.5319 6.439 13.3788 6.10825 13.9983 5.67075L13.9983 6.999C13.9983 7.3595 13.7305 7.692 13.1934 8C12.6562 8.308 11.9283 8.55125 11.0061 8.7315C10.0857 8.91 9.08311 8.99925 8 8.99925C6.91689 8.99925 5.91426 8.90825 4.99213 8.72975C4.06999 8.55125 3.34033 8.30625 2.8049 7.99825C2.26772 7.69025 2 7.35775 2 6.99725L2 5.669C2.62117 6.11 3.46807 6.44075 4.54068 6.663L4.54068 6.663ZM4.54068 9.66425C5.6133 9.88825 6.7664 10.0002 8 10.0002C9.23359 10.0002 10.3867 9.88825 11.4593 9.66425C12.5319 9.44025 13.3788 9.1095 13.9983 8.672L13.9983 10.0002C13.9983 10.3607 13.7305 10.6932 13.1934 11.0013C12.6562 11.3093 11.9283 11.5525 11.0061 11.7327C10.084 11.913 9.08311 12.0023 8 12.0023C6.91689 12.0023 5.91426 11.9113 4.99213 11.7327C4.06999 11.5542 3.34208 11.3092 2.8049 11.0013C2.26772 10.6933 2 10.3608 2 10.0002L2 8.672C2.62117 9.1095 3.46807 9.44025 4.54068 9.66425L4.54068 9.66425ZM4.54068 12.6637C5.6133 12.8877 6.7664 12.9998 8 12.9998C9.23359 12.9998 10.3867 12.8877 11.4593 12.6637C12.5319 12.4397 13.3788 12.109 13.9983 11.6715L13.9983 12.9998C13.9983 13.3602 13.7305 13.6927 13.1934 14.0008C12.6562 14.3088 11.9283 14.552 11.0061 14.7323C10.084 14.9125 9.08311 15 8 15C6.91689 15 5.91426 14.909 4.99213 14.7305C4.06999 14.552 3.34033 14.307 2.8049 13.999C2.26772 13.691 2 13.3585 2 12.998L2 11.6697C2.62117 12.109 3.46807 12.4397 4.54068 12.6637L4.54068 12.6637Z"
fill=
"rgb(59,65,75)"
fill-rule=
"nonzero"
/>
</svg>
src/views/bill/influence/ProgressForecast/assets/source.svg
0 → 100644
浏览文件 @
d37759f1
<svg
viewBox=
"0 0 28 28"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"28.000000"
height=
"28.000000"
fill=
"none"
clip-path=
"url(#clipPath_0)"
customFrame=
"url(#clipPath_0)"
>
<defs>
<clipPath
id=
"clipPath_0"
>
<rect
width=
"28.000000"
height=
"28.000000"
x=
"0.000000"
y=
"0.000000"
rx=
"14.000000"
fill=
"rgb(255,255,255)"
/>
</clipPath>
<clipPath
id=
"clipPath_1"
>
<rect
width=
"20.000000"
height=
"20.000000"
x=
"4.000000"
y=
"4.000000"
fill=
"rgb(255,255,255)"
/>
</clipPath>
</defs>
<rect
id=
"数据源"
width=
"28.000000"
height=
"28.000000"
x=
"0.000000"
y=
"0.000000"
rx=
"14.000000"
/>
<g
id=
"数据库 1"
clip-path=
"url(#clipPath_1)"
customFrame=
"url(#clipPath_1)"
>
<rect
id=
"数据库 1"
width=
"20.000000"
height=
"20.000000"
x=
"4.000000"
y=
"4.000000"
/>
<path
id=
"合并"
d=
"M10.6426 6.48828C11.6719 6.28906 12.791 6.1875 14 6.1875C15.209 6.1875 16.3281 6.28906 17.3574 6.48828C18.3867 6.6875 19.2012 6.96094 19.7988 7.30469C20.3965 7.64844 20.6973 8.01953 20.6973 8.42188L20.6973 9.53906C20.6973 9.94141 20.3984 10.3125 19.7988 10.6563C19.1992 11 18.3867 11.2715 17.3574 11.4727C16.3281 11.6699 15.209 11.7695 14 11.7695C12.791 11.7695 11.6719 11.668 10.6426 11.4688C9.61328 11.2695 8.79883 10.9961 8.20117 10.6523C7.60156 10.3086 7.30273 9.9375 7.30273 9.53516L7.30273 8.41797C7.30273 8.01563 7.60156 7.64453 8.20117 7.30078C8.80078 6.96094 9.61328 6.68945 10.6426 6.48828ZM10.1387 12.5078C11.3359 12.7578 12.623 12.8828 14 12.8828C15.377 12.8828 16.6641 12.7578 17.8613 12.5078C19.0586 12.2578 20.0039 11.8887 20.6953 11.4004L20.6953 12.8828C20.6953 13.2852 20.3965 13.6563 19.7969 14C19.1973 14.3438 18.3848 14.6152 17.3555 14.8164C16.3281 15.0156 15.209 15.1152 14 15.1152C12.791 15.1152 11.6719 15.0137 10.6426 14.8145C9.61328 14.6152 8.79883 14.3418 8.20117 13.998C7.60156 13.6543 7.30273 13.2832 7.30273 12.8809L7.30273 11.3984C7.99609 11.8906 8.94141 12.2598 10.1387 12.5078ZM10.1387 15.8574C11.3359 16.1074 12.623 16.2324 14 16.2324C14.6624 16.2324 15.3041 16.2035 15.9249 16.1456C14.2088 16.4715 12.8443 17.3161 12.2805 18.3935C11.7114 18.3432 11.1654 18.2672 10.6426 18.166C9.61328 17.9668 8.80078 17.6934 8.20117 17.3496C7.60156 17.0059 7.30273 16.6348 7.30273 16.2324L7.30273 14.75C7.9961 15.2383 8.94141 15.6074 10.1387 15.8574ZM17.5 16C17.3281 16 17.1581 16.005 16.9902 16.0148C17.2857 15.9695 17.5761 15.917 17.8613 15.8574C19.0586 15.6074 20.0039 15.2383 20.6953 14.75L20.6953 16.2324C20.6953 16.3614 20.6646 16.4872 20.6031 16.6099C19.7199 16.2251 18.6512 16 17.5 16ZM13 19.5C13 18.1193 15.0147 17 17.5 17C19.9853 17 22 18.1193 22 19.5C22 20.8807 19.9853 22 17.5 22C15.0147 22 13 20.8807 13 19.5ZM17.5 18C18.3284 18 19 18.6716 19 19.5C19 20.3284 18.3284 21 17.5 21C16.6716 21 16 20.3284 16 19.5C16 18.6716 16.6716 18 17.5 18ZM12 19.5L12 19.4861C11.3572 19.4236 10.7367 19.33 10.1387 19.2051C8.94141 18.9551 7.99609 18.5859 7.30273 18.0957L7.30273 19.5781C7.30273 19.9805 7.60156 20.3516 8.20117 20.6953C8.79883 21.0391 9.61328 21.3125 10.6426 21.5117C11.4872 21.6752 12.3923 21.7729 13.3579 21.8027C12.5123 21.1873 12 20.3817 12 19.5Z"
fill=
"rgb(132,136,142)"
fill-rule=
"evenodd"
/>
</g>
</svg>
src/views/bill/influence/ProgressForecast/components/ActionButtons.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"action-buttons"
>
<button
class=
"btn-secondary"
@
click=
"emit('reset')"
>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
d=
"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"
/>
<path
d=
"M3 3v5h5"
/>
</svg>
<span>
重置筛选条件
</span>
</button>
<div
class=
"action-right"
>
<button
v-if=
"showPrevious"
class=
"btn-secondary"
@
click=
"emit('previous')"
>
<span>
{{
previousText
||
'返回设置'
}}
</span>
</button>
<button
class=
"btn-primary"
:disabled=
"nextDisabled"
@
click=
"emit('next')"
>
<span>
{{
nextText
||
'下一步'
}}
</span>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<line
x1=
"5"
y1=
"12"
x2=
"19"
y2=
"12"
/>
<polyline
points=
"12 5 19 12 12 19"
/>
</svg>
</button>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
defineProps
<
{
showPrevious
?:
boolean
previousText
?:
string
nextText
?:
string
nextDisabled
?:
boolean
}
>
()
const
emit
=
defineEmits
<
{
reset
:
[]
previous
:
[]
next
:
[]
}
>
()
</
script
>
<
style
scoped
>
.action-buttons
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
padding-top
:
24px
;
}
.action-right
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
}
.btn-secondary
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
padding
:
10px
16px
;
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-4
);
background-color
:
var
(
--bg-white-100
);
font-size
:
14px
;
color
:
var
(
--text-primary-80-color
);
cursor
:
pointer
;
transition
:
background-color
0.2s
;
}
.btn-secondary
:hover
{
background-color
:
var
(
--bg-black-2
);
}
.btn-primary
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
gap
:
8px
;
min-width
:
260px
;
padding
:
10px
32px
;
border
:
none
;
border-radius
:
var
(
--radius-4
);
background-color
:
var
(
--color-primary-100
);
font-size
:
14px
;
color
:
var
(
--bg-white-100
);
cursor
:
pointer
;
transition
:
opacity
0.2s
;
}
.btn-primary
:hover
{
opacity
:
0.9
;
}
.btn-primary
:disabled
{
opacity
:
0.5
;
cursor
:
not-allowed
;
}
.icon
{
width
:
16px
;
height
:
16px
;
}
.btn-secondary
.icon
{
color
:
var
(
--text-primary-65-color
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/BillCard.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"bill-card"
>
<div
class=
"card-content"
>
<input
type=
"checkbox"
:checked=
"bill.selected"
class=
"checkbox"
@
change=
"emit('toggleSelect', bill.id)"
/>
<div
class=
"card-main"
>
<h4
class=
"text-title-3-bold text-primary-80-clor"
>
{{
bill
.
title
}}
</h4>
<div
class=
"fields-grid"
>
<div
v-for=
"field in billFields"
:key=
"field.key"
class=
"field-item"
>
<span
class=
"field-label text-tip-2 text-primary-50-clor"
>
{{
field
.
label
}}
</span>
<template
v-if=
"field.key === 'areas'"
>
<div
class=
"area-tags"
>
<AreaTag
v-for=
"area in bill.areas"
:key=
"area"
:tagName=
"area"
/>
</div>
</
template
>
<
template
v-else-if=
"field.key === 'passDays'"
>
<span
class=
"text-tip-2 main-color"
>
{{
bill
.
passDays
}}
天
</span>
</
template
>
<
template
v-else
>
<span
class=
"text-tip-2"
>
{{
bill
[
field
.
key
as
keyof
typeof
bill
]
}}
</span>
</
template
>
</div>
</div>
</div>
</div>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
type
{
BillInfo
}
from
'../api'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
defineProps
<
{
bill
:
BillInfo
}
>
()
const
emit
=
defineEmits
<
{
toggleSelect
:
[
id
:
string
]
}
>
()
const
billFields
=
[
{
key
:
'proposalDate'
,
label
:
'提案时间:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'electionPhase'
,
label
:
'选举周期阶段:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'proposerPosition'
,
label
:
'提案人职务:'
},
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
},
{
key
:
'budgetScale'
,
label
:
'法案预算规模:'
},
{
key
:
'passDays'
,
label
:
'通过耗时:'
}
]
</
script
>
<
style
scoped
>
.bill-card
{
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-10
);
padding
:
16px
;
}
.card-content
{
display
:
flex
;
align-items
:
flex-start
;
gap
:
12px
;
}
.checkbox
{
width
:
16px
;
height
:
16px
;
margin-top
:
2px
;
flex-shrink
:
0
;
cursor
:
pointer
;
accent-color
:
var
(
--color-primary-100
);
}
.card-main
{
flex
:
1
;
}
.card-main
h4
{
margin
:
0
0
16px
0
;
line-height
:
20px
;
}
.fields-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
3
,
1
fr
);
gap
:
12px
24px
;
}
.field-item
{
display
:
flex
;
align-items
:
center
;
}
.field-label
{
width
:
100px
;
flex-shrink
:
0
;
}
.area-tags
{
display
:
flex
;
gap
:
8px
;
}
.area-tag
{
padding
:
2px
8px
;
font-size
:
12px
;
color
:
var
(
--color-primary-100
);
background-color
:
var
(
--color-primary-10
);
border
:
1px
solid
var
(
--color-primary-10
);
border-radius
:
var
(
--radius-4
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/FilterSection.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<section
class=
"filter-section"
>
<div
class=
"section-header"
>
<div
class=
"header-left"
>
<img
src=
"../assets/fitller.svg"
/>
<h2
class=
"section-title text-title-3-bold"
>
核心相似度维度筛选
</h2>
</div>
<button
class=
"btn-outline"
@
click=
"emit('setAsCurrent')"
>
设置为当前提案
</button>
</div>
<div
class=
"divider"
/>
<div
class=
"fields-grid"
>
<div
v-for=
"field in fields"
:key=
"field.id"
class=
"field-item"
>
<div
class=
"field-label-wrapper"
>
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
{{
field
.
label
}}
</span>
</div>
<div
class=
"field-content"
>
<FilterSelect
:options=
"field.options"
:model-value=
"field.selectedValues"
@
update:model-value=
"(val) => handleFieldUpdate(field.id, val)"
/>
<div
v-if=
"field.hint"
class=
"field-hint"
>
<img
src=
"../assets/importent.svg"
/>
<span
class=
"text-tip-2 text-primary-50-clor"
>
{{
field
.
hint
}}
</span>
</div>
</div>
</div>
</div>
</section>
</
template
>
<
script
setup
lang=
"ts"
>
import
type
{
FilterField
}
from
'../api'
import
FilterSelect
from
'./FilterSelect.vue'
const
props
=
defineProps
<
{
fields
:
FilterField
[]
}
>
()
const
emit
=
defineEmits
<
{
'update:fields'
:
[
fields
:
FilterField
[]]
setAsCurrent
:
[]
}
>
()
// 更新字段选中值
function
handleFieldUpdate
(
fieldId
:
string
,
newValues
:
string
[])
{
const
updatedFields
=
props
.
fields
.
map
(
f
=>
f
.
id
===
fieldId
?
{
...
f
,
selectedValues
:
newValues
}
:
f
)
emit
(
'update:fields'
,
updatedFields
)
}
</
script
>
<
style
scoped
>
.filter-section
{
margin-top
:
24px
;
}
.section-header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
margin-bottom
:
16px
;
}
.header-left
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
}
.section-icon
{
width
:
16px
;
height
:
16px
;
color
:
var
(
--text-primary-80-color
);
}
.btn-outline
{
padding
:
6px
16px
;
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-4
);
background-color
:
var
(
--bg-white-100
);
font-size
:
14px
;
color
:
var
(
--text-primary-80-color
);
cursor
:
pointer
;
transition
:
background-color
0.2s
;
}
.btn-outline
:hover
{
background-color
:
var
(
--bg-black-2
);
}
.divider
{
height
:
1px
;
background-color
:
var
(
--border-black-5
);
margin-bottom
:
24px
;
}
.fields-grid
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
24px
24px
;
padding-left
:
30px
;
}
.field-item
{
width
:
580px
;
display
:
flex
;
align-items
:
flex-start
;
}
.field-label-wrapper
{
padding-top
:
4px
;
}
.field-label
{
display
:
block
;
width
:
150px
;
}
.field-content
{
display
:
flex
;
flex-direction
:
column
;
gap
:
6px
;
}
.field-hint
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
.hint-icon
{
width
:
16px
;
height
:
16px
;
flex-shrink
:
0
;
color
:
var
(
--text-primary-50-color
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/FilterSelect.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"filter-select"
>
<div
class=
"select-trigger flex-display-start"
@
click
.
stop=
"isOpen = !isOpen"
>
<div
class=
"selected-tags"
>
<template
v-if=
"selectedLabels.length > 0"
>
<div
v-for=
"(label, index) in selectedLabels"
:key=
"modelValue[index]"
class=
"tag-item flex-display-center"
>
<span>
{{
label
}}
</span>
<button
class=
"tag-remove flex-display-center"
@
click
.
stop=
"removeValue(modelValue[index])"
>
<svg
class=
"icon-xs"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<line
x1=
"18"
y1=
"6"
x2=
"6"
y2=
"18"
/>
<line
x1=
"6"
y1=
"6"
x2=
"18"
y2=
"18"
/>
</svg>
</button>
</div>
</
template
>
<span
v-else
class=
"placeholder text-tip-2 text-primary-50-clor"
>
{{ placeholder || '请选择' }}
</span>
</div>
<svg
class=
"icon-sm dropdown-arrow"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<polyline
points=
"6 9 12 15 18 9"
/>
</svg>
</div>
<div
v-if=
"isOpen"
class=
"dropdown-menu"
>
<div
v-for=
"option in options"
:key=
"option.value"
class=
"dropdown-item flex-display-start"
:class=
"{ 'dropdown-item--selected': modelValue.includes(option.value) }"
@
click
.
stop=
"toggleOption(option.value)"
>
<span>
{{ option.label }}
</span>
</div>
</div>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
onMounted
,
onUnmounted
}
from
'vue'
import
type
{
FilterOption
}
from
'../api'
const
props
=
defineProps
<
{
options
:
FilterOption
[]
modelValue
:
string
[]
placeholder
?:
string
}
>
()
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
string
[]]
}
>
()
// 下拉框展开状态
const
isOpen
=
ref
(
false
)
// 已选中的标签文字
const
selectedLabels
=
computed
(()
=>
{
return
props
.
modelValue
.
map
(
v
=>
props
.
options
.
find
(
opt
=>
opt
.
value
===
v
)?.
label
)
.
filter
(
Boolean
)
as
string
[]
})
// 切换选项选中状态
function
toggleOption
(
value
:
string
)
{
const
index
=
props
.
modelValue
.
indexOf
(
value
)
if
(
index
===
-
1
)
{
emit
(
'update:modelValue'
,
[...
props
.
modelValue
,
value
])
}
else
{
emit
(
'update:modelValue'
,
props
.
modelValue
.
filter
(
v
=>
v
!==
value
))
}
}
// 移除已选中的值
function
removeValue
(
value
:
string
)
{
emit
(
'update:modelValue'
,
props
.
modelValue
.
filter
(
v
=>
v
!==
value
))
}
// 点击外部关闭下拉框
function
handleClickOutside
(
e
:
MouseEvent
)
{
const
target
=
e
.
target
as
HTMLElement
if
(
!
target
.
closest
(
'.filter-select'
))
{
isOpen
.
value
=
false
}
}
// 组件挂载时添加事件监听
onMounted
(()
=>
{
document
.
addEventListener
(
'click'
,
handleClickOutside
)
})
// 组件卸载时移除事件监听
onUnmounted
(()
=>
{
document
.
removeEventListener
(
'click'
,
handleClickOutside
)
})
</
script
>
<
style
scoped
>
.filter-select
{
position
:
relative
;
width
:
420px
;
}
.select-trigger
{
height
:
32px
;
padding
:
5px
8px
;
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-4
);
background-color
:
var
(
--bg-white-100
);
justify-content
:
space-between
;
cursor
:
pointer
;
}
.selected-tags
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
4px
;
flex
:
1
;
min-width
:
0
;
}
.tag-item
{
gap
:
4px
;
padding
:
0
4px
0
8px
;
background-color
:
var
(
--bg-black-2
);
border-radius
:
var
(
--radius-4
);
font-size
:
14px
;
line-height
:
22px
;
color
:
var
(
--text-primary-80-color
);
}
.tag-remove
{
width
:
13px
;
height
:
13px
;
background
:
none
;
border
:
none
;
padding
:
0
;
cursor
:
pointer
;
color
:
var
(
--text-primary-50-color
);
transition
:
color
0.2s
;
}
.tag-remove
:hover
{
color
:
var
(
--text-primary-80-color
);
}
.dropdown-arrow
{
flex-shrink
:
0
;
width
:
11px
;
height
:
6px
;
margin-left
:
8px
;
color
:
var
(
--text-primary-50-color
);
}
.dropdown-menu
{
position
:
absolute
;
top
:
100%
;
left
:
0
;
right
:
0
;
margin-top
:
4px
;
background-color
:
var
(
--bg-white-100
);
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-4
);
box-shadow
:
var
(
--shadow-card
);
z-index
:
10
;
}
.dropdown-item
{
padding
:
8px
12px
;
font-size
:
14px
;
color
:
var
(
--text-primary-80-color
);
cursor
:
pointer
;
gap
:
8px
;
transition
:
background-color
0.2s
;
}
.dropdown-item
:hover
{
background-color
:
var
(
--bg-black-2
);
}
.dropdown-item--selected
{
background-color
:
var
(
--color-primary-10
);
color
:
var
(
--color-primary-100
);
}
.icon-xs
{
width
:
13px
;
height
:
13px
;
}
.icon-sm
{
width
:
16px
;
height
:
16px
;
}
</
style
>
src/views/bill/influence/ProgressForecast/components/FilterTag.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"filter-tag flex-display-center"
>
<span>
{{
label
}}
</span>
<button
class=
"tag-remove flex-display-center"
@
click=
"emit('remove')"
>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<line
x1=
"18"
y1=
"6"
x2=
"6"
y2=
"18"
/>
<line
x1=
"6"
y1=
"6"
x2=
"18"
y2=
"18"
/>
</svg>
</button>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
defineProps
<
{
label
:
string
}
>
()
const
emit
=
defineEmits
<
{
remove
:
[]
}
>
()
</
script
>
<
style
scoped
>
.filter-tag
{
gap
:
4px
;
padding
:
2px
8px
;
background-color
:
var
(
--bg-black-2
);
border-radius
:
var
(
--radius-4
);
font-size
:
14px
;
color
:
var
(
--text-primary-80-color
);
}
.tag-remove
{
width
:
12px
;
height
:
12px
;
background
:
none
;
border
:
none
;
padding
:
0
;
color
:
var
(
--text-primary-50-color
);
transition
:
color
0.2s
;
}
.tag-remove
:hover
{
color
:
var
(
--text-primary-80-color
);
}
.icon
{
width
:
12px
;
height
:
12px
;
}
</
style
>
src/views/bill/influence/ProgressForecast/components/PageHeader.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<header
class=
"page-header"
>
<div
class=
"header-content"
>
<div
class=
"header-left"
>
<div
class=
"text-title-2-bold"
>
科技法案立法进展预测
</div>
<div
class=
"text-tip-2 text-primary-65-clor"
>
H.R.1-大而美法案(2025年5月20日)
</div>
</div>
<div
class=
"header-actions"
>
<button
class=
"action-btn"
>
<svg
class=
"action-icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
d=
"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
/>
<polyline
points=
"14 2 14 8 20 8"
/>
<line
x1=
"16"
y1=
"13"
x2=
"8"
y2=
"13"
/>
<line
x1=
"16"
y1=
"17"
x2=
"8"
y2=
"17"
/>
<polyline
points=
"10 9 9 9 8 9"
/>
</svg>
<span>
选择法案
</span>
</button>
<button
class=
"action-btn"
>
<svg
class=
"action-icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
d=
"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"
/>
<polyline
points=
"7 10 12 15 17 10"
/>
<line
x1=
"12"
y1=
"15"
x2=
"12"
y2=
"3"
/>
</svg>
<span>
导出分析结果
</span>
</button>
</div>
</div>
</header>
</
template
>
<
script
setup
lang=
"ts"
>
</
script
>
<
style
scoped
>
.page-header
{
width
:
2548px
;
height
:
80px
;
/* margin-left: 160px; */
box-sizing
:
border-box
;
display
:
flex
;
align-items
:
center
;
margin-left
:
-478px
;
padding-left
:
478px
;
background
:
#ffffff
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0.1
);
margin-bottom
:
16px
;
}
.header-content
{
width
:
1600px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
}
.header-left
{
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.header-actions
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
}
.action-btn
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
padding
:
6px
12px
;
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
5px
;
background-color
:
var
(
--bg-white-100
);
font-size
:
16px
;
line-height
:
24px
;
color
:
var
(
--text-primary-65-color
);
cursor
:
pointer
;
transition
:
background-color
0.2s
;
}
.action-btn
:hover
{
background-color
:
var
(
--bg-black-2
);
}
.action-icon
{
width
:
20px
;
height
:
20px
;
color
:
var
(
--text-primary-65-color
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/PredictionPhaseCard.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
v-if=
"phase"
class=
"phase-card"
:class=
"borderColorClass"
>
<div
class=
"phase-header flex-display-start"
>
<div>
<h3
class=
"phase-title main-color text-title-2-bold"
>
{{
phase
.
title
}}
</h3>
<p
class=
"text-tip-2 text-primary-50-clor"
>
{{
phase
.
description
}}
</p>
</div>
<div
class=
"phase-status"
>
<span
class=
"risk-badge"
:class=
"riskColorClass"
>
{{
riskLabel
}}
</span>
<div
class=
"progress-bar-container"
>
<div
v-for=
"i in 3"
:key=
"i"
class=
"progress-segment"
:class=
"getSegmentClass(i)"
></div>
</div>
<p
v-if=
"phase.riskLevel !== 'passed'"
class=
"text-tip-2 text-primary-50-clor"
>
{{
phase
.
estimatedDays
}}
</p>
</div>
</div>
<div
class=
"model-inputs-box"
>
<div
class=
"box-header flex-display-start"
>
<div
class=
"box-title-row flex-display-center"
>
<img
src=
"../assets/input.svg"
/>
<span
class=
"text-compact-bold"
>
预测模型数据输入
</span>
</div>
<div
class=
"box-hint flex-display-center text-tip-2 text-primary-50-clor"
>
<img
src=
"../assets/importent.svg"
/>
<span>
此阶段预测基于以下多维特征
</span>
</div>
</div>
<div
class=
"model-inputs"
>
<p
v-for=
"(input, index) in phase.modelInputs"
:key=
"index"
class=
"text-tip-2 text-primary-65-clor"
>
{{
input
}}
</p>
</div>
</div>
<div
class=
"facts-section"
>
<div
class=
"box-header flex-display-start"
>
<div
class=
"box-title-row flex-display-center"
>
<img
src=
"../assets/icon1.svg"
/>
<span
class=
"text-compact-bold"
>
{{
phase
.
supportingFacts
.
title
}}
</span>
</div>
<div
class=
"box-hint flex-display-center text-tip-2 text-primary-50-clor"
>
<img
src=
"../assets/importent.svg"
/>
<span>
{{
phase
.
supportingFacts
.
basedOn
}}
</span>
</div>
</div>
<div
class=
"stats-grid"
>
<div
v-for=
"(stat, index) in phase.supportingFacts.stats"
:key=
"index"
class=
"stat-card"
>
<div
class=
"stat-value main-color"
>
{{
stat
.
value
}}
</div>
<div
class=
"text-tip-3 text-primary-65-clor"
>
{{
stat
.
label
}}
</div>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'vue'
import
type
{
PredictionPhase
}
from
'../api'
const
props
=
defineProps
<
{
phase
:
PredictionPhase
}
>
()
// 根据风险等级返回边框颜色类
const
borderColorClass
=
computed
(()
=>
{
if
(
!
props
.
phase
)
return
''
switch
(
props
.
phase
.
riskLevel
)
{
case
'passed'
:
return
'border-primary'
case
'high'
:
return
'border-high'
case
'medium'
:
return
'border-medium'
case
'low'
:
return
'border-low'
default
:
return
''
}
})
// 根据索引和progressLevel返回进度条格子的类名
function
getSegmentClass
(
index
:
number
)
{
if
(
!
props
.
phase
)
return
'segment-inactive'
const
level
=
props
.
phase
.
progressLevel
const
isActive
=
index
<=
level
if
(
!
isActive
)
{
return
'segment-inactive'
}
switch
(
props
.
phase
.
riskLevel
)
{
case
'passed'
:
return
'segment-primary'
case
'high'
:
return
'segment-high'
case
'medium'
:
return
'segment-medium'
case
'low'
:
return
'segment-low'
default
:
return
'segment-inactive'
}
}
// 根据风险等级返回文字颜色类
const
riskColorClass
=
computed
(()
=>
{
if
(
!
props
.
phase
)
return
''
switch
(
props
.
phase
.
riskLevel
)
{
case
'passed'
:
return
'main-color'
case
'high'
:
return
'color-red-100'
case
'medium'
:
return
'color-orange-100'
case
'low'
:
return
'color-green-100'
default
:
return
''
}
})
// 根据风险等级返回标签文字
const
riskLabel
=
computed
(()
=>
{
if
(
!
props
.
phase
)
return
''
switch
(
props
.
phase
.
riskLevel
)
{
case
'passed'
:
return
'已通过'
case
'high'
:
return
'高'
case
'medium'
:
return
'中'
case
'low'
:
return
'低'
default
:
return
''
}
})
</
script
>
<
style
scoped
>
.phase-card
{
padding-left
:
24px
;
padding-bottom
:
32px
;
}
.border-primary
{
border-left-color
:
var
(
--color-primary-100
);
}
.border-high
{
border-left-color
:
var
(
--color-red-100
);
}
.border-medium
{
border-left-color
:
var
(
--color-orange-100
);
}
.border-low
{
border-left-color
:
var
(
--color-green-100
);
}
.phase-header
{
justify-content
:
space-between
;
align-items
:
flex-start
;
margin-bottom
:
16px
;
}
.phase-header
>
div
:first-child
{
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.phase-title
{
font-size
:
18px
;
font-weight
:
bold
;
}
.phase-status
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
flex-end
;
gap
:
4px
;
}
.progress-bar-container
{
display
:
flex
;
gap
:
4px
;
}
.progress-segment
{
width
:
50px
;
height
:
4px
;
border-radius
:
2px
;
}
.segment-inactive
{
background-color
:
var
(
--bg-black-5
);
}
.segment-primary
{
background-color
:
var
(
--color-primary-100
);
}
.segment-high
{
background-color
:
var
(
--color-red-100
);
}
.segment-medium
{
background-color
:
var
(
--color-orange-100
);
}
.segment-low
{
background-color
:
var
(
--color-green-100
);
}
.risk-badge
{
font-size
:
20px
;
font-weight
:
bold
;
}
.model-inputs-box
{
background-color
:
var
(
--bg-black-2
);
border-radius
:
var
(
--radius-10
);
padding
:
16px
;
margin-bottom
:
16px
;
}
.box-header
{
justify-content
:
space-between
;
margin-bottom
:
12px
;
}
.box-title-row
{
gap
:
8px
;
}
.box-hint
{
gap
:
4px
;
}
.model-inputs
{
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
}
.model-inputs
p
{
line-height
:
1.6
;
}
.stats-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
4
,
1
fr
);
gap
:
12px
;
}
.stat-card
{
background-color
:
var
(
--color-primary-2
);
border
:
1px
;
padding
:
12px
;
text-align
:
center
;
border
:
1px
solid
var
(
--color-primary-10
);
}
.stat-value
{
font-size
:
18px
;
font-weight
:
bold
;
margin-bottom
:
4px
;
}
.icon
{
width
:
16px
;
height
:
16px
;
color
:
var
(
--text-primary-65-color
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/ProposalInfoSection.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<section
class=
"proposal-section"
>
<div
class=
"section-header"
>
<div
class=
"header-left"
>
<svg
class=
"section-icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
d=
"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
/>
<polyline
points=
"14 2 14 8 20 8"
/>
<line
x1=
"16"
y1=
"13"
x2=
"8"
y2=
"13"
/>
<line
x1=
"16"
y1=
"17"
x2=
"8"
y2=
"17"
/>
<polyline
points=
"10 9 9 9 8 9"
/>
</svg>
<h2
class=
"section-title text-title-3-bold"
>
当前提案信息
</h2>
</div>
<div
class=
"header-actions"
>
<button
class=
"action-btn"
title=
"对比"
>
<img
src=
"../assets/source.svg"
/>
</button>
<button
class=
"action-btn"
title=
"下载"
>
<img
src=
"../assets/down.svg"
/>
</button>
<button
class=
"action-btn"
title=
"收藏"
>
<img
src=
"../assets/collection.svg"
/>
</button>
</div>
</div>
<div
class=
"divider"
/>
<div
class=
"info-grid"
>
<div
v-for=
"field in infoFields"
:key=
"field.key"
class=
"info-item"
>
<span
class=
"info-label text-body-1"
>
{{
field
.
label
}}
</span>
<template
v-if=
"field.key === 'areas'"
>
<div
class=
"area-tags"
>
<!--
<span
v-for=
"area in info.areas"
:key=
"area"
class=
"area-tag"
>
{{
area
}}
</span>
-->
<AreaTag
v-for=
"area in info.areas"
:key=
"area"
:tagName=
"area"
/>
</div>
</
template
>
<
template
v-else
>
<span
class=
"info-value text-body-1"
>
{{
info
[
field
.
key
as
keyof
typeof
info
]
}}
</span>
</
template
>
</div>
</div>
</section>
</template>
<
script
setup
lang=
"ts"
>
import
type
{
ProposalInfo
}
from
'../api'
defineProps
<
{
info
:
ProposalInfo
}
>
()
// 信息字段配置
const
infoFields
=
[
{
key
:
'title'
,
label
:
'提案标题:'
},
{
key
:
'date'
,
label
:
'提案时间:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'electionPhase'
,
label
:
'选举周期阶段:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'proposerPosition'
,
label
:
'提案人职务:'
},
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
},
{
key
:
'budgetScale'
,
label
:
'法案预算规模:'
}
]
</
script
>
<
style
scoped
>
.section-header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
margin-bottom
:
16px
;
}
.header-left
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
}
.section-icon
{
width
:
16px
;
height
:
16px
;
color
:
var
(
--text-primary-80-color
);
}
.header-actions
{
display
:
flex
;
gap
:
8px
;
}
.action-btn
{
width
:
28px
;
height
:
28px
;
padding
:
4px
;
border
:
none
;
border-radius
:
50px
;
background-color
:
transparent
;
cursor
:
pointer
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.action-btn
svg
{
width
:
20px
;
height
:
20px
;
color
:
var
(
--text-primary-50-color
);
}
.action-btn
:hover
svg
{
color
:
var
(
--color-primary-100
);
}
.divider
{
height
:
1px
;
background-color
:
var
(
--border-black-5
);
margin-bottom
:
24px
;
}
.info-grid
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
24px
24px
;
padding-left
:
30px
;
}
.info-item
{
width
:
580px
;
display
:
flex
;
align-items
:
center
;
}
.info-label
{
width
:
150px
;
flex-shrink
:
0
;
color
:
var
(
--text-primary-65-color
);
}
.info-value
{
color
:
var
(
--text-primary-80-color
);
}
.area-tags
{
display
:
flex
;
gap
:
8px
;
}
.area-tag
{
padding
:
1px
8px
;
font-size
:
14px
;
line-height
:
20px
;
color
:
var
(
--color-primary-100
);
background-color
:
var
(
--color-primary-10
);
border
:
1px
solid
var
(
--color-primary-10
);
border-radius
:
var
(
--radius-4
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/Step1FilterCondition.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"step-container"
>
<div
v-if=
"loading"
class=
"loading-wrapper flex-display-center"
>
<span
class=
"text-tip-2 text-primary-50-clor"
>
加载中...
</span>
</div>
<template
v-else
>
<div
class=
"content-wrapper"
>
<ProposalInfoSection
v-if=
"proposalInfo"
:info=
"proposalInfo"
/>
<FilterSection
:fields=
"filterFields"
@
update:fields=
"filterFields = $event"
@
set-as-current=
"handleSetAsCurrent"
/>
</div>
<ActionButtons
@
reset=
"handleReset"
@
next=
"handleNext"
/>
</
template
>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
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
emit
=
defineEmits
<
{
next
:
[]
}
>
()
// 提案信息
const
proposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
// 筛选字段列表
const
filterFields
=
ref
<
FilterField
[]
>
([])
// 加载状态
const
loading
=
ref
(
true
)
// 页面初始化时加载数据
onMounted
(
async
()
=>
{
try
{
const
[
info
,
fields
]
=
await
Promise
.
all
([
fetchProposalInfo
(),
fetchFilterFields
()
])
proposalInfo
.
value
=
info
filterFields
.
value
=
fields
}
finally
{
loading
.
value
=
false
}
})
// 重置所有筛选条件
function
handleReset
()
{
filterFields
.
value
=
filterFields
.
value
.
map
(
field
=>
({
...
field
,
selectedValues
:
[]
}))
}
// 设置为当前提案
function
handleSetAsCurrent
()
{
console
.
log
(
'设置为当前提案'
)
}
// 下一步
function
handleNext
()
{
emit
(
'next'
)
}
</
script
>
<
style
scoped
>
.step-container
{
display
:
flex
;
flex-direction
:
column
;
height
:
100%
;
}
.loading-wrapper
{
flex
:
1
;
}
.content-wrapper
{
flex
:
1
;
overflow
:
auto
;
}
</
style
>
src/views/bill/influence/ProgressForecast/components/Step2FilterBills.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"step-container"
>
<div
v-if=
"loading"
class=
"loading-wrapper flex-display-center"
>
<span
class=
"text-tip-2 text-tip-1"
>
加载中...
</span>
</div>
<template
v-else-if=
"stats"
>
<div
class=
"content-wrapper"
>
<div
class=
"stats-section"
>
<div
class=
"stats-header"
>
<div
class=
"header-left"
>
<div
class=
"flex-display-center section-title-row"
>
<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>
</div>
<div
class=
"header-right"
>
<div
class=
"total-number-row"
>
<span
class=
"total-number main-color"
>
{{
stats
.
totalBills
}}
</span>
<span
class=
"total-unit main-color"
>
项
</span>
</div>
<p
class=
"total-label main-color"
>
历史相似法案
</p>
</div>
</div>
<div
class=
"stats-cards"
>
<div
v-for=
"stat in statsItems"
: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>
</div>
</div>
<div
class=
"select-row"
>
<label
class=
"select-all-label flex-display-center"
>
<input
type=
"checkbox"
:checked=
"isAllSelected"
class=
"checkbox"
@
change=
"toggleSelectAll"
/>
<span
class=
"text-compact"
>
全选
</span>
</label>
<span
class=
" text-tip-1"
>
已选择
{{
selectedCount
}}
项法案
</span>
</div>
<div
class=
"bills-list"
>
<BillCard
v-for=
"bill in bills"
:key=
"bill.id"
:bill=
"bill"
@
toggle-select=
"toggleBillSelect"
/>
</div>
</div>
</div>
<div
class=
"action-footer"
>
<button
class=
"btn-secondary flex-display-center"
@
click=
"handleBack"
>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<path
d=
"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"
/>
<path
d=
"M3 3v5h5"
/>
</svg>
<span>
返回设置
</span>
</button>
<button
class=
"btn-primary flex-display-center"
@
click=
"handleStartAnalysis"
>
<span>
开始预测分析
</span>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<line
x1=
"5"
y1=
"12"
x2=
"19"
y2=
"12"
/>
<polyline
points=
"12 5 19 12 12 19"
/>
</svg>
</button>
</div>
</
template
>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
onMounted
}
from
'vue'
import
type
{
FilterStats
,
BillInfo
}
from
'../api'
import
{
fetchFilterStats
,
fetchBillList
}
from
'../api'
import
BillCard
from
'./BillCard.vue'
const
emit
=
defineEmits
<
{
previous
:
[]
next
:
[]
}
>
()
// 筛选统计数据
const
stats
=
ref
<
FilterStats
|
null
>
(
null
)
// 法案列表
const
bills
=
ref
<
BillInfo
[]
>
([])
// 加载状态
const
loading
=
ref
(
true
)
// 统计项配置,使用循环避免重复代码
const
statsItems
=
computed
(()
=>
{
if
(
!
stats
.
value
)
return
[]
return
[
{
label
:
'成为法律'
,
value
:
`
${
stats
.
value
.
becameLaw
}
个`
},
{
label
:
'未通过/搁置'
,
value
:
`
${
stats
.
value
.
notPassedOrShelved
}
个`
},
{
label
:
'中位数耗时'
,
value
:
`
${
stats
.
value
.
medianDays
}
天`
},
{
label
:
'法案通过率'
,
value
:
`
${
stats
.
value
.
passRate
}
%`
}
]
})
// 计算属性:是否全选
const
isAllSelected
=
computed
(()
=>
{
return
bills
.
value
.
length
>
0
&&
bills
.
value
.
every
(
bill
=>
bill
.
selected
)
})
// 计算属性:已选中数量
const
selectedCount
=
computed
(()
=>
{
return
bills
.
value
.
filter
(
bill
=>
bill
.
selected
).
length
})
// 加载数据
onMounted
(
async
()
=>
{
try
{
const
[
statsData
,
billsData
]
=
await
Promise
.
all
([
fetchFilterStats
(),
fetchBillList
()
])
stats
.
value
=
statsData
bills
.
value
=
billsData
}
finally
{
loading
.
value
=
false
}
})
// 切换全选
function
toggleSelectAll
()
{
const
newValue
=
!
isAllSelected
.
value
bills
.
value
.
forEach
(
bill
=>
{
bill
.
selected
=
newValue
})
}
// 切换单个法案选中状态
function
toggleBillSelect
(
id
:
string
)
{
const
bill
=
bills
.
value
.
find
(
b
=>
b
.
id
===
id
)
if
(
bill
)
{
bill
.
selected
=
!
bill
.
selected
}
}
// 返回设置
function
handleBack
()
{
emit
(
'previous'
)
}
// 开始预测分析
function
handleStartAnalysis
()
{
emit
(
'next'
)
}
</
script
>
<
style
scoped
>
.text-tip-1
{
color
:
#5f656c
;
}
.step-container
{
display
:
flex
;
flex-direction
:
column
;
height
:
100%
;
}
.loading-wrapper
{
flex
:
1
;
}
.content-wrapper
{
flex
:
1
;
overflow
:
auto
;
}
.stats-section
{
display
:
flex
;
flex-direction
:
column
;
}
.stats-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
flex-start
;
margin-bottom
:
16px
;
}
.header-left
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.section-title-row
{
gap
:
8px
;
justify-content
:
left
;
}
.stats-subtitle
{
margin
:
0
;
}
.header-right
{
text-align
:
right
;
}
.total-number-row
{
display
:
flex
;
align-items
:
baseline
;
justify-content
:
flex-end
;
}
.total-number
{
font-size
:
36px
;
font-weight
:
bold
;
line-height
:
1
;
}
.total-unit
{
font-size
:
14px
;
margin-left
:
4px
;
}
.total-label
{
font-size
:
14px
;
margin
:
4px
0
0
0
;
}
.stats-cards
{
display
:
flex
;
gap
:
16px
;
margin-bottom
:
20px
;
}
.stat-card
{
position
:
relative
;
flex
:
1
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
16px
24px
;
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
;
}
.stat-card
::before
{
content
:
''
;
position
:
absolute
;
left
:
0
;
top
:
15px
;
bottom
:
15px
;
width
:
4px
;
border-radius
:
0
2px
2px
0
;
background-color
:
var
(
--color-red-100
);
}
.stat-label
{
font-size
:
16px
;
font-weight
:
400
;
color
:
rgb
(
59
,
65
,
75
);
}
.stat-value
{
font-size
:
30px
;
font-weight
:
700
;
color
:
var
(
--color-red-100
);
}
.select-row
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding-bottom
:
10px
;
border-bottom
:
1px
solid
var
(
--border-black-5
);
margin-bottom
:
16px
;
}
.select-all-label
{
gap
:
8px
;
cursor
:
pointer
;
}
.checkbox
{
width
:
16px
;
height
:
16px
;
cursor
:
pointer
;
accent-color
:
var
(
--color-primary-100
);
}
.bills-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
}
.action-footer
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding-top
:
24px
;
border-top
:
1px
solid
var
(
--border-black-5
);
margin-top
:
24px
;
}
.btn-secondary
{
gap
:
8px
;
padding
:
8px
16px
;
border
:
1px
solid
var
(
--bg-black-10
);
border-radius
:
var
(
--radius-10
);
background-color
:
var
(
--bg-white-100
);
font-size
:
14px
;
color
:
var
(
--text-primary-65-color
);
cursor
:
pointer
;
transition
:
background-color
0.2s
;
}
.btn-secondary
:hover
{
background-color
:
var
(
--bg-black-2
);
}
.btn-primary
{
gap
:
8px
;
padding
:
8px
24px
;
border
:
none
;
border-radius
:
var
(
--radius-10
);
background-color
:
var
(
--color-primary-100
);
font-size
:
14px
;
color
:
var
(
--bg-white-100
);
min-width
:
200px
;
justify-content
:
center
;
cursor
:
pointer
;
transition
:
opacity
0.2s
;
}
.btn-primary
:hover
{
opacity
:
0.9
;
}
.icon
{
width
:
16px
;
height
:
16px
;
}
.icon-sm
{
width
:
16px
;
height
:
16px
;
color
:
var
(
--color-primary-100
);
}
.btn-secondary
.icon
{
color
:
var
(
--text-primary-65-color
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/Step3PredictionAnalysis.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"step-container"
>
<div
v-if=
"predictionData"
class=
"content-wrapper"
>
<div
class=
"header-section flex-display-start"
>
<div
class=
"header-left flex-display-start"
>
<div>
<div
class=
"text-title-2-bold"
>
<img
src=
"../assets/forecast.svg"
/>
{{
predictionData
?.
title
}}
</div>
<div
class=
"text-tip-2 text-primary-50-clor"
style=
"margin-left: 21px;"
>
{{
predictionData
?.
subtitle
}}
</div>
</div>
</div>
<div
class=
"header-right"
>
<span
class=
"risk-label"
:class=
"overallRiskColorClass"
>
{{
overallRiskLabel
}}
</span>
<div
class=
"progress-bar-container"
>
<div
v-for=
"i in 3"
:key=
"i"
class=
"progress-segment"
:class=
"getOverallSegmentClass(i)"
></div>
</div>
<p
class=
"estimated-days text-tip-2 text-primary-50-clor"
>
{{
predictionData
?.
overallEstimatedDays
}}
</p>
</div>
</div>
<div
class=
"highlight-box text-regular"
>
<div
class=
"highlight-wave text-regular"
></div>
<div
class=
"highlight-content text-regular"
>
<p
class=
"highlight-title text-regular"
>
《大而美法案》的通过概率非常高。
</p>
<p
class=
"highlight-text text-regular"
>
该法案由众议院共和党领导层在5月正式提出,作为特朗普第二任期核心经济议程,旨在通过一揽子税收、贸易和预算改革提振经济。到6月初,法案已快速通过关键的筹款委员会和预算委员会审议,进入众议院全院辩论阶段。共和党当时控制众议院,且党内团结支持;白宫已明确表示强烈支持。虽然民主党普遍反对,但共和党凭借席位优势足以在众议院通过。主要不确定性在于参议院,但预计部分温和民主党议员可能支持,或通过预算和解程序(只需简单多数)规避阻挠议事。因此,该法案在6月初已势在必行,最终成法几无悬念。
</p>
</div>
</div>
<div
v-if=
"predictionData?.phases?.length"
class=
"phases-list"
>
<PredictionPhaseCard
v-for=
"phase in predictionData.phases"
:key=
"phase.id"
:phase=
"phase"
/>
</div>
</div>
<div
class=
"action-footer flex-display-start"
>
<button
class=
"btn-text flex-display-center"
@
click=
"handleViewHistory"
>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<line
x1=
"19"
y1=
"12"
x2=
"5"
y2=
"12"
/>
<polyline
points=
"12 19 5 12 12 5"
/>
</svg>
<span>
查看预测基于的历史法案
</span>
</button>
<button
class=
"btn-primary flex-display-center"
@
click=
"handleRepredict"
>
<svg
class=
"icon"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
stroke-width=
"2"
>
<polyline
points=
"23 4 23 10 17 10"
/>
<path
d=
"M20.49 15a9 9 0 1 1-2.12-9.36L23 10"
/>
</svg>
<span>
重新预测
</span>
</button>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
computed
}
from
'vue'
import
{
fetchPredictionAnalysis
,
type
PredictionAnalysis
}
from
'../api'
import
PredictionPhaseCard
from
'./PredictionPhaseCard.vue'
const
emit
=
defineEmits
<
{
(
e
:
'prev'
):
void
(
e
:
'repredict'
):
void
}
>
()
// 预测分析数据
const
predictionData
=
ref
<
PredictionAnalysis
|
null
>
(
null
)
// 获取预测分析数据
onMounted
(
async
()
=>
{
console
.
log
(
'[v0] Step3 mounted, fetching data...'
)
const
data
=
await
fetchPredictionAnalysis
()
console
.
log
(
'[v0] Data fetched:'
,
data
)
predictionData
.
value
=
data
console
.
log
(
'[v0] predictionData set:'
,
predictionData
.
value
)
})
// 根据索引和progressLevel返回进度条格子的类名
function
getOverallSegmentClass
(
index
:
number
)
{
const
level
=
predictionData
.
value
?.
overallProgressLevel
||
0
const
isActive
=
index
<=
level
if
(
!
isActive
)
{
return
'segment-inactive'
}
switch
(
predictionData
.
value
?.
overallRisk
)
{
case
'high'
:
return
'segment-high'
case
'medium'
:
return
'segment-medium'
case
'low'
:
return
'segment-low'
default
:
return
'segment-inactive'
}
}
// 根据风险等级返回颜色类
const
overallRiskColorClass
=
computed
(()
=>
{
if
(
!
predictionData
.
value
)
return
''
switch
(
predictionData
.
value
.
overallRisk
)
{
case
'high'
:
return
'color-red-100'
case
'medium'
:
return
'color-orange-100'
case
'low'
:
return
'color-green-100'
default
:
return
''
}
})
// 根据风险等级返回标签文字
const
overallRiskLabel
=
computed
(()
=>
{
if
(
!
predictionData
.
value
)
return
''
switch
(
predictionData
.
value
.
overallRisk
)
{
case
'high'
:
return
'高'
case
'medium'
:
return
'中'
case
'low'
:
return
'低'
default
:
return
''
}
})
// 查看历史法案
function
handleViewHistory
()
{
emit
(
'prev'
)
}
// 重新预测
function
handleRepredict
()
{
emit
(
'repredict'
)
}
</
script
>
<
style
scoped
>
/* .text-title-2-bold{
color: #3b414b;
} */
.step-container
{
display
:
flex
;
flex-direction
:
column
;
height
:
100%
;
}
.content-wrapper
{
flex
:
1
;
overflow
:
auto
;
}
.header-section
{
justify-content
:
space-between
;
align-items
:
flex-start
;
margin-bottom
:
24px
;
}
.header-left
{
gap
:
12px
;
align-items
:
flex-start
;
}
.header-left
>
div
{
display
:
flex
;
flex-direction
:
column
;
gap
:
4px
;
}
.header-right
{
text-align
:
right
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
flex-end
;
gap
:
4px
;
}
.progress-bar-container
{
display
:
flex
;
gap
:
4px
;
}
.progress-segment
{
width
:
50px
;
height
:
4px
;
border-radius
:
2px
;
}
.segment-inactive
{
background-color
:
var
(
--bg-black-5
);
}
.segment-high
{
background-color
:
var
(
--color-red-100
);
}
.segment-medium
{
background-color
:
var
(
--color-orange-100
);
}
.segment-low
{
background-color
:
var
(
--color-green-100
);
}
.risk-label
{
font-size
:
20px
;
font-weight
:
bold
;
}
.highlight-box
{
position
:
relative
;
background-color
:
var
(
--bg-white-100
);
border
:
1px
solid
var
(
--color-primary-10
);
border-radius
:
4px
;
margin-bottom
:
32px
;
overflow
:
hidden
;
}
.highlight-wave
{
position
:
absolute
;
top
:
0
;
left
:
0
;
right
:
0
;
height
:
61px
;
background
:
linear-gradient
(
180deg
,
rgba
(
231
,
243
,
255
,
1
)
0%
,
rgba
(
246
,
250
,
255
,
0
)
100%
);
pointer-events
:
none
;
}
.highlight-content
{
position
:
relative
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
padding
:
16px
24px
;
}
.highlight-title
{
font-size
:
16px
;
font-weight
:
400
;
line-height
:
30px
;
color
:
var
(
--color-primary-100
);
}
.highlight-text
{
font-size
:
16px
;
font-weight
:
400
;
line-height
:
30px
;
color
:
var
(
--color-primary-100
);
text-align
:
justify
;
}
.phases-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
24px
;
}
.action-footer
{
justify-content
:
space-between
;
padding-top
:
24px
;
border-top
:
1px
solid
var
(
--border-black-5
);
margin-top
:
24px
;
}
.btn-text
{
gap
:
8px
;
padding
:
10px
24px
;
border
:
none
;
background
:
none
;
font-size
:
14px
;
color
:
var
(
--text-primary-65-color
);
transition
:
color
0.2s
;
}
.btn-text
:hover
{
color
:
var
(
--text-primary-80-color
);
}
.btn-primary
{
gap
:
8px
;
padding
:
10px
32px
;
border
:
none
;
border-radius
:
var
(
--radius-10
);
background-color
:
var
(
--color-primary-100
);
font-size
:
14px
;
color
:
var
(
--bg-white-100
);
transition
:
opacity
0.2s
;
}
.btn-primary
:hover
{
opacity
:
0.9
;
}
.icon
{
width
:
16px
;
height
:
16px
;
}
.icon-primary
{
width
:
20px
;
height
:
20px
;
color
:
var
(
--color-primary-100
);
}
</
style
>
src/views/bill/influence/ProgressForecast/components/StepSidebar.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"step-sidebar"
>
<div
class=
"steps-container"
>
<div
v-for=
"(step, index) in steps"
:key=
"step.id"
class=
"step-item"
>
<div
class=
"step-indicator-wrapper"
>
<div
class=
"step-number"
:class=
"
{ 'step-number--active': currentStep === step.id }"
>
{{
step
.
id
}}
</div>
<div
v-if=
"index
<
steps
.
length
-
1
"
class=
"step-line"
/>
</div>
<div
class=
"step-content"
>
<p
class=
"step-title"
:class=
"currentStep === step.id ? 'text-title-3-bold' : 'text-body-1'"
>
{{
step
.
title
}}
</p>
<p
class=
"step-desc text-tip-2"
>
{{
step
.
description
}}
</p>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
type
{
Step
}
from
'../api'
defineProps
<
{
steps
:
Step
[]
currentStep
:
number
}
>
()
</
script
>
<
style
scoped
>
.step-sidebar
{
width
:
320px
;
flex-shrink
:
0
;
border-right
:
1px
solid
var
(
--border-black-5
);
background-color
:
var
(
--bg-white-100
);
height
:
100%
;
padding
:
24px
;
display
:
flex
;
flex-direction
:
column
;
}
.steps-container
{
width
:
272px
;
display
:
flex
;
flex-direction
:
column
;
}
.step-item
{
display
:
flex
;
gap
:
16px
;
min-height
:
80px
;
padding-bottom
:
12px
;
}
.step-item
:last-child
{
padding-bottom
:
0
;
}
.step-indicator-wrapper
{
width
:
32px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
}
.step-number
{
width
:
32px
;
height
:
32px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
border-radius
:
100px
;
font-size
:
14px
;
font-family
:
"Microsoft YaHei"
,
sans-serif
;
line-height
:
22px
;
flex-shrink
:
0
;
background-color
:
var
(
--bg-black-5
);
color
:
var
(
--text-primary-50-color
);
}
.step-number--active
{
background-color
:
var
(
--color-primary-100
);
color
:
var
(
--bg-white-100
);
}
.step-line
{
width
:
1px
;
flex
:
1
;
margin-top
:
8px
;
background-color
:
var
(
--bg-black-5
);
}
.step-content
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
padding-top
:
4px
;
flex
:
1
;
}
.step-title
{
line-height
:
24px
;
color
:
var
(
--text-primary-80-color
);
margin
:
0
;
}
.step-desc
{
line-height
:
22px
;
color
:
var
(
--text-primary-65-color
);
margin
:
0
;
}
</
style
>
src/views/bill/influence/ProgressForecast/index.vue
0 → 100644
浏览文件 @
d37759f1
<
template
>
<div
class=
"app-container"
>
<PageHeader
/>
<main
class=
"layout-main-center"
>
<div
class=
"content-card background-as-card"
>
<StepSidebar
v-if=
"!loading && steps.length > 0"
:steps=
"steps"
:current-step=
"currentStep"
/>
<div
class=
"content-area"
>
<template
v-if=
"!loading"
>
<Step1FilterCondition
v-if=
"currentStep === 1"
@
next=
"goToNextStep"
/>
<Step2FilterBills
v-else-if=
"currentStep === 2"
@
previous=
"goToPreviousStep"
@
next=
"goToNextStep"
/>
<Step3PredictionAnalysis
v-else-if=
"currentStep === 3"
@
prev=
"goToPreviousStep"
@
repredict=
"handleRepredict"
/>
</
template
>
<div
v-else
class=
"loading-container"
>
<span
class=
"text-tip-2 text-primary-50-clor"
>
加载中...
</span>
</div>
</div>
</div>
</main>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
}
from
'vue'
import
type
{
Step
}
from
'./api'
import
{
fetchSteps
}
from
'./api'
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
steps
=
ref
<
Step
[]
>
([])
// 当前步骤
const
currentStep
=
ref
(
1
)
// 加载状态
const
loading
=
ref
(
true
)
// 页面初始化时加载数据
onMounted
(
async
()
=>
{
try
{
steps
.
value
=
await
fetchSteps
()
}
finally
{
loading
.
value
=
false
}
})
// 下一步
function
goToNextStep
()
{
if
(
currentStep
.
value
<
steps
.
value
.
length
)
{
currentStep
.
value
++
}
}
// 上一步
function
goToPreviousStep
()
{
if
(
currentStep
.
value
>
1
)
{
currentStep
.
value
--
}
}
// 重新预测:返回第一步
function
handleRepredict
()
{
currentStep
.
value
=
1
}
</
script
>
<
style
scoped
>
.app-container
{
width
:
100%
;
min-height
:
100vh
;
display
:
flex
;
flex-direction
:
column
;
background-color
:
var
(
--bg-black-2
);
}
.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
);
border
:
1px
solid
var
(
--border-black-5
);
}
.content-area
{
flex
:
1
;
padding
:
10px
26px
29px
25px
;
overflow
:
auto
;
}
.loading-container
{
height
:
100%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
</
style
>
src/views/bill/influence/ProgressForecast/main.ts
0 → 100644
浏览文件 @
d37759f1
import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
'./style.css'
createApp
(
App
).
mount
(
'#app'
)
src/views/bill/influence/ProgressForecast/shims-vue.d.ts
0 → 100644
浏览文件 @
d37759f1
declare
module
'*.vue'
{
import
type
{
DefineComponent
}
from
'vue'
const
component
:
DefineComponent
<
{},
{},
any
>
export
default
component
}
src/views/bill/influence/ProgressForecast/style.css
0 → 100644
浏览文件 @
d37759f1
*
{
margin
:
0
;
padding
:
0
;
box-sizing
:
border-box
;
}
html
,
body
,
#app
{
width
:
100%
;
height
:
100%
;
}
src/views/characterPage/components/thinkTankPerson/components/characterRelationships/index.vue
浏览文件 @
d37759f1
...
...
@@ -106,8 +106,7 @@ const getProxyUrl = (url) => {
// 创建圆形图片的函数
const
createCircularImage
=
(
imageUrl
,
size
)
=>
{
return
new
Promise
((
resolve
)
=>
{
if
(
!
imageUrl
)
{
console
.
log
(
"[v0] No image URL provided"
);
if
(
!
imageUrl
)
{
resolve
(
''
);
return
;
}
...
...
@@ -132,15 +131,13 @@ const createCircularImage = (imageUrl, size) => {
ctx
.
drawImage
(
img
,
0
,
0
,
size
,
size
);
resolve
(
canvas
.
toDataURL
(
'image/png'
));
}
catch
(
error
)
{
console
.
log
(
"[v0] Canvas error:"
,
error
);
}
catch
(
error
)
{
resolve
(
imageUrl
);
}
};
img
.
onload
=
onLoadSuccess
;
img
.
onerror
=
()
=>
{
console
.
log
(
"[v0] Image failed to load, trying without crossOrigin:"
,
imageUrl
);
img
.
onerror
=
()
=>
{
// 如果加载失败,尝试不带 crossOrigin 的方式
const
img2
=
new
Image
();
img2
.
onload
=
()
=>
{
...
...
@@ -157,13 +154,11 @@ const createCircularImage = (imageUrl, size) => {
ctx
.
drawImage
(
img2
,
0
,
0
,
size
,
size
);
resolve
(
canvas
.
toDataURL
(
'image/png'
));
}
catch
(
error
)
{
console
.
log
(
"[v0] Fallback canvas error:"
,
error
);
}
catch
(
error
)
{
resolve
(
imageUrl
);
}
};
img2
.
onerror
=
()
=>
{
console
.
log
(
"[v0] Image loading failed completely:"
,
imageUrl
);
img2
.
onerror
=
()
=>
{
resolve
(
imageUrl
);
};
img2
.
src
=
imageUrl
;
...
...
@@ -213,15 +208,11 @@ const getCharacterRelationFn = async () => {
}
}
// 创建圆形头像
console
.
log
(
"[v0] characterInfo.value:"
,
characterInfo
.
value
);
console
.
log
(
"[v0] characterInfo.value.imageUrl:"
,
characterInfo
.
value
.
imageUrl
);
// 创建圆形头像
const
centerImageUrl
=
getProxyUrl
(
characterInfo
.
value
.
imageUrl
);
console
.
log
(
"[v0] centerImageUrl:"
,
centerImageUrl
);
const
centerImageUrl
=
getProxyUrl
(
characterInfo
.
value
.
imageUrl
);
const
centerCircularImage
=
await
createCircularImage
(
centerImageUrl
,
160
);
console
.
log
(
"[v0] centerCircularImage:"
,
centerCircularImage
?.
substring
(
0
,
50
));
const
centerCircularImage
=
await
createCircularImage
(
centerImageUrl
,
160
);
if
(
CharacterRelation
.
value
.
length
>
0
){
// 并行创建所有圆形头像
...
...
vite.config.js
浏览文件 @
d37759f1
...
...
@@ -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://
8.140.26.4:9085
/'
,
target
:
'http://
10.119.133.162:28080
/'
,
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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论