Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
8148dacc
提交
8148dacc
authored
3月 25, 2026
作者:
张伊明
浏览文件
操作
浏览文件
下载
差异文件
合并分支 'liuyuqi' 到 'pre'
Liuyuqi 查看合并请求
!227
上级
4d409c10
ed1f2324
流水线
#57
已通过 于阶段
in 6 分 15 秒
变更
21
流水线
1
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
21 个修改的文件
包含
567 行增加
和
416 行删除
+567
-416
billHome.js
src/api/bill/billHome.js
+65
-34
overview.js
src/api/innovationSubject/overview.js
+54
-57
InnovationSubject.js
src/router/modules/InnovationSubject.js
+4
-6
index.ts
src/views/bill/influence/ProgressForecast/api/index.ts
+0
-0
BillCard.vue
...s/bill/influence/ProgressForecast/components/BillCard.vue
+0
-3
FilterSection.vue
...l/influence/ProgressForecast/components/FilterSection.vue
+170
-51
PredictionPhaseCard.vue
...uence/ProgressForecast/components/PredictionPhaseCard.vue
+15
-12
ProposalInfoSection.vue
...uence/ProgressForecast/components/ProposalInfoSection.vue
+2
-12
Step1FilterCondition.vue
...ence/ProgressForecast/components/Step1FilterCondition.vue
+23
-81
Step2FilterBills.vue
...nfluence/ProgressForecast/components/Step2FilterBills.vue
+20
-21
Step3PredictionAnalysis.vue
...e/ProgressForecast/components/Step3PredictionAnalysis.vue
+19
-17
index.vue
src/views/bill/influence/ProgressForecast/index.vue
+0
-0
symbol.png
...erOfCongress/components/characterRelationships/symbol.png
+0
-0
BillCard.vue
...ess/components/historicalProposal/components/BillCard.vue
+19
-15
index.vue
src/views/characterPage/components/techLeader/index.vue
+17
-6
index.vue
src/views/characterPage/components/thinkTankPerson/index.vue
+17
-5
index.vue
...ontrol/v2.0SingleSanction/components/deepMining/index.vue
+12
-13
ResourceLibrary.vue
...ews/innovationSubject/ResourceLibrary/ResourceLibrary.vue
+29
-17
index.vue
src/views/innovationSubject/innovativeInstitutions/index.vue
+9
-1
OtherInfo.vue
...novationSubject/innovativeInstitutions/tabs/OtherInfo.vue
+92
-65
ResearchStrength.vue
...nSubject/innovativeInstitutions/tabs/ResearchStrength.vue
+0
-0
没有找到文件。
src/api/bill/billHome.js
浏览文件 @
8148dacc
...
@@ -328,16 +328,20 @@ export function getProgressPrediction(billId) {
...
@@ -328,16 +328,20 @@ export function getProgressPrediction(billId) {
/**
/**
* 获取相似法案列表
* 获取相似法案列表
* @param {Object} params - 查询参数
* @param {Object} params - 查询参数
* @param {string} params.patternType - 政府结构类型,如 "统一政府"
* @param {string} params.billIds - 当前法案的ID
* @param {string} params.proposalType - 提案类型,默认 "两党共同提案"
* @param {string[]} params.domains - 领域名称列表
* @param {string} params.patternType - 政府结构类型,如 "统一政府"/"分裂政府"/"微弱多数"
* @param {string} params.proposalType - 提案类型,如 "两党共同提案"/"单政党提案(共和党提案)"/"单政党提案(民主党提案)"
* @returns {Promise<Object>} 相似法案列表
* @returns {Promise<Object>} 相似法案列表
*/
*/
export
function
getSimiBills
(
params
=
{})
{
export
function
getSimiBills
(
params
=
{})
{
return
request
(
'/api/BillProgressPrediction/simiBills'
,
{
return
request
(
'/api/BillProgressPrediction/simiBills'
,
{
method
:
'GET'
,
method
:
'GET'
,
params
:
{
params
:
{
patternType
:
params
.
patternType
||
'统一政府'
,
billIds
:
params
.
billIds
,
proposalType
:
params
.
proposalType
||
'两党共同提案'
,
domains
:
params
.
domains
,
patternType
:
params
.
patternType
,
proposalType
:
params
.
proposalType
,
...
params
...
params
}
}
})
})
...
@@ -400,31 +404,42 @@ export function transformSimiBillsData(apiData) {
...
@@ -400,31 +404,42 @@ export function transformSimiBillsData(apiData) {
const
transformedBills
=
bills
.
map
(
bill
=>
({
const
transformedBills
=
bills
.
map
(
bill
=>
({
id
:
bill
.
bill_id
,
id
:
bill
.
bill_id
,
title
:
bill
.
bill_name
||
bill
.
bill_id
,
title
:
bill
.
bill_name
||
bill
.
bill_id
,
tags
:
[
bill
.
poli_pattern_type
,
bill
.
bill_proposal_type
].
filter
(
Boolean
),
proposalDate
:
extractProposalDate
(
bill
.
poli_pattern_desc
),
passTime
:
extractPassTime
(
bill
.
bill_actions
),
areas
:
bill
.
domain_name
?
(
Array
.
isArray
(
bill
.
domain_name
)
?
bill
.
domain_name
:
[
bill
.
domain_name
])
:
[],
totalDays
:
calculateTotalDays
(
bill
.
bill_actions
),
proposer
:
extractProposer
(
bill
.
bill_sponsors
),
selected
:
true
,
// 默认全选
coProposers
:
bill
.
bill_proposal_desc
||
''
,
_raw
:
bill
governmentType
:
formatGovernmentType
(
bill
.
poli_pattern_type
,
bill
.
poli_pattern_desc
),
passDays
:
calculateTotalDays
(
bill
.
bill_actions
),
selected
:
true
// 默认全选
}))
}))
return
{
stats
,
bills
:
transformedBills
}
return
{
stats
,
bills
:
transformedBills
}
}
}
/**
/**
* 从
法案动作中提取通过
时间
* 从
政治格局描述中提取提案
时间
* @param {
Array} actions - 法案动作列表
* @param {
string} desc - 政治格局描述
* @returns {string}
通过
时间
* @returns {string}
提案
时间
*/
*/
function
extractPassTime
(
actions
)
{
function
extractProposalDate
(
desc
)
{
if
(
!
actions
||
actions
.
length
===
0
)
return
''
if
(
!
desc
)
return
''
// 找到最后一个动作的日期
const
match
=
desc
.
match
(
/
(\d{4})
-
(\d{2})
-
(\d{2})
/
)
const
lastAction
=
actions
[
actions
.
length
-
1
]
if
(
match
)
{
if
(
lastAction
&&
lastAction
.
action_date
)
{
return
`
${
match
[
1
]}
年
${
parseInt
(
match
[
2
])}
月
${
parseInt
(
match
[
3
])}
日`
const
date
=
new
Date
(
lastAction
.
action_date
)
return
`
${
date
.
getFullYear
()}
年
${
date
.
getMonth
()
+
1
}
月
${
date
.
getDate
()}
日`
}
}
return
''
return
''
}
}
/**
* 从提案人列表中提取主提案人
* @param {Array} sponsors - 提案人列表
* @returns {string} 主提案人姓名
*/
function
extractProposer
(
sponsors
)
{
if
(
!
sponsors
||
sponsors
.
length
===
0
)
return
''
const
mainProposer
=
sponsors
.
find
(
s
=>
s
.
sponsor_type
===
'提案人'
)
return
mainProposer
?
mainProposer
.
person_name
:
''
}
/**
/**
/**
* 计算法案总耗时
* 计算法案总耗时
...
@@ -488,29 +503,30 @@ export function transformProposalInfo(apiData) {
...
@@ -488,29 +503,30 @@ export function transformProposalInfo(apiData) {
}
}
return
{
return
{
// 提案标题
- 需要从其他 API 获取或使用默认值
// 提案标题
title
:
data
.
bill_title
||
'H.R.1-大而美法案'
,
title
:
data
.
bill_title
||
'H.R.1-大而美法案'
,
// 提案时间 - 从政治格局描述中提取
或使用默认值
// 提案时间 - 从政治格局描述中提取
date
:
extractDateFromDesc
(
data
.
poli_pattern_desc
)
||
'2025年5月20日'
,
date
:
extractDateFromDesc
(
data
.
poli_pattern_desc
)
||
'2025年5月20日'
,
// 涉及领域
// 涉及领域 TAG - 使用 domain_name
areas
:
data
.
domain_name
||
[],
areas
:
Array
.
isArray
(
data
.
domain_name
)
?
data
.
domain_name
:
(
data
.
domain_name
?
[
data
.
domain_name
]
:
[]),
// 选举周期阶段 - 从政治格局描述推断
// 政策领域完整选项列表 - 使用 bill_domain(用于筛选下拉框)
electionPhase
:
inferElectionPhase
(
data
.
poli_pattern_desc
)
||
'执政初期/蜜月期'
,
billDomain
:
Array
.
isArray
(
data
.
bill_domain
)
?
data
.
bill_domain
:
[],
// 政策领域默认已选项 - 使用 domain_name
defaultDomains
:
Array
.
isArray
(
data
.
domain_name
)
?
data
.
domain_name
:
(
data
.
domain_name
?
[
data
.
domain_name
]
:
[]),
// 提案人
// 提案人
proposer
:
mainProposer
?
`
${
mainProposer
.
person_name
}
`
:
''
,
proposer
:
mainProposer
?
mainProposer
.
person_name
:
''
,
// 共同提案人
// 共同提案人
coProposers
:
coProposersDesc
,
coProposers
:
coProposersDesc
,
// 提案人职务 - 需要从其他 API 获取
proposerPosition
:
data
.
proposer_position
||
'委员会主席'
,
// 政府结构类型
// 政府结构类型
governmentType
:
formatGovernmentType
(
data
.
poli_pattern_type
,
data
.
poli_pattern_desc
),
governmentType
:
formatGovernmentType
(
data
.
poli_pattern_type
,
data
.
poli_pattern_desc
),
//
法案预算规模 - 需要从其他 API 获取
//
政治格局类型(用于筛选条件默认值)
budgetScale
:
data
.
budget_scale
||
'4万亿美元
'
,
patternType
:
data
.
poli_pattern_type
||
'统一政府
'
,
// 原始数据,供筛选条件使用
// 原始数据,供筛选条件使用
_raw
:
data
_raw
:
data
}
}
}
}
/**
/**
* 从政治格局描述中提取日期
* 从政治格局描述中提取日期
* @param {string} desc - 政治格局描述
* @param {string} desc - 政治格局描述
...
@@ -578,9 +594,7 @@ export function generateDefaultFilters(proposalInfo) {
...
@@ -578,9 +594,7 @@ export function generateDefaultFilters(proposalInfo) {
// 提案人职务
// 提案人职务
proposerPosition
:
proposalInfo
.
proposerPosition
===
'委员会主席'
?
[
'chairman'
]
:
[],
proposerPosition
:
proposalInfo
.
proposerPosition
===
'委员会主席'
?
[
'chairman'
]
:
[],
// 政府结构类型
// 政府结构类型
governmentType
:
proposalInfo
.
governmentType
.
includes
(
'一致'
)
?
[
'unified'
]
:
[
'divided'
],
governmentType
:
proposalInfo
.
governmentType
.
includes
(
'一致'
)
?
[
'unified'
]
:
[
'divided'
],
// 选举周期阶段
electionPhase
:
proposalInfo
.
electionPhase
.
includes
(
'蜜月'
)
?
[
'honeymoon'
]
:
[],
// 法案预算规模
// 法案预算规模
budgetScale
:
[
'trillion_plus'
],
budgetScale
:
[
'trillion_plus'
],
// 对方党派提案人
// 对方党派提案人
...
@@ -589,3 +603,19 @@ export function generateDefaultFilters(proposalInfo) {
...
@@ -589,3 +603,19 @@ export function generateDefaultFilters(proposalInfo) {
proposalTime
:
[
'recent_5'
]
proposalTime
:
[
'recent_5'
]
}
}
}
}
/**
* 获取预测分析结果
* @param {Object} params - 请求参数
* @param {string} params.bill_id - 当前法案ID
* @param {string} params.bill_name - 当前法案名称
* @param {Array} params.bill_actions - 当前法案动作列表
* @param {Array} params.bill_sponsors - 当前法案提案人列表
* @param {Array} params.simi_bills - 用户勾选的相似法案列表
* @returns {Promise<Object>} 预测分析结果
*/
export
function
getPassProd
(
params
)
{
return
request
(
'/api/BillProgressPrediction/passProd'
,
{
method
:
'POST'
,
data
:
params
})
}
\ No newline at end of file
src/api/innovationSubject/overview.js
浏览文件 @
8148dacc
...
@@ -138,120 +138,118 @@ export function getPersonList(params) {
...
@@ -138,120 +138,118 @@ export function getPersonList(params) {
params
params
})
})
}
}
//
创新主体科研实力:专利数量统计
//
合作情况:与中国合作数量变化
export
function
get
PatentList
(
params
)
{
export
function
get
CooperateNumWithChina
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/
patentList
/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/
cooperateNumWithChina
/
${
params
.
id
}
`
,
params
params
})
})
}
}
// 合作情况:与中国合作类型变化
// 创新主体科研实力:论文数量统计
export
function
getCooperateTypeWithChina
(
params
)
{
export
function
getPaperList
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/
paperList
/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/
cooperateTypeWithChina/
${
params
.
year
}
/
${
params
.
id
}
`
,
params
params
})
})
}
}
//
创新主体科研实力:领域实力分布
//
合作情况:与中国合作领域变化
export
function
get
StudyFieldList
(
params
)
{
export
function
get
CooperateAreaWithChina
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/studyFieldList/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/cooperateAreaWithChina/
${
params
.
id
}
`
,
params
})
})
}
}
//
创新主体科研实力:经费增长情况
//
合作情况:与中国合作经费变化
export
function
get
FundGrowth
(
params
)
{
export
function
get
CooperateFundWithChina
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/
fundGrowth
/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/
cooperateFundWithChina
/
${
params
.
id
}
`
,
params
params
})
})
}
}
//
创新主体科研实力:经费来源
//
合作情况:与中国合作事例
export
function
get
FundFromList
(
params
)
{
export
function
get
CooperateExampleWithChina
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/
fundFromList
/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/
cooperateExampleWithChina
/
${
params
.
id
}
`
,
params
params
})
})
}
}
//创新主体科研实力:经费分配
export
function
getFundToList
(
params
)
{
// 专利数量统计
export
function
getPatentList
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/fundToList/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/patentList/
${
orgId
}
`
params
})
})
}
}
//
合作情况:与中国合作数量变化
//
论文数量统计
export
function
get
CooperateNumWithChina
(
params
)
{
export
function
get
PaperList
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/cooperateNumWithChina/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/paperList/
${
orgId
}
`
params
})
})
}
}
//
合作情况:与中国合作类型变化
//
领域实力分布
export
function
get
CooperateTypeWithChina
(
params
)
{
export
function
get
StudyFieldList
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/cooperateTypeWithChina/
${
params
.
year
}
/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/studyFieldList/
${
orgId
}
`
params
})
})
}
}
//
合作情况:与中国合作领域变化
//
经费增长情况
export
function
get
CooperateAreaWithChina
(
params
)
{
export
function
get
FundGrowth
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/cooperateAreaWithChina/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/fundGrowth/
${
orgId
}
`
params
})
})
}
}
//
合作情况:与中国合作经费变化
//
经费来源
export
function
get
CooperateFundWithChina
(
params
)
{
export
function
get
FundFromList
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/cooperateFundWithChina/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/fundFromList/
${
orgId
}
`
params
})
})
}
}
//
合作情况:与中国合作事例
//
经费分配
export
function
get
CooperateExampleWithChina
(
params
)
{
export
function
get
FundToList
(
orgId
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
"GET"
,
url
:
`/api/innovateSubject/cooperateExampleWithChina/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/fundToList/
${
orgId
}
`
params
})
})
}
}
// 获取实验室列表
//创新主体其他情况:重点实验室
export
function
getLabList
(
orgId
)
{
export
function
getLabList
(
params
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/labList/
${
params
.
id
}
`
,
url
:
`/api/innovateSubject/labList/
${
orgId
}
`
})
})
}
}
//
创新主体其他情况:政策文件
//
获取政策文件列表
export
function
getPolicyList
(
params
)
{
export
function
getPolicyList
(
orgId
,
currentPage
=
1
,
pageSize
=
6
)
{
return
request
({
return
request
({
method
:
'GET'
,
method
:
'GET'
,
url
:
`/api/innovateSubject/policyList/
${
params
.
i
d
}
`
,
url
:
`/api/innovateSubject/policyList/
${
orgI
d
}
`
,
params
params
:
{
currentPage
,
pageSize
}
})
})
}
}
\ No newline at end of file
src/router/modules/InnovationSubject.js
浏览文件 @
8148dacc
...
@@ -4,22 +4,20 @@ const InnovationInstitution = () => import('@/views/innovationSubject/innovative
...
@@ -4,22 +4,20 @@ const InnovationInstitution = () => import('@/views/innovationSubject/innovative
const
innovationSubjectRoutes
=
[
const
innovationSubjectRoutes
=
[
//创新主体
{
{
path
:
"/innovationSubject"
,
path
:
"/innovationSubject"
,
name
:
"InnovationSubject"
,
name
:
"InnovationSubject"
,
component
:
InnovationSubject
,
component
:
InnovationSubject
,
meta
:
{
meta
:
{
title
:
"M
国主要创新主体分析概览
"
title
:
"M
"
}
}
},
},
{
{
path
:
"/InnovativeInstitutions/:id"
,
path
:
"/InnovativeInstitutions/:id
/:type
"
,
name
:
"InnovativeInstitutions"
,
name
:
"InnovativeInstitutions"
,
component
:
InnovationInstitution
,
component
:
InnovationInstitution
,
// meta: {
// title: "学校详情"
// },
}
}
]
]
...
...
src/views/bill/influence/ProgressForecast/api/index.ts
浏览文件 @
8148dacc
差异被折叠。
点击展开。
src/views/bill/influence/ProgressForecast/components/BillCard.vue
浏览文件 @
8148dacc
...
@@ -50,12 +50,9 @@ const emit = defineEmits<{
...
@@ -50,12 +50,9 @@ const emit = defineEmits<{
const
billFields
=
[
const
billFields
=
[
{
key
:
'proposalDate'
,
label
:
'提案时间:'
},
{
key
:
'proposalDate'
,
label
:
'提案时间:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'electionPhase'
,
label
:
'选举周期阶段:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'proposerPosition'
,
label
:
'提案人职务:'
},
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
},
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
},
{
key
:
'budgetScale'
,
label
:
'法案预算规模:'
},
{
key
:
'passDays'
,
label
:
'通过耗时:'
}
{
key
:
'passDays'
,
label
:
'通过耗时:'
}
]
]
</
script
>
</
script
>
...
...
src/views/bill/influence/ProgressForecast/components/FilterSection.vue
浏览文件 @
8148dacc
...
@@ -2,59 +2,192 @@
...
@@ -2,59 +2,192 @@
<section
class=
"filter-section"
>
<section
class=
"filter-section"
>
<div
class=
"section-header"
>
<div
class=
"section-header"
>
<div
class=
"header-left"
>
<div
class=
"header-left"
>
<img
src=
"../assets/fitller.svg"
/>
<img
src=
"../assets/fitller.svg"
/>
<h2
class=
"section-title text-title-3-bold"
>
核心相似度维度筛选
</h2>
<h2
class=
"section-title text-title-3-bold"
>
核心相似度维度筛选
</h2>
</div>
</div>
<button
class=
"btn-outline"
@
click=
"
emit('setAsCurrent')
"
>
设置为当前提案
</button>
<button
class=
"btn-outline"
@
click=
"
setAsCurrent
"
>
设置为当前提案
</button>
</div>
</div>
<div
class=
"divider"
/>
<div
class=
"divider"
/>
<div
class=
"fields-grid"
>
<div
class=
"fields-grid"
>
<div
v-for=
"field in fields"
<!-- 政策领域 -->
:key=
"field.id"
<div
class=
"field-item"
>
class=
"field-item"
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
政策领域:
</span>
>
<div
class=
"field-content"
>
<div
class=
"field-label-wrapper"
>
<el-select
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
{{
field
.
label
}}
</span>
v-model=
"localValues.policyArea"
multiple
placeholder=
"请选择"
style=
"width: 420px"
@
change=
"handleChange"
>
<el-option
v-for=
"opt in fields.policyArea.options"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 政府结构类型 -->
<div
class=
"field-item"
>
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
政府结构类型:
</span>
<div
class=
"field-content"
>
<div
class=
"field-content"
>
<FilterSelect
<el-select
:options=
"field.options"
v-model=
"localValues.governmentType"
:model-value=
"field.selectedValues"
multiple
@
update:model-value=
"(val) => handleFieldUpdate(field.id, val)"
placeholder=
"请选择"
/>
style=
"width: 420px"
<div
v-if=
"field.hint"
class=
"field-hint"
>
@
change=
"handleChange"
<img
src=
"../assets/importent.svg"
/>
>
<span
class=
"text-tip-2 text-primary-50-clor"
>
{{
field
.
hint
}}
</span>
<el-option
v-for=
"opt in fields.governmentType.options"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
<div
v-if=
"fields.governmentType.hint"
class=
"field-hint"
>
<img
src=
"../assets/importent.svg"
/>
<span
class=
"text-tip-2 text-primary-50-clor"
>
{{
fields
.
governmentType
.
hint
}}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 对方党派提案人 -->
<div
class=
"field-item"
>
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
对方党派提案人:
</span>
<div
class=
"field-content"
>
<el-select
v-model=
"localValues.oppositionProposer"
multiple
placeholder=
"请选择"
style=
"width: 420px"
@
change=
"handleChange"
>
<el-option
v-for=
"opt in fields.oppositionProposer.options"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
</div>
</div>
<!-- 提案时间 -->
<div
class=
"field-item"
>
<span
class=
"field-label text-tip-1 text-primary-65-clor"
>
提案时间:
</span>
<div
class=
"field-content"
>
<el-select
v-model=
"localValues.proposalTime"
multiple
placeholder=
"请选择"
style=
"width: 420px"
@
change=
"handleChange"
>
<el-option
v-for=
"opt in fields.proposalTime.options"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
</div>
</div>
</div>
</div>
</section>
</section>
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
type
{
FilterField
}
from
'../api
'
import
{
ref
,
computed
,
watch
}
from
'vue
'
import
FilterSelect
from
'./FilterSelect.vue
'
import
type
{
ProposalInfo
}
from
'../api
'
const
props
=
defineProps
<
{
const
props
=
defineProps
<
{
fields
:
FilterField
[]
proposalInfo
?:
ProposalInfo
|
null
defaultFilters
?:
Record
<
string
,
string
[]
>
}
>
()
}
>
()
const
emit
=
defineEmits
<
{
// 本地 v-model 值
'update:fields'
:
[
fields
:
FilterField
[]]
const
localValues
=
ref
({
setAsCurrent
:
[]
policyArea
:
[]
as
string
[],
}
>
()
governmentType
:
[]
as
string
[],
oppositionProposer
:
[]
as
string
[],
proposalTime
:
[]
as
string
[],
})
// 更新字段选中值
// 根据 proposalInfo 计算初始筛选值(即"设置为当前提案"的目标状态)
function
handleFieldUpdate
(
fieldId
:
string
,
newValues
:
string
[])
{
function
buildInitialValues
(
info
?:
ProposalInfo
|
null
):
Record
<
string
,
string
[]
>
{
const
updatedFields
=
props
.
fields
.
map
(
f
=>
if
(
!
info
)
return
{
policyArea
:
[],
governmentType
:
[],
oppositionProposer
:
[],
proposalTime
:
[]
}
f
.
id
===
fieldId
return
{
?
{
...
f
,
selectedValues
:
newValues
}
policyArea
:
info
.
defaultDomains
?.
length
?
[...
info
.
defaultDomains
]
:
[...(
info
.
areas
||
[])],
:
f
governmentType
:
info
.
patternType
?
[
info
.
patternType
]
:
[],
)
oppositionProposer
:
[],
emit
(
'update:fields'
,
updatedFields
)
proposalTime
:
[],
}
}
}
// 监听 proposalInfo 首次传入时设置初始值
watch
(
()
=>
props
.
proposalInfo
,
(
newInfo
)
=>
{
Object
.
assign
(
localValues
.
value
,
buildInitialValues
(
newInfo
))
},
{
immediate
:
true
}
)
// 重置:清空所有已选项
function
reset
()
{
localValues
.
value
=
{
policyArea
:
[],
governmentType
:
[],
oppositionProposer
:
[],
proposalTime
:
[],
}
}
// 设置为当前提案:恢复为 proposalInfo 的初始状态
function
setAsCurrent
()
{
Object
.
assign
(
localValues
.
value
,
buildInitialValues
(
props
.
proposalInfo
))
}
defineExpose
({
reset
,
setAsCurrent
,
getValues
:
()
=>
localValues
.
value
})
// 固定的字段配置,options 由 proposalInfo 动态注入
const
fields
=
computed
(()
=>
{
const
billDomain
=
props
.
proposalInfo
?.
billDomain
const
domainOptions
=
Array
.
isArray
(
billDomain
)
?
billDomain
.
map
(
d
=>
({
value
:
d
,
label
:
d
}))
:
[]
return
{
policyArea
:
{
options
:
domainOptions
},
governmentType
:
{
options
:
[
{
value
:
'统一政府'
,
label
:
'统一政府'
},
{
value
:
'分裂政府'
,
label
:
'分裂政府'
},
{
value
:
'微弱多数'
,
label
:
'微弱多数'
}
],
hint
:
'总统所属政党同时控制国会参众两院'
},
oppositionProposer
:
{
options
:
[
{
value
:
'两党共同提案'
,
label
:
'两党共同提案'
},
{
value
:
'单政党提案(共和党提案)'
,
label
:
'单政党提案(共和党提案)'
},
{
value
:
'单政党提案(民主党提案)'
,
label
:
'单政党提案(民主党提案)'
}
]
},
proposalTime
:
{
options
:
[
{
value
:
'近五年'
,
label
:
'近五年'
},
{
value
:
'近十年'
,
label
:
'近十年'
},
{
value
:
'全部'
,
label
:
'全部'
}
]
}
}
})
function
handleChange
()
{}
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
...
@@ -75,12 +208,6 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
...
@@ -75,12 +208,6 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
gap
:
12px
;
gap
:
12px
;
}
}
.section-icon
{
width
:
16px
;
height
:
16px
;
color
:
var
(
--text-primary-80-color
);
}
.btn-outline
{
.btn-outline
{
padding
:
6px
16px
;
padding
:
6px
16px
;
border
:
1px
solid
var
(
--bg-black-10
);
border
:
1px
solid
var
(
--bg-black-10
);
...
@@ -105,7 +232,7 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
...
@@ -105,7 +232,7 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
.fields-grid
{
.fields-grid
{
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
24px
24px
;
gap
:
24px
;
padding-left
:
30px
;
padding-left
:
30px
;
}
}
...
@@ -113,15 +240,14 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
...
@@ -113,15 +240,14 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
width
:
580px
;
width
:
580px
;
display
:
flex
;
display
:
flex
;
align-items
:
flex-start
;
align-items
:
flex-start
;
}
gap
:
0
;
.field-label-wrapper
{
padding-top
:
4px
;
}
}
.field-label
{
.field-label
{
display
:
block
;
display
:
block
;
width
:
150px
;
width
:
140px
;
flex-shrink
:
0
;
padding-top
:
6px
;
}
}
.field-content
{
.field-content
{
...
@@ -135,11 +261,4 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
...
@@ -135,11 +261,4 @@ function handleFieldUpdate(fieldId: string, newValues: string[]) {
align-items
:
center
;
align-items
:
center
;
gap
:
8px
;
gap
:
8px
;
}
}
.hint-icon
{
width
:
16px
;
height
:
16px
;
flex-shrink
:
0
;
color
:
var
(
--text-primary-50-color
);
}
</
style
>
</
style
>
src/views/bill/influence/ProgressForecast/components/PredictionPhaseCard.vue
浏览文件 @
8148dacc
...
@@ -39,26 +39,19 @@
...
@@ -39,26 +39,19 @@
</p>
</p>
</div>
</div>
</div>
</div>
<div
class=
"facts-section"
>
<div
v-if=
"phase.predictionBasis"
class=
"facts-section"
>
<div
class=
"box-header flex-display-start"
>
<div
class=
"box-header flex-display-start"
>
<div
class=
"box-title-row flex-display-center"
>
<div
class=
"box-title-row flex-display-center"
>
<img
src=
"../assets/icon1.svg"
/>
<img
src=
"../assets/icon1.svg"
/>
<span
class=
"text-compact-bold"
>
{{
phase
.
supportingFacts
.
title
}}
</span>
<span
class=
"text-compact-bold"
>
通过性预测依据
</span>
</div>
</div>
<div
class=
"box-hint flex-display-center text-tip-2 text-primary-50-clor"
>
<div
class=
"box-hint flex-display-center text-tip-2 text-primary-50-clor"
>
<img
src=
"../assets/importent.svg"
/>
<img
src=
"../assets/importent.svg"
/>
<span>
{{
phase
.
supportingFacts
.
basedOn
}}
</span>
<span>
此阶段预测基于以下观点
</span>
</div>
</div>
</div>
</div>
<div
class=
"stats-grid"
>
<div
class=
"prediction-basis-content"
>
<div
<p
class=
"text-tip-2 text-primary-65-clor"
>
{{
phase
.
predictionBasis
}}
</p>
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>
</div>
</div>
</div>
...
@@ -260,6 +253,16 @@ const riskLabel = computed(() => {
...
@@ -260,6 +253,16 @@ const riskLabel = computed(() => {
line-height
:
1.6
;
line-height
:
1.6
;
}
}
.prediction-basis-content
{
background-color
:
var
(
--bg-black-2
);
border-radius
:
var
(
--radius-10
);
padding
:
16px
;
}
.prediction-basis-content
p
{
line-height
:
1.6
;
}
.stats-grid
{
.stats-grid
{
display
:
grid
;
display
:
grid
;
grid-template-columns
:
repeat
(
4
,
1
fr
);
grid-template-columns
:
repeat
(
4
,
1
fr
);
...
...
src/views/bill/influence/ProgressForecast/components/ProposalInfoSection.vue
浏览文件 @
8148dacc
...
@@ -33,13 +33,6 @@
...
@@ -33,13 +33,6 @@
<span
class=
"info-label text-body-1"
>
{{
field
.
label
}}
</span>
<span
class=
"info-label text-body-1"
>
{{
field
.
label
}}
</span>
<template
v-if=
"field.key === 'areas'"
>
<template
v-if=
"field.key === 'areas'"
>
<div
class=
"area-tags"
>
<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"
/>
<AreaTag
v-for=
"area in info.areas"
:key=
"area"
:tagName=
"area"
/>
</div>
</div>
...
@@ -59,17 +52,14 @@ defineProps<{
...
@@ -59,17 +52,14 @@ defineProps<{
info
:
ProposalInfo
info
:
ProposalInfo
}
>
()
}
>
()
// 信息字段配置
// 信息字段配置
- 只保留:提案标题、提案时间、涉及领域、提案人、共同提案人、政府结构类型
const
infoFields
=
[
const
infoFields
=
[
{
key
:
'title'
,
label
:
'提案标题:'
},
{
key
:
'title'
,
label
:
'提案标题:'
},
{
key
:
'date'
,
label
:
'提案时间:'
},
{
key
:
'date'
,
label
:
'提案时间:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'areas'
,
label
:
'涉及领域:'
},
{
key
:
'electionPhase'
,
label
:
'选举周期阶段:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'proposer'
,
label
:
'提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'coProposers'
,
label
:
'共同提案人:'
},
{
key
:
'proposerPosition'
,
label
:
'提案人职务:'
},
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
}
{
key
:
'governmentType'
,
label
:
'政府结构类型:'
},
{
key
:
'budgetScale'
,
label
:
'法案预算规模:'
}
]
]
</
script
>
</
script
>
...
...
src/views/bill/influence/ProgressForecast/components/Step1FilterCondition.vue
浏览文件 @
8148dacc
...
@@ -7,9 +7,9 @@
...
@@ -7,9 +7,9 @@
<div
class=
"content-wrapper"
>
<div
class=
"content-wrapper"
>
<ProposalInfoSection
v-if=
"currentProposalInfo"
:info=
"currentProposalInfo"
/>
<ProposalInfoSection
v-if=
"currentProposalInfo"
:info=
"currentProposalInfo"
/>
<FilterSection
<FilterSection
:fields=
"filterFields
"
ref=
"filterSectionRef
"
@
update:fields=
"filterFields = $event
"
:proposal-info=
"currentProposalInfo
"
@
set-as-current=
"handleSetAsCurrent
"
:default-filters=
"defaultFilters
"
/>
/>
</div>
</div>
<ActionButtons
<ActionButtons
...
@@ -22,8 +22,7 @@
...
@@ -22,8 +22,7 @@
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
watch
}
from
'vue'
import
{
ref
,
onMounted
,
watch
}
from
'vue'
import
type
{
ProposalInfo
,
FilterField
}
from
'../api'
import
type
{
ProposalInfo
}
from
'../api'
import
{
fetchProposalInfo
,
fetchFilterFields
}
from
'../api'
import
ProposalInfoSection
from
'./ProposalInfoSection.vue'
import
ProposalInfoSection
from
'./ProposalInfoSection.vue'
import
FilterSection
from
'./FilterSection.vue'
import
FilterSection
from
'./FilterSection.vue'
import
ActionButtons
from
'./ActionButtons.vue'
import
ActionButtons
from
'./ActionButtons.vue'
...
@@ -34,99 +33,42 @@ const props = defineProps<{
...
@@ -34,99 +33,42 @@ const props = defineProps<{
}
>
()
}
>
()
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
next
:
[]
next
:
[
selectedFilters
:
Record
<
string
,
string
[]
>
]
}
>
()
}
>
()
// 提案信息(优先使用 props
,否则从 API 获取
)
// 提案信息(优先使用 props)
const
currentProposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
const
currentProposalInfo
=
ref
<
ProposalInfo
|
null
>
(
null
)
//
筛选字段列表
//
FilterSection 组件引用
const
filter
Fields
=
ref
<
FilterField
[]
>
([]
)
const
filter
SectionRef
=
ref
<
InstanceType
<
typeof
FilterSection
>
|
null
>
(
null
)
// 加载状态
// 加载状态
const
loading
=
ref
(
true
)
const
loading
=
ref
(
true
)
//
初始筛选字段(用于重置)
//
默认筛选条件
const
initialFilterFields
=
ref
<
FilterField
[]
>
([]
)
const
defaultFilters
=
ref
<
Record
<
string
,
string
[]
>>
({}
)
// 监听 props.proposalInfo 变化
// 监听 props.proposalInfo 变化
watch
(()
=>
props
.
proposalInfo
,
(
newInfo
)
=>
{
watch
(()
=>
props
.
proposalInfo
,
(
newInfo
)
=>
{
if
(
newInfo
)
{
if
(
newInfo
)
{
currentProposalInfo
.
value
=
newInfo
currentProposalInfo
.
value
=
newInfo
}
// 根据提案信息生成默认筛选条件(仅用于 Step1 内部状态跟踪,不再传给 FilterSection)
},
{
immediate
:
true
})
defaultFilters
.
value
=
{
policyArea
:
newInfo
.
defaultDomains
?.
length
?
newInfo
.
defaultDomains
:
newInfo
.
areas
,
// 页面初始化时加载数据
governmentType
:
newInfo
.
patternType
?
[
newInfo
.
patternType
]
:
[],
onMounted
(
async
()
=>
{
oppositionProposer
:
[],
try
{
proposalTime
:
[],
// 如果没有从 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
}
}
})
loading
.
value
=
false
},
{
immediate
:
true
})
// 重置所有筛选条件
// 重置所有筛选条件
:清空所有已选项
function
handleReset
()
{
function
handleReset
()
{
filterFields
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
initialFilterFields
.
value
))
filterSectionRef
.
value
?.
reset
()
}
// 设置为当前提案
function
handleSetAsCurrent
()
{
// 根据当前提案信息重新设置筛选条件
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
[]
}
}
}
// 下一步
// 下一步
:获取已选的筛选条件并传给父组件
function
handleNext
()
{
function
handleNext
()
{
emit
(
'next'
)
const
selectedFilters
=
filterSectionRef
.
value
?.
getValues
()
||
{}
emit
(
'next'
,
selectedFilters
)
}
}
</
script
>
</
script
>
...
...
src/views/bill/influence/ProgressForecast/components/Step2FilterBills.vue
浏览文件 @
8148dacc
...
@@ -77,13 +77,12 @@
...
@@ -77,13 +77,12 @@
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
onMounted
,
inject
,
watch
}
from
'vue'
import
{
ref
,
computed
,
onMounted
,
inject
,
watch
}
from
'vue'
import
type
{
FilterStats
,
BillInfo
}
from
'../api'
import
type
{
FilterStats
,
BillInfo
}
from
'../api'
import
{
fetchFilterStats
,
fetchBillList
}
from
'../api'
import
{
getSimiBills
,
transformSimiBillsData
}
from
'@/api/bill/billHome'
import
{
getSimiBills
,
transformSimiBillsData
}
from
'@/api/bill/billHome'
import
BillCard
from
'./BillCard.vue'
import
BillCard
from
'./BillCard.vue'
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
previous
:
[]
previous
:
[]
next
:
[]
next
:
[
selectedBills
:
any
[]
]
}
>
()
}
>
()
// 从父组件注入筛选参数
// 从父组件注入筛选参数
...
@@ -93,6 +92,8 @@ const filterParams = inject<any>('filterParams', null)
...
@@ -93,6 +92,8 @@ const filterParams = inject<any>('filterParams', null)
const
stats
=
ref
<
FilterStats
|
null
>
(
null
)
const
stats
=
ref
<
FilterStats
|
null
>
(
null
)
// 法案列表
// 法案列表
const
bills
=
ref
<
BillInfo
[]
>
([])
const
bills
=
ref
<
BillInfo
[]
>
([])
// 原始法案数据(用于传给第三页)
const
rawBillsData
=
ref
<
any
[]
>
([])
// 加载状态
// 加载状态
const
loading
=
ref
(
true
)
const
loading
=
ref
(
true
)
...
@@ -121,36 +122,30 @@ const selectedCount = computed(() => {
...
@@ -121,36 +122,30 @@ const selectedCount = computed(() => {
async
function
loadData
()
{
async
function
loadData
()
{
loading
.
value
=
true
loading
.
value
=
true
try
{
try
{
//
优先使用真实 API
//
使用真实 API,传入 billIds、domains、patternType、proposalType
const
params
=
{
const
params
=
{
patternType
:
filterParams
?.
governmentType
||
'统一政府'
,
billIds
:
filterParams
?.
value
.
billIds
,
proposalType
:
'两党共同提案'
domains
:
JSON
.
stringify
(
filterParams
?.
value
.
domains
)
||
[],
patternType
:
filterParams
?.
value
.
patternType
||
'统一政府'
,
proposalType
:
filterParams
?.
value
.
proposalType
||
'两党共同提案'
}
}
const
response
=
await
getSimiBills
(
params
)
const
response
=
await
getSimiBills
(
params
)
if
(
response
&&
response
.
success
&&
response
.
data
)
{
if
(
response
&&
response
.
data
)
{
// 保存原始数据
rawBillsData
.
value
=
response
.
data
const
{
stats
:
apiStats
,
bills
:
apiBills
}
=
transformSimiBillsData
(
response
)
const
{
stats
:
apiStats
,
bills
:
apiBills
}
=
transformSimiBillsData
(
response
)
stats
.
value
=
apiStats
stats
.
value
=
apiStats
bills
.
value
=
apiBills
bills
.
value
=
apiBills
}
else
{
}
else
{
// 如果 API 失败,使用模拟数据
stats
.
value
=
null
const
[
statsData
,
billsData
]
=
await
Promise
.
all
([
bills
.
value
=
[]
fetchFilterStats
(),
fetchBillList
()
])
stats
.
value
=
statsData
bills
.
value
=
billsData
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'获取相似法案失败:'
,
error
)
console
.
error
(
'获取相似法案失败:'
,
error
)
// 使用模拟数据作为 fallback
stats
.
value
=
null
const
[
statsData
,
billsData
]
=
await
Promise
.
all
([
bills
.
value
=
[]
fetchFilterStats
(),
fetchBillList
()
])
stats
.
value
=
statsData
bills
.
value
=
billsData
}
finally
{
}
finally
{
loading
.
value
=
false
loading
.
value
=
false
}
}
...
@@ -189,7 +184,11 @@ function handleBack() {
...
@@ -189,7 +184,11 @@ function handleBack() {
// 开始预测分析
// 开始预测分析
function
handleStartAnalysis
()
{
function
handleStartAnalysis
()
{
emit
(
'next'
)
// 获取用户勾选的法案ID列表
const
selectedIds
=
bills
.
value
.
filter
(
b
=>
b
.
selected
).
map
(
b
=>
b
.
id
)
// 从原始数据中筛选出用户勾选的法案
const
selectedBills
=
rawBillsData
.
value
.
filter
(
b
=>
selectedIds
.
includes
(
b
.
bill_id
))
emit
(
'next'
,
selectedBills
)
}
}
</
script
>
</
script
>
...
...
src/views/bill/influence/ProgressForecast/components/Step3PredictionAnalysis.vue
浏览文件 @
8148dacc
<
template
>
<
template
>
<div
class=
"step-container"
>
<div
class=
"step-container"
>
<div
v-if=
"predictionData"
class=
"content-wrapper"
>
<div
v-if=
"props.loading"
class=
"loading-wrapper flex-display-center"
>
<span
class=
"text-tip-2 text-primary-50-clor"
>
预测分析中...
</span>
</div>
<div
v-else-if=
"predictionData"
class=
"content-wrapper"
>
<div
class=
"header-section flex-display-start"
>
<div
class=
"header-section flex-display-start"
>
<div
class=
"header-left flex-display-start"
>
<div
class=
"header-left flex-display-start"
>
...
@@ -27,8 +30,8 @@
...
@@ -27,8 +30,8 @@
<div
class=
"highlight-box text-regular"
>
<div
class=
"highlight-box text-regular"
>
<div
class=
"highlight-wave text-regular"
></div>
<div
class=
"highlight-wave text-regular"
></div>
<div
class=
"highlight-content text-regular"
>
<div
class=
"highlight-content text-regular"
>
<p
class=
"highlight-title text-regular"
>
《大而美法案》的通过概率非常高
。
</p>
<p
class=
"highlight-title text-regular"
>
该法案的通过概率为
{{
predictionData
?.
overallProbability
||
'—'
}}
。
</p>
<p
class=
"highlight-text text-regular"
>
该法案由众议院共和党领导层在5月正式提出,作为特朗普第二任期核心经济议程,旨在通过一揽子税收、贸易和预算改革提振经济。到6月初,法案已快速通过关键的筹款委员会和预算委员会审议,进入众议院全院辩论阶段。共和党当时控制众议院,且党内团结支持;白宫已明确表示强烈支持。虽然民主党普遍反对,但共和党凭借席位优势足以在众议院通过。主要不确定性在于参议院,但预计部分温和民主党议员可能支持,或通过预算和解程序(只需简单多数)规避阻挠议事。因此,该法案在6月初已势在必行,最终成法几无悬念。
</p>
<p
class=
"highlight-text text-regular"
>
{{
predictionData
?.
highlightText
}}
</p>
</div>
</div>
</div>
</div>
<div
v-if=
"predictionData?.phases?.length"
class=
"phases-list"
>
<div
v-if=
"predictionData?.phases?.length"
class=
"phases-list"
>
...
@@ -59,23 +62,22 @@
...
@@ -59,23 +62,22 @@
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
computed
}
from
'vue'
import
{
ref
,
computed
}
from
'vue'
import
{
fetchPredictionAnalysis
,
type
PredictionAnalysis
}
from
'../api'
import
type
{
PredictionAnalysis
}
from
'../api'
import
PredictionPhaseCard
from
'./PredictionPhaseCard.vue'
import
PredictionPhaseCard
from
'./PredictionPhaseCard.vue'
const
props
=
defineProps
<
{
data
?:
PredictionAnalysis
|
null
loading
?:
boolean
}
>
()
const
emit
=
defineEmits
<
{
const
emit
=
defineEmits
<
{
(
e
:
'prev'
):
void
(
e
:
'prev'
):
void
(
e
:
'repredict'
):
void
(
e
:
'repredict'
):
void
}
>
()
}
>
()
// 预测分析数据
// 预测分析数据 - 从 props 获取,暂无真实接口
const
predictionData
=
ref
<
PredictionAnalysis
|
null
>
(
null
)
const
predictionData
=
computed
(()
=>
props
.
data
||
null
)
// 获取预测分析数据
onMounted
(
async
()
=>
{
const
data
=
await
fetchPredictionAnalysis
()
predictionData
.
value
=
data
})
// 根据索引和progressLevel返回进度条格子的类名
// 根据索引和progressLevel返回进度条格子的类名
function
getOverallSegmentClass
(
index
:
number
)
{
function
getOverallSegmentClass
(
index
:
number
)
{
...
@@ -140,15 +142,16 @@ function handleRepredict() {
...
@@ -140,15 +142,16 @@ function handleRepredict() {
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
/* .text-title-2-bold{
color: #3b414b;
} */
.step-container
{
.step-container
{
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
height
:
100%
;
height
:
100%
;
}
}
.loading-wrapper
{
flex
:
1
;
}
.content-wrapper
{
.content-wrapper
{
flex
:
1
;
flex
:
1
;
overflow
:
auto
;
overflow
:
auto
;
...
@@ -157,7 +160,6 @@ function handleRepredict() {
...
@@ -157,7 +160,6 @@ function handleRepredict() {
.header-section
{
.header-section
{
justify-content
:
space-between
;
justify-content
:
space-between
;
align-items
:
flex-start
;
align-items
:
flex-start
;
/* margin-bottom: 24px; */
}
}
.header-left
{
.header-left
{
...
...
src/views/bill/influence/ProgressForecast/index.vue
浏览文件 @
8148dacc
差异被折叠。
点击展开。
src/views/characterPage/components/memberOfCongress/components/characterRelationships/symbol.png
0 → 100644
浏览文件 @
8148dacc
3.5 KB
src/views/characterPage/components/memberOfCongress/components/historicalProposal/components/BillCard.vue
浏览文件 @
8148dacc
...
@@ -25,8 +25,8 @@
...
@@ -25,8 +25,8 @@
</div>
</div>
<div
class=
"meta-row"
>
<div
class=
"meta-row"
>
<span
class=
"meta-label"
>
相关领域:
</span>
<span
class=
"meta-label"
>
相关领域:
</span>
<div
class=
"meta-tags"
>
<div
class=
"meta-tags"
>
<TagBadge
v-for=
"item in bill.industryList"
:key=
"item.industryName"
:label=
"item.industryName"
tag-class=
"tag3
"
/>
<AreaTag
v-for=
"item in bill.industryList"
:key=
"item.industryName"
:tagName=
"item.industryName
"
/>
</div>
</div>
</div>
</div>
<div
class=
"meta-row"
>
<div
class=
"meta-row"
>
...
@@ -44,8 +44,7 @@
...
@@ -44,8 +44,7 @@
<
script
setup
>
<
script
setup
>
import
{
computed
}
from
'vue'
import
{
computed
}
from
'vue'
import
DocumentPreview
from
'./DocumentPreview.vue'
import
DocumentPreview
from
'./DocumentPreview.vue'
import
TagBadge
from
'./TagBadge.vue'
import
ProgressBar
from
'./ProgressBar.vue'
import
ProgressBar
from
'./ProgressBar.vue'
const
props
=
defineProps
({
const
props
=
defineProps
({
...
@@ -179,23 +178,25 @@ const currentStageIndex = computed(() => {
...
@@ -179,23 +178,25 @@ const currentStageIndex = computed(() => {
.bill-card-meta
{
.bill-card-meta
{
width
:
100%
;
width
:
100%
;
flex
:
1
;
flex
:
1
;
position
:
relative
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
min-height
:
0
;
min-height
:
0
;
overflow
:
hidden
;
}
}
.meta-row
{
.meta-row
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
flex-start
;
position
:
absolute
;
left
:
0
;
width
:
100%
;
width
:
100%
;
gap
:
12px
;
}
}
.meta-row
:nth-child
(
1
)
{
top
:
0
;
}
.meta-row
:nth-child
(
1
)
{
}
.meta-row
:nth-child
(
2
)
{
top
:
36px
;
}
.meta-row
:nth-child
(
2
)
{
}
.meta-row
:nth-child
(
3
)
{
top
:
72px
;
}
.meta-row
:nth-child
(
3
)
{
}
.meta-row
:nth-child
(
4
)
{
top
:
108px
;
}
.meta-row
:nth-child
(
4
)
{
}
.meta-row
:nth-child
(
5
)
{
top
:
144px
;
}
.meta-row
:nth-child
(
5
)
{
}
.meta-label
{
.meta-label
{
font-size
:
16px
;
font-size
:
16px
;
...
@@ -206,6 +207,7 @@ const currentStageIndex = computed(() => {
...
@@ -206,6 +207,7 @@ const currentStageIndex = computed(() => {
white-space
:
nowrap
;
white-space
:
nowrap
;
flex-shrink
:
0
;
flex-shrink
:
0
;
width
:
100px
;
width
:
100px
;
padding-top
:
2px
;
}
}
.meta-value
{
.meta-value
{
...
@@ -213,6 +215,8 @@ const currentStageIndex = computed(() => {
...
@@ -213,6 +215,8 @@ const currentStageIndex = computed(() => {
font-weight
:
400
;
font-weight
:
400
;
color
:
rgba
(
95
,
101
,
108
,
1
);
color
:
rgba
(
95
,
101
,
108
,
1
);
line-height
:
24px
;
line-height
:
24px
;
flex
:
1
;
min-width
:
0
;
}
}
.sponsor-name
{
.sponsor-name
{
...
@@ -225,11 +229,11 @@ const currentStageIndex = computed(() => {
...
@@ -225,11 +229,11 @@ const currentStageIndex = computed(() => {
gap
:
8px
;
gap
:
8px
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
align-items
:
center
;
align-items
:
center
;
flex
:
1
;
min-width
:
0
;
}
}
.meta-row-progress
{
.meta-row-progress
{
left
:
0
;
right
:
0
;
width
:
100%
;
width
:
100%
;
}
}
.meta-row-progress
:deep
(
.progress-bar
)
{
.meta-row-progress
:deep
(
.progress-bar
)
{
...
...
src/views/characterPage/components/techLeader/index.vue
浏览文件 @
8148dacc
...
@@ -84,7 +84,7 @@
...
@@ -84,7 +84,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
<
div
class=
"line-test"
></div
>
<
!-- <div class="line-test"></div> --
>
</div>
</div>
<div
class=
"pagination"
>
<div
class=
"pagination"
>
<div
class=
"total"
>
{{ `共 ${total} 项` }}
</div>
<div
class=
"total"
>
{{ `共 ${total} 项` }}
</div>
...
@@ -853,10 +853,12 @@ onMounted(() => {
...
@@ -853,10 +853,12 @@ onMounted(() => {
z-index
:
110
;
z-index
:
110
;
margin-top
:
10px
;
margin-top
:
10px
;
.main-item
{
.main-item
{
width
:
1014px
;
width
:
1014px
;
margin-bottom
:
40px
;
display
:
flex
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
flex-start
;
margin-bottom
:
20px
;
position
:
relative
;
.time
{
.time
{
width
:
77px
;
width
:
77px
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
...
@@ -995,7 +997,16 @@ onMounted(() => {
...
@@ -995,7 +997,16 @@ onMounted(() => {
}
}
}
}
}
}
.
main-item
:
:
after
{
content
:
''
;
position
:
absolute
;
left
:
109px
;
top
:
24px
;
bottom
:
-20px
;
width
:
1px
;
background-color
:
rgb
(
230
,
231
,
232
);
z-index
:
-1
;
}
.line-test
{
.line-test
{
position
:
absolute
;
position
:
absolute
;
top
:
10px
;
top
:
10px
;
...
...
src/views/characterPage/components/thinkTankPerson/index.vue
浏览文件 @
8148dacc
...
@@ -69,7 +69,7 @@
...
@@ -69,7 +69,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
<
div
class=
"line-test"
></div
>
<
!-- <div class="line-test"></div> --
>
</div>
</div>
<div
class=
"pagination"
>
<div
class=
"pagination"
>
<div
class=
"total"
>
{{ `共 ${total} 项` }}
</div>
<div
class=
"total"
>
{{ `共 ${total} 项` }}
</div>
...
@@ -605,10 +605,13 @@ const companyList = ref([
...
@@ -605,10 +605,13 @@ const companyList = ref([
z-index
:
110
;
z-index
:
110
;
.main-item
{
.main-item
{
width
:
100%
;
width
:
100%
;
margin-bottom
:
40px
;
display
:
flex
;
display
:
flex
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
flex-start
;
margin-bottom
:
20px
;
position
:
relative
;
.time
{
.time
{
width
:
77px
;
width
:
77px
;
box-sizing
:
border-box
;
box-sizing
:
border-box
;
...
@@ -717,7 +720,16 @@ const companyList = ref([
...
@@ -717,7 +720,16 @@ const companyList = ref([
}
}
}
}
}
}
.
main-item
:
:
after
{
content
:
''
;
position
:
absolute
;
left
:
109px
;
top
:
24px
;
bottom
:
-20px
;
width
:
1px
;
background-color
:
rgb
(
230
,
231
,
232
);
z-index
:
-1
;
}
.line-test
{
.line-test
{
position
:
absolute
;
position
:
absolute
;
top
:
10px
;
top
:
10px
;
...
...
src/views/exportControl/v2.0SingleSanction/components/deepMining/index.vue
浏览文件 @
8148dacc
...
@@ -187,11 +187,11 @@ const handleMouseLeave = () => {
...
@@ -187,11 +187,11 @@ const handleMouseLeave = () => {
isInChart
.
value
=
false
;
isInChart
.
value
=
false
;
};
};
const
handleNodeClick
=
(
node
)
=>
{
const
handleNodeClick
=
node
=>
{
selectedNode
.
value
=
node
;
selectedNode
.
value
=
node
;
};
};
const
handleLayoutChange
=
(
type
)
=>
{
const
handleLayoutChange
=
type
=>
{
controlActive
.
value
=
type
;
controlActive
.
value
=
type
;
if
(
type
!==
2
)
{
if
(
type
!==
2
)
{
isInChart
.
value
=
true
;
isInChart
.
value
=
true
;
...
@@ -201,9 +201,8 @@ const handleLayoutChange = (type) => {
...
@@ -201,9 +201,8 @@ const handleLayoutChange = (type) => {
};
};
const
updateGraphData
=
()
=>
{
const
updateGraphData
=
()
=>
{
const
data
=
rightActiveTab
.
value
===
'supplyChain'
const
data
=
?
singleSanctionEntitySupplyChainData
.
value
rightActiveTab
.
value
===
"supplyChain"
?
singleSanctionEntitySupplyChainData
.
value
:
singleSanctionEntityEquityData
.
value
;
:
singleSanctionEntityEquityData
.
value
;
if
(
!
data
)
return
;
if
(
!
data
)
return
;
...
@@ -231,7 +230,7 @@ const updateGraphData = () => {
...
@@ -231,7 +230,7 @@ const updateGraphData = () => {
links
.
push
({
links
.
push
({
source
:
`p-
${
item
.
id
||
index
}
`
,
source
:
`p-
${
item
.
id
||
index
}
`
,
target
:
"0"
,
target
:
"0"
,
name
:
rightActiveTab
.
value
===
'supplyChain'
?
"供应商"
:
(
item
.
type
||
"持股"
)
name
:
rightActiveTab
.
value
===
"supplyChain"
?
"供应商"
:
item
.
type
||
"持股"
});
});
});
});
...
@@ -248,14 +247,14 @@ const updateGraphData = () => {
...
@@ -248,14 +247,14 @@ const updateGraphData = () => {
links
.
push
({
links
.
push
({
source
:
"0"
,
source
:
"0"
,
target
:
`c-
${
item
.
id
||
index
}
`
,
target
:
`c-
${
item
.
id
||
index
}
`
,
name
:
rightActiveTab
.
value
===
'supplyChain'
?
"客户"
:
(
item
.
type
||
"投资"
)
name
:
rightActiveTab
.
value
===
"supplyChain"
?
"客户"
:
item
.
description
||
"投资"
});
});
});
});
graphData
.
value
=
{
nodes
,
links
};
graphData
.
value
=
{
nodes
,
links
};
};
};
const
updateTreeData
=
(
data
)
=>
{
const
updateTreeData
=
data
=>
{
if
(
!
data
)
return
;
if
(
!
data
)
return
;
treeData
.
value
=
{
treeData
.
value
=
{
...
@@ -336,17 +335,17 @@ const getSingleSanctionEntityListRequest = async () => {
...
@@ -336,17 +335,17 @@ const getSingleSanctionEntityListRequest = async () => {
}
}
};
};
watch
(
rightActiveTab
,
async
(
newTab
)
=>
{
watch
(
rightActiveTab
,
async
newTab
=>
{
if
(
newTab
===
'supplyChain'
)
{
if
(
newTab
===
"supplyChain"
)
{
await
getSingleSanctionEntitySupplyChainRequest
();
await
getSingleSanctionEntitySupplyChainRequest
();
}
else
{
}
else
{
await
getSingleSanctionEntityEquityRequest
();
await
getSingleSanctionEntityEquityRequest
();
}
}
});
});
watch
(
activeEntityId
,
async
(
newId
)
=>
{
watch
(
activeEntityId
,
async
newId
=>
{
if
(
newId
)
{
if
(
newId
)
{
if
(
rightActiveTab
.
value
===
'supplyChain'
)
{
if
(
rightActiveTab
.
value
===
"supplyChain"
)
{
await
getSingleSanctionEntitySupplyChainRequest
();
await
getSingleSanctionEntitySupplyChainRequest
();
}
else
{
}
else
{
await
getSingleSanctionEntityEquityRequest
();
await
getSingleSanctionEntityEquityRequest
();
...
@@ -355,7 +354,7 @@ watch(activeEntityId, async (newId) => {
...
@@ -355,7 +354,7 @@ watch(activeEntityId, async (newId) => {
});
});
watch
(
is50PercentRule
,
async
()
=>
{
watch
(
is50PercentRule
,
async
()
=>
{
if
(
rightActiveTab
.
value
===
'equity'
)
{
if
(
rightActiveTab
.
value
===
"equity"
)
{
await
getSingleSanctionEntityEquityRequest
();
await
getSingleSanctionEntityEquityRequest
();
}
}
});
});
...
...
src/views/innovationSubject/ResourceLibrary/ResourceLibrary.vue
浏览文件 @
8148dacc
...
@@ -71,21 +71,19 @@
...
@@ -71,21 +71,19 @@
v-for=
"item in listData"
v-for=
"item in listData"
:key=
"item.id"
:key=
"item.id"
class=
"resource-card"
class=
"resource-card"
@
click=
"goToInstitution(item.id)"
>
>
<div
class=
"card-logo"
>
<div
class=
"card-logo"
>
<img
:src=
"item.logo
Url
|| defaultLogo"
:alt=
"item.name"
/>
<img
:src=
"item.logo || defaultLogo"
:alt=
"item.name"
/>
</div>
</div>
<div
class=
"card-name"
>
{{
item
.
name
}}
</div>
<div
class=
"card-name"
>
{{
item
.
name
}}
</div>
<div
class=
"card-location"
>
{{
item
.
address
}}
</div>
<div
class=
"card-location"
>
{{
item
.
location
}}
</div>
<div
class=
"card-majors"
>
{{
item
.
majors
}}
</div>
<div
class=
"card-majors"
>
{{
item
.
majors
}}
</div>
<div
class=
"card-tags"
>
<div
class=
"card-tags"
>
<span
v-for=
"tag in item.tags"
<AreaTag
v-for=
"tag in item.tags"
:key=
"tag"
:key=
"tag"
:tagName=
"tag"
/>
class=
"tag"
>
{{
tag
}}
</span>
</div>
</div>
</div>
</div>
</
template
>
</
template
>
...
@@ -134,7 +132,10 @@
...
@@ -134,7 +132,10 @@
import
{
ref
,
computed
,
onMounted
,
watch
}
from
'vue'
import
{
ref
,
computed
,
onMounted
,
watch
}
from
'vue'
import
{
getIndustryKeyList
}
from
'@/api/bill/billHome.js'
import
{
getIndustryKeyList
}
from
'@/api/bill/billHome.js'
import
{
getSubjectList
}
from
'@/api/characterPage/characterPage.js'
import
{
getSubjectList
}
from
'@/api/characterPage/characterPage.js'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
{
useRouter
}
from
'vue-router'
const
router
=
useRouter
()
// Props
// Props
const
props
=
defineProps
<
{
const
props
=
defineProps
<
{
initialTab
?:
string
initialTab
?:
string
...
@@ -142,7 +143,9 @@ const props = defineProps<{
...
@@ -142,7 +143,9 @@ const props = defineProps<{
// 默认logo
// 默认logo
const
defaultLogo
=
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9IiNhMzE4MWIiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTggOGgxNnYxNkg4eiIvPjwvc3ZnPg=='
const
defaultLogo
=
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9IiNhMzE4MWIiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTggOGgxNnYxNkg4eiIvPjwvc3ZnPg=='
const
goOrigin
=
(
id
)
=>
{
router
.
push
(
`/InnovativeInstitutions/
${
id
}
`
)
}
// 标签配置 - 添加 subjectTypeId
// 标签配置 - 添加 subjectTypeId
const
tabs
=
ref
([
const
tabs
=
ref
([
{
label
:
'大学'
,
value
:
'university'
,
subjectTypeId
:
1
},
{
label
:
'大学'
,
value
:
'university'
,
subjectTypeId
:
1
},
...
@@ -165,7 +168,17 @@ const fields = ref<{ label: string; value: string }[]>([
...
@@ -165,7 +168,17 @@ const fields = ref<{ label: string; value: string }[]>([
{
label
:
'全部领域'
,
value
:
'all'
}
{
label
:
'全部领域'
,
value
:
'all'
}
])
])
const
fieldsLoading
=
ref
(
false
)
const
fieldsLoading
=
ref
(
false
)
const
goToInstitution
=
(
id
)
=>
{
router
.
push
({
name
:
'InnovativeInstitutions'
,
params
:
{
id
:
id
,
type
:
1
}
})
}
// 获取科技领域选项
// 获取科技领域选项
async
function
fetchFieldOptions
()
{
async
function
fetchFieldOptions
()
{
fieldsLoading
.
value
=
true
fieldsLoading
.
value
=
true
...
@@ -273,8 +286,7 @@ function goToPage(page: number | string) {
...
@@ -273,8 +286,7 @@ function goToPage(page: number | string) {
fetchData
()
fetchData
()
}
}
}
}
// 模拟API获取数据
async
function
fetchData
()
{
async
function
fetchData
()
{
loading
.
value
=
true
loading
.
value
=
true
try
{
try
{
...
@@ -299,12 +311,12 @@ async function fetchData() {
...
@@ -299,12 +311,12 @@ async function fetchData() {
if
(
res
.
code
===
200
&&
res
.
data
)
{
if
(
res
.
code
===
200
&&
res
.
data
)
{
// 映射 API 返回的数据到卡片显示格式
// 映射 API 返回的数据到卡片显示格式
const
mappedData
=
(
res
.
data
.
content
||
[]).
map
((
item
:
any
)
=>
({
const
mappedData
=
(
res
.
data
.
content
||
[]).
map
((
item
:
any
)
=>
({
id
:
item
.
i
d
,
id
:
item
.
orgI
d
,
name
:
item
.
orgName
,
name
:
item
.
orgName
,
location
:
item
.
countryName
?
`
${
item
.
countryName
}
·
${
item
.
provinceName
}
`
:
item
.
provinceName
||
'--'
,
location
:
item
.
address
||
'--'
,
majors
:
item
.
fieldList
?.
join
(
'、'
)
||
'--'
,
majors
:
item
.
fieldList
?.
join
(
'、'
)
||
'--'
,
tags
:
item
.
areaList
?.
map
((
a
:
any
)
=>
a
.
areaName
)
||
[],
tags
:
item
.
taglist
||
[],
logo
:
defaultLogo
logo
:
item
.
logoUrl
}))
}))
listData
.
value
=
mappedData
listData
.
value
=
mappedData
...
...
src/views/innovationSubject/innovativeInstitutions/index.vue
浏览文件 @
8148dacc
...
@@ -70,13 +70,21 @@
...
@@ -70,13 +70,21 @@
</
template
>
</
template
>
<
script
setup
>
<
script
setup
>
import
{
ref
}
from
'vue'
import
{
ref
,
computed
,
provide
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
SchoolDetail
from
'./tabs/SchoolDetail.vue'
import
SchoolDetail
from
'./tabs/SchoolDetail.vue'
import
ResearchStrength
from
'./tabs/ResearchStrength.vue'
import
ResearchStrength
from
'./tabs/ResearchStrength.vue'
import
Cooperation
from
'./tabs/Cooperation.vue'
import
Cooperation
from
'./tabs/Cooperation.vue'
import
OtherInfo
from
'./tabs/OtherInfo.vue'
import
OtherInfo
from
'./tabs/OtherInfo.vue'
// 从路由获取 orgId
const
route
=
useRoute
()
const
orgId
=
computed
(()
=>
route
.
params
.
id
||
''
)
// 提供给子组件使用
provide
(
'orgId'
,
orgId
)
// 大学基本信息
// 大学基本信息
const
universityInfo
=
ref
({
const
universityInfo
=
ref
({
name
:
'哈佛大学'
,
name
:
'哈佛大学'
,
...
...
src/views/innovationSubject/innovativeInstitutions/tabs/OtherInfo.vue
浏览文件 @
8148dacc
...
@@ -42,100 +42,127 @@
...
@@ -42,100 +42,127 @@
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
}
from
'vue'
import
{
ref
,
computed
,
onMounted
,
watch
}
from
'vue'
import
AnalysisBox
from
'@/components/base/boxBackground/analysisBox.vue'
import
AnalysisBox
from
'@/components/base/boxBackground/analysisBox.vue'
import
LeftBtn
from
'@/components/base/pageBtn/leftBtn.vue'
import
LeftBtn
from
'@/components/base/pageBtn/leftBtn.vue'
import
RightBtn
from
'@/components/base/pageBtn/rightBtn.vue'
import
RightBtn
from
'@/components/base/pageBtn/rightBtn.vue'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
AreaTag
from
'@/components/base/AreaTag/index.vue'
import
{
getLabList
,
getPolicyList
}
from
'@/api/innovationSubject/overview.js'
const
props
=
defineProps
<
{
orgId
?:
string
}
>
()
const
areaTypeMap
:
Record
<
string
,
string
>
=
{
'人工智能'
:
'tag1'
,
'生物科技'
:
'tag2'
,
'太空'
:
'tag3'
,
'航空航天'
:
'tag4'
,
'先进制造'
:
'tag5'
,
'物理学'
:
'tag6'
,
'海洋'
:
'tag7'
,
'新能源'
:
'tag8'
,
'医学'
:
'tag9'
,
'化学'
:
'tag10'
,
'新一代通信网络'
:
'tag11'
,
'其他'
:
'tag12'
}
// 重点实验室数据
const
labsData
=
ref
<
Array
<
{
const
labsData
=
ref
([
logo
:
string
{
name
:
string
logo
:
'/images/lab-wyss.png'
,
description
:
string
name
:
'怀斯生物启发工程研究所'
,
tags
:
Array
<
{
name
:
string
;
type
:
string
}
>
description
:
'成立于2009年,由汉斯约尔格·怀斯(Hansjörg Wyss)捐赠建立,聚焦仿生学与跨学科工程,推动医疗、机器人、材料等领域的突破。'
,
}
>>
([])
tags
:
[{
name
:
'生物科技'
,
type
:
'tag2'
}]
},
const
policyData
=
ref
<
Array
<
{
{
title
:
string
logo
:
'/images/lab-rowland.png'
,
description
:
string
name
:
'罗兰研究所'
,
}
>>
([])
description
:
'原为独立研究机构,2002年并入哈佛,支持高风险、高回报的基础科学研究,尤其鼓励青年科学家。'
,
tags
:
[{
name
:
'物理学'
,
type
:
'tag6'
},
{
name
:
'化学'
,
type
:
'tag10'
}]
const
currentPage
=
ref
(
1
)
},
const
pageSize
=
ref
(
6
)
{
const
totalCount
=
ref
(
0
)
logo
:
'/images/lab-quantum.png'
,
const
totalPages
=
ref
(
0
)
name
:
'哈佛量子计划'
,
const
loading
=
ref
(
false
)
description
:
'跨学院合作平台,整合物理、工程、计算机科学等资源,推动量子科学与技术发展。'
,
tags
:
[{
name
:
'物理'
,
type
:
'tag6'
}]
const
fetchLabList
=
async
()
=>
{
},
const
orgId
=
props
.
orgId
||
'6789'
{
try
{
logo
:
'/images/lab-broad.png'
,
const
result
=
await
getLabList
(
orgId
)
name
:
'博德研究所'
,
if
(
result
.
success
&&
result
.
data
)
{
description
:
'全球顶尖基因组学与生物医学研究中心,推动精准医学与疾病机制研究。'
,
labsData
.
value
=
result
.
data
.
map
((
item
:
any
)
=>
({
tags
:
[{
name
:
'医学'
,
type
:
'tag9'
}]
logo
:
item
.
logoUrl
||
'/images/lab-default.png'
,
},
name
:
item
.
labName
,
{
description
:
item
.
introduction
,
logo
:
'/images/lab-stem.png'
,
tags
:
(
item
.
arealist
||
[]).
map
((
area
:
string
)
=>
({
name
:
'哈佛干细胞研究所'
,
name
:
area
,
description
:
'成立于2004年,联合哈佛医学院、牙医学院、文理学院等,推动干细胞基础研究与临床转化。'
,
type
:
areaTypeMap
[
area
]
||
'tag12'
tags
:
[{
name
:
'医学'
,
type
:
'tag9'
}]
}))
},
}))
{
}
logo
:
'/images/lab-cfa.png'
,
}
catch
(
error
)
{
name
:
'哈佛大学天体物理中心'
,
console
.
error
(
'获取实验室列表失败:'
,
error
)
description
:
'由哈佛大学与史密森尼学会于1973年联合成立,是全球规模最大、最活跃的天体物理研究机构之一。'
,
tags
:
[{
name
:
'天体物理'
,
type
:
'tag3'
}]
}
}
])
}
// 政策文件数据
const
fetchPolicyList
=
async
()
=>
{
const
policyData
=
ref
([
const
orgId
=
props
.
orgId
||
'6789'
{
title
:
'《哈佛大学权利与责任声明》'
,
description
:
'阐明学生在言论自由、学术自由、正当程序、尊重他人等方面的权利与义务。'
},
loading
.
value
=
true
{
title
:
'《哈佛大学学术诚信政策》'
,
description
:
'定义抄袭、作弊、伪造等学术不端行为,并规定处理流程。'
},
try
{
{
title
:
'《哈佛大学反歧视与反骚扰政策》'
,
description
:
'禁止基于种族、性别、性取向、宗教、残疾等的歧视与骚扰,明确举报与调查机制。'
},
const
result
=
await
getPolicyList
(
orgId
,
currentPage
.
value
,
pageSize
.
value
)
{
title
:
'《研究合规与人类受试者保护政策》'
,
description
:
'规范涉及人类受试者的研究(如医学、心理学、社会学实验),确保符合联邦法规(如Common Rule)。'
},
if
(
result
.
success
&&
result
.
data
)
{
{
title
:
'《哈佛法学院学术政策手册》'
,
description
:
'详述J.D./LL.M./S.J.D.学位要求、课程规则、成绩制度、书面作业要求、出勤规定、荣誉毕业标准等。'
},
policyData
.
value
=
result
.
data
.
content
.
map
((
item
:
any
)
=>
({
{
title
:
'《哈佛文理研究生院学生手册》'
,
description
:
'涵盖博士生资格考试、论文提交、助教职责、奖学金续期、学术进展评估等。'
}
title
:
`《
${
item
.
name
}
》`
,
])
description
:
item
.
introduction
}))
// 分页相关
totalCount
.
value
=
result
.
data
.
totalElements
const
currentPage
=
ref
(
5
)
totalPages
.
value
=
result
.
data
.
totalPages
const
totalCount
=
ref
(
105
)
}
const
totalPages
=
computed
(()
=>
Math
.
ceil
(
totalCount
.
value
/
10
))
}
catch
(
error
)
{
console
.
error
(
'获取政策文件列表失败:'
,
error
)
}
finally
{
loading
.
value
=
false
}
}
watch
(
currentPage
,
()
=>
{
fetchPolicyList
()
})
onMounted
(()
=>
{
fetchLabList
()
fetchPolicyList
()
})
const
displayPages
=
computed
(()
=>
{
const
displayPages
=
computed
(()
=>
{
const
pages
:
(
number
|
string
)[]
=
[]
const
pages
:
(
number
|
string
)[]
=
[]
if
(
totalPages
.
value
<=
7
)
{
const
total
=
totalPages
.
value
for
(
let
i
=
1
;
i
<=
totalPages
.
value
;
i
++
)
pages
.
push
(
i
)
if
(
total
<=
7
)
{
for
(
let
i
=
1
;
i
<=
total
;
i
++
)
pages
.
push
(
i
)
}
else
{
}
else
{
pages
.
push
(
1
)
pages
.
push
(
1
)
if
(
currentPage
.
value
>
3
)
pages
.
push
(
'...'
)
if
(
currentPage
.
value
>
3
)
pages
.
push
(
'...'
)
const
start
=
Math
.
max
(
2
,
currentPage
.
value
-
1
)
const
start
=
Math
.
max
(
2
,
currentPage
.
value
-
1
)
const
end
=
Math
.
min
(
total
Pages
.
value
-
1
,
currentPage
.
value
+
1
)
const
end
=
Math
.
min
(
total
-
1
,
currentPage
.
value
+
1
)
for
(
let
i
=
start
;
i
<=
end
;
i
++
)
pages
.
push
(
i
)
for
(
let
i
=
start
;
i
<=
end
;
i
++
)
pages
.
push
(
i
)
if
(
currentPage
.
value
<
total
Pages
.
value
-
2
)
pages
.
push
(
'...'
)
if
(
currentPage
.
value
<
total
-
2
)
pages
.
push
(
'...'
)
pages
.
push
(
total
Pages
.
value
)
pages
.
push
(
total
)
}
}
return
pages
return
pages
})
})
// 上一页
const
prevPage
=
()
=>
{
const
prevPage
=
()
=>
{
if
(
currentPage
.
value
>
1
)
currentPage
.
value
--
if
(
currentPage
.
value
>
1
)
currentPage
.
value
--
}
}
// 下一页
const
nextPage
=
()
=>
{
const
nextPage
=
()
=>
{
if
(
currentPage
.
value
<
totalPages
.
value
)
currentPage
.
value
++
if
(
currentPage
.
value
<
totalPages
.
value
)
currentPage
.
value
++
}
}
// 跳转到指定页
const
goToPage
=
(
page
:
number
|
string
)
=>
{
const
goToPage
=
(
page
:
number
|
string
)
=>
{
if
(
typeof
page
===
'number'
)
currentPage
.
value
=
page
if
(
typeof
page
===
'number'
)
currentPage
.
value
=
page
}
}
// 获取标签样式
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
...
src/views/innovationSubject/innovativeInstitutions/tabs/ResearchStrength.vue
浏览文件 @
8148dacc
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论