Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
47bc38c9
提交
47bc38c9
authored
3月 12, 2026
作者:
张伊明
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat 新增法案原文页面
feat 完成法案概况-内容概要页面 fix 修复法案首页某些bug
上级
fd608e1e
隐藏空白字符变更
内嵌
并排
正在显示
19 个修改的文件
包含
1463 行增加
和
979 行删除
+1463
-979
bill.js
src/api/bill.js
+1
-1
billHome.js
src/api/bill/billHome.js
+10
-0
request.js
src/api/request.js
+9
-0
index.vue
src/components/base/SiderTabs/index.vue
+3
-3
analysisBox.vue
src/components/base/boxBackground/analysisBox.vue
+17
-0
bill.js
src/router/modules/bill.js
+9
-0
WordCloudMap.vue
src/views/bill/background/WordCloudMap.vue
+1
-1
index.vue
src/views/bill/background/index.vue
+4
-4
ResourceLibrarySection.vue
src/views/bill/billHome/ResourceLibrarySection.vue
+127
-43
index.vue
src/views/bill/billHome/index.vue
+101
-45
doublePieChart.js
src/views/bill/billHome/utils/doublePieChart.js
+21
-6
BillHeader.vue
src/views/bill/billLayout/components/BillHeader.vue
+441
-0
index.vue
src/views/bill/billLayout/index.vue
+48
-555
index.vue
src/views/bill/billOriginalText/index.vue
+110
-0
index.vue
src/views/bill/index.vue
+28
-18
STimeline.vue
src/views/bill/introdoction/STimeline.vue
+1
-1
compare-icon.svg
src/views/bill/template/assets/icons/compare-icon.svg
+3
-0
translate-icon.svg
src/views/bill/template/assets/icons/translate-icon.svg
+4
-0
index.vue
src/views/bill/template/index.vue
+525
-302
没有找到文件。
src/api/bill.js
浏览文件 @
47bc38c9
...
...
@@ -117,7 +117,7 @@ export function getBillContentId(params) {
// 主要条款-根据原文ID获取条款内容
/**
* @param {bill
id,id,cRelated,currentPage,pageSize
}
* @param {bill
Id,id,cRelated,currentPage,pageSize,domainNameList,measuresNameList,content
}
* @header token
*/
export
function
getBillContentTk
(
params
)
{
...
...
src/api/bill/billHome.js
浏览文件 @
47bc38c9
...
...
@@ -145,6 +145,16 @@ export function getBillsPersonRel(params, signal) {
})
}
// 获取涉华委员会及其法案
export
function
getBillsIsCnCommittee
(
params
,
signal
)
{
return
request
({
method
:
'GET'
,
url
:
`/api/BillOverview/billsIsCnCommittee`
,
params
,
signal
})
}
// 获取提出部门列表
export
function
getPostOrgList
()
{
return
request
({
...
...
src/api/request.js
浏览文件 @
47bc38c9
...
...
@@ -72,6 +72,15 @@ service.interceptors.response.use(
},
error
=>
{
console
.
log
(
'err'
+
error
)
const
isCanceledError
=
error
?.
code
===
'ERR_CANCELED'
||
error
?.
name
===
'CanceledError'
||
error
?.
name
===
'AbortError'
||
(
typeof
error
?.
message
===
'string'
&&
/canceled/i
.
test
(
error
.
message
))
// 重复请求触发的取消不提示错误
if
(
isCanceledError
)
return
Promise
.
reject
(
error
)
// 处理token过期或无效的情况
if
(
error
.
response
&&
(
error
.
response
.
status
===
401
||
error
.
response
.
status
===
403
))
{
...
...
src/components/base/SiderTabs/index.vue
浏览文件 @
47bc38c9
...
...
@@ -2,14 +2,14 @@
<div
class=
"sider-tabs-wrapper"
>
<div
class=
"sider-item"
:class=
"
{'sider-item-active': sider.active}"
v-for="
sider, index in siderList" :key="
index"
v-for="
(sider, index) in siderList" :key="sider.name ||
index"
@click="handleClickSiderItem(sider)"
>
<div
class=
"sider-item-text text-primary-65-clor text-tip-1"
:class=
"
{'sider-item-text-active': sider.active}">
{{
sider
.
name
}}
</div>
<div
class=
"sider-item-icon"
v-show=
"sider.active"
>
<img
src=
"./active-icon.svg"
alt=
""
>
<img
src=
"./active-icon.svg"
alt=
""
/
>
</div>
</div>
</div>
...
...
@@ -20,7 +20,7 @@
const
props
=
defineProps
({
siderList
:
{
type
:
Array
,
default
:
[
default
:
()
=>
[
{
name
:
'分析内容1'
,
active
:
true
...
...
src/components/base/boxBackground/analysisBox.vue
浏览文件 @
47bc38c9
...
...
@@ -47,21 +47,38 @@ const props = defineProps({
showAllBtn
:
{
type
:
Boolean
,
default
:
true
},
// 当业务功能尚未实现时,点击右上角图标仅弹出统一提示
devTip
:
{
type
:
Boolean
,
default
:
false
}
})
const
handleSave
=
()
=>
{
if
(
props
.
devTip
)
{
ElMessage
.
warning
(
'当前功能正在开发中,敬请期待!'
)
return
}
ElMessage
.
success
(
'保存当前内容'
)
// emit('save')
}
const
handleDownload
=
()
=>
{
if
(
props
.
devTip
)
{
ElMessage
.
warning
(
'当前功能正在开发中,敬请期待!'
)
return
}
ElMessage
.
success
(
'下载当前内容'
)
// emit('download')
}
const
handleCollect
=
()
=>
{
if
(
props
.
devTip
)
{
ElMessage
.
warning
(
'当前功能正在开发中,敬请期待!'
)
return
}
ElMessage
.
success
(
'收藏当前内容'
)
// emit('collect')
...
...
src/router/modules/bill.js
浏览文件 @
47bc38c9
...
...
@@ -13,6 +13,7 @@ const BillInfluenceLayout = () => import('@/views/bill/influence/index.vue')
const
BillInfluenceIndustry
=
()
=>
import
(
'@/views/bill/influence/industry/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'
)
const
billRoutes
=
[
...
...
@@ -35,6 +36,14 @@ const billRoutes = [
dynamicTitle
:
true
// 标记需要动态设置标题
},
children
:
[
{
path
:
"originalText"
,
name
:
"BillOriginalText"
,
component
:
BillOriginalText
,
meta
:
{
title
:
"法案原文"
}
},
// 法案分析路由
{
path
:
"bill"
,
...
...
src/views/bill/background/WordCloudMap.vue
浏览文件 @
47bc38c9
...
...
@@ -51,7 +51,7 @@ const initChart = () => {
color
:
"#fff"
,
backgroundColor
:
"rgba(189, 33, 33, 0.9)"
,
borderRadius
:
20
,
padding
:
[
8
,
16
]
// 适当增加 padding 以确保背景块足够大
padding
:
[
10
,
16
]
// 适当增加 padding 以确保背景块足够大
}
};
}
...
...
src/views/bill/background/index.vue
浏览文件 @
47bc38c9
<
template
>
<div
class=
"background-wrap"
>
<div
class=
"background-wrap-left"
>
<AnalysisBox
class=
"left-box left-box--background"
title=
"立法背景"
:showAllBtn=
"false"
>
<AnalysisBox
class=
"left-box left-box--background"
title=
"立法背景"
:showAllBtn=
"false"
:devTip=
"true"
>
<template
#
header-btn
>
<div
class=
"header-btn-box"
>
<el-button
:type=
"box1Btn1Type"
plain
@
click=
"handleClickBox1Btn(1)"
>
涉华背景
</el-button>
...
...
@@ -28,7 +28,7 @@
</div>
</AnalysisBox>
<AnalysisBox
class=
"left-box left-box--event"
title=
"相关事件"
:showAllBtn=
"false"
>
<AnalysisBox
class=
"left-box left-box--event"
title=
"相关事件"
:showAllBtn=
"false"
:devTip=
"true"
>
<div
class=
"box2-main"
>
<div
class=
"box2-main-item"
v-for=
"item in eventDisplayList"
:key=
"item.id"
@
click=
"handleClickEvent(item)"
>
<div
class=
"left"
>
...
...
@@ -47,7 +47,7 @@
</div>
</AnalysisBox>
</div>
<AnalysisBox
class=
"right-panel"
title=
"议员相关性分析"
:showAllBtn=
"false"
>
<AnalysisBox
class=
"right-panel"
title=
"议员相关性分析"
:showAllBtn=
"false"
:devTip=
"true"
>
<
template
#
header-btn
>
<div
class=
"header-btn-box"
>
<el-button
:type=
"box2Btn1Type"
plain
@
click=
"handleClickBox2Btn(1)"
>
赞成议员
</el-button>
...
...
@@ -266,7 +266,7 @@ const handleGetRelatedEvent = async () => {
};
try
{
const
res
=
await
getBillInfoEvent
(
params
);
eventList
.
value
=
res
.
data
;
eventList
.
value
=
res
.
data
.
slice
(
0
,
5
)
;
}
catch
(
error
)
{
}
};
...
...
src/views/bill/billHome/ResourceLibrarySection.vue
浏览文件 @
47bc38c9
...
...
@@ -61,6 +61,17 @@
</div>
</template>
</div>
<div
class=
"right-header"
v-else
>
<div
class=
"right-header-box right-header-sort"
style=
"margin-left: auto"
>
<el-checkbox
v-model=
"committeeIsInvolveCn"
class=
"involve-checkbox"
@
change=
"handleCommitteeInvolveCnChange"
>
只看涉华委员会
</el-checkbox>
</div>
</div>
<div
class=
"right-main"
v-loading=
"loading"
>
<
template
v-if=
"activeTabName === '国会法案'"
>
<div
class=
"right-main-box"
v-for=
"item in bills"
:key=
"item.billId"
>
...
...
@@ -100,7 +111,7 @@
<
div
class
=
"member-card"
v
-
for
=
"item in memberList"
:
key
=
"item.id"
>
<
div
class
=
"member-card-top"
>
<
div
class
=
"member-avatar-wrap"
>
<
img
class
=
"member-avatar"
:
src
=
"item.avatar || defaultAvatar"
alt
=
"avatar"
/>
<
img
class
=
"member-avatar"
:
src
=
"item.avatar || defaultAvatar"
alt
=
"avatar"
@
click
=
"handleClickAvatar(item)"
/>
<
div
class
=
"member-icon-row"
>
<
img
v
-
if
=
"item.partyIcon"
class
=
"member-mini-icon-img"
:
src
=
"item.partyIcon"
alt
=
"party"
/>
<
img
v
-
if
=
"item.chamberIcon"
class
=
"member-mini-icon-img"
:
src
=
"item.chamberIcon"
alt
=
"chamber"
/>
...
...
@@ -137,12 +148,12 @@
<
div
class
=
"coop-card-header"
>
<
div
class
=
"coop-members"
>
<
div
class
=
"coop-member"
>
<
img
class
=
"coop-avatar"
:
src
=
"item.left.avatar || defaultAvatar"
alt
=
"avatar-left"
/>
<
img
class
=
"coop-avatar"
:
src
=
"item.left.avatar || defaultAvatar"
alt
=
"avatar-left"
@
click
=
"handleClickAvatar(item.left)"
/>
<
div
class
=
"coop-member-name"
:
title
=
"item.left.name"
>
{{
item
.
left
.
name
}}
<
/div
>
<
/div
>
<
div
class
=
"coop-dot"
>
·
<
/div
>
<
div
class
=
"coop-member"
>
<
img
class
=
"coop-avatar"
:
src
=
"item.right.avatar || defaultAvatar"
alt
=
"avatar-right"
/>
<
img
class
=
"coop-avatar"
:
src
=
"item.right.avatar || defaultAvatar"
alt
=
"avatar-right"
@
click
=
"handleClickAvatar(item.right)"
/>
<
div
class
=
"coop-member-name"
:
title
=
"item.right.name"
>
{{
item
.
right
.
name
}}
<
/div
>
<
/div
>
<
/div
>
...
...
@@ -200,15 +211,20 @@
{{
item
.
desc
}}
<
/div
>
<
div
class
=
"coop-count"
>
{{
`${(item.bills || []).length
}
项重点法案`
}}
{{
`${
item.proposalSize ??
(item.bills || []).length
}
项重点法案`
}}
<
/div
>
<
slot
name
=
"committee-extra"
:
committee
=
"item"
/>
<
/div
>
<
div
class
=
"coop-proposals"
>
<
div
class
=
"coop-proposal-item"
v
-
for
=
"bill in item.bills"
:
key
=
"`${item.id
}
-${bill
}
`"
>
<
div
class
=
"coop-proposal-item"
v
-
for
=
"bill in item.bills"
:
key
=
"`${item.id
}
-${bill.billId || bill.title
}
`"
@
click
=
"handleClickCommitteeBill(bill)"
>
<
div
class
=
"coop-proposal-main"
>
<
div
class
=
"coop-proposal-subtitle"
:
title
=
"bill"
>
{{
bill
}}
<
div
class
=
"coop-proposal-subtitle"
:
title
=
"bill
.title
"
>
{{
bill
.
title
}}
<
/div
>
<
/div
>
<
div
class
=
"coop-proposal-arrow"
>><
/div
>
...
...
@@ -216,6 +232,19 @@
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"right-footer"
>
<
div
class
=
"footer-left"
>
{{
`共 ${committeeTotal
}
项`
}}
<
/div
>
<
div
class
=
"footer-right"
>
<
el
-
pagination
@
current
-
change
=
"handleCommitteeCurrentChange"
:
page
-
size
=
"committeePageSize"
:
current
-
page
=
"committeeCurrentPage"
background
layout
=
"prev, pager, next"
:
total
=
"committeeTotal"
/>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
...
...
@@ -225,7 +254,8 @@
<
script
setup
>
import
{
computed
,
onMounted
,
ref
}
from
"vue"
;
import
{
getHylyList
,
getPostOrgList
,
getPostMemberList
,
getBills
,
getBillsPerson
,
getBillsPersonRel
}
from
"@/api/bill/billHome"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
getHylyList
,
getPostOrgList
,
getPostMemberList
,
getBills
,
getBillsPerson
,
getBillsPersonRel
,
getBillsIsCnCommittee
}
from
"@/api/bill/billHome"
;
import
CommonPrompt
from
"../commonPrompt/index.vue"
;
import
desc
from
"./assets/icons/icon-desc.png"
;
import
defaultAvatar
from
"@/assets/icons/default-icon1.png"
;
...
...
@@ -235,6 +265,8 @@ import cyyIcon from "@/assets/icons/cyy.png";
import
ghdIcon
from
"@/assets/icons/ghd.png"
;
import
mzdIcon
from
"@/assets/icons/mzd.png"
;
const
router
=
useRouter
();
const
props
=
defineProps
({
onClickToDetail
:
{
type
:
Function
,
required
:
true
}
,
onAfterPageChange
:
{
type
:
Function
,
default
:
null
}
...
...
@@ -276,6 +308,7 @@ const releaseTimeList = ref([
{
label
:
"发布时间倒序"
,
value
:
false
}
]);
const
isInvolveCn
=
ref
(
"Y"
);
const
committeeIsInvolveCn
=
ref
(
false
);
const
cateKuList
=
ref
([]);
const
activeAreaList
=
ref
([
"全部领域"
]);
...
...
@@ -310,7 +343,7 @@ const postOrgList = ref([{ departmentName: "全部委员会", departmentId: "全
const
postMemberList
=
ref
([{
memberName
:
"全部提出议员"
,
memberId
:
"全部提出议员"
}
]);
const
memberList
=
ref
([]);
// sortFun: boolean(议员接口字段),当前接口表现与“正序/倒序”相反,发参时取反
const
memberSortFun
=
ref
(
tru
e
);
const
memberSortFun
=
ref
(
fals
e
);
const
memberSortFunList
=
ref
([
{
label
:
"提案数量正序"
,
value
:
true
}
,
{
label
:
"提案数量倒序"
,
value
:
false
}
...
...
@@ -326,29 +359,10 @@ const coopPageSize = ref(6);
const
coopCurrentPage
=
ref
(
1
);
const
coopPagedList
=
computed
(()
=>
coopList
.
value
||
[]);
const
committeeList
=
ref
([
{
id
:
"committee-1"
,
avatar
:
""
,
name
:
"美中经济与安全审查委员会"
,
desc
:
"聚焦中美经贸、科技与国家安全议题,跟踪涉华政策与立法动态。"
,
bills
:
[
"2025年涉华关键技术审查法案"
,
"供应链安全与关键矿产保障法案"
,
"对华投资限制与透明度加强法案"
,
"联邦科研合作安全审查法案"
]
}
,
{
id
:
"committee-2"
,
avatar
:
""
,
name
:
"众议院外交事务委员会"
,
desc
:
"负责对外政策与国际关系立法,持续推进对华议题相关审议与听证。"
,
bills
:
[
"印太战略资源配置法案"
,
"对外援助合规与问责法案"
,
"跨境数据安全与执法协作法案"
,
"关键基础设施网络韧性法案"
]
}
,
{
id
:
"committee-3"
,
avatar
:
""
,
name
:
"参议院军事委员会"
,
desc
:
"围绕国防授权、军费及安全合作展开审查,关注涉华国防与技术议题。"
,
bills
:
[
"国防授权法涉华条款(示例)"
,
"先进半导体供应安全法案"
,
"军民两用技术出口管制法案"
,
"海外关键设施安全评估法案"
]
}
]);
const
committeeList
=
ref
([]);
const
committeeTotal
=
ref
(
0
);
const
committeePageSize
=
ref
(
6
);
const
committeeCurrentPage
=
ref
(
1
);
const
bills
=
ref
([]);
const
total
=
ref
(
0
);
...
...
@@ -387,19 +401,71 @@ const handleBillImageError = e => {
img
.
src
=
defaultBill
;
}
;
const
handleClickAvatar
=
member
=>
{
if
(
!
member
?.
id
)
return
;
window
.
sessionStorage
.
setItem
(
"curTabName"
,
member
.
name
||
""
);
const
routeData
=
router
.
resolve
({
path
:
"/characterPage"
,
query
:
{
personId
:
member
.
id
}
}
);
window
.
open
(
routeData
.
href
,
"_blank"
);
}
;
const
getReversedProgress
=
progress
=>
(
Array
.
isArray
(
progress
)
?
[...
progress
].
reverse
()
:
[]);
// 获取委员会列表(占位)
const
handleClickCommitteeBill
=
bill
=>
{
if
(
!
bill
?.
billId
)
return
;
props
.
onClickToDetail
({
billId
:
bill
.
billId
,
name
:
bill
.
title
||
""
}
);
}
;
// 获取委员会列表
const
handleGetCommitteeList
=
async
()
=>
{
loading
.
value
=
true
;
const
{
token
,
signal
}
=
beginAbortableRequest
();
const
params
=
{
currentPage
:
committeeCurrentPage
.
value
,
pageSize
:
committeePageSize
.
value
,
isInvolveCn
:
committeeIsInvolveCn
.
value
}
;
if
(
!
activeYyList
.
value
.
includes
(
"全部议院"
))
params
.
congressIds
=
activeYyList
.
value
.
join
(
","
);
try
{
// TODO: 接入委员会列表接口后,在此替换为真实请求并赋值 committeeList
// const res = await getCommitteeList(params);
// committeeList.value = res?.data?.content || [];
const
res
=
await
getBillsIsCnCommittee
(
params
,
signal
);
if
(
res
.
code
===
200
&&
res
.
data
&&
Array
.
isArray
(
res
.
data
.
content
))
{
committeeList
.
value
=
res
.
data
.
content
.
map
(
item
=>
{
const
billInfoPage
=
item
.
billInfoPage
||
[];
const
bills
=
billInfoPage
.
slice
(
0
,
4
).
map
(
bill
=>
({
billId
:
bill
.
billId
,
title
:
bill
.
billName
||
"-"
}
));
const
descText
=
billInfoPage
[
0
]?.
originDepart
||
""
;
return
{
id
:
item
.
id
,
avatar
:
""
,
name
:
item
.
name
||
"-"
,
desc
:
descText
,
proposalSize
:
Number
(
item
.
proposalSize
||
0
),
bills
}
;
}
);
committeeTotal
.
value
=
res
.
data
.
totalElements
||
0
;
}
else
{
committeeList
.
value
=
[];
committeeTotal
.
value
=
0
;
}
}
catch
(
error
)
{
committeeList
.
value
=
[];
if
(
error
.
name
!==
"AbortError"
)
{
committeeList
.
value
=
[];
committeeTotal
.
value
=
0
;
}
}
finally
{
loading
.
value
=
false
;
endAbortableRequest
(
token
)
;
}
}
;
...
...
@@ -441,10 +507,12 @@ const handleGetCoopList = async () => {
return
{
id
:
id
||
Math
.
random
().
toString
(
36
).
slice
(
2
),
left
:
{
id
:
left
.
id
,
name
:
leftName
,
avatar
:
left
.
imageUrl
||
""
}
,
right
:
{
id
:
right
.
id
,
name
:
rightName
,
avatar
:
right
.
imageUrl
||
""
}
,
...
...
@@ -623,6 +691,7 @@ const resetPages = () => {
currentPage
.
value
=
1
;
memberCurrentPage
.
value
=
1
;
coopCurrentPage
.
value
=
1
;
committeeCurrentPage
.
value
=
1
;
}
;
const
refreshByActiveTab
=
()
=>
{
...
...
@@ -679,6 +748,12 @@ const handleInvolveCnChange = val => {
handleGetBills
();
}
;
const
handleCommitteeInvolveCnChange
=
val
=>
{
committeeIsInvolveCn
.
value
=
val
;
committeeCurrentPage
.
value
=
1
;
handleGetCommitteeList
();
}
;
const
handleCurrentChange
=
page
=>
{
currentPage
.
value
=
page
;
handleGetBills
();
...
...
@@ -719,6 +794,12 @@ const handleMemberCurrentChange = page => {
props
.
onAfterPageChange
&&
props
.
onAfterPageChange
();
}
;
const
handleCommitteeCurrentChange
=
page
=>
{
committeeCurrentPage
.
value
=
page
;
handleGetCommitteeList
();
props
.
onAfterPageChange
&&
props
.
onAfterPageChange
();
}
;
onMounted
(()
=>
{
handleGetHylyList
();
handleGetPostOrgList
();
...
...
@@ -987,6 +1068,7 @@ onMounted(() => {
height
:
88
px
;
border
-
radius
:
50
%
;
object
-
fit
:
cover
;
cursor
:
pointer
;
}
.
member
-
icon
-
row
{
...
...
@@ -1156,18 +1238,20 @@ onMounted(() => {
border
-
radius
:
50
%
;
object
-
fit
:
cover
;
flex
-
shrink
:
0
;
cursor
:
pointer
;
}
.
coop
-
member
-
name
{
max
-
width
:
130
px
;
max
-
width
:
none
;
color
:
var
(
--
text
-
primary
-
80
-
color
);
font
-
family
:
"Source Han Sans CN"
;
font
-
size
:
var
(
--
font
-
size
-
base
);
font
-
weight
:
700
;
line
-
height
:
24
px
;
white
-
space
:
nowrap
;
overflow
:
hidden
;
text
-
overflow
:
ellipsis
;
white
-
space
:
normal
;
overflow
:
visible
;
text
-
overflow
:
clip
;
word
-
break
:
break
-
word
;
}
.
coop
-
dot
{
...
...
src/views/bill/billHome/index.vue
浏览文件 @
47bc38c9
...
...
@@ -101,7 +101,13 @@
<DivideHeader
id=
"position2"
class=
"divide2"
:titleText=
"'资讯要闻'"
></DivideHeader>
<div
class=
"center-center"
>
<NewsList
:list=
"newsList"
/>
<NewsList
:newsList=
"newsList"
img=
"newsImage"
title=
"newsTitle"
from=
"from"
content=
"newsContent"
/>
<MessageBubble
:messageList=
"messageList"
imageUrl=
"personImage"
@
more-click=
"handleToSocialDetail"
@
person-click=
"handleClickToCharacter"
name=
"personName"
content=
"remarks"
source=
"orgName"
/>
...
...
@@ -116,9 +122,12 @@
<el-option
v-for=
"item in categoryList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</
template
>
<div
class=
"box5-main"
:style=
"getEmptyStateStyle(box5HasData)"
>
<el-empty
v-if=
"!box5HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box5Chart"
style=
"width: 100%; height: 100%"
></div>
<div
class=
"overview-card-body box5-main"
>
<div
class=
"overview-chart-wrap"
:class=
"{ 'is-empty': !box5HasData }"
>
<el-empty
v-if=
"!box5HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box5Chart"
class=
"overview-chart"
></div>
</div>
<TipTab
class=
"overview-tip"
/>
</div>
</OverviewCard>
<OverviewCard
class=
"overview-card--single box6"
title=
"涉华法案领域分布"
:icon=
"box6HeaderIcon"
>
...
...
@@ -127,9 +136,12 @@
<el-option
v-for=
"item in box9YearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</
template
>
<div
class=
"box6-main"
:style=
"getEmptyStateStyle(box9HasData)"
>
<el-empty
v-if=
"!box9HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box9Chart"
style=
"width: 100%; height: 100%"
></div>
<div
class=
"overview-card-body box6-main"
>
<div
class=
"overview-chart-wrap"
:class=
"{ 'is-empty': !box9HasData }"
>
<el-empty
v-if=
"!box9HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box9Chart"
class=
"overview-chart"
></div>
</div>
<TipTab
class=
"overview-tip"
/>
</div>
</OverviewCard>
</div>
...
...
@@ -140,9 +152,12 @@
<el-option
v-for=
"item in box7YearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</
template
>
<div
class=
"box7-main"
:style=
"getEmptyStateStyle(box7HasData)"
>
<el-empty
v-if=
"!box7HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box7Chart"
style=
"width: 100%; height: 100%"
></div>
<div
class=
"overview-card-body box7-main"
>
<div
class=
"overview-chart-wrap"
:class=
"{ 'is-empty': !box7HasData }"
>
<el-empty
v-if=
"!box7HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<div
v-else
id=
"box7Chart"
class=
"overview-chart"
></div>
</div>
<TipTab
class=
"overview-tip"
/>
</div>
</OverviewCard>
<OverviewCard
class=
"overview-card--single box8"
title=
"涉华法案进展分布"
:icon=
"box7HeaderIcon"
>
...
...
@@ -151,16 +166,24 @@
<el-option
v-for=
"item in box8YearList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</
template
>
<div
class=
"box8-main"
:style=
"getEmptyStateStyle(box8HasData)"
>
<el-empty
v-if=
"!box8HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<
template
v-else
>
<div
class=
"box8-desc"
>
• 通过涉华法案
{{
box8Summary
}}
项
</div>
<div
id=
"box8Chart"
class=
"box8-chart"
></div>
</
template
>
<div
class=
"overview-card-body box8-main"
>
<div
class=
"overview-chart-wrap"
:class=
"{ 'is-empty': !box8HasData }"
>
<el-empty
v-if=
"!box8HasData"
description=
"暂无数据"
:image-size=
"100"
/>
<
template
v-else
>
<div
class=
"box8-desc"
>
• 通过涉华法案
{{
box8Summary
}}
项
</div>
<div
id=
"box8Chart"
class=
"overview-chart box8-chart"
></div>
</
template
>
</div>
<TipTab
class=
"overview-tip"
/>
</div>
</OverviewCard>
<OverviewCard
class=
"overview-card--single box9"
title=
"涉华法案关键条款"
:icon=
"box7HeaderIcon"
>
<div
class=
"box9-main"
id=
"wordCloudChart"
></div>
<div
class=
"overview-card-body box9-main"
>
<div
class=
"overview-chart-wrap"
>
<div
id=
"wordCloudChart"
class=
"overview-chart"
></div>
</div>
<TipTab
class=
"overview-tip"
/>
</div>
</OverviewCard>
</div>
</div>
...
...
@@ -196,6 +219,7 @@ import overviewMainBox from "@/components/base/boxBackground/overviewMainBox.vue
import
OverviewCard
from
"./OverviewCard.vue"
;
import
ResourceLibrarySection
from
"./ResourceLibrarySection.vue"
;
import
{
useContainerScroll
}
from
"@/hooks/useScrollShow"
;
import
TipTab
from
"@/components/base/TipTab/index.vue"
;
import
getMultiLineChart
from
"./utils/multiLineChart"
;
import
getWordCloudChart
from
"./utils/worldCloudChart"
;
...
...
@@ -325,12 +349,6 @@ const handleToMoreRiskSignal = () => {
// 风险信号
const
warningList
=
ref
([]);
const
getEmptyStateStyle
=
hasData
=>
({
display
:
hasData
?
"block"
:
"flex"
,
justifyContent
:
"center"
,
alignItems
:
"center"
});
const
box7selectetedTime
=
ref
(
"2025"
);
const
box7YearList
=
ref
([
{
...
...
@@ -420,15 +438,15 @@ const handleGetNews = async () => {
try
{
const
res
=
await
getNews
(
params
);
console
.
log
(
"新闻资讯"
,
res
);
if
(
res
.
code
===
200
)
{
newsList
.
value
=
re
s
.
data
||
[].
map
(
item
=>
{
return
{
...
item
,
from
:
`
${
item
.
newsOrg
}
·
${
item
.
newsDate
?
item
.
newsDate
.
slice
(
5
)
:
""
}
`
};
})
;
if
(
res
.
code
===
200
&&
Array
.
isArray
(
res
.
data
)
)
{
newsList
.
value
=
res
.
data
.
map
(
item
=>
{
re
turn
{
...
item
,
from
:
`
${
item
.
newsOrg
}
·
${
item
.
newsDate
?
item
.
newsDate
.
slice
(
5
)
:
""
}
`
};
});
}
else
{
newsList
.
value
=
[]
;
}
}
catch
(
error
)
{
}
};
...
...
@@ -568,17 +586,28 @@ const handleBox7Data = async () => {
await
nextTick
();
const
data1
=
[];
const
houseTotal
=
Number
(
orgBillNumMap
?.
H
||
0
);
const
senateTotal
=
Number
(
orgBillNumMap
?.
S
||
0
);
const
houseTotal
=
Number
(
orgBillNumMap
?.
H
ouse
||
0
);
const
senateTotal
=
Number
(
orgBillNumMap
?.
S
enate
||
0
);
if
(
houseTotal
>
0
)
data1
.
push
({
name
:
"众议院"
,
value
:
houseTotal
});
if
(
senateTotal
>
0
)
data1
.
push
({
name
:
"参议院"
,
value
:
senateTotal
});
const
data2
=
orgBillNumList
.
map
(
item
=>
({
name
:
item
.
orgName
,
value
:
Number
(
item
.
count
||
0
),
percent
:
typeof
item
.
percent
===
"number"
?
item
.
percent
:
Number
(
item
.
percent
||
0
),
type
:
item
.
orgType
===
"S"
?
"参议院"
:
"众议院"
}));
const
getOrgTypeLabel
=
orgType
=>
(
orgType
===
"Senate"
?
"参议院"
:
"众议院"
);
const
typeOrderMap
=
{
众议院
:
0
,
参议院
:
1
};
const
data2
=
orgBillNumList
.
map
(
item
=>
({
name
:
item
.
orgName
,
value
:
Number
(
item
.
count
||
0
),
percent
:
typeof
item
.
percent
===
"number"
?
item
.
percent
:
Number
(
item
.
percent
||
0
),
type
:
getOrgTypeLabel
(
item
.
orgType
)
}))
// 关键:外环顺序必须按内环(众→参)分组,否则扇区角度会交错导致“不对应”
.
sort
((
a
,
b
)
=>
{
const
t1
=
typeOrderMap
[
a
.
type
]
??
99
;
const
t2
=
typeOrderMap
[
b
.
type
]
??
99
;
if
(
t1
!==
t2
)
return
t1
-
t2
;
return
(
b
.
value
??
0
)
-
(
a
.
value
??
0
);
});
const
box7Chart
=
getDoublePieChart
(
data1
,
data2
);
setChart
(
box7Chart
,
"box7Chart"
);
...
...
@@ -2051,13 +2080,13 @@ onUnmounted(() => {
.box9-main
{
height
:
100%
;
box-sizing
:
border-box
;
padding
:
8px
30px
;
padding
:
8px
30px
20px
;
}
.box8-main
{
height
:
100%
;
box-sizing
:
border-box
;
padding
:
12px
30px
18
px
;
padding
:
12px
30px
20
px
;
.box8-desc
{
height
:
24px
;
...
...
@@ -2069,14 +2098,41 @@ onUnmounted(() => {
}
.box8-chart
{
width
:
100%
;
height
:
calc
(
100%
-
30px
);
cursor
:
pointer
;
}
}
.box9-main
{
padding
:
10px
30px
;
padding
:
10px
30px
20px
;
}
.overview-card-body
{
display
:
flex
;
flex-direction
:
column
;
}
.overview-chart-wrap
{
flex
:
1
;
min-height
:
0
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
stretch
;
justify-content
:
flex-start
;
&
.is-empty
{
align-items
:
center
;
justify-content
:
center
;
}
}
.overview-chart
{
width
:
100%
;
height
:
100%
;
min-height
:
0
;
}
.overview-tip
{
margin-top
:
10px
;
}
}
}
...
...
src/views/bill/billHome/utils/doublePieChart.js
浏览文件 @
47bc38c9
...
...
@@ -7,9 +7,22 @@ const truncateLabel = (value, maxLen = 6) => {
return
`
${
chars
.
slice
(
0
,
maxLen
).
join
(
''
)}
...`
}
const
getCssVar
=
(
varName
,
fallback
)
=>
{
try
{
if
(
typeof
window
===
'undefined'
)
return
fallback
const
val
=
window
.
getComputedStyle
(
document
.
documentElement
).
getPropertyValue
(
varName
)
return
(
val
||
''
).
trim
()
||
fallback
}
catch
(
e
)
{
return
fallback
}
}
const
getDoublePieChart
=
(
data1
,
data2
)
=>
{
const
colorList
=
[
'#8AC4FF'
,
'#FFD591'
]
const
colorList1
=
[
'#055FC2'
,
'#FFA940'
]
const
senateInnerColor
=
getCssVar
(
'--color-primary-100'
,
'#055FC2'
)
const
senateDeptColor
=
getCssVar
(
'--color-primary-50'
,
'#89C1FF'
)
const
houseInnerColor
=
'rgba(255, 169, 64, 1)'
const
houseDeptColor
=
'rgba(255, 213, 145, 1)'
const
innerLabelColor
=
getCssVar
(
'--text-primary-80-color'
,
'#3b414b'
)
let
option
=
{
series
:
[
{
...
...
@@ -26,14 +39,16 @@ const getDoublePieChart = (data1, data2) => {
position
:
'inside'
,
fontSize
:
'16px'
,
fontWeight
:
700
,
// color: '#333'
color
:
innerLabelColor
,
textBorderColor
:
'#fff'
,
textBorderWidth
:
1
},
data
:
data1
.
map
(
item
=>
{
return
{
name
:
item
.
name
,
value
:
item
.
value
,
itemStyle
:
{
color
:
item
.
name
===
'参议院'
?
'#055FC2'
:
'#FFA940'
color
:
item
.
name
===
'参议院'
?
senateInnerColor
:
houseInnerColor
}
}
})
...
...
@@ -70,7 +85,7 @@ const getDoublePieChart = (data1, data2) => {
time
:
{
fontSize
:
14
,
fontFamily
:
'Microsoft YaHei'
,
color
:
'
#
rgba(95, 101, 108, 1)'
,
color
:
'rgba(95, 101, 108, 1)'
,
padding
:
[
10
,
0
,
10
,
0
]
}
}
...
...
@@ -97,7 +112,7 @@ const getDoublePieChart = (data1, data2) => {
value
:
item
.
value
,
percent
:
item
.
percent
,
itemStyle
:
{
color
:
item
.
type
===
'参议院'
?
'#8AC4FF'
:
'#FFD591'
color
:
item
.
type
===
'参议院'
?
senateDeptColor
:
houseDeptColor
}
}
...
...
src/views/bill/billLayout/components/BillHeader.vue
0 → 100644
浏览文件 @
47bc38c9
<
template
>
<div
class=
"header-main"
>
<div
class=
"layout-main-header"
>
<div
class=
"layout-main-header-left-box"
>
<div
class=
"left-box-top"
>
<el-skeleton
:loading=
"isLoading"
animated
>
<template
#
template
>
<div
class=
"icon"
>
<el-skeleton-item
class=
"skeleton-avatar"
variant=
"image"
/>
</div>
<div
class=
"info"
>
<el-skeleton-item
class=
"skeleton-title"
variant=
"h1"
/>
<el-skeleton-item
class=
"skeleton-subtitle"
variant=
"text"
/>
</div>
</
template
>
<
template
#
default
>
<div
class=
"icon"
>
<img
:src=
"billInfo?.imageUrl || defaultLogo"
alt=
""
/>
</div>
<div
class=
"info"
>
<div
class=
"info-box1"
>
{{
billInfo
?.
billName
}}
</div>
<div
class=
"info-box2"
>
{{
billInfo
?.
description
}}
{{
billInfo
?.
billNameEn
}}
</div>
</div>
</
template
>
</el-skeleton>
</div>
<div
class=
"left-box-bottom"
v-if=
"showTabs"
>
<
template
v-if=
"isLoading"
>
<div
class=
"left-box-bottom-item is-skeleton"
v-for=
"n in 4"
:key=
"n"
>
<div
class=
"icon"
>
<el-skeleton-item
class=
"skeleton-tab-icon"
variant=
"text"
/>
</div>
<div
class=
"name"
>
<el-skeleton-item
class=
"skeleton-tab-text"
variant=
"text"
/>
</div>
</div>
</
template
>
<
template
v-else
>
<div
class=
"left-box-bottom-item"
:class=
"
{ leftBoxBottomItemActive: activeTitle === item.name }"
v-for="item in tabs"
:key="item.path"
@click="emit('tab-click', item)"
>
<div
class=
"icon"
>
<img
v-if=
"activeTitle === item.name"
:src=
"item.activeIcon"
alt=
""
/>
<img
v-else
:src=
"item.icon"
alt=
""
/>
</div>
<div
class=
"name"
:class=
"
{ nameActive: activeTitle === item.name }">
{{
item
.
name
}}
</div>
</div>
</
template
>
</div>
</div>
<div
class=
"layout-main-header-right-box"
>
<div
class=
"right-box-top"
>
<el-skeleton
:loading=
"isLoading"
animated
>
<
template
#
template
>
<div
class=
"time"
>
<el-skeleton-item
class=
"skeleton-right-line"
variant=
"text"
/>
</div>
<div
class=
"name"
>
<el-skeleton-item
class=
"skeleton-right-line"
variant=
"text"
/>
</div>
</
template
>
<
template
#
default
>
<div
class=
"time"
>
{{
billInfo
?.
introductionDate
}}
</div>
<div
class=
"name"
>
{{
billInfo
?.
tarName
}}
</div>
</
template
>
</el-skeleton>
</div>
<div
class=
"right-box-bottom"
v-if=
"showActions"
>
<
template
v-if=
"isLoading"
>
<div
class=
"btn1 is-skeleton"
>
<div
class=
"icon"
>
<el-skeleton-item
class=
"skeleton-action-icon"
variant=
"text"
/>
</div>
<div
class=
"text"
>
<el-skeleton-item
class=
"skeleton-action-text"
variant=
"text"
/>
</div>
</div>
<div
class=
"btn3 is-skeleton"
>
<div
class=
"icon"
>
<el-skeleton-item
class=
"skeleton-action-icon"
variant=
"text"
/>
</div>
<div
class=
"text"
>
<el-skeleton-item
class=
"skeleton-action-text"
variant=
"text"
/>
</div>
</div>
</
template
>
<
template
v-else
>
<div
class=
"btn1"
@
click=
"emit('open-original-text')"
>
<div
class=
"icon"
>
<img
:src=
"btnIconOriginalText"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"法案原文"
}}
</div>
</div>
<div
class=
"btn3"
@
click=
"emit('open-analysis')"
>
<div
class=
"icon"
>
<img
:src=
"btnIconAnalysis"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"分析报告"
}}
</div>
</div>
</
template
>
</div>
</div>
</div>
</div>
</template>
<
script
setup
>
import
{
computed
}
from
"vue"
;
import
btnIconOriginalText
from
"@/views/thinkTank/ReportDetail/images/btn-icon1.png"
;
import
btnIconAnalysis
from
"@/views/thinkTank/ReportDetail/images/btn-icon3.png"
;
const
props
=
defineProps
({
billInfo
:
{
type
:
Object
,
default
:
()
=>
({})
},
defaultLogo
:
{
type
:
String
,
default
:
""
},
tabs
:
{
type
:
Array
,
default
:
()
=>
[]
},
activeTitle
:
{
type
:
String
,
default
:
""
},
showTabs
:
{
type
:
Boolean
,
default
:
true
},
showActions
:
{
type
:
Boolean
,
default
:
true
}
});
const
isLoading
=
computed
(()
=>
!
props
.
billInfo
||
!
props
.
billInfo
.
billName
);
const
emit
=
defineEmits
([
"tab-click"
,
"open-original-text"
,
"open-analysis"
]);
</
script
>
<
style
lang=
"scss"
scoped
>
.skeleton-avatar
{
width
:
64px
;
height
:
64px
;
border-radius
:
50%
;
}
.skeleton-title
{
width
:
360px
;
height
:
20px
;
}
.skeleton-subtitle
{
width
:
520px
;
height
:
14px
;
margin-top
:
8px
;
}
.skeleton-right-line
{
width
:
140px
;
height
:
14px
;
}
.skeleton-tab-icon
,
.skeleton-action-icon
{
width
:
16px
;
height
:
16px
;
border-radius
:
4px
;
}
.skeleton-tab-text
,
.skeleton-action-text
{
width
:
80px
;
height
:
14px
;
border-radius
:
6px
;
}
.header-main
{
position
:
sticky
;
top
:
0
;
z-index
:
1000
;
width
:
100%
;
background-color
:
#fff
;
box-shadow
:
0px
4px
10px
0px
rgba
(
0
,
0
,
0
,
0
.05
);
overflow
:
hidden
;
}
.layout-main-header
{
width
:
1600px
;
background
:
#fff
;
display
:
flex
;
justify-content
:
space-between
;
margin
:
0
auto
;
padding-top
:
10px
;
.layout-main-header-left-box
{
width
:
2600px
;
.left-box-top
{
height
:
64px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
left
;
margin-bottom
:
21px
;
:deep
(
.el-skeleton
)
{
width
:
100%
;
display
:
flex
;
align-items
:
center
;
}
.icon
{
width
:
64px
;
height
:
64px
;
flex
:
0
0
64px
;
flex-shrink
:
0
;
img
{
width
:
100%
;
height
:
100%
;
display
:
block
;
object-fit
:
contain
;
}
}
.info
{
margin-left
:
8px
;
display
:
flex
;
align-items
:
left
;
flex-direction
:
column
;
.info-box1
{
color
:
rgba
(
59
,
65
,
75
,
1
);
font-family
:
"Microsoft YaHei"
;
font-size
:
20px
;
font-weight
:
600
;
letter-spacing
:
0px
;
text-align
:
left
;
}
.info-box2
{
margin-top
:
4px
;
color
:
rgba
(
132
,
136
,
142
,
1
);
font-family
:
"Microsoft YaHei"
;
font-size
:
14px
;
font-weight
:
400
;
letter-spacing
:
0px
;
text-align
:
left
;
display
:
-
webkit-box
;
-webkit-box-orient
:
vertical
;
-webkit-line-clamp
:
2
;
line-clamp
:
2
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
}
}
.left-box-bottom
{
display
:
flex
;
height
:
40px
;
// margin-top: 21px;
.left-box-bottom-item
{
display
:
flex
;
margin-right
:
32px
;
margin-top
:
3px
;
height
:
36px
;
cursor
:
pointer
;
.icon
{
margin-top
:
4px
;
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.name
{
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
18px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
left
;
margin-left
:
3px
;
}
.nameActive
{
color
:
rgba
(
20
,
89
,
187
,
1
);
font-weight
:
700
;
}
}
.leftBoxBottomItemActive
{
border-bottom
:
3px
solid
rgba
(
20
,
89
,
187
,
1
);
}
}
}
.layout-main-header-right-box
{
width
:
600px
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
align-items
:
right
;
.right-box-top
{
height
:
64px
;
display
:
flex
;
align-items
:
right
;
flex-direction
:
column
;
justify-content
:
center
;
:deep
(
.el-skeleton
)
{
width
:
100%
;
}
.time
{
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
right
;
}
.name
{
margin-top
:
4px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
right
;
}
}
.right-box-bottom
{
margin-bottom
:
8px
;
display
:
flex
;
justify-content
:
flex-end
;
gap
:
8px
;
.btn1
{
cursor
:
pointer
;
width
:
120px
;
height
:
36px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
230
,
231
,
232
,
1
);
border-radius
:
6px
;
background
:
rgba
(
255
,
255
,
255
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
8px
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
letter-spacing
:
0px
;
text-align
:
left
;
}
}
.btn3
{
cursor
:
pointer
;
width
:
120px
;
height
:
36px
;
border-radius
:
6px
;
background
:
rgba
(
5
,
95
,
194
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
8px
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
255
,
255
,
255
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
letter-spacing
:
0px
;
text-align
:
left
;
}
}
.is-skeleton
{
pointer-events
:
none
;
}
}
}
}
</
style
>
src/views/bill/billLayout/index.vue
浏览文件 @
47bc38c9
...
...
@@ -2,141 +2,34 @@
<div
class=
"layout-container"
>
<!-- 导航菜单 -->
<div
class=
"layout-main"
>
<div
class=
"header-main"
>
<div
class=
"layout-main-header"
>
<div
class=
"layout-main-header-left-box"
>
<div
class=
"left-box-top"
>
<div
class=
"icon"
>
<img
:src=
"billInfoGlobal.imageUrl || USALogo"
alt=
""
/>
</div>
<div
class=
"info"
>
<div
class=
"info-box1"
>
{{
billInfoGlobal
.
billName
}}
</div>
<div
class=
"info-box2"
>
{{
billInfoGlobal
.
description
}}
{{
billInfoGlobal
.
billNameEn
}}
</div>
</div>
</div>
<div
class=
"left-box-bottom"
>
<div
class=
"left-box-bottom-item"
:class=
"
{ leftBoxBottomItemActive: activeTitle === item.name }"
v-for="item in mainHeaderBtnList"
:key="item.path"
@click="handleClickMainHeaderBtn(item)"
>
<div
class=
"icon"
>
<img
v-if=
"activeTitle === item.name"
:src=
"item.activeIcon"
alt=
""
/>
<img
v-else
:src=
"item.icon"
alt=
""
/>
</div>
<div
class=
"name"
:class=
"
{ nameActive: activeTitle === item.name }">
{{
item
.
name
}}
</div>
</div>
</div>
</div>
<div
class=
"layout-main-header-right-box"
>
<div
class=
"right-box-top"
>
<div
class=
"time"
>
{{
billInfoGlobal
.
introductionDate
}}
</div>
<div
class=
"name"
>
{{
billInfoGlobal
.
tarName
}}
</div>
</div>
<div
class=
"right-box-bottom"
>
<div
class=
"btn1"
@
click=
"handleSwitchActiveName('法案原文')"
>
<div
class=
"icon"
>
<img
src=
"./assets/icons/btn-icon1.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"法案原文"
}}
</div>
</div>
<!--
<div
class=
"btn2"
>
<div
class=
"icon"
>
<img
src=
"./assets/icons/btn-icon2.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"查看官网"
}}
</div>
</div>
-->
<div
class=
"btn3"
@
click=
"handleAnalysisClick"
>
<div
class=
"icon"
>
<img
src=
"./assets/icons/btn-icon3.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"分析报告"
}}
</div>
</div>
<!--
<div
class=
"btn4"
>
<div
class=
"icon"
>
<img
src=
"./assets/icons/btn-icon4.png"
alt=
""
/>
</div>
<div
class=
"text"
>
{{
"特别重大风险"
}}
</div>
<div
class=
"icon1"
>
<img
src=
"./assets/icons/btn-icon5.png"
alt=
""
/>
</div>
</div>
-->
</div>
</div>
</div>
</div>
<BillHeader
:billInfo=
"billInfoGlobal"
:defaultLogo=
"USALogo"
:tabs=
"mainHeaderBtnList"
:activeTitle=
"activeTitle"
:showTabs=
"!isBillOriginalTextPage"
:showActions=
"!isBillOriginalTextPage"
@
tab-click=
"handleClickMainHeaderBtn"
@
open-original-text=
"handleOpenBillOriginalText"
@
open-analysis=
"handleAnalysisClick"
/>
<div
class=
"layout-main-center"
>
<router-view
/>
</div>
</div>
<div
class=
"layout-report-box"
v-if=
"activeName === '法案原文'"
>
<div
class=
"report-main"
>
<div
class=
"report-close"
@
click=
"handleSwitchActiveName('分析报告')"
>
<img
src=
"./assets/images/report-close-icon.png"
alt=
""
/>
</div>
<div
class=
"report-header"
>
<div
class=
"report-header-left"
>
<!--
<div
class=
"text"
>
法案版本:
</div>
<div
class=
"select-box"
>
<el-select
v-model=
"curBill"
placeholder=
"选择法案"
style=
"width: 240px"
>
<el-option
v-for=
"item in billList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</div>
-->
</div>
<div
class=
"report-header-right"
>
<!--
<div
class=
"btn"
>
<div
class=
"icon"
>
<img
src=
"./assets/images/report-header-icon1.png"
alt=
""
/>
</div>
<div
class=
"text"
>
翻译
</div>
</div>
-->
<!--
<div
class=
"btn"
>
<div
class=
"icon"
>
<img
src=
"./assets/images/report-header-icon2.png"
alt=
""
/>
</div>
<div
class=
"text"
>
下载
</div>
</div>
<div
class=
"btn"
>
<div
class=
"icon"
>
<img
src=
"./assets/images/report-header-icon3.png"
alt=
""
/>
</div>
<div
class=
"text"
>
对比
</div>
</div>
<div
class=
"btn"
>
<div
class=
"icon"
>
<img
src=
"./assets/images/report-header-icon4.png"
alt=
""
/>
</div>
<div
class=
"text"
>
查找
</div>
</div>
-->
</div>
</div>
<div
class=
"report-content"
>
<div
class=
"content-left"
>
<iframe
v-if=
"billFullText"
:src=
"billFullText"
width=
"100%"
height=
"100%"
frameborder=
"0"
></iframe>
<!--
<img
v-else
src=
"./assets/images/report1.png"
alt=
""
/>
-->
</div>
<!--
<div
class=
"content-right"
>
<img
src=
"./assets/images/report2.png"
alt=
""
/>
</div>
-->
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
ref
,
onMounted
,
computed
,
watch
}
from
"vue"
;
import
router
from
"@/router"
;
import
{
useRoute
}
from
"vue-router"
;
import
{
ElMessage
}
from
"element-plus
"
;
import
{
getBillInfoGlobal
,
getBillFullText
}
from
"@/api/bill
"
;
import
{
getBillInfoGlobal
}
from
"@/api/bill
"
;
import
BillHeader
from
"./components/BillHeader.vue
"
;
const
route
=
useRoute
();
const
isBillOriginalTextPage
=
computed
(()
=>
route
.
path
===
"/billLayout/originalText"
);
import
icon1
from
"./assets/icons/icon1.svg"
;
import
icon1Active
from
"./assets/icons/icon1_active.svg"
;
...
...
@@ -148,22 +41,6 @@ import icon4 from "./assets/icons/icon4.svg";
import
icon4Active
from
"./assets/icons/icon4_active.svg"
;
import
USALogo
from
"./assets/images/USA-logo.png"
;
// 法案原文
const
billFullText
=
ref
(
""
);
const
getBillFullTextFn
=
async
()
=>
{
const
res
=
await
getBillFullText
({
id
:
route
.
query
.
billId
});
if
(
res
.
code
===
200
)
{
console
.
log
(
"法案全文"
,
res
);
if
(
res
.
data
)
{
billFullText
.
value
=
typeof
res
.
data
===
"string"
?
res
.
data
.
trim
()
:
res
.
data
;
}
}
};
const
activeName
=
ref
(
"分析报告"
);
// 获取法案全局信息
const
billInfoGlobal
=
ref
({});
const
getBillInfoGlobalFn
=
async
()
=>
{
...
...
@@ -178,26 +55,16 @@ const getBillInfoGlobalFn = async () => {
}
};
const
handleSwitchActiveName
=
name
=>
{
activeName
.
value
=
name
;
if
(
name
===
"法案原文"
)
{
getBillFullTextFn
();
}
const
handleOpenBillOriginalText
=
()
=>
{
const
targetRoute
=
router
.
resolve
({
path
:
"/billLayout/originalText"
,
query
:
{
billId
:
route
.
query
.
billId
}
});
window
.
open
(
targetRoute
.
href
,
"_blank"
);
};
const
curBill
=
ref
(
"公法(2025年7月4日)"
);
const
billList
=
ref
([
{
label
:
"公法(2025年7月4日)"
,
value
:
"公法(2025年7月4日)"
},
{
label
:
"公法(2025年7月2日)"
,
value
:
"公法(2025年7月2日)"
}
]);
const
mainHeaderBtnList
=
ref
([
{
icon
:
icon1
,
...
...
@@ -227,6 +94,18 @@ const mainHeaderBtnList = ref([
const
activeTitle
=
ref
(
"法案概况"
);
const
getActiveTitleByRoutePath
=
path
=>
{
if
(
path
.
startsWith
(
"/billLayout/deepDig"
))
return
"深度挖掘"
;
if
(
path
.
startsWith
(
"/billLayout/influence"
))
return
"影响分析"
;
if
(
path
.
startsWith
(
"/billLayout/relevantCircumstance"
))
return
"相关情况"
;
// /billLayout/bill 及其子路由(introduction/background/template)都归为“法案概况”
return
"法案概况"
;
};
const
syncActiveTitleFromRoute
=
()
=>
{
activeTitle
.
value
=
getActiveTitleByRoutePath
(
route
.
path
);
};
const
handleClickMainHeaderBtn
=
item
=>
{
// if (["影响分析", "相关情况"].includes(item.name)) {
// ElMessage.warning("当前功能正在开发中,敬请期待!");
...
...
@@ -254,10 +133,20 @@ const handleAnalysisClick = () => {
onMounted
(()
=>
{
getBillInfoGlobalFn
();
if
(
window
.
sessionStorage
.
getItem
(
"activeTitle"
))
{
activeTitle
.
value
=
window
.
sessionStorage
.
getItem
(
"activeTitle"
);
}
// 以当前路由为准,避免 sessionStorage 造成高亮错乱
syncActiveTitleFromRoute
();
// 兜底:如果未来出现未知路由且有缓存,再用缓存
const
cachedTitle
=
window
.
sessionStorage
.
getItem
(
"activeTitle"
);
if
(
!
activeTitle
.
value
&&
cachedTitle
)
activeTitle
.
value
=
cachedTitle
;
});
watch
(
()
=>
route
.
path
,
()
=>
{
syncActiveTitleFromRoute
();
},
{
immediate
:
true
}
);
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -271,285 +160,6 @@ onMounted(() => {
width
:
100%
;
height
:
calc
(
100vh
-
72px
);
overflow-y
:
auto
;
.header-main
{
position
:
sticky
;
top
:
0
;
z-index
:
1000
;
width
:
100%
;
background-color
:
#fff
;
box-shadow
:
0px
4px
10px
0px
rgba
(
0
,
0
,
0
,
0
.05
);
overflow
:
hidden
;
}
.layout-main-header
{
width
:
1600px
;
background
:
#fff
;
display
:
flex
;
justify-content
:
space-between
;
margin
:
0
auto
;
padding-top
:
10px
;
.layout-main-header-left-box
{
width
:
2600px
;
.left-box-top
{
height
:
64px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
left
;
.icon
{
width
:
64px
;
height
:
64px
;
}
.info
{
margin-left
:
8px
;
display
:
flex
;
align-items
:
left
;
flex-direction
:
column
;
.info-box1
{
color
:
rgba
(
59
,
65
,
75
,
1
);
font-family
:
"Microsoft YaHei"
;
font-size
:
20px
;
font-weight
:
600
;
// line-height: 22px;
letter-spacing
:
0px
;
text-align
:
left
;
// margin-top: 5px;
}
.info-box2
{
margin-top
:
4px
;
// height: 22px;
// line-height: 22px;
color
:
rgba
(
132
,
136
,
142
,
1
);
font-family
:
"Microsoft YaHei"
;
font-size
:
14px
;
font-weight
:
400
;
// line-height: 22px;
letter-spacing
:
0px
;
text-align
:
left
;
display
:
-
webkit-box
;
-webkit-box-orient
:
vertical
;
-webkit-line-clamp
:
2
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
}
}
}
.left-box-bottom
{
display
:
flex
;
height
:
40px
;
margin-top
:
21px
;
.left-box-bottom-item
{
display
:
flex
;
margin-right
:
32px
;
margin-top
:
3px
;
height
:
36px
;
cursor
:
pointer
;
// &:hover{
// .name {
// color: rgba(22, 119, 255, 1);
// }
// }
.icon
{
margin-top
:
4px
;
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.name
{
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
18px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
left
;
margin-left
:
3px
;
}
.nameActive
{
color
:
rgba
(
20
,
89
,
187
,
1
);
font-weight
:
700
;
}
}
.leftBoxBottomItemActive
{
border-bottom
:
3px
solid
rgba
(
20
,
89
,
187
,
1
);
}
}
}
.layout-main-header-right-box
{
width
:
600px
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
align-items
:
right
;
// margin-right: 150px;
// margin-top: 19px;
.right-box-top
{
height
:
64px
;
display
:
flex
;
align-items
:
right
;
flex-direction
:
column
;
justify-content
:
center
;
.time
{
// height: 24px;
// line-height: 24px;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
right
;
}
.name
{
// height: 24px;
// line-height: 24px;
margin-top
:
4px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
22px
;
letter-spacing
:
0px
;
text-align
:
right
;
}
}
.right-box-bottom
{
margin-bottom
:
8px
;
display
:
flex
;
justify-content
:
flex-end
;
gap
:
8px
;
.btn1
{
cursor
:
pointer
;
width
:
120px
;
height
:
36px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
230
,
231
,
232
,
1
);
border-radius
:
6px
;
background
:
rgba
(
255
,
255
,
255
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
8px
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
letter-spacing
:
0px
;
text-align
:
left
;
}
}
.btn2
{
width
:
120px
;
height
:
36px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
230
,
231
,
232
,
1
);
border-radius
:
6px
;
background
:
rgba
(
255
,
255
,
255
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
8px
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
letter-spacing
:
0px
;
text-align
:
left
;
}
}
.btn3
{
cursor
:
pointer
;
width
:
120px
;
height
:
36px
;
border-radius
:
6px
;
background
:
rgba
(
5
,
95
,
194
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
8px
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
255
,
255
,
255
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
24px
;
letter-spacing
:
0px
;
text-align
:
left
;
}
}
.btn4
{
width
:
180px
;
height
:
36px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
255
,
163
,
158
,
1
);
border-radius
:
4px
;
background
:
rgba
(
255
,
241
,
240
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
gap
:
10px
;
.icon
{
width
:
20px
;
height
:
20px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
height
:
24px
;
color
:
rgba
(
206
,
79
,
81
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
700
;
line-height
:
24px
;
letter-spacing
:
1px
;
}
.icon1
{
width
:
20px
;
height
:
20px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
}
}
}
}
.layout-main-center
{
// height: calc(100% - 137px);
width
:
1600px
;
...
...
@@ -557,121 +167,5 @@ onMounted(() => {
padding-bottom
:
20px
;
}
}
.layout-report-box
{
position
:
absolute
;
z-index
:
9999
;
top
:
154px
;
left
:
0
;
width
:
100%
;
height
:
926px
;
background
:
rgba
(
248
,
249
,
250
,
1
);
.report-main
{
position
:
relative
;
width
:
1744px
;
height
:
926px
;
margin
:
0
auto
;
background
:
#fff
;
box-sizing
:
border-box
;
padding
:
0
69px
;
// border-radius: 10px;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
.report-close
{
position
:
absolute
;
top
:
24px
;
right
:
24px
;
width
:
32px
;
height
:
32px
;
cursor
:
pointer
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.report-header
{
height
:
77px
;
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
display
:
flex
;
justify-content
:
space-between
;
.report-header-left
{
display
:
flex
;
.text
{
margin-top
:
32px
;
width
:
70px
;
height
:
14px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
font-weight
:
400
;
line-height
:
14px
;
}
.select-box
{
margin-left
:
8px
;
margin-top
:
23px
;
}
}
.report-header-right
{
display
:
flex
;
margin-top
:
24px
;
.btn
{
display
:
flex
;
width
:
88px
;
height
:
32px
;
margin-left
:
8px
;
box-sizing
:
border-box
;
border
:
1px
solid
rgba
(
230
,
231
,
232
,
1
);
border-radius
:
4px
;
background
:
rgba
(
255
,
255
,
255
,
1
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
.icon
{
width
:
16px
;
height
:
16px
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.text
{
margin-left
:
8px
;
height
:
32px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
16px
;
font-weight
:
400
;
line-height
:
32px
;
}
}
}
}
.report-content
{
width
:
100%
;
display
:
flex
;
margin-top
:
35px
;
.content-left
{
// width: 680px;
width
:
100%
;
height
:
786px
;
// background: #eee;
// overflow-y: auto;
img
{
width
:
100%
;
height
:
100%
;
}
}
.content-right
{
margin-left
:
89px
;
width
:
680px
;
height
:
786px
;
// background: #eee;
// overflow-y: auto;
img
{
width
:
100%
;
height
:
100%
;
}
}
}
}
}
}
</
style
>
\ No newline at end of file
src/views/bill/billOriginalText/index.vue
0 → 100644
浏览文件 @
47bc38c9
<
template
>
<div
class=
"bill-original-text-page"
>
<div
class=
"page-header"
>
<div
class=
"page-title"
>
法案原文
</div>
<div
class=
"page-actions"
>
<div
class=
"action-btn"
@
click=
"handleBack"
>
返回
</div>
</div>
</div>
<div
class=
"page-content"
>
<iframe
v-if=
"billFullText"
:src=
"billFullText"
width=
"100%"
height=
"100%"
frameborder=
"0"
></iframe>
<div
v-else
class=
"empty-state"
>
暂无原文
</div>
</div>
</div>
</
template
>
<
script
setup
>
import
{
onMounted
,
ref
}
from
"vue"
;
import
{
useRoute
,
useRouter
}
from
"vue-router"
;
import
{
getBillFullText
}
from
"@/api/bill"
;
const
route
=
useRoute
();
const
router
=
useRouter
();
const
billFullText
=
ref
(
""
);
const
getBillFullTextFn
=
async
()
=>
{
const
res
=
await
getBillFullText
({
id
:
route
.
query
.
billId
});
if
(
res
.
code
===
200
&&
res
.
data
)
{
billFullText
.
value
=
typeof
res
.
data
===
"string"
?
res
.
data
.
trim
()
:
res
.
data
;
}
};
const
handleBack
=
()
=>
{
router
.
back
();
};
onMounted
(()
=>
{
getBillFullTextFn
();
});
</
script
>
<
style
lang=
"scss"
scoped
>
.bill-original-text-page
{
width
:
100%
;
box-sizing
:
border-box
;
background
:
rgba
(
248
,
249
,
250
,
1
);
padding
:
0
0
20px
;
.page-header
{
width
:
100%
;
height
:
64px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
.page-title
{
color
:
rgba
(
59
,
65
,
75
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
18px
;
font-weight
:
700
;
}
.page-actions
{
display
:
flex
;
justify-content
:
flex-end
;
.action-btn
{
cursor
:
pointer
;
height
:
32px
;
line-height
:
32px
;
padding
:
0
12px
;
border-radius
:
6px
;
border
:
1px
solid
rgba
(
230
,
231
,
232
,
1
);
background
:
rgba
(
255
,
255
,
255
,
1
);
color
:
rgba
(
95
,
101
,
108
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
font-weight
:
400
;
}
}
}
.page-content
{
width
:
100%
;
height
:
calc
(
100vh
-
320px
);
min-height
:
600px
;
background
:
#fff
;
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
overflow
:
hidden
;
iframe
{
display
:
block
;
}
.empty-state
{
height
:
100%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
color
:
rgba
(
132
,
136
,
142
,
1
);
font-family
:
Microsoft
YaHei
;
font-size
:
14px
;
}
}
}
</
style
>
src/views/bill/index.vue
浏览文件 @
47bc38c9
...
...
@@ -2,20 +2,7 @@
<div
class=
"home-container"
>
<div
class=
"home-center"
>
<div
class=
"home-sider"
>
<div
class=
"sider-btn"
:class=
"
{ siderBtnActive: siderBtnActive === item.name }"
@click="handleClickLeftSiderBtn(item)"
v-for="item,index in siderBtnList"
:key="index"
>
<div
class=
"btn-text"
>
{{
item
.
name
}}
</div>
<div
class=
"btn-icon"
>
<el-icon
v-if=
"siderBtnActive === item.name"
color=
"#fff"
><CaretRight
/></el-icon>
</div>
</div>
<SiderTabs
:siderList=
"siderTabs"
@
clickSiderItem=
"handleClickLeftSiderBtn"
/>
</div>
<div
class=
"home-main"
>
<router-view
/>
...
...
@@ -25,9 +12,10 @@
</
template
>
<
script
setup
>
import
{
onMounted
,
ref
}
from
"vue"
;
import
{
onMounted
,
ref
,
computed
,
watch
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
router
from
'@/router'
import
SiderTabs
from
"@/components/base/SiderTabs/index.vue"
;
const
route
=
useRoute
();
...
...
@@ -47,6 +35,20 @@ const siderBtnList = ref([
])
const
siderBtnActive
=
ref
(
"法案简介"
);
const
getSiderActiveByRoutePath
=
path
=>
{
if
(
path
.
includes
(
"/billLayout/bill/background"
))
return
"法案背景"
;
if
(
path
.
includes
(
"/billLayout/bill/template"
))
return
"内容概要"
;
return
"法案简介"
;
};
const
siderTabs
=
computed
(()
=>
siderBtnList
.
value
.
map
(
item
=>
({
...
item
,
active
:
siderBtnActive
.
value
===
item
.
name
}))
);
const
handleClickLeftSiderBtn
=
(
item
)
=>
{
siderBtnActive
.
value
=
item
.
name
router
.
push
({
...
...
@@ -58,6 +60,14 @@ const handleClickLeftSiderBtn = (item) => {
}
onMounted
(()
=>
{});
watch
(
()
=>
route
.
path
,
(
newPath
)
=>
{
siderBtnActive
.
value
=
getSiderActiveByRoutePath
(
newPath
);
},
{
immediate
:
true
}
);
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -168,10 +178,10 @@ onMounted(() => {});
height
:
879px
;
position
:
relative
;
.home-sider
{
width
:
1
6
0px
;
width
:
1
2
0px
;
position
:
absolute
;
top
:
8
px
;
left
:
-1
6
0px
;
top
:
16
px
;
left
:
-1
4
0px
;
.sider-btn
{
margin-top
:
20px
;
margin-left
:
20px
;
...
...
src/views/bill/introdoction/STimeline.vue
浏览文件 @
47bc38c9
...
...
@@ -17,7 +17,7 @@
<div
class=
"item-title"
>
{{
item
.
actionTitle
}}
</div>
</el-tooltip>
<div
class=
"right"
>
<
div
v-if=
"item.riskText"
class=
"risk-tag"
:class=
"item.riskClass"
>
•
{{
item
.
riskText
}}
</div
>
<
!--
<div
v-if=
"item.riskText"
class=
"risk-tag"
:class=
"item.riskClass"
>
•
{{
item
.
riskText
}}
</div>
--
>
<div
class=
"arrow"
>
>
</div>
</div>
</div>
...
...
src/views/bill/template/assets/icons/compare-icon.svg
0 → 100644
浏览文件 @
47bc38c9
<svg
viewBox=
"0 0 12.375 13.5"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"12.375000"
height=
"13.500000"
fill=
"none"
customFrame=
"#000000"
>
<path
id=
"矢量 359"
d=
"M10.125 1L10.125 5.0625L11.375 5.0625C11.9273 5.0625 12.375 5.51022 12.375 6.0625L12.375 12.5C12.375 13.0523 11.9273 13.5 11.375 13.5L3.8125 13.5C3.26022 13.5 2.8125 13.0523 2.8125 12.5L2.8125 11.25L1 11.25C0.447715 11.25 0 10.8023 0 10.25L0 1C0 0.447715 0.447715 0 1 0L9.125 0C9.67728 0 10.125 0.447715 10.125 1ZM11.25 6.1875L10.125 6.1875L10.125 10.25C10.125 10.8023 9.67729 11.25 9.125 11.25L3.9375 11.25L3.9375 12.375L11.25 12.375L11.25 6.1875ZM9 1.125L1.125 1.125L1.125 10.125L9 10.125L9 1.125ZM6.1875 7.3125L6.1875 8.4375L2.8125 8.4375L2.8125 7.3125L6.1875 7.3125ZM7.3125 5.0625L7.3125 6.1875L2.8125 6.1875L2.8125 5.0625L7.3125 5.0625ZM7.3125 2.8125L7.3125 3.9375L2.8125 3.9375L2.8125 2.8125L7.3125 2.8125Z"
fill=
"rgb(5,95,194)"
fill-rule=
"nonzero"
/>
</svg>
src/views/bill/template/assets/icons/translate-icon.svg
0 → 100644
浏览文件 @
47bc38c9
<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=
"翻译 1"
width=
"16.000000"
height=
"16.000000"
x=
"0.000000"
y=
"0.000000"
/>
<path
id=
"矢量 455"
d=
"M3.33398 10.0007L3.33398 11.334C3.33397 11.3756 3.3359 11.4171 3.33977 11.4585C3.34364 11.4999 3.34943 11.541 3.35715 11.5819C3.36487 11.6227 3.37448 11.6632 3.38598 11.7031C3.39748 11.7431 3.41082 11.7824 3.426 11.8211C3.44119 11.8599 3.45814 11.8978 3.47687 11.9349C3.4956 11.9721 3.51602 12.0082 3.53814 12.0434C3.56026 12.0787 3.58397 12.1128 3.60927 12.1458C3.63458 12.1788 3.66137 12.2105 3.68965 12.241C3.71793 12.2715 3.74757 12.3006 3.77857 12.3283C3.80957 12.3561 3.8418 12.3823 3.87525 12.407C3.9087 12.4317 3.94324 12.4548 3.97885 12.4762C4.01447 12.4977 4.051 12.5175 4.08847 12.5355C4.12593 12.5536 4.16416 12.5699 4.20314 12.5843C4.24213 12.5988 4.2817 12.6114 4.32187 12.6222C4.36204 12.633 4.40262 12.6419 4.44362 12.6488C4.48461 12.6558 4.52585 12.6609 4.56732 12.664L4.66732 12.6673L6.66732 12.6673L6.66732 14.0007L4.66732 14.0007C4.57998 14.0007 4.49285 13.9964 4.40594 13.9878C4.31902 13.9792 4.23273 13.9665 4.14708 13.9494C4.06142 13.9324 3.9768 13.9112 3.89322 13.8858C3.80965 13.8605 3.72752 13.8311 3.64683 13.7977C3.56614 13.7642 3.48728 13.7269 3.41026 13.6858C3.33324 13.6446 3.25841 13.5998 3.1858 13.5512C3.11318 13.5027 3.04311 13.4508 2.9756 13.3953C2.90809 13.3399 2.84346 13.2814 2.7817 13.2196C2.71994 13.1578 2.66136 13.0932 2.60596 13.0257C2.55055 12.9582 2.49859 12.8881 2.45007 12.8155C2.40154 12.7429 2.3567 12.6681 2.31553 12.591C2.27436 12.514 2.23706 12.4352 2.20364 12.3545C2.17022 12.2738 2.14083 12.1917 2.11548 12.1081C2.09012 12.0245 2.06893 11.9399 2.05189 11.8542C2.03485 11.7686 2.02205 11.6823 2.01349 11.5954C2.00493 11.5084 2.00065 11.4213 2.00065 11.334L2.00065 10.0007L3.33398 10.0007L3.33398 10.0007ZM12.0007 6.66732L14.934 14.0007L13.4973 14.0007L12.6967 12.0007L9.96999 12.0007L9.17065 14.0007L7.73465 14.0007L10.6673 6.66732L12.0007 6.66732L12.0007 6.66732ZM11.334 8.59065L10.5027 10.6673L12.164 10.6673L11.334 8.59065ZM5.33398 1.33398L5.33398 2.66732L8.00065 2.66732L8.00065 7.33398L5.33398 7.33398L5.33398 9.33398L4.00065 9.33398L4.00065 7.33398L1.33398 7.33398L1.33398 2.66732L4.00065 2.66732L4.00065 1.33398L5.33398 1.33398ZM11.334 2.00065C11.4213 2.00065 11.5084 2.00493 11.5954 2.01349C11.6823 2.02205 11.7686 2.03485 11.8542 2.05189C11.9399 2.06893 12.0245 2.09012 12.1081 2.11548C12.1917 2.14083 12.2738 2.17022 12.3545 2.20364C12.4352 2.23706 12.514 2.27436 12.591 2.31553C12.6681 2.3567 12.7429 2.40154 12.8155 2.45007C12.8881 2.49859 12.9582 2.55055 13.0257 2.60596C13.0932 2.66136 13.1578 2.71994 13.2196 2.7817C13.2814 2.84346 13.3399 2.90809 13.3953 2.9756C13.4508 3.04311 13.5027 3.11318 13.5512 3.1858C13.5998 3.25841 13.6446 3.33324 13.6858 3.41026C13.7269 3.48728 13.7642 3.56614 13.7977 3.64683C13.8311 3.72752 13.8605 3.80965 13.8858 3.89323C13.9112 3.9768 13.9324 4.06142 13.9494 4.14708C13.9665 4.23274 13.9793 4.31902 13.9878 4.40594C13.9964 4.49286 14.0007 4.57998 14.0007 4.66732L14.0007 6.00065L12.6673 6.00065L12.6673 4.66732C12.6673 4.62365 12.6652 4.58009 12.6609 4.53663C12.6566 4.49317 12.6502 4.45003 12.6417 4.4072C12.6332 4.36437 12.6226 4.32206 12.6099 4.28027C12.5972 4.23848 12.5825 4.19742 12.5658 4.15707C12.5491 4.11673 12.5305 4.0773 12.5099 4.03879C12.4893 4.00028 12.4669 3.96287 12.4426 3.92656C12.4184 3.89025 12.3924 3.85522 12.3647 3.82146C12.337 3.7877 12.3077 3.75539 12.2768 3.72451C12.2459 3.69363 12.2136 3.66434 12.1798 3.63664C12.1461 3.60893 12.1111 3.58295 12.0747 3.55869C12.0384 3.53443 12.001 3.51201 11.9625 3.49142C11.924 3.47084 11.8846 3.45219 11.8442 3.43548C11.8039 3.41877 11.7628 3.40407 11.721 3.3914C11.6792 3.37872 11.6369 3.36812 11.5941 3.3596C11.5513 3.35108 11.5081 3.34468 11.4647 3.3404C11.4212 3.33612 11.3777 3.33398 11.334 3.33398L9.33399 3.33398L9.33399 2.00065L11.334 2.00065L11.334 2.00065ZM4.00065 4.00065L2.66732 4.00065L2.66732 6.00065L4.00065 6.00065L4.00065 4.00065ZM6.66732 4.00065L5.33398 4.00065L5.33398 6.00065L6.66732 6.00065L6.66732 4.00065Z"
fill=
"rgb(95,101,108)"
fill-rule=
"nonzero"
/>
</svg>
src/views/bill/template/index.vue
浏览文件 @
47bc38c9
<
template
>
<div
class=
"temp-wrap"
>
<div
class=
"side"
>
<div
class=
"side-box side-box-domain"
>
<AnalysisBox
title=
"涉及领域"
width=
"520px"
height=
"415px"
>
<div
:class=
"['right-box2-main',
{ 'right-box-main--full': !domainFooterText }]" id="chart2">
</div>
<div
v-if=
"domainFooterText"
class=
"right-box2-footer"
>
<div
class=
"right-box2-footer-left"
>
<img
src=
"./assets/icons/right-icon1.png"
alt=
""
/>
</div>
<div
class=
"right-box2-footer-center"
>
{{
domainFooterText
}}
</div>
<div
class=
"right-box2-footer-right"
>
<img
src=
"./assets/icons/arrow-right.png"
alt=
""
/>
</div>
</div>
</AnalysisBox>
</div>
<div
class=
"side-box side-box-limit"
>
<AnalysisBox
title=
"限制手段"
width=
"520px"
height=
"415px"
>
<div
:class=
"['right-box1-main',
{ 'right-box-main--full': !limitFooterText }]" id="chart1">
</div>
<div
v-if=
"limitFooterText"
class=
"right-box1-footer"
>
<div
class=
"right-box1-footer-left"
>
<img
src=
"./assets/icons/right-icon1.png"
alt=
""
/>
</div>
<div
class=
"right-box1-footer-center"
>
{{
limitFooterText
}}
</div>
<div
class=
"right-box1-footer-right"
>
<img
src=
"./assets/icons/arrow-right.png"
alt=
""
/>
</div>
</div>
</AnalysisBox>
</div>
</div>
<div
class=
"terms"
>
<!--
<div
class=
"box-header"
>
<div
class=
"box-header-left"
></div>
<div
class=
"box-header-title"
>
主要条款
</div>
<div
class=
"box-header-btn-box"
></div>
<div
class=
"header-right"
>
<div
class=
"icon"
>
<img
src=
"@/assets/icons/box-header-icon2.png"
alt=
""
/>
</div>
<div
class=
"icon"
>
<img
src=
"@/assets/icons/box-header-icon3.png"
alt=
""
/>
</div>
</div>
</div>
<div
class=
"left-top"
>
<el-select
v-model=
"curBill"
placeholder=
"请选择"
@
change=
"handleChangeBill"
>
<el-option
v-for=
"item in billList"
:key=
"item.id"
:label=
"item.label"
:value=
"item.value"
/>
<div
class=
"tools-row"
>
<div
class=
"tools-row-left"
>
<el-select
v-model=
"curBill"
placeholder=
"请选择版本"
@
change=
"handleChangeBill"
class=
"tools-row-control tools-row-control--primary tools-row-control--bill"
>
<el-option
v-for=
"item in billList"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-checkbox
style=
"margin-left: 30px"
v-model=
"checkedValue"
label=
"只看涉华条款"
size=
"large"
@
change=
"handleChangeCheckbox"
/>
<div
class=
"search"
style=
"width: 240px; margin-left: 475px"
>
<el-input
v-model=
"searchValue"
placeholder=
"搜索条款"
:suffix-icon=
"Search"
clearable
/>
<el-button
class=
"tools-row-control tools-row-control--primary tools-row-compare-btn"
@
click=
"handleOpenVersionCompare"
>
<img
class=
"tools-row-compare-icon"
src=
"./assets/icons/compare-icon.svg"
alt=
""
/>
<span>
版本对比
</span>
</el-button>
</div>
<div
class=
"tools-row-right"
>
<el-input
v-model=
"searchValue"
class=
"tools-row-control--search"
placeholder=
"搜索条款"
clearable
@
keyup
.
enter=
"handleSearchSubmit"
>
<template
#
suffix
>
<el-icon
class=
"tools-row-search-icon"
@
click=
"handleSearchSubmit"
>
<Search
/>
</el-icon>
</
template
>
</el-input>
<div
class=
"tools-row-right-actions"
>
<el-checkbox
class=
"tools-row-control"
v-model=
"checkedValue"
label=
"只看涉华条款"
size=
"large"
@
change=
"handleChangeCheckbox"
/>
<el-select
v-model=
"selectedDomain"
class=
"tools-row-control tools-row-control--domain"
placeholder=
"全部领域"
>
<el-option
label=
"全部领域"
value=
""
/>
<el-option
v-for=
"item in domainOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-select
v-model=
"selectedLimit"
class=
"tools-row-control tools-row-control--limit"
placeholder=
"全部限制手段"
>
<el-option
label=
"全部限制手段"
value=
""
/>
<el-option
v-for=
"item in limitOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</div>
</div>
<div
class=
"left-main"
>
<div
class=
"left-main-item"
v-for=
"(term, index) in mainTermsList"
:key=
"index"
>
<div
class=
"id"
>
{{
(
currentPage
-
1
)
*
pageSize
+
index
+
1
}}
</div>
<div
class=
"info"
>
<div
class=
"title"
>
<span
class=
"title-active"
>
第
{{
term
.
tkxh
}}
条.
</span>
{{
term
.
fynr
}}
</div>
<div
class=
"content"
>
<span
class=
"content-active"
>
Sec.
{{
term
.
tkxh
}}
</span>
{{
term
.
ywnr
}}
</div>
<div
class=
"content-row"
>
<div
class=
"side"
>
<div
class=
"side-box side-box-domain"
>
<AnalysisBox
title=
"涉及领域"
width=
"520px"
height=
"415px"
v-loading=
"domainLoading"
>
<div
:class=
"['right-box2-main', { 'right-box-main--full': !domainFooterText }]"
id=
"chart2"
></div>
<div
v-if=
"domainFooterText"
class=
"right-box2-footer"
>
<div
class=
"right-box2-footer-left"
>
<img
src=
"./assets/icons/right-icon1.png"
alt=
""
/>
</div>
<div
class=
"right-box2-footer-center"
>
{{ domainFooterText }}
</div>
<div
class=
"right-box2-footer-right"
>
<img
src=
"./assets/icons/arrow-right.png"
alt=
""
/>
</div>
</div>
</div>
<div
class=
"tags-box"
>
<div
class=
"tag"
v-for=
"(val, idx) in (term.hylyList || []).slice(0, 2)"
:key=
"idx"
:class=
"
{
tag1: val === '人工智能',
tag2: val === '新一代信息技术' || !['人工智能', '政治', '经济', '军事', '科技'].includes(val),
tag3: val === '政治',
tag4: val === '经济',
tag5: val === '军事',
tag6: val === '科技'
}"
>
{{
val
}}
</AnalysisBox>
</div>
<div
class=
"side-box side-box-limit"
>
<AnalysisBox
title=
"限制手段"
width=
"520px"
height=
"415px"
v-loading=
"limitLoading"
>
<div
:class=
"['right-box1-main', { 'right-box-main--full': !limitFooterText }]"
id=
"chart1"
></div>
<div
v-if=
"limitFooterText"
class=
"right-box1-footer"
>
<div
class=
"right-box1-footer-left"
>
<img
src=
"./assets/icons/right-icon1.png"
alt=
""
/>
</div>
<div
class=
"right-box1-footer-center"
>
{{ limitFooterText }}
</div>
<div
class=
"right-box1-footer-right"
>
<img
src=
"./assets/icons/arrow-right.png"
alt=
""
/>
</div>
</div>
</
div
>
</
AnalysisBox
>
</div>
</div>
<div
class=
"left-footer"
>
<div
class=
"left-footer-text"
>
{{
`共 ${total
}
项`
}}
<
/div
>
<
div
class
=
"left-footer-right"
>
<
el
-
pagination
background
layout
=
"prev, pager, next"
:
total
=
"total"
v
-
model
:
current
-
page
=
"currentPage"
v
-
model
:
page
-
size
=
"pageSize"
@
current
-
change
=
"handleCurrentChange"
<div
class=
"terms"
>
<div
class=
"terms-switch"
>
<span
class=
"terms-switch-label"
>
高亮实体
</span>
<el-switch
v-model=
"termsHighlight"
inline-prompt
active-text=
"开"
inactive-text=
"关"
/>
<span
class=
"terms-switch-divider"
></span>
<span
class=
"terms-switch-label"
>
显示原文
</span>
<el-switch
v-model=
"termsShowOriginal"
inline-prompt
active-text=
"开"
inactive-text=
"关"
/>
</div>
<
/div> --
>
<
AnalysisBox
title
=
"主要条款"
:
showAllBtn
=
"false"
>
<
div
class
=
"left-top"
>
<
el
-
select
v
-
model
=
"curBill"
placeholder
=
"请选择"
@
change
=
"handleChangeBill"
>
<
el
-
option
v
-
for
=
"item in billList"
:
key
=
"item.id"
:
label
=
"item.label"
:
value
=
"item.value"
/>
<
/el-select
>
<
el
-
checkbox
style
=
"margin-left: 30px"
v
-
model
=
"checkedValue"
label
=
"只看涉华条款"
size
=
"large"
@
change
=
"handleChangeCheckbox"
/>
<
div
class
=
"search"
style
=
"width: 240px; margin-left: 475px"
>
<
el
-
input
v
-
model
=
"searchValue"
placeholder
=
"搜索条款"
:
suffix
-
icon
=
"Search"
clearable
/>
<!--
<
div
class
=
"icon"
>
-->
<!--
<
img
src
=
"./assets/icons/search-icon.png"
alt
=
""
/>
-->
<!--
<
/div> --
>
<
/div
>
<
/div
>
<
div
class
=
"left-main"
>
<
div
class
=
"left-main-item"
v
-
for
=
"(term, index) in mainTermsList"
:
key
=
"getTermKey(term, index)"
>
<
div
class
=
"id"
>
{{
(
currentPage
-
1
)
*
pageSize
+
index
+
1
}}
<
/div
>
<
div
class
=
"info"
>
<
div
class
=
"title"
>
<
span
class
=
"title-active"
>
第
{{
term
.
tkxh
}}
条
.
<
/span
>
{{
term
.
fynr
}}
<
/div
>
<
div
class
=
"content"
>
<
span
class
=
"content-active"
>
Sec
.{{
term
.
tkxh
}}
<
/span
>
{{
term
.
ywnr
}}
<AnalysisBox
title=
"主要条款"
:showAllBtn=
"false"
v-loading=
"termsLoading"
>
<div
class=
"left-main"
>
<div
class=
"left-main-item"
v-for=
"(term, index) in displayTermsList"
:key=
"getTermKey(term, index)"
>
<div
class=
"term-body"
>
<div
class=
"term-index"
>
{{ getTermSerial(index) }}
</div>
<div
class=
"term-main"
>
<div
class=
"term-row term-row-cn"
>
<div
class=
"term-no-cn"
>
第{{ term.tkxh }}条.
</div>
<div
class=
"term-content-cn"
>
{{ term.fynr }}
</div>
</div>
<div
class=
"term-row term-row-en"
v-if=
"termsShowOriginal"
>
<div
class=
"term-no-en"
>
Sec.{{ term.tkxh }}
</div>
<div
class=
"term-content-en"
>
{{ term.ywnr }}
</div>
</div>
</div>
</div>
<!-- <div class="open">
<img src="./assets/icons/open-icon.png" alt="" />
</div> -->
</div>
<
div
class
=
"tags-box"
>
<
div
class
=
"tag"
v
-
for
=
"(val, idx) in (term.hylyList || []).slice(0, 2)"
:
key
=
"getTagKey(val, idx)"
:
class
=
"{
tag1: val === '人工智能',
tag2: val === '新一代信息技术' || !['人工智能', '政治', '经济', '军事', '科技'].includes(val),
tag3: val === '政治',
tag4: val === '经济',
tag5: val === '军事',
tag6: val === '科技'
}
"
>
{{
val
}}
<
/div
>
<
/div
>
<!--
<
div
class
=
"open"
>
<
img
src
=
"./assets/icons/open-icon.png"
alt
=
""
/>
<
/div> --
>
<
/div
>
<
/div
>
<
div
class
=
"left-footer"
>
<
div
class
=
"left-footer-text"
>
{{
`共 ${total
}
项`
}}
</div>
<
div
class
=
"left-footer-right"
>
<
el
-
pagination
background
layout
=
"prev, pager, next"
:
total
=
"total"
v
-
model
:
current
-
page
=
"currentPage"
v
-
model
:
page
-
size
=
"pageSize"
@
current
-
change
=
"handleCurrentChange"
/>
<div
class=
"left-footer"
>
<div
class=
"left-footer-text"
>
{{ `共 ${total} 项` }}
</div>
<div
class=
"left-footer-right"
>
<el-pagination
background
layout=
"prev, pager, next"
:total=
"total"
v-model:current-page=
"currentPage"
v-model:page-size=
"pageSize"
@
current-change=
"handleCurrentChange"
/>
</div>
</div>
<
/
div
>
<
/
AnalysisBox
>
</
AnalysisBox
>
</
div
>
</div>
</div>
</template>
<
script
setup
>
import
{
ref
,
onMounted
}
from
"vue"
;
import
{
ref
,
onMounted
,
computed
,
watch
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
*
as
echarts
from
"echarts"
;
import
{
Search
}
from
"@element-plus/icons-vue"
;
import
getPieChart
from
"./utils/piechart"
;
import
{
getBillContentId
,
getBillContentTk
,
getBillContentXzfs
,
getBillHyly
}
from
"@/api/bill"
;
...
...
@@ -182,11 +144,27 @@ const curBill = ref("");
const
curBillId
=
ref
(
null
);
const
checkedValue
=
ref
(
false
);
const
searchValue
=
ref
(
""
);
const
searchKeyword
=
ref
(
""
);
const
selectedDomain
=
ref
(
""
);
const
selectedLimit
=
ref
(
""
);
const
domainOptions
=
ref
([]);
const
limitOptions
=
ref
([]);
const
billList
=
ref
([]);
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
total
=
ref
(
0
);
const
termsLoading
=
ref
(
false
);
const
limitLoading
=
ref
(
false
);
const
domainLoading
=
ref
(
false
);
const
termsHighlight
=
ref
(
true
);
const
termsShowOriginal
=
ref
(
true
);
const
tkRequestToken
=
ref
(
0
);
const
xzfsRequestToken
=
ref
(
0
);
const
hylyRequestToken
=
ref
(
0
);
const
mainTermsList
=
ref
([]);
const
domainFooterText
=
ref
(
""
);
const
limitFooterText
=
ref
(
""
);
...
...
@@ -199,8 +177,8 @@ const getTermKey = (term, index) => {
return
term
?.
ywid
??
term
?.
id
??
term
?.
tkxh
??
index
;
};
const
getT
agKey
=
(
val
,
idx
)
=>
{
return
`${val
}
-${idx
}
`
;
const
getT
ermSerial
=
index
=>
{
return
(
currentPage
.
value
-
1
)
*
pageSize
.
value
+
index
+
1
;
};
const
chart1Data
=
ref
([]);
...
...
@@ -210,6 +188,42 @@ const chart2ColorList = ref(["#ff7875", "#85a5ff", "#95de64", "#ffc069", "#85e5d
const
chart2Data
=
ref
([]);
const
displayTermsList
=
computed
(()
=>
{
const
keyword
=
(
searchKeyword
.
value
||
""
).
trim
().
toLowerCase
();
const
domain
=
selectedDomain
.
value
;
const
limit
=
selectedLimit
.
value
;
return
(
mainTermsList
.
value
||
[]).
filter
(
term
=>
{
if
(
keyword
)
{
const
cn
=
String
(
term
?.
fynr
??
""
).
toLowerCase
();
const
en
=
String
(
term
?.
ywnr
??
""
).
toLowerCase
();
const
no
=
String
(
term
?.
tkxh
??
""
).
toLowerCase
();
if
(
!
cn
.
includes
(
keyword
)
&&
!
en
.
includes
(
keyword
)
&&
!
no
.
includes
(
keyword
))
return
false
;
}
if
(
domain
)
{
const
list
=
term
?.
hylyList
||
term
?.
hyly
||
[];
const
arr
=
Array
.
isArray
(
list
)
?
list
:
[];
if
(
!
arr
.
includes
(
domain
))
return
false
;
}
if
(
limit
)
{
const
list
=
term
?.
xzfsList
||
term
?.
xzfs
||
[];
const
arr
=
Array
.
isArray
(
list
)
?
list
:
[];
if
(
!
arr
.
includes
(
limit
))
return
false
;
}
return
true
;
});
});
watch
([
selectedDomain
,
selectedLimit
],
()
=>
{
currentPage
.
value
=
1
;
handleGetBillContentTk
(
checkedValue
.
value
?
"Y"
:
"N"
);
});
const
handleSearchSubmit
=
()
=>
{
searchKeyword
.
value
=
searchValue
.
value
;
currentPage
.
value
=
1
;
handleGetBillContentTk
(
checkedValue
.
value
?
"Y"
:
"N"
);
};
// 绘制echarts图表
const
setChart
=
(
option
,
chartId
)
=>
{
let
chartDom
=
document
.
getElementById
(
chartId
);
...
...
@@ -224,7 +238,9 @@ const handleChangeBill = val => {
curBill
.
value
=
val
;
const
item
=
billList
.
value
.
find
(
item
=>
item
.
value
===
val
);
if
(
item
)
{
curBillId
.
value
=
item
.
id
;
curBillId
.
value
=
item
.
value
;
selectedDomain
.
value
=
""
;
selectedLimit
.
value
=
""
;
currentPage
.
value
=
1
;
handleGetBillContentTk
(
checkedValue
.
value
?
"Y"
:
"N"
);
handleGetBillContentXzfs
();
...
...
@@ -232,6 +248,11 @@ const handleChangeBill = val => {
}
};
const
handleOpenVersionCompare
=
()
=>
{
const
targetUrl
=
`/billLayout/deepDig/processOverview?billId=
${
route
.
query
.
billId
}
`
;
window
.
open
(
targetUrl
,
"_blank"
);
};
// 获取法案id列表
const
handleGetBillList
=
async
()
=>
{
const
params
=
{
...
...
@@ -240,16 +261,24 @@ const handleGetBillList = async () => {
try
{
const
res
=
await
getBillContentId
(
params
);
console
.
log
(
"法案id列表"
,
res
);
billList
.
value
=
res
.
data
.
map
(
item
=>
{
return
{
label
:
item
.
bbmc
,
value
:
item
.
bbmc
,
id
:
item
.
ywid
}
;
}
);
const
rawList
=
Array
.
isArray
(
res
?.
data
)
?
res
.
data
:
[];
const
seen
=
new
Set
();
billList
.
value
=
rawList
.
map
(
item
=>
{
return
{
label
:
item
.
bbmc
,
value
:
item
.
bbmc
};
})
.
filter
(
item
=>
{
const
uniqKey
=
`
${
item
.
value
??
""
}
`;
if (seen.has(uniqKey)) return false;
seen.add(uniqKey);
return true;
});
if (billList.value.length > 0) {
curBill.value = billList.value[0].value;
curBillId
.
value
=
billList
.
value
[
0
].
id
;
curBillId.value = billList.value[0].
value
;
}
} catch (error) { }
};
...
...
@@ -268,6 +297,9 @@ const handleCurrentChange = val => {
// 根据原文ID获取条款列表
const handleGetBillContentTk = async cRelated => {
const currentToken = ++tkRequestToken.value;
termsLoading.value = true;
const params = {
billId: route.query.billId,
id: curBill.value,
...
...
@@ -275,8 +307,23 @@ const handleGetBillContentTk = async cRelated => {
pageSize: pageSize.value,
currentPage: currentPage.value
};
if (selectedDomain.value) {
params.domainNameList = selectedDomain.value;
}
if (selectedLimit.value) {
params.measuresNameList = selectedLimit.value;
}
if (searchKeyword.value && searchKeyword.value.trim()) {
params.content = searchKeyword.value.trim();
}
try {
const res = await getBillContentTk(params);
if (currentToken !== tkRequestToken.value) {
return;
}
console.log("条款内容", res);
if (res && res.data) {
mainTermsList.value = (res.data.content || []).map(item => {
...
...
@@ -320,14 +367,24 @@ const handleGetBillContentTk = async cRelated => {
total.value = 0;
}
} catch (error) {
if (currentToken !== tkRequestToken.value) {
return;
}
console.error("获取条款内容失败", error);
mainTermsList.value = [];
total.value = 0;
} finally {
if (currentToken === tkRequestToken.value) {
termsLoading.value = false;
}
}
};
// 获取限制方式列表
const handleGetBillContentXzfs = async () => {
const currentToken = ++xzfsRequestToken.value;
limitLoading.value = true;
const params = {
billId: route.query.billId,
versionId: curBill.value,
...
...
@@ -336,7 +393,22 @@ const handleGetBillContentXzfs = async () => {
try {
const res = await getBillContentXzfs(params);
if (currentToken !== xzfsRequestToken.value) {
return;
}
console.log("限制方式", res);
const limitSeen = new Set();
limitOptions.value = (res?.data || [])
.map(item => item?.xzfsmc)
.filter(Boolean)
.filter(name => {
if (limitSeen.has(name)) return false;
limitSeen.add(name);
return true;
})
.map(name => {
return { label: name, value: name };
});
chart1Data.value = res.data.map(item => {
return {
name: item.xzfsmc,
...
...
@@ -345,11 +417,22 @@ const handleGetBillContentXzfs = async () => {
});
let chart1 = getPieChart(chart1Data.value, chart1ColorList.value);
setChart(chart1, "chart1");
}
catch
(
error
)
{
}
} catch (error) {
if (currentToken !== xzfsRequestToken.value) {
return;
}
} finally {
if (currentToken === xzfsRequestToken.value) {
limitLoading.value = false;
}
}
};
// 行业领域
const handleGetBillHyly = async () => {
const currentToken = ++hylyRequestToken.value;
domainLoading.value = true;
const params = {
billId: route.query.billId,
versionId: curBill.value,
...
...
@@ -358,7 +441,22 @@ const handleGetBillHyly = async () => {
try {
const res = await getBillHyly(params);
if (currentToken !== hylyRequestToken.value) {
return;
}
console.log("行业领域", res.data);
const domainSeen = new Set();
domainOptions.value = (res?.data || [])
.map(item => item?.hylymc)
.filter(Boolean)
.filter(name => {
if (domainSeen.has(name)) return false;
domainSeen.add(name);
return true;
})
.map(name => {
return { label: name, value: name };
});
chart2Data.value = res.data.map(item => {
return {
name: item.hylymc,
...
...
@@ -368,7 +466,15 @@ const handleGetBillHyly = async () => {
let chart2 = getPieChart(chart2Data.value, chart2ColorList.value);
setChart(chart2, "chart2");
}
catch
(
error
)
{
}
} catch (error) {
if (currentToken !== hylyRequestToken.value) {
return;
}
} finally {
if (currentToken === hylyRequestToken.value) {
domainLoading.value = false;
}
}
};
onMounted(async () => {
...
...
@@ -382,6 +488,138 @@ onMounted(async () => {
<
style
lang=
"scss"
scoped
>
.temp-wrap
{
display
:
flex
;
flex-direction
:
column
;
.tools-row
{
margin-top
:
16px
;
display
:
flex
;
align-items
:
center
;
.tools-row-left
{
width
:
520px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
column-gap
:
16px
;
}
.tools-row-right
{
width
:
1064px
;
margin-left
:
16px
;
display
:
flex
;
align-items
:
center
;
column-gap
:
16px
;
}
.tools-row-right-actions
{
margin-left
:
auto
;
display
:
flex
;
align-items
:
center
;
column-gap
:
16px
;
}
.tools-row-control
{
height
:
32px
;
font-size
:
16px
;
font-weight
:
400
;
:deep
(
.el-select__wrapper
)
{
height
:
32px
;
font-size
:
16px
;
font-weight
:
400
;
}
:deep
(
.el-checkbox__label
)
{
font-size
:
16px
;
font-weight
:
400
;
}
}
.tools-row-control--primary
{
:deep
(
.el-select__wrapper
)
{
background
:
rgb
(
246
,
250
,
255
);
border
:
1px
solid
var
(
--
color-primary-35
);
box-shadow
:
none
;
color
:
var
(
--
color-primary-100
);
}
:deep
(
.el-select__selected-item
)
{
color
:
var
(
--
color-primary-100
);
font-size
:
16px
;
font-weight
:
400
;
}
:deep
(
.el-select__caret
)
{
color
:
var
(
--
color-primary-100
);
}
}
:deep
(
.el-button.tools-row-control--primary
)
{
height
:
32px
;
padding
:
0
12px
;
border
:
1px
solid
var
(
--
color-primary-35
);
background
:
rgb
(
246
,
250
,
255
);
color
:
var
(
--
color-primary-100
);
font-size
:
16px
;
font-weight
:
400
;
width
:
111px
;
}
.tools-row-compare-btn
{
display
:
inline-flex
;
align-items
:
center
;
}
.tools-row-compare-icon
{
width
:
14px
;
height
:
14px
;
flex
:
0
0
14px
;
margin-right
:
6px
;
}
.tools-row-control--bill
{
width
:
392px
;
}
.tools-row-control--search
{
width
:
200px
;
margin-right
:
auto
;
:deep
(
.el-input__wrapper
)
{
height
:
32px
;
background
:
var
(
--
el-fill-color-blank
);
border-radius
:
4px
;
box-shadow
:
0
0
0
1px
var
(
--
el-border-color
)
inset
;
}
:deep
(
.el-input__wrapper.is-focus
)
{
box-shadow
:
0
0
0
1px
var
(
--
el-color-primary
)
inset
!
important
;
border-color
:
var
(
--
el-color-primary
)
!
important
;
}
:deep
(
.el-input__wrapper
:not
(
.is-focus
)
:hover
)
{
box-shadow
:
0
0
0
1px
var
(
--
el-border-color
)
inset
!
important
;
border-color
:
var
(
--
el-border-color
)
!
important
;
}
:deep
(
.el-input__inner
)
{
font-size
:
16px
;
}
}
.tools-row-search-icon
{
cursor
:
pointer
;
}
.tools-row-control--domain
,
.tools-row-control--limit
{
width
:
150px
;
}
}
.content-row
{
display
:
flex
;
}
.box-header
{
display
:
flex
;
...
...
@@ -446,152 +684,135 @@ onMounted(async () => {
margin-top
:
16px
;
margin-left
:
16px
;
width
:
1064px
;
height
:
845
px
;
.
left
-
top
{
// height: 45px;
height
:
1232px
;
position
:
relative
;
margin
:
0
px
24
px
8
px
24
px
;
.terms-switch
{
position
:
absolute
;
top
:
13px
;
right
:
85px
;
z-index
:
5
;
display
:
flex
;
column-gap
:
6px
;
align-items
:
center
;
:
deep
(.
el
-
input
)
{
width
:
240
px
;
border
-
radius
:
4
px
;
border
:
1
px
solid
var
(
--
border
-
black
-
10
);
.terms-switch-label
{
font-size
:
16px
;
color
:
var
(
--
text-primary-65-color
);
}
:
deep
(.
el
-
select
)
{
width
:
240
px
;
border
-
radius
:
4
px
;
// border: 1px solid var(--border-black-10);
.terms-switch-divider
{
display
:
inline-block
;
width
:
1px
;
height
:
14px
;
background
:
var
(
--
el-border-color
);
margin
:
0
2px
;
}
:deep
(
.el-button--small
)
{
height
:
26px
;
padding
:
0
10px
;
font-size
:
12px
;
}
}
:deep
(
.wrapper-main
)
{
display
:
flex
;
flex-direction
:
column
;
height
:
calc
(
100%
-
45px
);
gap
:
16px
;
}
.left-main
{
height
:
660
px
;
overflow
:
hidden
;
flex
:
1
;
min-height
:
0
;
overflow-y
:
auto
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
padding
:
0
16px
;
.
left
-
main
-
item
{
margin
-
left
:
22
px
;
.left-main-item
{
width
:
1016px
;
// height: 65px;
box-sizing
:
border-box
;
border-radius
:
2px
;
background
:
rgb
a
(
255
,
255
,
255
,
1
);
background
:
rgb
(
247
,
248
,
249
);
display
:
flex
;
align-items
:
flex-start
;
position
:
relative
;
padding
:
16px
;
.
id
{
margin
-
top
:
20
px
;
margin
-
left
:
15
px
;
.term-body
{
display
:
flex
;
column-gap
:
18px
;
flex
:
1
;
min-width
:
0
;
width
:
100%
;
}
.term-index
{
width
:
24px
;
height
:
24px
;
border
-
radius
:
12
px
;
background
:
rgba
(
231
,
241
,
255
);
color
:
var
(
--
color
-
main
-
active
);
border-radius
:
50%
;
background
:
var
(
--
color-primary-10
);
color
:
var
(
--
color-
primary-100
);
font-size
:
12px
;
text
-
align
:
center
;
font-weight
:
400
;
line-height
:
24px
;
text-align
:
center
;
flex
:
0
0
24px
;
}
.
info
{
margin
-
left
:
13
px
;
margin
-
top
:
15
px
;
width
:
920
px
;
.
title
{
// height: 14px;
color
:
rgba
(
59
,
65
,
75
,
1
);
font
-
family
:
Microsoft
YaHei
;
font
-
size
:
14
px
;
font
-
weight
:
600
;
line
-
height
:
22
px
;
text
-
align
:
left
;
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
.
title
-
active
{
color
:
var
(
--
color
-
main
-
active
);
}
}
.
content
{
margin
-
top
:
6
px
;
// height: 14px;
color
:
rgba
(
132
,
136
,
142
,
1
);
font
-
family
:
Microsoft
YaHei
;
font
-
size
:
14
px
;
font
-
weight
:
400
;
line
-
height
:
24
px
;
text
-
align
:
left
;
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
.
content
-
active
{
color
:
var
(
--
color
-
main
-
active
);
}
}
.term-main
{
flex
:
1
;
min-width
:
0
;
display
:
flex
;
flex-direction
:
column
;
row-gap
:
6px
;
}
.
t
ags
-
box
{
.t
erm-row
{
display
:
flex
;
justify
-
content
:
right
;
align
-
items
:
center
;
flex
:
1
;
margin
-
right
:
50
px
;
.
tag
{
text
-
align
:
right
;
line
-
height
:
18
px
;
padding
:
1
px
8
px
;
border
-
radius
:
4
px
;
margin
-
left
:
5
px
;
font
-
size
:
12
px
;
font
-
family
:
"Microsoft YaHei"
;
font
-
size
:
14
px
;
font
-
weight
:
400
;
}
.
tag1
{
border
:
1
px
solid
rgba
(
217
,
247
,
190
,
1
);
background
:
rgba
(
246
,
255
,
237
,
1
);
color
:
rgba
(
82
,
196
,
26
,
1
);
}
.
tag2
{
border
:
1
px
solid
rgba
(
186
,
224
,
255
,
1
);
background
:
rgba
(
230
,
244
,
255
,
1
);
color
:
rgba
(
22
,
119
,
255
,
1
);
}
align-items
:
flex-start
;
column-gap
:
18px
;
}
.
tag3
{
border
:
1
px
solid
rgba
(
135
,
232
,
222
,
1
);
background
:
rgba
(
230
,
255
,
251
,
1
);
color
:
rgba
(
19
,
168
,
168
,
1
);
}
.term-no-cn
{
font-size
:
16px
;
font-weight
:
700
;
line-height
:
24px
;
color
:
var
(
--
color-primary-100
);
white-space
:
nowrap
;
width
:
90px
;
text-align
:
center
;
flex
:
0
0
90px
;
}
.
tag4
{
border
:
1
px
solid
rgba
(
211
,
173
,
247
,
1
);
background
:
rgba
(
249
,
240
,
255
,
1
);
color
:
rgba
(
114
,
46
,
209
,
1
);
}
.term-no-en
{
font-size
:
14px
;
font-weight
:
400
;
line-height
:
24px
;
color
:
var
(
--
color-primary-100
);
white-space
:
nowrap
;
width
:
90px
;
text-align
:
center
;
flex
:
0
0
90px
;
}
.
tag5
{
border
:
1
px
solid
rgba
(
255
,
229
,
143
,
1
);
background
:
rgba
(
255
,
251
,
230
,
1
);
color
:
rgba
(
250
,
173
,
20
,
1
);
}
.term-content-cn
{
flex
:
1
;
font-size
:
16px
;
font-weight
:
700
;
line-height
:
24px
;
color
:
var
(
--
text-primary-80-color
);
}
.
tag6
{
border
:
1
px
solid
rgba
(
255
,
163
,
158
,
1
);
background
:
rgba
(
255
,
241
,
240
,
1
);
color
:
rgba
(
245
,
34
,
45
,
1
);
}
.term-content-en
{
flex
:
1
;
font-size
:
14px
;
font-weight
:
400
;
line-height
:
24px
;
color
:
var
(
--
text-primary-65-color
);
}
.open
{
...
...
@@ -614,8 +835,8 @@ onMounted(async () => {
}
.left-footer
{
margin
:
30
px
;
height
:
3
0
px
;
margin
:
0
16px
16
px
;
height
:
4
0px
;
display
:
flex
;
justify-content
:
space-between
;
...
...
@@ -641,19 +862,20 @@ onMounted(async () => {
width
:
520px
;
}
.
side
-
box
-
limit
{
margin
-
top
:
15
px
;
.side-box-limit
{
margin-top
:
15px
;
width
:
520px
;
height
:
415px
;
.right-box1-main
{
width
:
520px
;
height
:
415
px
;
.
right
-
box1
-
main
{
width
:
520
px
;
height
:
315
px
;
padding
:
16
px
;
}
height
:
315px
;
padding
:
16px
;
}
.
right
-
box
-
main
--
full
{
height
:
375
px
;
}
.right-box-main--full
{
height
:
375px
;
}
.right-box1-footer
{
width
:
493px
;
...
...
@@ -708,18 +930,19 @@ onMounted(async () => {
}
}
.
side
-
box
-
domain
{
.side-box-domain
{
width
:
520px
;
height
:
415px
;
.right-box2-main
{
width
:
520px
;
height
:
415
px
;
.
right
-
box2
-
main
{
width
:
520
px
;
height
:
315
px
;
padding
:
16
px
;
}
height
:
315px
;
padding
:
16px
;
}
.
right
-
box
-
main
--
full
{
height
:
375
px
;
}
.right-box-main--full
{
height
:
375px
;
}
.right-box2-footer
{
width
:
493px
;
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论