提交 a4b931c9 authored 作者: 张烨's avatar 张烨

Merge branch 'master' into zy-dev

---
alwaysApply: true
---
# Overview
Insert overview text here. The agent will only see this should they choose to apply the rule.
```markdown
---
alwaysApply: true
---
# 前端开发(Cursor 专用执行要点)
## 1. HTML / 模板
- 属性命名:统一用小写-中线分割。动态 class 也用这种格式,使用单引号:`:class="{ 'xxx-xxx': isXxx }"`
- v-for 必须有唯一 key,`v-for + :key`(如`:key="item.id"`),禁止仅凭索引。示例:`<li v-for="item in list" :key="item.id"></li>`
- 禁止 v-if 与 v-for 同时在同一节点;如需筛选请用计算属性/方法过滤后再 v-for
- 无内容组件或标签必须自闭合,如 `<MyComp />` `<img />` `<input />`
- HTML 属性用双引号,动态属性外已双引号则用单引号
- 模板表达式保持极简,只写取值或很简单的显示;任何有数据处理(如数组/字符串操作、三元、函数执行等)必须抽到方法或计算属性完成
## 2. CSS
- 样式模块优先通用组件,确需定制严格按设计来写
- 全部样式变量统一来源于全局(如 `:root { --color-main-primary: #055fC2; ... }`)
- `<style scoped>` 默认加 scoped
- 修改子组件样式用 `:deep()`(Vue3 推荐写法),如:`.xxx :deep(.el-xxx) { ... }`
- 严禁页面大量内联 style,样式尽量写到 class 或 :style 绑定表达式
## 3. 文件系统
- 文件夹采用小驼峰,无数字/无关字符
- 每个业务模块 API 独立放模块对应文件夹
- 静态资源分全局 assets 与模块 assets:全局图片 assets/images,icon 建议 SVG,模块区分子文件夹
## 4. 组件
- 组件名大驼峰(PascalCase)。组件类型目录小驼峰,具体组件目录(如 AreaTag)大驼峰
- 单文件组件名 index.vue,多组件文件按具体功能如 LeftBtn.vue
- 优先用通用组件,业务组件只能放业务模块,不得放全局
## 5. JS/TS
- 命名统一规范
- 变量:小驼峰 userName、isVisible
- 常量:大写+下划线,如 MAX_COUNT
- 枚举:枚举名大驼峰,枚举值全大写+下划线 enum Status { SUCCESS = 'SUCCESS' }
- 普通函数:小驼峰+动词前缀 getUser/formatTime
- 事件函数:小驼峰+handle/on handleSubmit/onClose
- 布尔型函数:is/has/should + 大驼峰,如 isValid()
- 异步强制 async/await,catch 错误。禁止 Promise.then 链式嵌套(如遇回调 hell 必须拆分/抽象重写)
- 一律用 const/let 替换 var,优先 const
- 注释:
- 单行注释:后加空格,如 `// 注释内容`
- 方法/复杂模块加 /** JSDoc 注释 */
## 6. 其它
- 所有复杂渲染逻辑逻辑一律抽到计算属性或方法,模板结构保持简洁
...@@ -14,20 +14,20 @@ export function getBillIndustry(params) { ...@@ -14,20 +14,20 @@ export function getBillIndustry(params) {
// 涉华法案统计 // 涉华法案统计
export function getBillCount(params) { export function getBillCount(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/BillOverview/billCount`, url: `/api/BillOverview/billCount`,
params params
}) })
} }
// 获取关键条款 // 获取关键条款
export function getBillOverviewKeyTK() { export function getBillOverviewKeyTK() {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/BillOverview/keyTk`, url: `/api/BillOverview/keyTk`,
}) })
} }
// 获取热门法案列表 // 获取热门法案列表
...@@ -104,22 +104,32 @@ export function getBillPostOrg(params) { ...@@ -104,22 +104,32 @@ export function getBillPostOrg(params) {
}) })
} }
// 获取关键议员提案 // 获取涉华法案进展分布
/** /**
* @param {year} * @param {year}
*/ */
export function getMemberProposal(params) { export function getBillProcess(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/BillOverview/memberProposal/${params.year}`, url: `/bill/BillOverview/billsProcess/${params.year}`,
}) })
} }
// 获取资源库 // 获取资源库法案
export function getBills(params, signal) { export function getBills(params, signal) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/BillOverview/bills`, url: `/bill/BillOverview/bills`,
params,
signal
})
}
// 获取资源库国会议员
export function getBillsPerson(params, signal) {
return request({
method: 'GET',
url: `/bill/BillOverview/billsPerson`,
params, params,
signal signal
}) })
......
<template>
<div class="box3-item" @click="handleToNewsAnalysis(news)">
<div class="left">
<img :src="news[props.img] ? news[props.img] : DefaultIconNews" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="title"><span class="text-inner">{{ news[props.title] }}</span></div>
<div class="time">{{ news[props.from] }}</div>
</div>
<div class="right-footer">{{ news[props.content] }}</div>
</div>
</div>
</template>
<script setup>
import DefaultIconNews from "@/assets/icons/default-icon-news.png";
const props = defineProps({
// 新闻列表数据
news: {
type: Object,
default: () => { }
},
img: {
type: String,
default: 'img'
},
title: {
type: String,
default: "title"
},
from: {
type: String,
default: "from"
},
content: {
type: String,
default: "content"
},
});
const emit = defineEmits(['item-click', 'more-click']);
const handleToNewsAnalysis = (item, index) => {
emit('item-click', item, index)
};
</script>
<style lang="scss" scoped>
.box3-item {
display: flex;
align-items: center;
height: 78px;
margin: 0px 21px;
cursor: pointer;
&:hover {
.right-top .title {
color: rgb(5, 95, 194) !important;
font-weight: 700;
}
.right-top .text-inner {
border-bottom-color: rgb(5, 95, 194) !important;
}
}
}
.left {
width: 97px;
// flex-shrink: 0;
height: 72px;
img {
width: 100%;
height: 100%;
border-radius: 4px;
}
}
.right {
flex: 1;
min-width: 0;
margin-left: 20px;
.right-top {
display: flex;
justify-content: space-between;
.title {
// width: 500px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: 'Source Han Sans CN';
font-size: 16px;
font-weight: 700;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.text-inner {
border-bottom: 1px solid transparent;
}
}
.time {
text-align: right;
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: 'Source Han Sans CN';
font-size: 14px;
font-weight: 400;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.right-footer {
height: 48px;
/* 调整为2行的高度:24px × 2 = 48px */
color: rgba(59, 65, 75, 1);
font-family: 'Source Han Sans CN';
font-size: 16px;
font-weight: 400;
line-height: 24px;
overflow: hidden;
display: -webkit-box;
/* 关键 */
-webkit-line-clamp: 2;
/* 显示2行 */
-webkit-box-orient: vertical;
/* 垂直方向排列 */
text-overflow: ellipsis;
/* 第二行省略号 */
white-space: normal;
/* 改为 normal */
word-break: break-word;
/* 允许单词换行 */
}
}
</style>
\ No newline at end of file
<template>
<el-space @click="onClick(person)">
<ElAvatar :size="48" :src="person[img]" alignment="center" />
<el-space :size="0" direction="vertical" alignment="flex-start">
<div class="text-header">{{ person[name] }}</div>
<div class="person-position">{{ person[position] }}</div>
</el-space>
</el-space>
</template>
<script setup>
import '@/styles/common.scss'
import { ElSpace } from 'element-plus';
const props = defineProps({
// 新闻列表数据
person: {
type: Object,
default: () => { }
},
img: {
type: String,
default: 'avatarUrl'
},
name: {
type: String,
default: "name"
},
position: {
type: String,
default: "position"
},
introduction: {
type: String,
default: "introduction"
},
});
const emit = defineEmits(['item-click', 'more-click']);
const onClick = (item) => {
emit('item-click', item)
console.log(item)
};
</script>
<style lang="scss" scoped>
.person-position {
font-size: 16px;
line-height: 24px;
color: var(--text-primary-65-color);
}
</style>
\ No newline at end of file
<script setup>
import { ElSpace, ElButton } from 'element-plus';
import "@/styles/main.css"
import { copyToClipboardThenTip } from '@/utils/systemUtils'
const colors = [
{ title: "黑色90% / 主标题文字颜色", name: "--text-primary-90-color" },
{ title: "黑色80% / 小标题文字颜色", name: "--text-primary-80-color" },
{ title: "黑色65% / 正文颜色", name: "--text-primary-65-color" },
{ title: "黑色50%", name: "--text-primary-50-color" },
{ title: "黑色10%", name: "--bg-black-10" },
{ title: "黑色5% / 分割线颜色", name: "--bg-black-5" },
{ title: "黑色2% / 灰色背景色", name: "--bg-black-2" },
{ title: "白色主色", name: "--bg-white-100" },
{ title: "主色", name: "--color-primary-100" },
]
function copyColorVar(item) {
const color = `var(${item.name})`
copyToClipboardThenTip(color)
}
</script>
<template>
<el-space direction="vertical" alignment="flex-start">
<div class="text-title-2">颜色</div>
<el-space>
<el-button v-for="(item, index) in colors" :key="index" :color="`var(${item.name})`"
v-on:click="copyColorVar(item)">
{{ item.title }}
</el-button>
</el-space>
</el-space>
</template>
<style scoped></style>
\ No newline at end of file
<script setup lang="ts">
import "@/styles/container.scss"
import TextStyle from './textStyle.vue';
import ConstStyle from './constStyle.vue';
import { ElScrollbar, ElSpace } from "element-plus";
</script>
<template>
<el-scrollbar>
<div class="common-page">
<el-space direction="vertical" alignment="flex-start">
<div class="text-title-0-show">开发样式</div>
<div class="text-title-1-show">样式变量</div>
<const-style></const-style>
<div class="text-title-1-show">文字样式</div>
<text-style></text-style>
</el-space>
</div>
</el-scrollbar>
</template>
\ No newline at end of file
<template>
<table style="width: 100%; border-collapse: collapse; border: 1px solid #ebeef5;">
<!-- 表头 -->
<thead>
<tr class="text-title-2">
<th> 名称</th>
<th> 类型名称</th>
<th> 操作</th>
</tr>
</thead>
<!-- 表格内容 -->
<tbody>
<template v-for="(row, index) in tableData" :key="index">
<!-- 隔行变色效果 -->
<tr :style="{
height: '60px',
backgroundColor: index % 2 === 1 ? '#fafafa' : 'transparent'
}">
<!-- 名称列 -->
<td style="padding: 12px; border: 1px solid #ebeef5;">
<div :class="row.className">
{{ row.name }}
</div>
</td>
<!-- 类型名称列(带复制功能) -->
<td style="padding: 12px; border: 1px solid #ebeef5;">
<div @click="copyToClipboardThenTip(row.className)" style="cursor: pointer;">
<span>{{ row.className }}</span>
</div>
</td>
<!-- 操作列 -->
<td style="padding: 12px; border: 1px solid #ebeef5; text-align: center;">
<el-button type="primary" link @click="copyToClipboardThenTip(row.className)">
<el-icon>
<DocumentCopy />
</el-icon>
复制
</el-button>
</td>
</tr>
</template>
</tbody>
</table>
</template>
<script setup>
import { ref } from 'vue'
import { DocumentCopy } from '@element-plus/icons-vue'
import "@/styles/common.scss"
import { copyToClipboardThenTip } from '@/utils/systemUtils'
// 表格数据
const tableData = ref([
// { name: '0级标题', className: 'text-title-0' },
{ name: '0级标题-加粗', className: 'text-title-0-bold' },
{ name: '0级标题-综艺', className: 'text-title-0-show' },
// { name: '1级标题', className: 'text-title-1' },
{ name: '1级标题-加粗', className: 'text-title-1-bold' },
{ name: '1级标题-综艺', className: 'text-title-1-show' },
{ name: '2级标题', className: 'text-title-2' },
{ name: '2级标题-加粗', className: 'text-title-2-bold' },
{ name: '2级标题-综艺', className: 'text-title-2-show' },
{ name: '3级标题', className: 'text-title-3' },
{ name: '3级标题-加粗', className: 'text-title-3-bold' },
{ name: '3级标题-综艺', className: 'text-title-3-show' },
{ name: '正文', className: 'text-regular' },
{ name: '正文-加粗', className: 'text-bold' },
{ name: '正文-紧凑', className: 'text-compact' },
{ name: '正文-紧凑-加粗', className: 'text-compact-bold' },
{ name: '1级提示文字', className: 'text-tip-1' },
{ name: '1级提示文字-加粗', className: 'text-tip-1-bold' },
{ name: '2级提示文字', className: 'text-tip-2' },
{ name: '2级提示文字-加粗', className: 'text-tip-2-bold' },
{ name: '3级提示文字', className: 'text-tip-3' },
])
</script>
<style scoped></style>
\ No newline at end of file
//企业主页 //企业主页
const companyPages = () => import('@/views/companyPages/index.vue') const companyPages = () => import('@/views/companyPages/index.vue')
const companyPagesRoutes = [ const companyPagesRoutes = [
// 智库系统的主要路由 // 智库系统的主要路由
{ {
...@@ -9,7 +11,7 @@ const companyPagesRoutes = [ ...@@ -9,7 +11,7 @@ const companyPagesRoutes = [
component: companyPages, component: companyPages,
meta: { meta: {
title: "企业主页", title: "企业主页",
dynamicTitle: true dynamicTitle: true
} }
}, },
......
//样式主页
const StylePages = () => import("@/components/devStyle/components/index.vue");
const stylePagesRoutes = [
// 智库系统的主要路由
{
path: "/devStylePages",
name: "devStylePages",
component: StylePages,
meta: {
title: "开发样式",
dynamicTitle: true
}
},
]
export default stylePagesRoutes
\ No newline at end of file
...@@ -10,12 +10,141 @@ ...@@ -10,12 +10,141 @@
.flex-display{ .flex-display{
display: flex; display: flex;
} }
/***文本样式***/
.text-base{ .text-base{
color: var(--color-primary-90);
}
//0级标题
.text-title-0{
@extend .text-base;
color: var(--color-primary-90);
font-size: 32px;
}
.text-title-0-bold{
@extend .text-title-0;
font-weight: Bold;
}
.text-title-0-show{
@extend .text-title-0;
font-size: 48px;
font-family: "YouSheBiaoTiHei";
}
//1级标题
.text-title-1{
@extend .text-base;
color: var(--color-primary-90);
font-size: 24px;
}
.text-title-1-bold{
@extend .text-title-1;
font-weight: Bold;
}
.text-title-1-show{
@extend .text-title-1;
font-size: 30px;
font-family: "YouSheBiaoTiHei";
}
//2级标题
.text-title-2{
@extend .text-base;
color: var(--color-primary-90);
font-size: 20px;
line-height:26px;
}
.text-title-2-bold{
@extend .text-title-2;
font-weight: Bold;
}
.text-title-2-show{
@extend .text-title-2;
font-size: 24px;
font-family: "YouSheBiaoTiHei";
line-height: normal !important; // 取消继承的行高
}
//3级标题
.text-title-3{
@extend .text-base;
color: var(--color-primary-90);
font-size: 18px;
line-height:24px;
}
.text-title-3-bold{
@extend .text-title-3;
font-weight: Bold;
}
.text-title-3-show{
@extend .text-title-3;
font-size: 20px;
font-family: "YouSheBiaoTiHei";
line-height: normal !important; // 取消继承的行高
}
//正文
.text-regular{
@extend .text-base;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height:30px;
color: var(--text-primary-80-color);
} }
.text-header { //正文-加粗
.text-bold{
@extend .text-base; @extend .text-base;
font-weight: Bold; font-weight: Bold;
} }
//正文-紧凑
.text-compact{
@extend .text-base;
font-size: 16px;
line-height:24px;
}
.text-compact-bold{
@extend .text-base;
font-size: 16px;
line-height:24px;
font-weight: Bold;
}
//1级提示文字
.text-tip-1{
@extend .text-base;
color: var(--color-primary-90);
font-size: 16px;
line-height:24px;
}
.text-tip-1-bold{
@extend .text-tip-1;
font-weight: Bold;
}
//2级提示文字
.text-tip-2{
@extend .text-base;
color: var(--color-primary-90);
font-size: 14px;
line-height:22px;
}
.text-tip-2-bold{
@extend .text-tip-2;
font-weight: Bold;
}
//3级提示文字
.text-tip-3{
@extend .text-base;
color: var(--color-primary-90);
font-size: 12px;
}
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
/***没有nav下划线***/ /***没有nav下划线***/
.common-descriptions .el-descriptions__label{ .common-descriptions .el-descriptions__label{
@extend .text-header; @extend .text-tip-1-bold;
} }
.common-descriptions .el-descriptions__content{ .common-descriptions .el-descriptions__content{
@extend .text-base @extend .text-tip-1;
color: var(--text-primary-80-color);
} }
\ No newline at end of file
import { ElMessage } from "element-plus";
interface BaseReturn {
status: boolean;
message: string;
}
export async function copyToClipboard(txet: string): Promise<BaseReturn> {
try {
// 使用现代 Clipboard API
await navigator.clipboard.writeText(txet);
console.log("已复制:", txet);
return { status: true, message: "已复制:" + txet };
} catch (err) {
console.error("复制失败:", err);
// 降级方案:使用document.execCommand
const textArea = document.createElement("textarea");
textArea.value = txet;
document.body.appendChild(textArea);
textArea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
return { status: true, message: "已复制:" + txet };
}
} catch (err) {}
document.body.removeChild(textArea);
return { status: false, message: "复制失败,请手动复制" };
}
}
// 复制类型名称到剪贴板
export async function copyToClipboardThenTip(text: string): Promise<void> {
const { status, message } = await copyToClipboard(text);
if (status) {
ElMessage.success(message);
} else {
ElMessage.error(message);
}
}
<template>
<div class="overview-card">
<div class="overview-card-header">
<div class="overview-card-header-left">
<div class="overview-card-header-icon">
<img :src="icon" alt="" />
</div>
<div class="overview-card-header-title">{{ title }}</div>
</div>
<div v-if="$slots.right" class="overview-card-header-right">
<slot name="right" />
</div>
</div>
<div class="overview-card-main">
<slot />
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: ""
},
icon: {
type: String,
default: ""
}
});
</script>
<style lang="scss" scoped>
.overview-card {
height: 450px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
overflow: hidden;
.overview-card-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 27px 0 22px;
.overview-card-header-left {
display: flex;
align-items: center;
.overview-card-header-icon {
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.overview-card-header-title {
margin-left: 19px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.overview-card-header-right {
display: flex;
align-items: center;
justify-content: flex-end;
height: 100%;
}
}
.overview-card-main {
height: calc(100% - 53px);
}
}
</style>
<template>
<div class="resource-library-section">
<div class="home-content-footer-header">
<div class="btn-box">
<div class="btn" :class="{ btnActive: activeTabName === cate.name, disabled: index > 2 }"
v-for="(cate, index) in tabList" :key="index" @click="index <= 2 && handleClickTab(cate)">
{{ cate.name }}
</div>
</div>
</div>
<div class="home-content-footer-main">
<div class="left" v-if="['国会法案', '国会议员', '议员合作关系'].includes(activeTabName)">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">科技领域</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeAreaList" @change="handleAreaChange">
<el-checkbox class="filter-checkbox" label="全部领域"> 全部领域 </el-checkbox>
<el-checkbox v-for="(area, index) in cateKuList" :key="index" :label="area.id" class="filter-checkbox">
{{ area.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box" v-if="activeTabName !== '议员合作关系'">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">党派</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeDpList" @change="handleDpChange">
<el-checkbox v-for="(dp, index) in dpList" :key="index" :label="dp.id" class="filter-checkbox">
{{ dp.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box" v-if="activeTabName !== '议员合作关系'">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">议院</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeYyList" @change="handleYyChange">
<el-checkbox v-for="(yy, index) in yyList" :key="index" :label="yy.id" class="filter-checkbox">
{{ yy.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box" v-if="activeTabName === '国会法案'">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">发布时间</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activePubTime" @change="handlePubTimeChange">
<el-checkbox v-for="(time, index) in pubTime" :key="index" :label="time.id" class="filter-checkbox">
{{ time.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box" v-if="activeTabName === '议员合作关系'">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">合作关系</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeCoopList" @change="handleCoopChange">
<el-checkbox v-for="(coop, index) in coopList" :key="index" :label="coop.id" class="filter-checkbox">
{{ coop.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
<div class="right">
<div class="right-header">
<div class="right-header-box">
<el-select v-model="footerSelect1" placeholder="选择委员会" style="width: 240px" @change="handleFooterSelect1Change">
<el-option v-for="item in postOrgList" :key="item.departmentId" :label="item.departmentName" :value="item.departmentId" />
</el-select>
</div>
<template v-if="activeTabName === '国会法案'">
<div class="right-header-box">
<el-select v-model="footerSelect2" placeholder="选择提出议员" style="width: 240px" @change="handleFooterSelect2Change">
<el-option v-for="item in postMemberList" :key="item.memberId" :label="item.memberName" :value="item.memberId" />
</el-select>
</div>
<div class="right-header-box right-header-sort" style="margin-left: auto">
<el-checkbox v-model="isInvolveCn" true-label="Y" false-label="N" class="involve-checkbox" @change="handleInvolveCnChange">只看涉华法案</el-checkbox>
<el-select v-model="releaseTime" placeholder="选择排序方式" style="width: 120px" @change="handlePxChange">
<template #prefix>
<div style="display: flex; align-items: center; height: 100%">
<img :src="desc" style="width: 14px; height: 14px" />
</div>
</template>
<el-option v-for="item in releaseTimeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</template>
</div>
<div class="right-main" v-loading="loading">
<template v-if="activeTabName === '国会法案'">
<div class="right-main-box" v-for="(item, index) in bills" :key="index">
<div v-if="item.riskSignal" class="risk-tag" :class="getRiskTagClass(item.riskSignal)">{{ item.riskSignal }}</div>
<div class="header">
<div class="title" @click="onClickToDetail(item)" :title="item.name">{{ item.name }}</div>
<div class="en-title" :title="item.eName">{{ item.eName }}</div>
</div>
<div class="main">
<div class="item"><div class="item-left">提案人:</div><div class="item-right">{{ item.tcr }}</div></div>
<div class="item"><div class="item-left">委员会:</div><div class="item-right">{{ item.wyh }}</div></div>
<div class="item"><div class="item-left">相关领域:</div><div class="item-right1"><div class="tag" v-for="(val, idx) in item.areaList" :key="idx">{{ val }}</div></div></div>
<div class="item"><div class="item-left">最新动议:</div><div class="item-right"><CommonPrompt :content="item.zxdy" /></div></div>
<div class="item">
<div class="item-left">法案进展:</div>
<div class="item-right2">
<div class="tag" v-for="(val, idx) in [...item.progress].reverse()" :key="idx" :style="{ zIndex: item.progress.length - idx }">{{ val }}</div>
</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="footer-left">{{ `共 ${total} 项` }}</div>
<div class="footer-right">
<el-pagination @current-change="handleCurrentChange" :page-size="pageSize" :current-page="currentPage" background layout="prev, pager, next" :total="total" />
</div>
</div>
</template>
<div v-else-if="activeTabName === '国会议员'">
<div class="member-grid">
<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" />
<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" />
</div>
</div>
<div class="member-main">
<div class="member-title-row">
<div class="member-name">{{ item.name || '-' }}</div>
<div class="member-link">{{ item.billCountText || '0项提案 >' }}</div>
</div>
<div class="member-meta">{{ item.partyText || '-' }} · {{ item.chamberText || '-' }} · {{ item.termText || '-' }}</div>
<div class="member-committee">{{ item.committeeText || '-' }}</div>
<div class="member-tags">
<div class="member-tag" v-for="(tag, idx) in item.focusTags" :key="idx">{{ tag }}</div>
</div>
</div>
</div>
<div class="member-card-bottom" @click="handleClickLatestProposal(item)">
<div class="member-latest">最新提案:{{ item.latestProposal || '-' }}</div>
<div class="member-arrow">></div>
</div>
</div>
</div>
<div class="right-footer">
<div class="footer-left">{{ `共 ${memberTotal} 项` }}</div>
<div class="footer-right">
<el-pagination @current-change="handleMemberCurrentChange" :page-size="memberPageSize" :current-page="memberCurrentPage" background layout="prev, pager, next" :total="memberTotal" />
</div>
</div>
</div>
<div v-else-if="activeTabName === '议员合作关系'" class="coop-list">
<div class="member-card" v-for="item in memberList" :key="`coop-${item.id}`">
<div class="member-name">{{ item.name || '-' }}</div>
<div class="member-info">党派:{{ item.party || '-' }}</div>
<div class="member-info">议院:{{ item.chamber || '-' }}</div>
<div class="member-info">任期:{{ item.term || '-' }}</div>
<div class="member-info">委员会:{{ item.committee || '-' }}</div>
<div class="member-info">关注领域:{{ item.focus || '-' }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { getHylyList, getPostOrgList, getPostMemberList, getBills, getBillsPerson } from "@/api/bill/billHome";
import CommonPrompt from "../commonPrompt/index.vue";
import desc from "./assets/icons/icon-desc.png";
import defaultAvatar from "./assets/images/user.png";
import zyyIcon from "@/assets/icons/zyy.png";
import cyyIcon from "@/assets/icons/cyy.png";
import ghdIcon from "@/assets/icons/ghd.png";
import mzdIcon from "@/assets/icons/mzd.png";
const props = defineProps({
onClickToDetail: { type: Function, required: true },
onAfterPageChange: { type: Function, default: null }
});
const tabList = ref([
{ name: "国会法案", active: true },
{ name: "国会议员", active: false },
{ name: "议员合作关系", active: false },
{ name: "涉华委员会", active: false }
]);
const activeTabName = ref("国会法案");
const handleClickTab = tab => {
activeTabName.value = tab.name;
if (tab.name === "国会议员") {
memberCurrentPage.value = 1;
handleGetBillsPerson();
return;
}
if (tab.name === "国会法案") {
currentPage.value = 1;
handleGetBills();
}
};
const releaseTime = ref(true);
const releaseTimeList = ref([{ label: "正序", value: true }, { label: "倒序", value: false }]);
const isInvolveCn = ref("Y");
const cateKuList = ref([]);
const activeAreaList = ref(["全部领域"]);
const dpList = ref([{ id: "全部党派", name: "全部党派" }, { id: "Democratic", name: "民主党" }, { id: "Republican", name: "共和党" }]);
const activeDpList = ref(["全部党派"]);
const yyList = ref([{ id: "全部议院", name: "全部议院" }, { id: "S", name: "参议院" }, { id: "H", name: "众议院" }]);
const activeYyList = ref(["全部议院"]);
const pubTime = ref([{ id: "全部时间", name: "全部时间" }, { id: "2025", name: "2025年" }, { id: "2024", name: "2024年" }, { id: "2023", name: "2023年" }, { id: "2022", name: "2022年" }, { id: "2021", name: "2021年" }]);
const activePubTime = ref(["全部时间"]);
const coopList = ref([
{ id: "全部合作关系", name: "全部合作关系" },
{ id: "跨党派合作", name: "跨党派合作" },
{ id: "同党派合作", name: "同党派合作" },
{ id: "地域利益合作", name: "地域利益合作" },
{ id: "委员会内合作", name: "委员会内合作" }
]);
const activeCoopList = ref(["全部合作关系"]);
const footerSelect1 = ref("全部委员会");
const footerSelect2 = ref("全部提出议员");
const postOrgList = ref([{ departmentName: "全部委员会", departmentId: "全部委员会" }]);
const postMemberList = ref([{ memberName: "全部提出议员", memberId: "全部提出议员" }]);
const memberList = ref([]);
const memberTotal = ref(0);
const memberPageSize = ref(15);
const memberCurrentPage = ref(1);
const bills = ref([]);
const total = ref(0);
const pageSize = ref(4);
const currentPage = ref(1);
const loading = ref(false);
const abortController = ref(null);
const getRiskTagClass = riskSignal => {
if (riskSignal === "特别重大风险") return "risk-tag-critical";
if (riskSignal === "重大风险") return "risk-tag-high";
if (riskSignal === "较大风险") return "risk-tag-medium";
return "";
};
const handleGetHylyList = async () => {
try {
const res = await getHylyList();
cateKuList.value = res.data || [];
} catch (error) {}
};
const handleGetPostOrgList = async () => {
try {
const res = await getPostOrgList();
if (res.code === 200) {
const list = (res.data || []).filter(item => item.departmentId);
postOrgList.value = [{ departmentName: "全部委员会", departmentId: "全部委员会" }, ...list];
}
} catch (error) {}
};
const handleGetPostMemberList = async () => {
try {
const res = await getPostMemberList();
if (res.code === 200) {
const list = (res.data || []).filter(item => item.memberId);
postMemberList.value = [{ memberName: "全部提出议员", memberId: "全部提出议员" }, ...list];
}
} catch (error) {}
};
// 获取资源库法案列表
const handleGetBills = async () => {
if (abortController.value) abortController.value.abort();
abortController.value = new AbortController();
loading.value = true;
const params = {
currentPage: currentPage.value,
pageSize: pageSize.value,
isInvolveCn: isInvolveCn.value
};
if (!activeYyList.value.includes("全部议院")) params.congressIds = activeYyList.value.join(",");
if (footerSelect1.value !== "全部委员会") params.departmentId = footerSelect1.value;
if (!activeDpList.value.includes("全部党派")) params.partyIds = activeDpList.value.join(",");
if (footerSelect2.value !== "全部提出议员") params.personId = footerSelect2.value;
if (!activeAreaList.value.includes("全部领域")) params.researchIds = activeAreaList.value.join(",");
if (releaseTime.value !== true) params.sortFun = releaseTime.value;
if (!activePubTime.value.includes("全部时间")) params.years = activePubTime.value.join(",");
try {
const res = await getBills(params, abortController.value.signal);
if (res.code === 200 && res.data && res.data.content) {
bills.value = res.data.content.map(item => ({
billId: item.billId,
name: item.billName,
eName: item.billNameEn,
tcr: item.personName,
wyh: item.congressName,
areaList: item.hylyList || [],
zxdy: item.latestAction,
progress: item.stageList || [],
riskSignal: item.riskSignal || ""
}));
total.value = res.data.totalElements;
} else {
bills.value = [];
total.value = 0;
}
} catch (error) {
if (error.name !== "AbortError") {
bills.value = [];
total.value = 0;
}
} finally {
loading.value = false;
}
};
// 获取资源库国会议员列表
const handleGetBillsPerson = async () => {
if (abortController.value) abortController.value.abort();
abortController.value = new AbortController();
loading.value = true;
const params = {
currentPage: memberCurrentPage.value,
pageSize: memberPageSize.value
};
if (footerSelect1.value !== "全部委员会") params.committeeId = footerSelect1.value;
if (!activeYyList.value.includes("全部议院")) params.congressIds = activeYyList.value;
if (!activeDpList.value.includes("全部党派")) params.partyIds = activeDpList.value;
if (!activeAreaList.value.includes("全部领域")) params.domainIds = activeAreaList.value;
const formatDateYm = dateStr => {
if (!dateStr) return "";
const date = String(dateStr).slice(0, 10);
const parts = date.split("-");
if (parts.length < 2) return dateStr;
return `${parts[0]}.${parts[1]}`;
};
try {
const res = await getBillsPerson(params, abortController.value.signal);
if (res.code === 200 && res.data && res.data.content) {
memberList.value = res.data.content.map(item => {
const partyMap = { Democratic: "民主党", Republican: "共和党" };
const partyText = partyMap[item.partyId] || item.partyId || "-";
const chamberText = item.congressType || "-";
const start = formatDateYm(item.startTime);
const end = formatDateYm(item.endTime);
const termText = start || end ? `${start}${end ? `-${end}` : ""}` : "-";
const focusTags = (item.domainNames || []).slice(0, 3);
const proposalSize = Number(item.proposalSize || 0);
const partyIcon = item.partyId === "Democratic" ? mzdIcon : item.partyId === "Republican" ? ghdIcon : "";
const chamberIcon = chamberText === "众议院" ? zyyIcon : chamberText === "参议院" ? cyyIcon : "";
return {
id: item.id,
name: item.name || "-",
avatar: item.imageUrl || "",
partyIcon,
chamberIcon,
billCountText: `${proposalSize.toLocaleString()}项提案 >`,
partyText,
chamberText,
termText,
committeeText: item.position || "-",
focusTags,
latestProposal: item.latestBillInfo?.billName || "-",
latestProposalBillId: item.latestBillInfo?.billId || "",
party: item.partyId,
chamber: item.congressType,
term: `${item.startTime || ""}${item.endTime ? ` ${item.endTime}` : ""}`,
committee: item.position,
focus: (item.domainNames || []).join("、")
};
});
memberTotal.value = res.data.totalElements || 0;
} else {
memberList.value = [];
memberTotal.value = 0;
}
} catch (error) {
if (error.name !== "AbortError") {
memberList.value = [];
memberTotal.value = 0;
}
} finally {
loading.value = false;
}
};
const normalizeWithAll = (val, allLabel, targetRef) => {
if (val.includes(allLabel) && val.length > 1) {
targetRef.value = val[val.length - 1] === allLabel ? [allLabel] : val.filter(item => item !== allLabel);
} else if (val.length === 0) {
targetRef.value = [allLabel];
} else {
targetRef.value = val;
}
currentPage.value = 1;
memberCurrentPage.value = 1;
if (activeTabName.value === "国会议员") {
handleGetBillsPerson();
return;
}
handleGetBills();
};
const handleAreaChange = val => normalizeWithAll(val, "全部领域", activeAreaList);
const handleDpChange = val => normalizeWithAll(val, "全部党派", activeDpList);
const handleYyChange = val => normalizeWithAll(val, "全部议院", activeYyList);
const handlePubTimeChange = val => normalizeWithAll(val, "全部时间", activePubTime);
const handleCoopChange = val => normalizeWithAll(val, "全部合作关系", activeCoopList);
const handleFooterSelect1Change = val => {
footerSelect1.value = val;
currentPage.value = 1;
memberCurrentPage.value = 1;
if (activeTabName.value === "国会议员") {
handleGetBillsPerson();
return;
}
handleGetBills();
};
const handleFooterSelect2Change = val => {
footerSelect2.value = val;
currentPage.value = 1;
handleGetBills();
};
const handlePxChange = val => {
releaseTime.value = val;
currentPage.value = 1;
handleGetBills();
};
const handleInvolveCnChange = val => {
isInvolveCn.value = val;
currentPage.value = 1;
handleGetBills();
};
const handleCurrentChange = page => {
currentPage.value = page;
handleGetBills();
props.onAfterPageChange && props.onAfterPageChange();
};
const handleClickLatestProposal = item => {
if (!item?.latestProposalBillId) return;
props.onClickToDetail({
billId: item.latestProposalBillId,
name: item.latestProposal
});
};
const handleMemberCurrentChange = page => {
memberCurrentPage.value = page;
handleGetBillsPerson();
props.onAfterPageChange && props.onAfterPageChange();
};
onMounted(() => {
handleGetHylyList();
handleGetPostOrgList();
handleGetPostMemberList();
handleGetBills();
});
</script>
<style lang="scss" scoped>
.home-content-footer-header {
width: 1600px;
margin: 0 auto;
margin-top: 37px;
margin-bottom: 36px;
height: 42px;
display: flex;
justify-content: space-between;
.btn-box {
display: flex;
gap: 24px;
width: 1000px;
.btn {
height: 42px;
line-height: 42px;
padding: 0 20px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
border-radius: 21px;
cursor: pointer;
&:hover {
background: rgba(20, 89, 187, 0.1);
}
}
.btnActive {
background: var(--color-main-active);
color: #fff;
font-weight: 700;
&:hover {
color: #fff;
background: var(--color-main-active);
}
}
.disabled {
cursor: not-allowed;
opacity: 0.5;
&:hover {
background: transparent;
}
}
}
}
.home-content-footer-main {
width: 1600px;
height: 1401px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: flex-start;
.left {
width: 300px;
padding-bottom: 33px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.select-box {
margin-top: 20px;
.select-box-header {
display: flex;
gap: 17px;
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
.title {
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 1px;
text-align: left;
}
}
.select-main {
margin-left: 25px;
margin-top: 16px;
.checkbox-group {
display: flex;
flex-wrap: wrap;
.filter-checkbox {
width: 50%;
margin-right: 0;
margin-bottom: 4px;
:deep(.el-checkbox__label) {
color: rgb(95, 101, 108);
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
}
}
}
}
}
}
.right {
margin-left: 20px;
width: 1280px;
.right-header {
height: 48px;
display: flex;
gap: 18px;
.right-header-sort {
display: flex;
align-items: center;
gap: 20px;
}
.involve-checkbox {
height: 40px;
display: inline-flex;
align-items: center;
margin-right: 0;
:deep(.el-checkbox__inner) {
width: 18px;
height: 18px;
border-radius: 5px;
}
:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #1677ff;
border-color: #1677ff;
}
:deep(.el-checkbox__inner::after) {
box-sizing: content-box;
}
:deep(.el-checkbox__label) {
color: #5f656c;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 24px;
padding-left: 10px;
}
}
}
.right-main {
height: 1264px;
.member-grid,
.coop-list {
display: grid;
column-gap: 16px;
row-gap: 16px;
.member-card {
height: 200px;
padding: 10px 16px 0;
box-sizing: border-box;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: #fff;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
.member-card-top {
display: flex;
gap: 16px;
}
.member-avatar-wrap {
width: 88px;
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
position: relative;
/* padding-bottom: 22px; */
margin-top: 5px;
}
.member-avatar {
width: 88px;
height: 88px;
border-radius: 50%;
object-fit: cover;
}
.member-icon-row {
position: absolute;
left: 50%;
bottom: 18px;
transform: translateX(-50%);
display: flex;
gap: 2px;
align-items: center;
}
.member-mini-icon-img {
width: 16px;
height: 16px;
padding: 2px;
box-sizing: border-box;
object-fit: contain;
border-radius: 50%;
background: #fff;
}
.member-main {
flex: 1;
min-width: 0;
padding-top: 2px;
}
.member-title-row {
display: flex;
align-items: baseline;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.member-name {
color: rgb(59, 65, 75);
font-family: "Source Han Sans CN";
font-size: 18px;
font-weight: 700;
line-height: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.member-link {
flex-shrink: 0;
color: #1459bb;
font-size: 16px;
line-height: 24px;
}
.member-meta,
.member-committee {
margin-top: 6px;
color: #5f656c;
font-size: 16px;
line-height: 24px;
font-weight: 400;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.member-tags {
margin-top: 10px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.member-tag {
height: 24px;
padding: 5px 8px;
border-radius: 4px;
background: rgb(231, 243, 255);
color: rgb(5, 95, 194);
font-size: 14px;
font-weight: 400;
line-height: 14px;
}
.member-card-bottom {
height: 52px;
margin: 10px -16px 0;
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid #eaeced;
color: #5f656c;
font-size: 16px;
cursor: pointer;
&:hover {
background: rgba(20, 89, 187, 0.04);
}
}
.member-latest {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 8px;
}
.member-arrow {
font-size: 16px;
line-height: 1;
color: #5f656c;
}
.member-info {
color: #5f656c;
font-family: "Microsoft YaHei";
font-size: 15px;
font-weight: 400;
line-height: 28px;
}
}
}
.member-grid {
grid-template-columns: repeat(3, 1fr);
}
.coop-list {
grid-template-columns: 1fr;
}
.right-main-box {
position: relative;
width: 1280px;
height: 300px;
padding-bottom: 24px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
margin-bottom: 16px;
overflow: hidden;
.risk-tag {
position: absolute;
top: 16px;
right: 40px;
height: 28px;
border-radius: 20px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 0 10px;
box-sizing: border-box;
font-family: "Microsoft YaHei";
font-size: 14px;
font-weight: 500;
line-height: 28px;
white-space: nowrap;
&::before {
content: "";
width: 5px;
height: 5px;
border-radius: 50%;
background: currentColor;
flex-shrink: 0;
}
}
.risk-tag-critical {
background: rgba(206, 79, 81, 0.1);
color: rgb(206, 79, 81);
}
.risk-tag-high {
background: rgba(255, 149, 77, 0.1);
color: rgb(255, 149, 77);
}
.risk-tag-medium {
background: rgba(232, 189, 11, 0.1);
color: rgb(232, 189, 11);
}
.header {
height: 91px;
width: 1200px;
margin: 0 auto;
border-bottom: 1px solid rgba(234, 236, 238, 1);
padding-top: 19px;
.title {
cursor: pointer;
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.en-title {
margin-top: 8px;
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;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.main {
width: 1200px;
margin: 0 auto;
margin-top: 2px;
.item {
margin-top: 12px;
display: flex;
.item-left {
width: 100px;
color: rgb(59, 65, 75);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
.item-right {
max-width: 1000px;
margin-left: 10px;
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;
}
.item-right1 {
margin-left: 10px;
display: flex;
gap: 8px;
.tag {
height: 24px;
line-height: 24px;
padding: 0 8px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
color: var(--color-main-active);
font-family: "Microsoft YaHei";
font-size: 14px;
font-weight: 400;
}
}
.item-right2 {
margin-left: 10px;
display: flex;
align-items: center;
.tag {
height: 24px;
line-height: 22px;
padding: 0 10px 0 30px;
background: rgba(255, 255, 255, 1);
color: rgb(95, 101, 108);
border-top: 1px solid rgb(234, 236, 238);
border-bottom: 1px solid rgb(234, 236, 238);
position: relative;
margin-left: -10px;
font-family: "Microsoft YaHei";
font-size: 14px;
font-weight: 400;
&::after {
content: "";
position: absolute;
top: 50%;
right: -8.485px;
width: 16.97px;
height: 16.97px;
background: inherit;
border-top: 1px solid rgb(234, 236, 238);
border-right: 1px solid rgb(234, 236, 238);
transform: translateY(-50%) rotate(45deg);
z-index: 1;
box-shadow: 2px -2px 2px rgba(0, 0, 0, 0.05);
box-sizing: border-box;
}
&:first-child {
margin-left: 0;
padding-left: 10px;
border-left: 1px solid rgb(234, 236, 238);
border-radius: 4px 0 0 4px;
}
&:last-child {
background: rgb(59, 65, 75);
color: rgba(255, 255, 255, 1);
border-color: rgb(59, 65, 75);
padding-right: 10px;
border-radius: 0;
border-right: none;
&::after {
display: block;
border-color: rgb(59, 65, 75);
box-shadow: none;
}
}
&:first-child:last-child {
margin-left: 0;
padding: 0 10px;
border-radius: 4px 0 0 4px;
background: rgb(59, 65, 75);
color: rgba(255, 255, 255, 1);
border: 1px solid rgb(59, 65, 75);
border-right: none;
}
}
}
}
}
}
}
.right-footer {
height: 85px;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding-top: 12px;
.footer-left {
color: rgba(59, 65, 75, 1);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 32px;
}
}
}
}
</style>
...@@ -13,7 +13,10 @@ ...@@ -13,7 +13,10 @@
<DivideHeader id="position1" class="divide1" :titleText="'最新动态'"></DivideHeader> <DivideHeader id="position1" class="divide1" :titleText="'最新动态'"></DivideHeader>
<div class="home-content-center"> <div class="home-content-center">
<div class="center-top"> <div class="center-top">
<div class="box1"> <overviewMainBox class="box1" title="热门法案" @toDetail="handleClickToDetail">
<template #headerIcon>
<img style="width: 100%; height: 100%" src="./assets/images/box1-header-icon.png" alt="" />
</template>
<div class="box1-left" @click="handleSwithCurBill('left')"> <div class="box1-left" @click="handleSwithCurBill('left')">
<div class="icon"> <div class="icon">
<img src="./assets/images/box1-left.svg" alt="" /> <img src="./assets/images/box1-left.svg" alt="" />
...@@ -24,17 +27,6 @@ ...@@ -24,17 +27,6 @@
<img src="./assets/images/box1-right.svg" alt="" /> <img src="./assets/images/box1-right.svg" alt="" />
</div> </div>
</div> </div>
<div class="box1-header">
<div class="box1-header-left">
<div class="icon">
<img src="./assets/images/box1-header-icon.png" alt="" />
</div>
<div class="title">{{ "热门法案" }}</div>
</div>
<div class="box1-header-right" @click="handleClickToDetail()">
{{ "查看详情 >" }}
</div>
</div>
<div class="box1-main" style="display: block"> <div class="box1-main" style="display: block">
<el-carousel ref="carouselRef" height="354px" :autoplay="true" :interval="3000" <el-carousel ref="carouselRef" height="354px" :autoplay="true" :interval="3000"
arrow="never" indicator-position="none" @change="handleCarouselChange"> arrow="never" indicator-position="none" @change="handleCarouselChange">
...@@ -42,7 +34,6 @@ ...@@ -42,7 +34,6 @@
<div class="carousel-content" style="display: flex; height: 100%"> <div class="carousel-content" style="display: flex; height: 100%">
<div class="box1-main-left"> <div class="box1-main-left">
<div class="box1-main-left-title"> <div class="box1-main-left-title">
<!-- "H.R.1(119th)-大而美法案" -->
{{ bill.billName }} {{ bill.billName }}
</div> </div>
<div class="box1-main-left-info"> <div class="box1-main-left-info">
...@@ -102,7 +93,7 @@ ...@@ -102,7 +93,7 @@
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
</div> </div>
</div> </overviewMainBox>
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal"
@item-click="handleClickToDetailO" riskLevel="signalLevel" postDate="signalTime" @item-click="handleClickToDetailO" riskLevel="signalLevel" postDate="signalTime"
name="signalTitle" /> name="signalTitle" />
...@@ -112,320 +103,71 @@ ...@@ -112,320 +103,71 @@
<div class="center-center"> <div class="center-center">
<NewsList :list="newsList" /> <NewsList :list="newsList" />
<MessageBubble :messageList="messageList" imageUrl="personImage" <MessageBubble :messageList="messageList" imageUrl="personImage"
@more-click="handleToSocialDetail" @person-click="handleClcikToCharacter" name="personName" @more-click="handleToSocialDetail" @person-click="handleClickToCharacter" name="personName"
content="remarks" source="orgName" /> content="remarks" source="orgName" />
</div> </div>
<DivideHeader id="position3" class="divide3" :titleText="'数据总览'"></DivideHeader> <DivideHeader id="position3" class="divide3" :titleText="'数据总览'"></DivideHeader>
<div class="center-footer"> <div class="center-footer">
<div class="box5"> <OverviewCard class="overview-card--double box5" title="涉华法案数量" :icon="box5HeaderIcon">
<div class="box5-header"> <template #right>
<div class="box5-header-left"> <el-select v-model="box5Select" placeholder="选择领域" @change="handleBox5Change" style="width: 150px">
<div class="box5-header-icon"> <el-option label="全部领域" value="全部领域" />
<img src="./assets/images/box5-header-icon.png" alt="" /> <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id" />
</div> </el-select>
<div class="box5-header-title">{{ "涉华法案数量" }}</div> </template>
</div>
<div class="box5-header-right">
<!-- <div class="header-right-icon">
<img src="./assets/images/tips-icon.png" alt="" />
</div>
<div class="header-right-text">{{ "数据来源:美国国会官方网站" }}</div> -->
<div class="box5-select">
<el-select v-model="box5Select" placeholder="选择领域" @change="handleBox5Change"
style="width: 150px">
<el-option label="全部领域" value="全部领域" />
<el-option v-for="item in categoryList" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</div>
</div>
</div>
<div class="box5-main" :style="getEmptyStateStyle(box5HasData)"> <div class="box5-main" :style="getEmptyStateStyle(box5HasData)">
<el-empty v-if="!box5HasData" description="暂无数据" :image-size="100" /> <el-empty v-if="!box5HasData" description="暂无数据" :image-size="100" />
<div v-else id="box5Chart" style="width: 100%; height: 100%"></div> <div v-else id="box5Chart" style="width: 100%; height: 100%"></div>
</div> </div>
</div> </OverviewCard>
<div class="box6"> <OverviewCard class="overview-card--single box6" title="涉华法案领域分布" :icon="box6HeaderIcon">
<div class="box6-header"> <template #right>
<div class="header-icon"> <el-select v-model="box9selectetedTime" placeholder="选择时间" style="width: 90px">
<img src="./assets/images/box6-header-icon.png" alt="" /> <el-option v-for="item in box9YearList" :key="item.value" :label="item.label" :value="item.value" />
</div> </el-select>
<div class="header-title">{{ "涉华法案领域分布" }}</div> </template>
<div class="box6-header-right">
<el-select v-model="box9selectetedTime" placeholder="选择时间" style="width: 90px">
<el-option v-for="item in box9YearList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<div class="box6-main" :style="getEmptyStateStyle(box9HasData)"> <div class="box6-main" :style="getEmptyStateStyle(box9HasData)">
<el-empty v-if="!box9HasData" description="暂无数据" :image-size="100" /> <el-empty v-if="!box9HasData" description="暂无数据" :image-size="100" />
<div v-else id="box9Chart" style="width: 100%; height: 100%"></div> <div v-else id="box9Chart" style="width: 100%; height: 100%"></div>
</div> </div>
</div> </OverviewCard>
</div> </div>
<div class="center-footer1"> <div class="center-footer1">
<div class="box7"> <OverviewCard class="overview-card--single box7" title="法案提出部门" :icon="box7HeaderIcon">
<div class="box7-header"> <template #right>
<div class="box7-header-left">
<div class="box7-header-icon">
<img src="./assets/images/box7-header-icon.png" alt="" />
</div>
<div class="box7-header-title">{{ "法案提出部门" }}</div>
</div>
<div class="box7-header-right">
<!-- <div class="header-right-icon">
<img src="./assets/images/tips-icon.png" alt="" />
</div> -->
<!-- <div class="header-right-text">{{ "数据来源:美国国会官方网站" }}</div> -->
<el-select v-model="box7selectetedTime" placeholder="选择时间" style="width: 90px">
<el-option v-for="item in box7YearList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<!-- <div class="box-center">
<el-select v-model="box7selectetedTime" placeholder="选择时间" style="width: 90px"> <el-select v-model="box7selectetedTime" placeholder="选择时间" style="width: 90px">
<el-option <el-option v-for="item in box7YearList" :key="item.value" :label="item.label" :value="item.value" />
v-for="item in box7YearList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</div> --> </template>
<div class="box7-main" :style="getEmptyStateStyle(box7HasData)"> <div class="box7-main" :style="getEmptyStateStyle(box7HasData)">
<el-empty v-if="!box7HasData" description="暂无数据" :image-size="100" /> <el-empty v-if="!box7HasData" description="暂无数据" :image-size="100" />
<div v-else id="box7Chart" style="width: 100%; height: 100%"></div> <div v-else id="box7Chart" style="width: 100%; height: 100%"></div>
</div> </div>
</div> </OverviewCard>
<div class="box8"> <OverviewCard class="overview-card--single box8" title="涉华法案进展分布" :icon="box7HeaderIcon">
<div class="box8-header"> <template #right>
<div class="box8-header-left">
<div class="box8-header-icon">
<img src="./assets/images/box7-header-icon.png" alt="" />
</div>
<div class="box8-header-title">{{ "关键议员提案" }}</div>
</div>
<div class="box8-header-right">
<!-- <div class="header-right-icon">
<img src="./assets/images/tips-icon.png" alt="" />
</div> -->
<!-- <div class="header-right-text">{{ "数据来源:美国国会官方网站" }}</div> -->
<el-select v-model="box8selectetedTime" placeholder="选择时间" style="width: 90px">
<el-option v-for="item in box8YearList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<!-- <div class="box-center">
<el-select v-model="box8selectetedTime" placeholder="选择时间" style="width: 90px"> <el-select v-model="box8selectetedTime" placeholder="选择时间" style="width: 90px">
<el-option <el-option v-for="item in box8YearList" :key="item.value" :label="item.label" :value="item.value" />
v-for="item in box8YearList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</div> --> </template>
<div class="box8-main" :style="getEmptyStateStyle(box8Data.length > 0)"> <div class="box8-main" :style="getEmptyStateStyle(box8HasData)">
<el-empty v-if="box8Data.length === 0" description="暂无数据" :image-size="100" /> <el-empty v-if="!box8HasData" description="暂无数据" :image-size="100" />
<div v-else class="box8-main-item" v-for="(item, index) in box8Data" :key="index" <template v-else>
@click="handleClcikToCharacter(item.memberId, item.name)"> <div class="box8-desc">• 通过涉华法案{{ box8Summary }}</div>
<div class="box8-main-item-left"> <div id="box8Chart" class="box8-chart"></div>
<img :src="getProxyUrl(item.img)" alt="" referrerpolicy="no-referrer" </template>
class="left-img" />
<div class="left-icon1">
<img :src="item.dangpai" alt="" />
</div>
<div class="left-icon2">
<img :src="item.yuan" alt="" />
</div>
</div>
<div class="box8-main-item-center">
<div class="box8-main-item-center-top">{{ item.name }}</div>
<div class="box8-main-item-center-footer">{{ item.zhiwei }}</div>
</div>
<div class="box8-main-item-right">
{{ `${item.num}项提案 >` }}
</div>
</div>
</div>
</div>
<div class="box9">
<div class="box9-header">
<div class="box9-header-left">
<div class="box9-header-icon">
<img src="./assets/images/box7-header-icon.png" alt="" />
</div>
<div class="box9-header-title">{{ "关键条款" }}</div>
</div>
</div> </div>
</OverviewCard>
<OverviewCard class="overview-card--single box9" title="关键条款" :icon="box7HeaderIcon">
<div class="box9-main" id="wordCloudChart"></div> <div class="box9-main" id="wordCloudChart"></div>
</div> </OverviewCard>
</div> </div>
</div> </div>
</div> </div>
<div class="home-content-footer"> <div class="home-content-footer">
<DivideHeader id="position4" class="divide4" :titleText="'资源库'"></DivideHeader> <DivideHeader id="position4" class="divide4" :titleText="'资源库'"></DivideHeader>
<div class="home-content-footer-header"> <ResourceLibrarySection :on-click-to-detail="handleClickToDetailO" :on-after-page-change="handlePageChange" />
<div class="btn-box">
<div class="btn" :class="{ btnActive: activeTabName === cate.name, disabled: index !== 0 }"
v-for="(cate, index) in tabList" :key="index" @click="index === 0 && handleClickTab(cate)">
{{ cate.name }}
</div>
</div>
</div>
<div class="home-content-footer-main">
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeAreaList"
@change="handleAreaChange">
<el-checkbox class="filter-checkbox" label="全部领域"> 全部领域 </el-checkbox>
<el-checkbox v-for="(area, index) in cateKuList" :key="index" :label="area.id"
class="filter-checkbox">
{{ area.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "党派" }}</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeDpList"
@change="handleDpChange">
<el-checkbox v-for="(dp, index) in dpList" :key="index" :label="dp.id"
class="filter-checkbox">
{{ dp.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "议院" }}</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activeYyList"
@change="handleYyChange">
<el-checkbox v-for="(yy, index) in yyList" :key="index" :label="yy.id"
class="filter-checkbox">
{{ yy.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activePubTime"
@change="handlePubTimeChange">
<el-checkbox v-for="(time, index) in pubTime" :key="index" :label="time.id"
class="filter-checkbox">
{{ time.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
<div class="right">
<div class="right-header">
<div class="right-header-box">
<el-select v-model="footerSelect1" placeholder="选择委员会" style="width: 240px"
@change="handleFooterSelect1Change">
<el-option v-for="item in postOrgList" :key="item.departmentId"
:label="item.departmentName" :value="item.departmentId" />
</el-select>
</div>
<div class="right-header-box">
<el-select v-model="footerSelect2" placeholder="选择提出议员" style="width: 240px"
@change="handleFooterSelect2Change">
<el-option v-for="item in postMemberList" :key="item.memberId"
:label="item.memberName" :value="item.memberId" />
</el-select>
</div>
<div class="right-header-box" style="margin-left: auto">
<el-select v-model="releaseTime" placeholder="选择排序方式" style="width: 120px"
@change="handlePxChange">
<template #prefix>
<div style="display: flex; align-items: center; height: 100%">
<img :src="desc" style="width: 14px; height: 14px" />
</div>
</template>
<el-option v-for="item in releaseTimeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
</div>
<div class="right-main" v-loading="loading">
<div class="right-main-box" v-for="(item, index) in bills" :key="index">
<div class="header">
<div class="title" @click="handleClickToDetailO(item)" :title="item.name">
{{ item.name }}
</div>
<div class="en-title" :title="item.eName">{{ item.eName }}</div>
</div>
<div class="main">
<div class="item">
<div class="item-left">{{ "提案人:" }}</div>
<div class="item-right">{{ item.tcr }}</div>
</div>
<div class="item">
<div class="item-left">{{ "委员会:" }}</div>
<div class="item-right">{{ item.wyh }}</div>
</div>
<div class="item">
<div class="item-left">{{ "相关领域:" }}</div>
<div class="item-right1">
<div class="tag" v-for="(val, idx) in item.areaList" :key="idx">{{ val }}
</div>
</div>
</div>
<div class="item">
<div class="item-left">{{ "最新动议:" }}</div>
<div class="item-right">
<CommonPrompt :content="item.zxdy" />
</div>
</div>
<div class="item">
<div class="item-left">{{ "法案进展:" }}</div>
<div class="item-right2">
<div class="tag" v-for="(val, idx) in [...item.progress].reverse()"
:key="idx" :style="{ zIndex: item.progress.length - idx }">
{{ val }}
</div>
</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="footer-left">
{{ `共 ${total} 项` }}
</div>
<div class="footer-right">
<el-pagination @current-change="handleCurrentChange" :page-size="pageSize"
:current-page="currentPage" background layout="prev, pager, next"
:total="total" />
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -444,16 +186,15 @@ import { ...@@ -444,16 +186,15 @@ import {
getBillOverviewKeyTK, getBillOverviewKeyTK,
getBillCount, getBillCount,
getBillPostOrg, getBillPostOrg,
getMemberProposal, getBillProcess,
getNews, getNews,
getRemarks, getRemarks
getPostOrgList,
getPostMemberList,
getBills
} from "@/api/bill/billHome"; } from "@/api/bill/billHome";
import { getPersonSummaryInfo } from "@/api/common/index"; import { getPersonSummaryInfo } from "@/api/common/index";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import CommonPrompt from "../commonPrompt/index.vue"; import overviewMainBox from "@/components/base/boxBackground/overviewMainBox.vue";
import OverviewCard from "./OverviewCard.vue";
import ResourceLibrarySection from "./ResourceLibrarySection.vue";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
import getMultiLineChart from "./utils/multiLineChart"; import getMultiLineChart from "./utils/multiLineChart";
...@@ -461,34 +202,15 @@ import getWordCloudChart from "./utils/worldCloudChart"; ...@@ -461,34 +202,15 @@ import getWordCloudChart from "./utils/worldCloudChart";
import getPieChart from "./utils/piechart"; import getPieChart from "./utils/piechart";
import getDoublePieChart from "./utils/doublePieChart"; import getDoublePieChart from "./utils/doublePieChart";
import desc from "./assets/icons/icon-desc.png"; import box5HeaderIcon from "./assets/images/box5-header-icon.png";
import box6HeaderIcon from "./assets/images/box6-header-icon.png";
import box7HeaderIcon from "./assets/images/box7-header-icon.png";
import Cyy from "@/assets/icons/cyy.png";
import Ghd from "@/assets/icons/ghd.png";
import Mzd from "@/assets/icons/mzd.png";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
// 处理图片代理
const getProxyUrl = url => {
if (!url) return "";
const urlStr = String(url);
// 排除非 http 开头(相对路径)、已经是代理链接、或者是本地链接
if (
!urlStr.startsWith("http") ||
urlStr.includes("images.weserv.nl") ||
urlStr.includes("localhost") ||
urlStr.includes("127.0.0.1")
) {
return url;
}
// 移除协议头 http:// 或 https://
const cleanUrl = urlStr.replace(/^https?:\/\//i, "");
return `https://images.weserv.nl/?url=${encodeURIComponent(cleanUrl)}`;
};
// 跳转人物主页 // 跳转人物主页
const handleClcikToCharacter = async (id, name) => { const handleClickToCharacter = async (id, name) => {
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList")); const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0; let type = 0;
...@@ -1143,29 +865,189 @@ watch(box9selectetedTime, () => { ...@@ -1143,29 +865,189 @@ watch(box9selectetedTime, () => {
handleBox9Data(); handleBox9Data();
}); });
const box8Data = ref([]); const box8HasData = ref(true);
const box8Summary = ref(0);
let box8ChartInstance = null;
const box8MockDataByYear = {
"2025": {
passCount: 19,
stages: [
{ name: "已提案", count: 24 },
{ name: "众议院通过", count: 20 },
{ name: "众议院不通过", count: 25 },
{ name: "解决分歧", count: 23 },
{ name: "参议院通过", count: 26 },
{ name: "总统否决", count: 48 },
{ name: "已立法", count: 19 }
]
}
};
const getBox8ChartOption = stageList => {
const axisMax = 100;
const countList = stageList.map(item => item.count || 0);
const totalCount = countList.reduce((sum, cur) => sum + cur, 0);
const safeTotal = totalCount > 0 ? totalCount : 1;
const rawWidths = countList.map(count => (count / safeTotal) * axisMax);
const markerData = rawWidths.map((width, index) => {
if (index === rawWidths.length - 1) {
const used = rawWidths.slice(0, index).reduce((sum, cur) => sum + Number(cur.toFixed(4)), 0);
return Number(Math.max(axisMax - used, 0).toFixed(4));
}
return Number(width.toFixed(4));
});
let cumulative = 0;
const offsetData = markerData.map(width => {
const start = Number(cumulative.toFixed(4));
cumulative += width;
return start;
});
const trackData = stageList.map(() => axisMax);
return {
tooltip: {
trigger: "item",
confine: true,
backgroundColor: "rgba(34, 44, 58, 0.92)",
borderWidth: 0,
textStyle: {
color: "#fff",
fontSize: 13
},
formatter: params => {
const item = stageList[params.dataIndex];
if (!item) return "";
return `${item.name}<br/>${item.count}项`;
}
},
grid: {
left: 118,
right: 60,
top: 6,
bottom: 6
},
xAxis: {
type: "value",
min: 0,
max: axisMax,
show: false
},
yAxis: {
type: "category",
inverse: true,
data: stageList.map(item => item.name),
axisTick: { show: false },
axisLine: { show: false },
axisLabel: {
color: "#3b414b",
fontSize: 16
}
},
series: [
{
type: "bar",
barWidth: 32,
barGap: "-100%",
silent: true,
data: trackData,
itemStyle: {
color: params => (params.dataIndex === stageList.length - 1 ? "rgba(206, 79, 81, 0.1)" : "rgb(246, 250, 255)")
},
label: {
show: true,
position: "right",
formatter: params => `${countList[params.dataIndex]}项`,
color: params => (params.dataIndex === stageList.length - 1 ? "rgb(206, 79, 81)" : "#3b414b"),
fontSize: 36 / 2,
fontWeight: params => (params.dataIndex === stageList.length - 1 ? 700 : 400)
}
},
{
type: "bar",
stack: "progress",
barWidth: 32,
silent: true,
data: offsetData,
itemStyle: { color: "transparent" }
},
{
type: "bar",
stack: "progress",
barWidth: 32,
data: markerData,
itemStyle: {
color: params => (params.dataIndex === stageList.length - 1 ? "rgb(206, 79, 81)" : "rgb(5, 95, 194)")
},
emphasis: {
disabled: false,
itemStyle: {
borderColor: "rgba(255, 255, 255, 0.9)",
borderWidth: 2,
shadowBlur: 10,
shadowColor: "rgba(5, 95, 194, 0.35)"
}
}
}
]
};
};
const handleBox8Data = async () => { const handleBox8Data = async () => {
const stageOrder = ["提案", "众议院通过", "众议院不通过", "分歧已解决", "参议院通过", "总统否决或未签署", "完成立法"];
const stageNameMap = {
提案: "已提案",
众议院通过: "众议院通过",
众议院不通过: "众议院不通过",
分歧已解决: "解决分歧",
参议院通过: "参议院通过",
"总统否决或未签署": "总统否决",
完成立法: "已立法"
};
try { try {
const res = await getMemberProposal({ year: box8selectetedTime.value }); const res = await getBillProcess({ year: box8selectetedTime.value });
console.log("关键议员提案", res); console.log("涉华法案进展分布", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data && res.data.length > 0) {
box8Data.value = res.data.map(item => ({ const countMap = new Map(res.data.map(item => [item.progressName, item.countBill]));
memberId: item.memberId, const stages = stageOrder.map(name => ({
name: item.memberName, name: stageNameMap[name],
zhiwei: item.position, count: countMap.get(name) || 0
img: item.imageUrl,
num: item.countProposal,
dangpai: Cyy,
yuan: item.position === "Democratic" ? Mzd : Ghd
})); }));
box8HasData.value = true;
box8Summary.value = countMap.get("完成立法") || 0;
await nextTick();
const box8Chart = getBox8ChartOption(stages);
box8ChartInstance = setChart(box8Chart, "box8Chart");
} else { } else {
// 接口异常(如500)时,清空图表数据以避免报错或显示错误信息 const data = box8MockDataByYear[box8selectetedTime.value];
box8Data.value = []; if (data && data.stages && data.stages.length > 0) {
box8HasData.value = true;
box8Summary.value = data.passCount || 0;
await nextTick();
const box8Chart = getBox8ChartOption(data.stages);
box8ChartInstance = setChart(box8Chart, "box8Chart");
} else {
box8HasData.value = false;
box8Summary.value = 0;
setChart({}, "box8Chart");
}
} }
} catch (error) { } catch (error) {
console.error("获取关键议员提案失败", error); console.error("获取涉华法案进展分布失败", error);
box8Data.value = []; const data = box8MockDataByYear[box8selectetedTime.value];
if (data && data.stages && data.stages.length > 0) {
box8HasData.value = true;
box8Summary.value = data.passCount || 0;
await nextTick();
const box8Chart = getBox8ChartOption(data.stages);
box8ChartInstance = setChart(box8Chart, "box8Chart");
} else {
box8HasData.value = false;
box8Summary.value = 0;
setChart({}, "box8Chart");
}
} }
}; };
...@@ -1337,6 +1219,7 @@ const footerSelect2 = ref("全部提出议员"); ...@@ -1337,6 +1219,7 @@ const footerSelect2 = ref("全部提出议员");
// ]); // ]);
const handleResize = () => { const handleResize = () => {
box8ChartInstance && box8ChartInstance.resize();
box9ChartInstance && box9ChartInstance.resize(); box9ChartInstance && box9ChartInstance.resize();
}; };
...@@ -2449,510 +2332,65 @@ onUnmounted(() => { ...@@ -2449,510 +2332,65 @@ onUnmounted(() => {
margin-bottom: 36px; margin-bottom: 36px;
} }
$overview-card-gap: 16px;
$overview-single-width: calc((100% - #{$overview-card-gap} * 2) / 3);
.center-footer { .center-footer {
margin-top: 21px; margin-top: 21px;
height: 450px;
display: flex; display: flex;
gap: $overview-card-gap;
.box5 { width: 100%;
width: 1059px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.box5-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 27px 0 22px;
.box5-header-left {
display: flex;
.box5-header-icon {
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box5-header-title {
margin-top: 11px;
margin-left: 19px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.box5-header-right {
display: flex;
justify-content: flex-end;
gap: 8px;
height: 24px;
margin-top: 12px;
.header-right-icon {
margin-top: 4px;
width: 14px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.header-right-text {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
.box5-main {
height: 397px;
padding: 8px 16px 8px 16px;
}
}
.box6 {
margin-left: 20px;
width: 521px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.box6-header {
width: 521px;
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
box-sizing: border-box;
padding-left: 22px;
.header-icon {
margin-top: 15px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.header-title {
margin-top: 11px;
margin-left: 18px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
.box6-header-right {
margin-left: 130px;
display: flex;
justify-content: flex-end;
gap: 8px;
height: 24px;
margin-top: 12px;
.header-right-icon {
margin-top: 4px;
width: 14px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.header-right-text {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
.box6-main {
width: 100%;
height: calc(100% - 53px);
}
}
} }
.center-footer1 { .center-footer1 {
display: flex; display: flex;
gap: 16px; gap: $overview-card-gap;
margin-top: 16px; margin-top: 16px;
width: 100%;
}
.box7 { .overview-card--single {
width: 520px; width: $overview-single-width;
height: 450px; }
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box7-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 27px 0 22px;
.box7-header-left {
display: flex;
.box7-header-icon {
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box7-header-title {
margin-top: 11px;
margin-left: 19px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.box7-header-right {
display: flex;
justify-content: flex-end;
gap: 8px;
height: 24px;
margin-top: 12px;
.header-right-icon {
margin-top: 4px;
width: 14px;
height: 16px;
img { .overview-card--double {
width: 100%; width: calc(#{$overview-single-width} * 2 + #{$overview-card-gap});
height: 100%; }
} .box5-main,
} .box6-main,
.box7-main,
.box9-main {
height: 100%;
box-sizing: border-box;
}
.header-right-text { .box5-main {
color: rgba(132, 136, 142, 1); padding: 8px 16px;
font-family: Microsoft YaHei; }
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
.box-center { .box8-main {
height: 45px; height: 100%;
padding-right: 20px; box-sizing: border-box;
display: flex; padding: 12px 20px 18px;
align-items: center;
justify-content: flex-end;
}
.box7-main { .box8-desc {
height: 390px; height: 24px;
} line-height: 24px;
color: rgb(206, 79, 81);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
} }
.box8 { .box8-chart {
width: 527px; width: 100%;
height: 450px; height: calc(100% - 30px);
box-sizing: border-box; cursor: pointer;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box8-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 27px 0 22px;
.box8-header-left {
display: flex;
.box8-header-icon {
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box8-header-title {
margin-top: 11px;
margin-left: 19px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.box8-header-right {
display: flex;
justify-content: flex-end;
gap: 8px;
height: 24px;
margin-top: 12px;
.header-right-icon {
margin-top: 4px;
width: 14px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.header-right-text {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
.box8-main {
height: 380px;
overflow: hidden;
padding: 20px;
.box8-main-item {
// margin: 0 auto;
width: 478px;
height: 51px;
margin-bottom: 20px;
display: flex;
align-items: center;
position: relative;
// padding: 0 10px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.box8-main-item-left {
position: relative;
width: 42px;
height: 42px;
.left-img {
width: 42px;
height: 42px;
border-radius: 50%;
}
.left-icon1 {
position: absolute;
left: 2px;
bottom: -6px;
width: 20px;
height: 20px;
border-radius: 10px;
background: (255, 255, 255, 0.8);
box-sizing: border-box;
padding: 2px;
img {
width: 100%;
height: 100%;
}
}
.left-icon2 {
position: absolute;
right: 2px;
bottom: -6px;
width: 20px;
height: 20px;
border-radius: 10px;
background: (255, 255, 255, 0.8);
box-sizing: border-box;
padding: 2px;
img {
width: 100%;
height: 100%;
}
}
}
.box8-main-item-center {
height: 48px;
margin-left: 12px;
.box8-main-item-center-top {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.box8-main-item-center-footer {
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
.box8-main-item-right {
position: absolute;
top: 0;
right: 10px;
height: 51px;
text-align: right;
color: rgba(95, 101, 108, 1);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 51px;
}
}
}
} }
}
.box9 { .box9-main {
width: 521px; padding: 10px 20px;
height: 450px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box9-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 27px 0 22px;
.box9-header-left {
display: flex;
.box9-header-icon {
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box9-header-title {
margin-top: 11px;
margin-left: 19px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.box9-header-right {
display: flex;
justify-content: flex-end;
gap: 8px;
height: 24px;
margin-top: 12px;
.header-right-icon {
margin-top: 4px;
width: 14px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.header-right-text {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
.box-center {
height: 45px;
padding-right: 20px;
display: flex;
align-items: center;
justify-content: flex-end;
}
.box9-main {
height: 380px;
padding: 10px 20px;
}
}
} }
} }
} }
......
...@@ -89,8 +89,13 @@ export default defineConfig({ ...@@ -89,8 +89,13 @@ export default defineConfig({
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/temporarySearch/, '') rewrite: (path) => path.replace(/^\/temporarySearch/, '')
}, },
'^/bill(?:/|$)': {
target: 'http://172.20.10.3:28080/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/bill/, '')
},
'/pdfSse': { '/pdfSse': {
target: 'http://8.140.26.4:10020/', target: 'http://8.140.26.4:10020/',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/pdfSse/, ''), rewrite: (path) => path.replace(/^\/pdfSse/, ''),
configure: (proxy) => { configure: (proxy) => {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论