提交 7505facb authored 作者: 张伊明's avatar 张伊明

合并分支 'zym-dev' 到 'master'

Zym dev 查看合并请求 !170
......@@ -117,7 +117,7 @@ export function getBillContentId(params) {
// 主要条款-根据原文ID获取条款内容
/**
* @param {billid,id,cRelated,currentPage,pageSize}
* @param {billId,id,cRelated,currentPage,pageSize,domainNameList,measuresNameList,content}
* @header token
*/
export function getBillContentTk(params) {
......
......@@ -135,6 +135,26 @@ export function getBillsPerson(params, signal) {
})
}
// 获取议员合作关系
export function getBillsPersonRel(params, signal) {
return request({
method: 'GET',
url: `/api/BillOverview/billsPersonRel`,
params,
signal
})
}
// 获取涉华委员会及其法案
export function getBillsIsCnCommittee(params, signal) {
return request({
method: 'GET',
url: `/api/BillOverview/billsIsCnCommittee`,
params,
signal
})
}
// 获取提出部门列表
export function getPostOrgList() {
return request({
......
......@@ -130,3 +130,14 @@ export function getBillPersonPoliContribution(params) {
url: `/api/billDeepDive/processAnalyze/xj/${params.id}/${params.personId}`
})
}
// 根据法案ID和人物ID获取政治献金领域分布
/**
* @param {id, personId}
*/
export function getBillPersonPoliDomain(params) {
return request({
method: 'GET',
url: `/api/billDeepDive/processAnalyze/xjDomain/${params.id}/${params.personId}`
})
}
......@@ -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)) {
......
......@@ -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
......
......@@ -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')
......
......@@ -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",
......
......@@ -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 以确保背景块足够大
}
};
}
......
<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) { }
};
......
const truncateLabel = (value, maxLen = 6) => {
if (value === null || value === undefined) return ''
const str = String(value)
const chars = Array.from(str)
if (chars.length <= maxLen) return str
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: [
{
......@@ -18,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
}
}
})
......@@ -42,7 +65,12 @@ const getDoublePieChart = (data1, data2) => {
},
label: {
alignTo: 'edge',
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
formatter: params => {
const name = truncateLabel(params?.name, 6)
const value = params?.value ?? 0
const percent = typeof params?.percent === 'number' ? params.percent : 0
return `{name|${name}}\n{time|${value}${percent}%}`
},
minMargin: 5,
edgeDistance: 10,
lineHeight: 20,
......@@ -57,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]
}
}
......@@ -82,8 +110,9 @@ const getDoublePieChart = (data1, data2) => {
return {
name: item.name,
value: item.value,
percent: item.percent,
itemStyle: {
color: item.type === '参议院' ? '#8AC4FF' : '#FFD591'
color: item.type === '参议院' ? senateDeptColor : houseDeptColor
}
}
......
......@@ -68,7 +68,14 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
axisLabel: {
formatter: '{value}项',
color: '#666'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#e7f3ff',
type: 'dashed',
}
},
},
{
type: 'value',
......@@ -84,6 +91,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
show: true,
lineStyle: {
color: '#e7f3ff',
type: 'dashed',
}
},
}
......@@ -92,6 +100,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
{
name: '提出法案',
type: 'line',
smooth: true,
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: {
......@@ -111,6 +120,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
{
name: '通过法案',
type: 'line',
smooth: true,
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: {
......@@ -131,6 +141,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
name: '通过率',
type: 'line',
yAxisIndex: 1,
smooth: true,
symbol: 'emptyCircle',
symbolSize: 4,
lineStyle: {
......
const truncateLabel = (value, maxLen = 6) => {
if (value === null || value === undefined) return ''
const str = String(value)
const chars = Array.from(str)
if (chars.length <= maxLen) return str
return `${chars.slice(0, maxLen).join('')}...`
}
const getPieChart = (data, colorList) => {
let option = {
// color: colorList,
......@@ -14,7 +22,12 @@ const getPieChart = (data, colorList) => {
},
label: {
alignTo: 'edge',
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
formatter: params => {
const name = truncateLabel(params?.name, 6)
const value = params?.value ?? 0
const percent = typeof params?.percent === 'number' ? params.percent : 0
return `{name|${name}}\n{time|${value}${percent}%}`
},
minMargin: 5,
edgeDistance: 10,
lineHeight: 22,
......
......@@ -2,6 +2,14 @@ import "echarts-wordcloud";
const getWordCloudChart = (data = []) => {
const option = {
tooltip: {
show: true,
formatter: params => {
const name = params?.data?.fullName ?? params?.name ?? ''
const value = params?.value ?? ''
return `${name}${value !== '' ? `:${value}` : ''}`
},
},
grid: {
left: 0,
top: 0,
......@@ -57,7 +65,14 @@ const getWordCloudChart = (data = []) => {
},
},
// 设置词云数据
data: data,
data: (Array.isArray(data) ? data : []).map(item => {
const name = item?.name ?? ''
return {
...item,
fullName: name,
name,
}
}),
},
],
}
......
<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>
<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>
......@@ -2,63 +2,6 @@
<div class="wrapper">
<div class="left">
<div class="box1">
<!-- <div class="box-header">
<div class="icon"></div>
<div class="title">{{ "主要议员" }}</div>
<div class="btn-box">
<div class="btn" :class="{ btnActive: activeBtnIndex === 0 }" @click="activeBtnIndex = 0">
{{ "参议院" }}
</div>
<div class="btn" :class="{ btnActive: activeBtnIndex === 1 }" @click="activeBtnIndex = 1">
{{ "众议院" }}
</div>
</div>
<div class="header-right">
<div class="right-icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="right-icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box1-main">
<div class="box1-main-top">
<div class="top-item" v-for="(item, index) in partyContributionList" :key="index">
<div class="icon">
<img :src="item.img" alt="" />
</div>
<div class="name">{{ item.name }}</div>
<div class="line" :class="{ line1: index === 0, line2: index === 1 }"></div>
<div class="num" :class="{ num1: index === 0, num2: index === 1 }">{{ item.num }}</div>
</div>
</div>
<div class="box1-main-main">
<div class="item" :class="{ itemActive: itemActiveIndex === index }"
v-for="(item, index) in mainPoliContribution" :key="index" @click="handlePersonClick(item, index)">
<div class="item-left">
<img :src="item.image || Img1" alt="" />
</div>
<div class="item-center">
<div class="center-top">
<div class="name">{{ item.name }}</div>
<div class="icon1">
<img :src="item.dp === 'Republican' ? Ghd : Mzd" alt="" />
</div>
<div class="icon2">
<img :src="activeBtnIndex === 0 ? Cyy : Zyy" alt="" />
</div>
</div>
<div class="center-footer">
{{ item.zw }}
</div>
</div>
<div class="item-right">
{{ item.formattedTotalJe }}
</div>
</div>
</div>
</div> -->
<AnalysisBox title="主要议员" :showAllBtn="false">
<template #header-btn>
<div class="btn-box">
......@@ -91,7 +34,7 @@
<div class="center-top">
<div class="name">{{ item.name }}</div>
<div class="icon1">
<img :src="item.dp === 'Republican' ? Ghd : Mzd" alt="" />
<img :src="item.dp === '共和党' ? Ghd : Mzd" alt="" />
</div>
<div class="icon2">
<img :src="activeBtnIndex === 0 ? Cyy : Zyy" alt="" />
......@@ -214,8 +157,8 @@
<div class="box3-main" :class="{ 'box3-main-no-footer': !showHardcodedTips }">
<div class="box3-main-left" id="chart2"></div>
<div class="box3-main-right">
<el-empty v-if="!areaList.length" description="暂无数据" :image-size="100" />
<div class="box3-main-right-item" v-for="(item, index) in areaList" :key="index">
<el-empty v-if="!topAreaList.length" description="暂无数据" :image-size="100" />
<div class="box3-main-right-item" v-for="(item, index) in topAreaList" :key="index">
<div class="id">{{ index + 1 }}</div>
<div class="name">{{ item.name }}</div>
<div class="line">
......@@ -247,7 +190,7 @@
<script setup>
import { ref, onMounted, watch, computed, nextTick } from "vue";
import { getBillPoliContribution, getBillMainPoliContribution, getBillPersonPoliContribution } from "@/api/deepdig";
import { getBillPoliContribution, getBillMainPoliContribution, getBillPersonPoliContribution, getBillPersonPoliDomain } from "@/api/deepdig";
import setChart from "@/utils/setChart";
......@@ -486,28 +429,12 @@ const areaList = ref([]);
const chart2Data = ref([]);
const chart2ColorList = ref(["#4096FF", "#FFA39E", "#ADC6FF", "#FFC069", "#B5F5EC", "#B37FEB", "#D6E4FF"]);
// 使用静态行业分布数据,用于“政治献金领域分布”模块展示
const useStaticIndustryData = true;
const staticChart2Data = [
{ name: "集成电路", value: 21 },
{ name: "能源", value: 18 },
{ name: "量子科技", value: 16 },
{ name: "航空航天", value: 14 },
{ name: "通信设备", value: 12 },
{ name: "生物科技", value: 11 },
{ name: "其他", value: 8 }
];
// 领域分布列表仅展示前 5 条
const topAreaList = computed(() => {
return areaList.value.slice(0, 5);
});
const staticAreaList = [
{ name: "集成电路", amount: 186000, insNum: 8 },
{ name: "能源", amount: 180000, insNum: 5 },
{ name: "量子科技", amount: 171000, insNum: 2 },
{ name: "航空航天", amount: 75000, insNum: 3 },
{ name: "通信设备", amount: 70000, insNum: 2 }
];
const chart2ColorList = ref(["#4096FF", "#FFA39E", "#ADC6FF", "#FFC069", "#B5F5EC", "#B37FEB", "#D6E4FF"]);
const sankeyColors = [
"#5470c6",
......@@ -560,7 +487,11 @@ const getPoliContribution = async () => {
if (res.data && res.data.length > 0) {
res.data.forEach(item => {
const totalAmount = item.totalJe || 0;
if (item.dp === "Republican") {
const dp = item.dp || "";
// 兼容中文/英文党派名称,优先识别“共和党”
const isRepublican = dp === "共和党" || dp === "Republican";
if (isRepublican) {
partyContributionList.value[0].num = `$${totalAmount.toLocaleString()}`;
} else {
partyContributionList.value[1].num = `$${totalAmount.toLocaleString()}`;
......@@ -710,23 +641,15 @@ const getPersonPoliContribution = async personId => {
showAllSankeyData.value = false; // Reset to default (top 5)
renderSankeyChart();
// 政治献金领域分布:根据配置选择静态数据或接口数据
if (useStaticIndustryData) {
// 饼图数据(左侧圆环)
chart2Data.value = staticChart2Data;
const chart2Static = getPieChart(chart2Data.value, chart2ColorList.value);
setChart(chart2Static, "chart2");
// 政治献金领域分布:调用 /xjDomain/{billId}/{personId} 接口
const domainParams = {
id: window.sessionStorage.getItem("billId"),
personId
};
const domainRes = await getBillPersonPoliDomain(domainParams);
// 右侧列表(Top5 领域)
areaList.value = staticAreaList.map(item => ({
name: item.name,
num: item.amount,
numtext: `$${item.amount.toLocaleString()}`,
insNum: item.insNum
}));
} else {
// 使用接口返回的行业分布
const industries = res.data.industryList || [];
if (domainRes.code === 200 && Array.isArray(domainRes.data)) {
const industries = domainRes.data || [];
chart2Data.value = industries.map(item => ({
name: item.industryName,
......@@ -744,6 +667,11 @@ const getPersonPoliContribution = async personId => {
numtext: `$${(item.amount || 0).toLocaleString()}`,
insNum: item.orgNum
}));
} else {
chart2Data.value = [];
areaList.value = [];
const chart2Empty = getPieChart([], chart2ColorList.value);
setChart(chart2Empty, "chart2");
}
} else {
personPoliContribution.value = [];
......@@ -1192,7 +1120,8 @@ onMounted(() => {
.box3-main-right {
width: 518px;
margin-left: 10px;
overflow: auto;
overflow-x: hidden;
overflow-y: hidden;
.box3-main-right-item {
height: 60px;
......
......@@ -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: 160px;
width: 120px;
position: absolute;
top: 8px;
left: -160px;
top: 16px;
left: -140px;
.sider-btn {
margin-top: 20px;
margin-left: 20px;
......
......@@ -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>
......
<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>
<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>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论