提交 ab4c01b1 authored 作者: yanpeng's avatar yanpeng

Merge branch 'master' into yp-dev

......@@ -14,13 +14,13 @@ export function getBillInfo(params) {
// 提出人-根据动议ID获取对应的提出人信息
/**
* @param {id}
* @param {billId}
* @header token
*/
export function getBillPerson(params) {
return request({
method: 'GET',
url: `/api/billInfoBean/person/${params.id}`,
url: `/api/billInfoBean/person/${params.billId}`,
params,
})
}
......@@ -63,6 +63,18 @@ export function getBillBackground(params) {
params,
})
}
// 相关事件-根据法案ID获取相关事件信息
/**
* @param {id}
* @header token
*/
export function getBillInfoEvent(params) {
return request({
method: 'GET',
url: `/api/billInfoBean/event/${params.id}`,
params,
})
}
// 议员相关性-根据法案ID获取议员分析信息(现在只包括名称 支持 反对,没有标签和事件动态)
/**
......@@ -92,20 +104,20 @@ export function getBillContentId(params) {
// 主要条款-根据原文ID获取条款内容
/**
* @param {id,cRelated,currentPage,pageSize}
* @param {billid,id,cRelated,currentPage,pageSize}
* @header token
*/
export function getBillContentTk(params) {
return request({
method: 'GET',
url: `/api/billInfoBean/content/tk/${params.id}`,
url: `/api/billInfoBean/content/tk/${params.billid}/${params.id}`,
params,
})
}
// 限制方式-根据法案原文ID获取限制方式列表
/**
* @param {id}
* @param {billId}
* @header token
*/
export function getBillContentXzfs(params) {
......
......@@ -13,11 +13,11 @@ export function getBillIndustry(params) {
}
// 涉华法案统计
export function getBillCount() {
export function getBillCount(params) {
return request({
method: 'GET',
url: `/api/BillOverview/billCount`,
params
})
}
......@@ -65,3 +65,34 @@ export function getHylyList() {
url: `/api/billImpactAnalysis/industry/hylyList`,
})
}
// 获取法案提出部门
/**
* @param {year}
*/
export function getBillPostOrg(params) {
return request({
method: 'GET',
url: `/api/BillOverview/billPostOrg/${params.year}`,
})
}
// 获取关键议员提案
/**
* @param {year}
*/
export function getMemberProposal(params) {
return request({
method: 'GET',
url: `/api/BillOverview/memberProposal/${params.year}`,
})
}
// 获取资源库
export function getBills(params) {
return request({
method: 'GET',
url: `/api/BillOverview/bills`,
params
})
}
\ No newline at end of file
......@@ -12,6 +12,18 @@ export function getBillTimeAnalyze(params) {
})
}
// 修正案次数分析
/**
* @param {id}
*/
export function getBillAmeAnalyzeCount(params) {
return request({
method: 'GET',
url: `/api/billDeepDive/ameAnalyze/count/${params.id}`,
params,
})
}
// 根据法案ID获取党派政治献金
/**
* @param {id, personCongress}
......
......@@ -35,8 +35,8 @@
v-for="(item, index) in backgroundList"
:key="item.id"
>
<div class="id">{{ index + 1 }}</div>
<div class="title">{{ item.bjnr }}</div>
<div class="id">{{ (currentPage - 1) * 10 + index + 1 }}</div>
<div class="title">{{ item.backgroundTitle }}</div>
<div class="share">
<img src="./assets/icons/open.png" alt="打开" />
</div>
......@@ -45,7 +45,7 @@
<div class="box1-main-footer">
<div class="info">
{{
`共有${backgroundList.length}条${
`共有${total}条${
box1BtnActive === 1 ? "涉华" : "全部"
}背景`
}}
......@@ -54,7 +54,9 @@
<el-pagination
background
layout="prev, pager, next"
:total="backgroundList.length"
:total="total"
v-model:current-page="currentPage"
@current-change="handleGetBillBackground"
/>
</div>
</div>
......@@ -80,7 +82,7 @@
:key="index"
>
<div class="left">
<img :src="item.image" alt="" />
<img :src="item.imageUrl || item.image" alt="" />
</div>
<div class="center">
<div class="title">{{ item.sjbt }}</div>
......@@ -157,7 +159,7 @@
/></el-icon>
</div>
<div class="right-box1-main-bottom">
<WordCloudMap :data="wordCloudData" :shape="circle" />
<WordCloudMap :data="wordCloudData" shape="circle" />
</div>
</div>
</div>
......@@ -214,25 +216,26 @@ import {
getBillBackground,
getBillEvent,
getBillPersonAnalyze,
getBillInfoEvent
} from "@/api/bill";
const box1BtnActive = ref(1);
const currentPage = ref(1);
const total = ref(0);
const handleClickBox1Btn = (index) => {
box1BtnActive.value = index;
if (index === 2) {
handleGetBillBackground(false);
} else {
handleGetBillBackground(true);
}
currentPage.value = 1;
handleGetBillBackground();
};
const box2BtnActive = ref(1);
const handleClickBox2Btn = (index) => {
box2BtnActive.value = index;
if (index === 1) {
handleGetBillPersonAnalyze(true);
} else {
handleGetBillPersonAnalyze(false);
} else {
handleGetBillPersonAnalyze(true);
}
};
......@@ -439,27 +442,29 @@ const wordCloudData = [
];
// 获取立法背景内容
const handleGetBillBackground = async (cRelated) => {
const handleGetBillBackground = async () => {
const cRelated = box1BtnActive.value === 1 ? 'Y' : 'N';
const params = {
cRelated: cRelated,
id: window.sessionStorage.getItem("billId"),
currentPage: 0,
currentPage: currentPage.value - 1,
pageSize: 10,
};
try {
const res = await getBillBackground(params);
console.log("立法背景", res);
backgroundList.value = res.data.content;
total.value = res.data.totalElements; // 假设API返回totalElements
} catch (error) {}
};
// 获取相关事件
const handleGetRelatedEvent = async () => {
const params = {
id: 1,
id: window.sessionStorage.getItem("billId"),
};
try {
const res = await getBillEvent(params);
const res = await getBillInfoEvent(params);
console.log("相关事件", res);
eventList.value = res.data;
eventList.value.forEach((item, index) => {
......@@ -488,8 +493,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => {
const res = await getBillPersonAnalyze(params);
console.log("议员相关性分析", res);
personList.value = res.data;
personList.value.forEach((item) => {
item.image = user1;
personList.value.forEach((item, index) => {
const imgList = [user1, user2, user3, user4, user5];
item.image = imgList[index % imgList.length];
item.icon = userIcon;
item.icon1 = userIcon1;
});
......@@ -497,9 +503,9 @@ const handleGetBillPersonAnalyze = async (isOppose) => {
};
onMounted(() => {
handleGetBillBackground(true);
handleGetBillBackground();
handleGetRelatedEvent();
handleGetBillPersonAnalyze(true);
handleGetBillPersonAnalyze(false);
});
</script>
......@@ -572,7 +578,7 @@ onMounted(() => {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-content: flex-start;
.box1-main-item {
width: 544px;
height: 48px;
......@@ -598,11 +604,12 @@ onMounted(() => {
width: 440px;
height: 48px;
line-height: 48px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
color: rgb(59, 65, 75);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
text-align: left;
overflow: hidden;
}
.share {
margin-left: 13px;
......@@ -626,8 +633,8 @@ onMounted(() => {
.info {
height: 22px;
line-height: 22px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
color: rgb(132, 136, 142);
font-family: "Microsoft YaHei";
font-size: 14px;
font-weight: 400;
text-align: left;
......@@ -645,6 +652,7 @@ onMounted(() => {
margin-top: 9px;
margin-left: 23px;
height: 300px;
overflow: auto;
.box2-main-item {
width: 1103px;
height: 60px;
......@@ -659,9 +667,9 @@ onMounted(() => {
width: 64px;
height: 48px;
border-radius: 2px;
image {
width: 100px;
height: 100%;
img {
width: 64px;
height: 48px;
}
}
.center {
......
import * as echarts from 'echarts'
const getMultiLineChart = (dataX, dataY1, dataY2) => {
const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
return {
tooltip: {
trigger: 'axis',
......@@ -9,25 +9,34 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => {
label: {
backgroundColor: '#6a7985'
}
},
formatter: function (params) {
let res = params[0].name + '<br/>';
params.forEach(item => {
res += item.marker + item.seriesName + ': ' + item.value + (item.seriesName === '通过率' ? '%' : '') + '<br/>';
});
return res;
}
},
grid: {
top: '8%',
top: '15%',
right: '5%',
bottom: '5%',
left: '5%',
containLabel: true
},
legend: {
data: ['提出法案', '通过法案'],
data: ['提出法案', '通过法案', '通过率'],
show: true,
top: 0,
icon: 'circle',
textStyle: {
color: 'rgba(95, 101, 108, 1)',
fontFamily: 'Microsoft YaHei',
fontSize: '16px',
fontSize: '14px',
}
},
color: ['#1459bb', '#fa8c16'],
color: ['#1677FF', '#FA8C16', '#D9001B'],
xAxis: [
{
type: 'category',
......@@ -37,44 +46,80 @@ const getMultiLineChart = (dataX, dataY1, dataY2) => {
],
yAxis: [
{
type: 'value'
type: 'value',
position: 'left',
axisLabel: {
color: '#666'
}
},
{
type: 'value',
position: 'right',
min: 0,
max: 100,
interval: 20,
axisLabel: {
formatter: '{value}%',
color: '#666'
},
splitLine: {
show: false
}
}
],
series: [
{
name: '提出法案',
type: 'line',
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(22, 119, 255, 1)' // 起始颜色
color: 'rgba(22, 119, 255, 0.4)' // 起始颜色
}, {
offset: 1,
color: 'rgba(22, 119, 255, 0)' // 结束颜色
}])
},
emphasis: {
focus: 'series'
itemStyle: {
color: '#1677FF'
},
data: dataY1
},
{
name: '通过法案',
type: 'line',
symbol: 'emptyCircle',
symbolSize: 6,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255, 172, 77, 1)' // 起始颜色
color: 'rgba(250, 140, 22, 0.4)' // 起始颜色
}, {
offset: 1,
color: 'rgba(255, 172, 77, 0)' // 结束颜色
color: 'rgba(250, 140, 22, 0)' // 结束颜色
}])
},
emphasis: {
focus: 'series'
itemStyle: {
color: '#FA8C16'
},
data: dataY2
},
{
name: '通过率',
type: 'line',
yAxisIndex: 1,
symbol: 'emptyCircle',
symbolSize: 4,
lineStyle: {
type: 'dashed',
width: 2
},
itemStyle: {
color: '#D9001B'
},
data: dataY3
}
]
}
......
......@@ -11,6 +11,8 @@ const getWordCloudChart = (data) => {
series: [
{
type: "wordCloud",
width: '80%',
height: '80%',
shape: "rect", //
// 其他形状你可以使用形状路径
// 或者自定义路径
......
......@@ -35,7 +35,7 @@
<div class="box2">
<div class="box-header">
<div class="header-left"></div>
<div class="title">辩论投票时长</div>
<div class="title">修正案次数分析</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
......@@ -320,7 +320,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { getBillTimeAnalyze, getBillXj, getBillTotalXj, getBillTp } from "@/api/deepdig";
import { getBillTimeAnalyze, getBillAmeAnalyzeCount, getBillTp } from "@/api/deepdig";
import getBoxPlotChcart from "./utils/boxplot";
import * as echarts from "echarts";
......@@ -563,20 +563,69 @@ const handleGetBillTimeAnalyze = async () => {
return item.lcmc;
});
chartData1.value.dataY = res.data.map(item => {
return [item.zdhs, item.zshs, item.pjs, item.pjx, item.zws, item.dysc];
return [item.zshs, item.pjx, item.zws, item.pjs, item.zdhs, item.dysc];
});
} catch (error) {}
};
// 辩论投票时长
// 修正案次数分析
const handleGetBillAmeAnalyzeCount = async () => {
const params = {
id: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillAmeAnalyzeCount(params);
console.log("修正案次数分析", res);
chartData2.value.dataX = res.data.map(item => {
return item.lcmc;
});
chartData2.value.dataY = res.data.map(item => {
// 兼容多种字段名,防止字段不存在导致图表消失
// 顺序:最小(min), Q1(小), 中位数(median), Q3(大), 最大(max), 该法案(current)
return [
item.zsCount || item.zscs || item.zshs || 0,
item.jsx || item.pjx || 0,
item.zws || item.zwcs || 0,
item.pjs || item.pjcs || 0,
item.zdCount || item.zdcs || item.zdhs || 0,
item.dysc || item.dycs || 0
];
});
} catch (error) {
console.error("修正案次数分析 error", error);
}
};
// 获取投票分析
const handleGetBillVoteAnalyze = async () => {
const params = {
id: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillTp(params);
console.log("投票分析", res);
} catch (error) {
console.error("投票分析 error", error);
}
};
onMounted(async () => {
await handleGetBillTimeAnalyze();
await handleGetBillAmeAnalyzeCount();
await handleGetBillVoteAnalyze();
let chart1 = getBoxPlotChcart(chartData1.value, "天");
setChart(chart1, "chart1");
let chart2 = getBoxPlotChcart(chartData2.value, "小时");
const countLabels = {
max: '最大次数',
q3: '平均次数大',
median: '次数中位数',
q1: '平均次数小',
min: '最小次数',
current: '该法案修正案数量'
};
let chart2 = getBoxPlotChcart(chartData2.value, "次", countLabels);
setChart(chart2, "chart2");
});
</script>
......
const getBoxPlotChcart = (data,unit) => {
const getBoxPlotChcart = (data, unit, labelConfig = {}) => {
const labels = {
max: labelConfig.max || '最大耗时',
q3: labelConfig.q3 || '平均耗时大',
median: labelConfig.median || '耗时中位数',
q1: labelConfig.q1 || '平均耗时小',
min: labelConfig.min || '最小耗时',
current: labelConfig.current || '该法案耗时'
};
let option = {
// title: [
// {
......@@ -11,6 +20,22 @@ const getBoxPlotChcart = (data,unit) => {
trigger: 'item',
axisPointer: {
type: 'shadow'
},
formatter: function (params) {
if (params.seriesType === 'scatter') {
return `${params.name}<br/>${labels.current}: ${params.data[1]} ${unit}`;
}
const { name, data } = params;
let tip = `${name}<br/>`;
tip += `${labels.max}: ${data[5]} ${unit}<br/>`;
tip += `${labels.q3}: ${data[4]} ${unit}<br/>`;
tip += `${labels.median}: ${data[3]} ${unit}<br/>`;
tip += `${labels.q1}: ${data[2]} ${unit}<br/>`;
tip += `${labels.min}: ${data[1]} ${unit}<br/>`;
if (data[6] !== undefined && data[6] !== null) {
tip += `${labels.current}: ${data[6]} ${unit}`;
}
return tip;
}
},
grid: {
......@@ -48,13 +73,20 @@ const getBoxPlotChcart = (data,unit) => {
{
name: 'boxplot',
type: 'boxplot',
datasetIndex: 1,
data: data.dataY,
},
{
name: 'outlier',
name: labels.current,
type: 'scatter',
datasetIndex: 2
data: data.dataY.map((item, index) => [index, item[5]]),
itemStyle: {
color: '#ff5722'
},
tooltip: {
formatter: function (params) {
return `${params.name}<br/>${labels.current}: ${params.data[1]} ${unit}`;
}
}
}
]
}
......
<template>
<div class="container">
<div class="svg-timeline">
<svg :width="svgWidth" :height="svgHeight">
<svg :viewBox="`0 0 ${svgWidth} ${svgHeight}`" width="100%" height="100%">
<!-- <line
:x1="lines[0].x1 - 100"
:y1="lines[0].y1"
......@@ -31,10 +31,11 @@
stroke-width="2"
/> -->
<line
:x1="lines[0].x1 - 100"
:y1="lines[0].y1"
:x2="lines[0].x1"
:y2="lines[0].y1"
v-if="nodes.length > 0"
:x1="nodes[0].x - 100"
:y1="nodes[0].y"
:x2="nodes[0].x"
:y2="nodes[0].y"
stroke="#e8f2ff"
stroke-width="2"
marker-end="url(#arrow)"
......@@ -68,51 +69,60 @@
v-for="(node, idx) in nodes.slice(0, nodes.length)"
:key="'line' + idx"
:x1="node.x"
:y1="node.y + 2"
:y1="node.y + 4"
:x2="node.x"
:y2="node.dyms ? node.y + 80 : node.y + 50"
:y2="node.y + verticalLineLength"
stroke="#1677ff"
stroke-width="2"
:stroke-width="verticalLineWidth"
/>
<text
<foreignObject
v-for="(node, idx) in nodes"
:key="'actionDate' + idx"
:x="node.x + 10"
:y="node.y + 30"
text-anchor="start"
fill="#1677ff"
font-size="14"
font-weight="600"
:key="'fo-' + idx"
:x="node.x + 15"
:y="node.y + 5"
:width="nodeGapX - 30"
height="100"
style="overflow: visible;"
>
{{ node.actionDate }}
</text>
<div class="node-content" xmlns="http://www.w3.org/1999/xhtml">
<div class="date">{{ node.formattedDate }}</div>
<div class="title">{{ node.actionTitle }}</div>
<div class="votes" v-if="node.voteString">{{ node.voteString }}</div>
</div>
</foreignObject>
<text
v-for="(node, idx) in nodes"
:key="'actionTitle' + idx"
:x="node.x + 10"
:y="node.y + 50"
text-anchor="start"
fill="#3b414b"
font-size="14"
textLength="170"
lengthAdjust="spacing"
font-weight="bold"
v-if="startMonth && nodes.length > 0"
:x="nodes[0].x - 110"
:y="nodes[0].y + 5"
text-anchor="end"
fill="rgb(5, 95, 194)"
font-size="16"
font-weight="700"
>
{{ node.actionTitle.slice(0,24) }}
{{ startMonth }}
</text>
<line
v-if="nodes.length > 0"
:x1="nodes[nodes.length - 1].x"
:y1="nodes[nodes.length - 1].y"
:x2="nodes[nodes.length - 1].row % 2 === 0 ? nodes[nodes.length - 1].x + 100 : nodes[nodes.length - 1].x - 100"
:y2="nodes[nodes.length - 1].y"
stroke="#e8f2ff"
stroke-width="2"
marker-end="url(#arrow)"
/>
<text
v-for="(node, idx) in nodes"
:key="'actionTitle' + idx"
:x="node.x + 10"
:y="node.y + 70"
text-anchor="start"
fill="#84888e"
font-size="14"
v-if="endMonth && nodes.length > 0"
:x="nodes[nodes.length - 1].row % 2 === 0 ? nodes[nodes.length - 1].x + 110 : nodes[nodes.length - 1].x - 110"
:y="nodes[nodes.length - 1].y + 5"
:text-anchor="nodes[nodes.length - 1].row % 2 === 0 ? 'start' : 'end'"
fill="rgb(5, 95, 194)"
font-size="16"
font-weight="700"
>
{{ node.dyms ? node.dyms : "" }}
{{ endMonth }}
</text>
</svg>
</div>
......@@ -134,26 +144,55 @@ export default {
// ],
maxPerRow: 5,
nodeGapX: 200,
nodeGapY: 100
nodeGapY: 180,
leftMargin: 150,
verticalLineLength: 80,
verticalLineWidth: 1
};
},
computed: {
startMonth() {
if (this.sortedDataList.length === 0) return '';
const date = new Date(this.sortedDataList[0].actionDate);
return `${date.getFullYear()}${date.getMonth() + 1}月`;
},
endMonth() {
if (this.sortedDataList.length === 0) return '';
const date = new Date(this.sortedDataList[this.sortedDataList.length - 1].actionDate);
return `${date.getFullYear()}${date.getMonth() + 1}月`;
},
sortedDataList() {
if (!this.dataList) return [];
// Clone and sort by date ascending (Old -> New)
return [...this.dataList].sort((a, b) => new Date(a.actionDate) - new Date(b.actionDate));
},
nodes() {
// 计算每个节点的坐标(蛇形)
return this.dataList.map((item, idx) => {
return this.sortedDataList.map((item, idx) => {
const row = Math.floor(idx / this.maxPerRow);
const col = idx % this.maxPerRow;
let x, y;
const leftMargin = 10; // 你可以自定义这个值
// const leftMargin = 150; // 你可以自定义这个值
if (row % 2 === 0) {
x = leftMargin + col * this.nodeGapX + 50;
x = this.leftMargin + col * this.nodeGapX + 50;
} else {
x = leftMargin + (this.maxPerRow - 1 - col) * this.nodeGapX + 50;
x = this.leftMargin + (this.maxPerRow - 1 - col) * this.nodeGapX + 50;
}
// 节点纵坐标起始值
y = 60 + row * this.nodeGapY;
return { ...item, x, y, row };
// Format Date: 2025-07-04 -> 7月4日
const dateObj = new Date(item.actionDate);
const formattedDate = `${dateObj.getMonth() + 1}${dateObj.getDate()}日`;
// Format Votes
let voteString = '';
if (item.agreeVote !== null && item.disagreeVote !== null) {
voteString = `${item.agreeVote}票赞成 : ${item.disagreeVote}票反对`;
}
return { ...item, x, y, row, formattedDate, voteString };
});
},
lines() {
......@@ -194,7 +233,7 @@ export default {
console.log("prev", prev);
// 判断是否是行尾转折点
const isTurnPoint = i % 5 === 0;
const isTurnPoint = i % this.maxPerRow === 0;
if (isTurnPoint) {
// 计算半圆路径
......@@ -221,7 +260,7 @@ export default {
return path;
},
svgWidth() {
return this.maxPerRow * this.nodeGapX + 100;
return this.leftMargin + this.maxPerRow * this.nodeGapX + 50;
},
svgHeight() {
// SVG高度
......@@ -240,7 +279,7 @@ export default {
align-items: center;
}
.svg-timeline {
width: 1000px;
width: 100%;
// background-size: 100% 100%;
// position: relative;
// .title {
......@@ -264,5 +303,40 @@ export default {
// height: 24px;
// }
// }
.node-content {
font-family: Microsoft YaHei, sans-serif;
text-align: left;
padding-left: 4px;
.date {
color: rgb(5, 95, 194);
font-weight: 700;
font-size: 14px;
line-height: 22px;
margin-bottom: 0px;
margin-top: 6px;
}
.title {
color: rgb(59, 65, 75);
font-weight: 700;
font-size: 14px;
line-height: 22px;
margin-bottom: 0px;
white-space: nowrap; /* Keep text on one line */
overflow: hidden; /* Hide overflow */
text-overflow: ellipsis; /* Show ... for overflow */
width: 100%; /* Ensure it takes full width of container */
display: block; /* Block level for ellipsis to work */
}
.votes {
color: rgb(95, 101, 108);
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
</style>
......@@ -31,9 +31,10 @@
<div class="box1-right-item">
<div class="item-left">相关领域:</div>
<div class="item-right1">
<div class="right1-item">跨境电商</div>
<!-- <div class="right1-item">跨境电商</div>
<div class="right1-item">新能源产业</div>
<div class="right1-item">半导体产业</div>
<div class="right1-item">半导体产业</div> -->
<div class="right1-item" v-for="item in basicInfo.hylyList" :key="item">{{ item }}</div>
</div>
</div>
<div class="box1-right-item">
......@@ -81,7 +82,7 @@
<div class="box-header">
<div class="header-left"></div>
<div class="title">法案进展</div>
<div class="header-btn-box">
<!-- <div class="header-btn-box">
<div class="btn" @click="handleClcikBox2Btn(1)">
<el-badge :value="warningNum">
<el-button type="primary" plain v-if="box2BtnActive === 1">最新进展</el-button>
......@@ -92,7 +93,7 @@
<el-button type="primary" plain v-if="box2BtnActive === 2">前期进展</el-button>
<el-button type="info" plain v-else>前期进展</el-button>
</div>
</div>
</div> -->
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
......@@ -104,8 +105,8 @@
</div>
<div class="box2-main">
<div class="box2-main-center">
<STimeline v-if="box2BtnActive == 2" :dataList="timelineData" />
<div class="box2-center-item-box" v-if="box2BtnActive == 1">
<STimeline :dataList="timelineData" />
<!-- <div class="box2-center-item-box" v-if="box2BtnActive == 1">
<div class="box2-center-item" v-for="(item, index) in progressList" :key="index">
<div class="tip" :class="{ tipActive: item.fxdj }"></div>
<div class="date">{{ item.actionDate }}</div>
......@@ -128,7 +129,7 @@
<el-icon size="22" color="#777"><ArrowRightBold /></el-icon>
</div>
</div>
</div>
</div> -->
</div>
</div>
<div class="box2-footer">
......@@ -210,15 +211,15 @@
<div
class="tag-box"
:class="{
status0: tag.status === 0,
status1: tag.status === 1,
status2: tag.status === 2,
status3: tag.status === 3
status0: index === 0 || index === 4,
status1: index === 1 || index === 5,
status2: index === 2 || index === 6,
status3: index === 3 || index === 7
}"
v-for="(tag, index) in tagList"
v-for="(tag, index) in curPerson.tagList"
:key="index"
>
{{ tag.title }}
{{ tag }}
</div>
</div>
<div class="right-main-box3">
......@@ -231,13 +232,13 @@
<div class="right-main-box3-main">
<el-timeline style="max-width: 500px">
<el-timeline-item
:timestamp="item.sjsj"
:timestamp="item.newsDate"
placement="top"
v-for="(item, index) in personEventList"
v-for="(item, index) in curPerson.newsList"
:key="index"
>
<div class="timeline-content">
{{ item.sjnr }}
{{ item.newsContent }}
</div>
</el-timeline-item>
<!-- <el-timeline-item timestamp="2018/4/3" placement="top">
......@@ -324,14 +325,14 @@
<div class="inner-right-main">
<el-timeline style="max-width: 840px">
<el-timeline-item
:timestamp="item.sjsj"
:timestamp="item.newsDate"
placement="top"
v-for="(item, index) in personEventList"
v-for="(item, index) in curPerson.newsList"
:key="index"
>
<div class="timeline-content1">
<div class="text">
{{ item.sjnr }}
{{ item.newsContent }}
</div>
<div class="pic">
<img src="./assets/imgs/img1.png" alt="" />
......@@ -544,6 +545,7 @@ const handleGetBasicInfo = async () => {
const res = await getBillInfo(params);
console.log("基本信息", res);
basicInfo.value = res.data
basicInfo.value.stageList.reverse()
} catch (error) {
console.error(error);
}
......@@ -552,24 +554,24 @@ const handleGetBasicInfo = async () => {
const warningNum = ref(0);
// 法案进展 获取最新进展
const handleGetBillEvent = async () => {
warningNum.value = 0;
const params = {
id: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillEvent(params);
console.log("最新进展", res);
progressList.value = res.data;
progressList.value.forEach(item => {
if (item.fxdj) {
warningNum.value++;
}
});
} catch (error) {
console.error(error);
}
};
// const handleGetBillEvent = async () => {
// warningNum.value = 0;
// const params = {
// id: window.sessionStorage.getItem("billId")
// };
// try {
// const res = await getBillEvent(params);
// console.log("最新进展", res);
// progressList.value = res.data;
// progressList.value.forEach(item => {
// if (item.fxdj) {
// warningNum.value++;
// }
// });
// } catch (error) {
// console.error(error);
// }
// };
// 法案进展 获取前期进展 --也是提出人左上角列表
const handleGetBillDyqk = async () => {
......@@ -601,9 +603,26 @@ const curPerson = ref({});
const personEventList = ref([]);
// 提出人 --动议id
const handleGetBillPerson = async id => {
// const handleGetBillPerson = async id => {
// const params = {
// id: id
// };
// try {
// const res = await getBillPerson(params);
// console.log("提出人", res);
// personList.value = res.data;
// box3BtnActive.value = res.data.length ? res.data[0].name : "";
// curPerson.value = res.data.length ? res.data[0] : {};
// personEventList.value = res.data.length ? res.data[0].eventList : [];
// } catch (error) {
// console.error(error);
// }
// };
// 法案提出人
const handleGetBillPerson = async () => {
const params = {
id: id
billId: window.sessionStorage.getItem("billId")
};
try {
const res = await getBillPerson(params);
......@@ -619,8 +638,9 @@ const handleGetBillPerson = async id => {
onMounted(() => {
handleGetBasicInfo();
handleGetBillEvent();
// handleGetBillEvent();
handleGetBillDyqk();
handleGetBillPerson();
});
</script>
......@@ -695,11 +715,11 @@ onMounted(() => {
.box1-right {
margin-left: 31px;
margin-top: 5px;
width: 623px;
// width: 623px;
height: 350px;
.box1-right-item {
display: flex;
margin-bottom: 24px;
margin-bottom: 21px;
.item-left {
width: 100px;
height: 14px;
......@@ -722,7 +742,23 @@ onMounted(() => {
}
.item-right1 {
display: flex;
align-items: center;
width: 700px;
height: 40px;
overflow-x: auto;
overflow-y: hidden;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #e1e1e1;
}
&::-webkit-scrollbar-track {
background: transparent;
}
.right1-item {
flex-shrink: 0;
margin-right: 10px;
padding: 1px 8px;
box-sizing: border-box;
......@@ -774,10 +810,14 @@ onMounted(() => {
text-align: center;
position: relative;
.step-box {
padding: 0 10px 0 20px;
padding: 4px 10px;
color: #333;
position: relative;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 14px;
margin-left: 10px;
.right-arrow {
position: absolute;
right: -21px;
......@@ -792,7 +832,7 @@ onMounted(() => {
}
}
.step-box-active {
padding: 0 12px;
padding: 4 10px;
color: #fff;
background: #ce4f51;
position: relative;
......@@ -851,11 +891,13 @@ onMounted(() => {
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
.box2-main {
margin-top: 10px;
height: calc(100% - 70px); // Subtract header height
width: 100%;
.box2-main-center {
margin-left: 23px;
border-top: 1px solid rgba(243, 243, 244, 1);
// width: 100%;
height: 300px;
width: calc(100% - 46px); // Subtract margin
height: 100%;
// background: orange;
.box2-center-item {
display: flex;
......@@ -1082,10 +1124,10 @@ onMounted(() => {
}
}
.right-main-box2 {
width: 576px;
height: 150px;
// width: 576px;
// height: 150px;
box-sizing: border-box;
padding: 10px 26px;
padding: 22px 26px;
// border-bottom: 1px solid rgb(243, 243, 244);
// display: flex;
// flex-wrap: wrap;
......@@ -1140,7 +1182,7 @@ onMounted(() => {
}
.right-main-box3-main {
margin-top: 18px;
height: 299px;
height: 412px;
overflow-y: auto;
}
.right-main-box3-footer {
......@@ -1330,6 +1372,10 @@ onMounted(() => {
color: var(--btn-active-text-color);
}
}
.inner-right-main {
height: 860px;
overflow: auto;
}
}
}
}
......
......@@ -16,9 +16,15 @@
</div>
<div class="left-top">
<el-select v-model="curBill" placeholder="请选择" style="width: 240px" @change="handleChangeBill">
<el-option v-for="item in billList" :key="item.value" :label="item.label" :value="item.id" />
<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" />
<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" style="width: 240px" placeholder="搜索条款" />
<div class="icon">
......@@ -28,32 +34,32 @@
</div>
<div class="left-main">
<div class="left-main-item" v-for="(term, index) in mainTermsList" :key="index">
<div class="id">{{ index + 1 }}</div>
<div class="id">{{ (currentPage - 1) * pageSize + index + 1 }}</div>
<div class="info">
<div class="title">
<span class="title-active">{{ term.header }}</span>
{{ term.title }}
<span class="title-active">{{ term.tkxh }}条.</span>
{{ term.fynr }}
</div>
<div class="content">
<span class="content-active">{{ term.headerEn }}</span>
{{ term.content }}
<span class="content-active">Sec.{{ term.tkxh }}</span>
{{ term.ywnr }}
</div>
</div>
<div class="tags-box">
<div
class="tag"
v-for="(val, idx) in term.tags"
v-for="(val, idx) in (term.hylyList || []).slice(0, 2)"
:key="idx"
:class="{
tag1: val.status === 1,
tag2: val.status === 2,
tag3: val.status === 3,
tag4: val.status === 4,
tag5: val.status === 5,
tag6: val.status === 6
'tag1': val === '人工智能',
'tag2': val === '新一代信息技术' || !['人工智能', '政治', '经济', '军事', '科技'].includes(val),
'tag3': val === '政治',
'tag4': val === '经济',
'tag5': val === '军事',
'tag6': val === '科技'
}"
>
{{ val.name }}
{{ val }}
</div>
</div>
<div class="open">
......@@ -63,10 +69,17 @@
</div>
<div class="left-footer">
<div class="left-footer-text">
{{ "共96条涉华条款" }}
{{ `共${total}条${checkedValue ? "涉华" : ""}条款` }}
</div>
<div class="left-footer-right">
<el-pagination background layout="prev, pager, next" :total="96" />
<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>
......@@ -137,16 +150,12 @@ import { getBillContentId, getBillContentTk, getBillContentXzfs, getBillHyly } f
const curBill = ref("");
const curBillId = ref(null);
const billList = ref([
{
value: "公法(2025年7月4日)",
label: "公法(2025年7月4日)"
},
{
value: "公法(2025年7月8日)",
label: "公法(2025年7月8日)"
}
]);
const checkedValue = ref(false);
const searchValue = ref("");
const billList = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const mainTermsList = ref([
{
......@@ -351,7 +360,13 @@ const setChart = (option, chartId) => {
// 切换原文
const handleChangeBill = val => {
curBillId.value = val;
curBill.value = val;
const item = billList.value.find(item => item.value === val);
if (item) {
curBillId.value = item.id;
currentPage.value = 1;
handleGetBillContentTk(checkedValue.value ? "Y" : "N");
}
};
// 获取法案id列表
......@@ -369,22 +384,69 @@ const handleGetBillList = async () => {
id: item.ywid
};
});
curBill.value = billList.value[0].label;
if (billList.value.length > 0) {
curBill.value = billList.value[0].value;
curBillId.value = billList.value[0].id;
}
} catch (error) {}
};
const handleChangeCheckbox = val => {
currentPage.value = 1;
handleGetBillContentTk(val ? "Y" : "N");
};
const handleCurrentChange = val => {
currentPage.value = val;
handleGetBillContentTk(checkedValue.value ? "Y" : "N");
};
// 根据原文ID获取条款列表
const handleGetBillContentTk = async cRelated => {
const params = {
id: curBillId.value,
billid: window.sessionStorage.getItem("billId"),
id: curBill.value,
cRelated: cRelated,
currentPage: 0,
pageSize: 10
currentPage: currentPage.value - 1,
pageSize: pageSize.value
};
try {
const res = await getBillContentTk(params);
console.log("条款内容", res);
mainTermsList.value = res.data.content.map(item => {
// 处理 fynr
if (item.fynr) {
const matchComplex = item.fynr.match(/^(?:正文内容[::]\s*)?[“"]?\s*第\s*([0-9a-zA-Z]+)\s*[条节][::\.\。]?[”"]?\s*/);
if (matchComplex) {
// 匹配 "第xxx条"、"正文内容:第xxx条"、"“第xxx条" 等
if (!item.tkxh) {
item.tkxh = matchComplex[1];
}
item.fynr = item.fynr.replace(matchComplex[0], "");
} else {
// 匹配 "xxx."
item.fynr = item.fynr.replace(/^\d+\.\s*/, "");
}
}
// 处理 ywnr
if (item.ywnr) {
const matchSec = item.ywnr.match(/^(?:SEC\.|SECTION)\s*([0-9a-zA-Z]+)[\.:]?\s*/i);
if (matchSec) {
if (!item.tkxh) {
item.tkxh = matchSec[1];
}
item.ywnr = item.ywnr.replace(matchSec[0], "");
} else {
item.ywnr = item.ywnr.replace(/^\d+\.\s*/, "");
}
}
// 处理 tkxh 末尾的点
if (item.tkxh) {
item.tkxh = item.tkxh.replace(/\.$/, "");
}
return item;
});
total.value = res.data.totalElements;
} catch (error) {}
};
......@@ -430,7 +492,7 @@ const handleGetBillHyly = async () => {
onMounted(async () => {
await handleGetBillList();
handleGetBillContentTk(false);
handleGetBillContentTk("N");
await handleGetBillContentXzfs();
await handleGetBillHyly();
let chart1 = getPieChart(chart1Data.value, chart1ColorList.value);
......@@ -528,6 +590,7 @@ onMounted(async () => {
border-radius: 2px;
background: rgba(255, 255, 255, 1);
display: flex;
position: relative;
.id {
margin-top: 20px;
margin-left: 15px;
......@@ -543,7 +606,7 @@ onMounted(async () => {
.info {
margin-left: 13px;
margin-top: 15px;
width: 813px;
width: 780px;
.title {
height: 14px;
color: rgba(59, 65, 75, 1);
......@@ -577,20 +640,19 @@ onMounted(async () => {
}
}
.tags-box {
margin-left: 20px;
margin-top: 21px;
width: 160px;
height: 22px;
display: flex;
justify-content: flex-end;
justify-content: right;
align-items: center;
flex: 1;
margin-right: 50px;
.tag {
height: 18px;
text-align: right;
line-height: 18px;
padding: 0 8px;
padding: 1px 8px;
border-radius: 4px;
margin-left: 5px;
font-size: 12px;
font-family: Microsoft YaHei;
font-family: "Microsoft YaHei";
font-size: 14px;
font-weight: 400;
}
......@@ -626,8 +688,9 @@ onMounted(async () => {
}
}
.open {
margin-left: 10px;
margin-top: 22px;
position: absolute;
top: 22px;
right: 23px;
width: 20px;
height: 20px;
img {
......
......@@ -4,6 +4,7 @@ const getPieChart = (data,colorList) => {
series: [
{
type: 'pie',
minAngle: 28,
radius: [70, 100],
height: '100%',
left: 'center',
......@@ -38,7 +39,9 @@ const getPieChart = (data,colorList) => {
? params.labelRect.x
: params.labelRect.x + params.labelRect.width;
return {
labelLinePoints: points
labelLinePoints: points,
hideOverlap: false,
moveOverlap: 'shiftY'
};
},
data: data
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论