提交 a6e7aeaa authored 作者: 付康's avatar 付康

合并分支 'liuyuqi' 到 'pre'

Liuyuqi 查看合并请求 !334
流水线 #423 已通过 于阶段
in 2 分 40 秒
...@@ -3,41 +3,41 @@ import request from "@/api/request.js"; ...@@ -3,41 +3,41 @@ import request from "@/api/request.js";
// 规则限制-首页统计接口 // 规则限制-首页统计接口
export function getStatCount() { export function getStatCount() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/statCount` url: `/api/ruleLimitInfo/statCount`
}) });
} }
// 规则限制-查询最新动态接口 // 规则限制-查询最新动态接口
export function getLatestUpdates() { export function getLatestUpdates() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getLatestUpdates` url: `/api/ruleLimitInfo/getLatestUpdates`
}) });
} }
// 规则限制-风险信号 // 规则限制-风险信号
export function getRiskSignal(params) { export function getRiskSignal(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/commonFeature/riskSignal/${params}` url: `/api/commonFeature/riskSignal/${params}`
}) });
} }
// 规则限制-查询新闻资讯 // 规则限制-查询新闻资讯
export function getNews(params) { export function getNews(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/commonFeature/news/${params}` url: `/api/commonFeature/news/${params}`
}) });
} }
// 规则限制-查询社交媒体 // 规则限制-查询社交媒体
export function getRemarks(params) { export function getRemarks(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/commonFeature/remarks/${params}` url: `/api/commonFeature/remarks/${params}`
}) });
} }
// 规则限制-限制领域分布情况 // 规则限制-限制领域分布情况
...@@ -48,10 +48,10 @@ export function getRemarks(params) { ...@@ -48,10 +48,10 @@ export function getRemarks(params) {
*/ */
export function getAreaDistribution(params) { export function getAreaDistribution(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getAreaDistribution`, url: `/api/ruleLimitInfo/getAreaDistribution`,
params params
}) });
} }
// 规则限制-受限实体数量变化趋势 // 规则限制-受限实体数量变化趋势
...@@ -63,10 +63,10 @@ export function getAreaDistribution(params) { ...@@ -63,10 +63,10 @@ export function getAreaDistribution(params) {
*/ */
export function getEntityChangeTrend(params) { export function getEntityChangeTrend(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getEntityChangeTrend`, url: `/api/ruleLimitInfo/getEntityChangeTrend`,
params params
}) });
} }
// 规则限制-规则限制政令列表查询接口 // 规则限制-规则限制政令列表查询接口
...@@ -83,10 +83,10 @@ export function getEntityChangeTrend(params) { ...@@ -83,10 +83,10 @@ export function getEntityChangeTrend(params) {
*/ */
export function getRuleLimitList(params) { export function getRuleLimitList(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getRuleLimitList`, url: `/api/ruleLimitInfo/getRuleLimitList`,
params params
}) });
} }
// 规则限制-排华科技联盟列表接口 // 规则限制-排华科技联盟列表接口
...@@ -104,54 +104,51 @@ export function getRuleLimitList(params) { ...@@ -104,54 +104,51 @@ export function getRuleLimitList(params) {
*/ */
export function getACTAList(params) { export function getACTAList(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getACTAList`, url: `/api/ruleLimitInfo/getACTAList`,
params params
}) });
} }
export function getAcTAAllcountry() { export function getAcTAAllcountry() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getACTAAllCountry/` url: `/api/ruleLimitInfo/getACTAAllCountry/`
}) });
} }
// 规则限制-规则限制基本详情 // 规则限制-规则限制基本详情
export function getSanctionOverview(params) { export function getSanctionOverview(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getSanctionOverview/${params}` url: `/api/ruleLimitInfo/getSanctionOverview/${params}`
}) });
} }
// 规则限制-背景分析 // 规则限制-背景分析
export function getBackGround(params) { export function getBackGround(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getBackGround/${params}` url: `/api/ruleLimitInfo/getBackGround/${params}`
}) });
} }
// 规则限制-限制条款 // 规则限制-限制条款
export function getLimitClause(params) { export function getLimitClause(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getLimitClause/${params}` url: `/api/ruleLimitInfo/getLimitClause/${params}`
}) });
} }
// 规则限制-相关举措 // 规则限制-相关举措
export function getRelevantMeasures(params) { export function getRelevantMeasures(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/ruleLimitInfo/getRelevantMeasures/${params}` url: `/api/ruleLimitInfo/getRelevantMeasures/${params}`
}) });
} }
// // 实体清单-制裁概况-获取发布机构机构动态 // // 实体清单-制裁概况-获取发布机构机构动态
// /** // /**
// * @param {Object} data // * @param {Object} data
...@@ -168,7 +165,25 @@ export function getRelevantMeasures(params) { ...@@ -168,7 +165,25 @@ export function getRelevantMeasures(params) {
export function getRuleOrg(params) { export function getRuleOrg(params) {
return request({ return request({
method: 'POST', method: "POST",
url: `/api/organization/relate/ruleOrg`, data: params url: `/api/organization/relate/ruleOrg`,
}) data: params
});
}
// 排华联盟-联盟简介
export function getUnionIntroduction(unionId) {
return request({
method: "GET",
url: `/api/ruleLimitInfo/getUnionIntroduction/${unionId}`
});
}
// 排华联盟-联盟动态
export function getUnionDynamicList(params) {
return request({
method: "GET",
url: `/api/ruleLimitInfo/getUnionDynamicList/${params.unionId}`,
params: { currentPage: params.currentPage, pageSize: params.pageSize }
});
} }
...@@ -497,7 +497,8 @@ onMounted(() => { ...@@ -497,7 +497,8 @@ onMounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.module-header-wrapper { .module-header-wrapper {
width: 100%; width: 100%;
// height: 64px; position: relative;
z-index: 101;
border-bottom: 1px solid rgba(234, 236, 238, 1); border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: linear-gradient(180deg, rgba(246, 250, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%); background: linear-gradient(180deg, rgba(246, 250, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%);
......
// 规则限制 // 规则限制
const RuleRestriction = () => import('@/views/ruleRestriction/index.vue') const RuleRestriction = () => import("@/views/ruleRestriction/index.vue");
const RuleRestrictionDetail = () => import('@/views/ruleRestriction/detail/index.vue') const RuleRestrictionDetail = () => import("@/views/ruleRestriction/detail/index.vue");
const RuleRestrictionsAlliance = () => import('@/views/ruleRestriction/alliance/index.vue') const RuleRestrictionsAlliance = () => import("@/views/ruleRestriction/alliance/index.vue");
const ruleRestrictionsRoutes = [ const ruleRestrictionsRoutes = [
// 规则限制 // 规则限制
...@@ -23,16 +23,17 @@ const ruleRestrictionsRoutes = [ ...@@ -23,16 +23,17 @@ const ruleRestrictionsRoutes = [
title: "规则限制详情", title: "规则限制详情",
dynamicTitle: true dynamicTitle: true
} }
}, { },
{
path: "/ruleRestrictions/alliance", path: "/ruleRestrictions/alliance",
name: "RuleRestrictionsAlliance", name: "RuleRestrictionsAlliance",
component: RuleRestrictionsAlliance, component: RuleRestrictionsAlliance,
meta: { meta: {
title: "规则限制联盟详情", title: "规则限制联盟详情",
isShowHeader: true,
dynamicTitle: true dynamicTitle: true
} }
}, }
];
]
export default ruleRestrictionsRoutes export default ruleRestrictionsRoutes;
...@@ -54,41 +54,59 @@ ...@@ -54,41 +54,59 @@
} }
/***tabs-bar左边悬浮***/ /***tabs-bar左边悬浮***/
.left-float-nav-tabs,
.left-float-nav-tabs .el-tabs {
overflow: visible !important;
width: auto !important;
}
.left-float-nav-tabs { .left-float-nav-tabs {
position: relative;
.el-tabs__header.is-left { .el-tabs__header.is-left {
position: absolute; position: absolute !important;
left: -140px; left: -160px !important;
top: 0px; top: 0 !important;
width: auto !important;
display: flex;
flex-direction: column;
align-items: center;
overflow: visible !important;
.el-tabs__nav { .el-tabs__nav {
display: flex;
flex-direction: column;
gap: 16px; gap: 16px;
align-items: center;
overflow: visible !important;
float: none !important;
} }
.el-tabs__active-bar { .el-tabs__active-bar {
background-color: transparent; display: none;
right: 12px;
top: 11px;
height: 8px !important;
border-top: 6px solid transparent;
/* 顶部边框透明 */
border-bottom: 6px solid transparent;
/* 底部边框透明 */
border-left: 8px solid var(--bg-white-100);
/* 左侧边框有颜色 */
} }
} }
.el-tabs__content {
display: none;
}
.el-tabs__item.is-left { .el-tabs__item.is-left {
@extend .text-tip-1; @extend .text-tip-1;
color: var(--text-primary-65-color);
height: 32px; height: 32px;
padding: 4px 26px 4px 28px; padding: 4px 16px;
border-radius: 16px 16px 16px 16px; border-radius: 16px;
justify-content: center; justify-content: center;
color: var(--text-primary-65-color);
background-color: var(--bg-white-100);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
border: none !important;
width: auto;
} }
.el-tabs__item.is-left.is-active { .el-tabs__item.is-left.is-active {
color: var(--bg-white-100); color: var(--bg-white-100) !important;
background-color: var(--color-primary-100); background-color: var(--color-primary-100) !important;
box-shadow: none;
} }
} }
\ No newline at end of file
...@@ -270,6 +270,8 @@ $axis-width: 2px; ...@@ -270,6 +270,8 @@ $axis-width: 2px;
.axis-col { .axis-col {
width: $node-size; width: $node-size;
flex-shrink: 0; flex-shrink: 0;
position: relative;
// 修复:轴线不穿刺圆点 - 改为 flex-start,圆点用负margin浮在边界上
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
...@@ -281,7 +283,10 @@ $axis-width: 2px; ...@@ -281,7 +283,10 @@ $axis-width: 2px;
min-height: 12px; min-height: 12px;
&.invisible { &.invisible {
background-color: transparent; // 第一个节点上方无线:隐藏并占位,使圆点紧贴timeline起点
height: 0;
min-height: 0;
overflow: hidden;
} }
} }
...@@ -290,12 +295,28 @@ $axis-width: 2px; ...@@ -290,12 +295,28 @@ $axis-width: 2px;
height: $node-size; height: $node-size;
border-radius: 50%; border-radius: 50%;
flex-shrink: 0; flex-shrink: 0;
overflow: hidden; overflow: visible; // 改为 visible,使负 margin 不被裁剪
position: relative; position: relative;
z-index: 1; z-index: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
// 第一个节点特殊处理:居顶,上方无线
&:first-child {
margin-top: 0;
}
// 后续节点使用负 margin 浮在轴线上
&:not(:first-child) {
margin-top: -$node-size / 2;
}
// 最后一个节点特殊处理:下方无线
&:last-child {
margin-bottom: 0;
}
// 非首尾节点使用负 margin
&:not(:first-child):not(:last-child) {
margin-bottom: -$node-size / 2;
}
&.highlight { &.highlight {
background-color: rgba(245, 34, 45, 1); background-color: rgba(245, 34, 45, 1);
......
...@@ -23,7 +23,10 @@ ...@@ -23,7 +23,10 @@
</div> </div>
<!-- ECharts word cloud --> <!-- ECharts word cloud -->
<div ref="chartRef" class="pna-cloud"></div> <div v-if="keywords.length > 0" ref="chartRef" class="pna-cloud"></div>
<div v-else class="pna-cloud">
<el-empty description="暂无数据" :image-size="80" />
</div>
</div> </div>
</template> </template>
...@@ -172,5 +175,8 @@ onBeforeUnmount(() => { ...@@ -172,5 +175,8 @@ onBeforeUnmount(() => {
.pna-cloud { .pna-cloud {
flex: 1; flex: 1;
min-height: 400px; min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
} }
</style> </style>
...@@ -32,11 +32,12 @@ ...@@ -32,11 +32,12 @@
</div> </div>
</div> </div>
<div class="pn-rows" :class="{ 'pn-rows-loading': loading }"> <div class="pn-rows" :class="{ 'pn-rows-loading': loading || newsList.length === 0 }">
<div v-if="loading" class="pn-rows-spinner"> <div v-if="loading" class="pn-rows-spinner">
<div class="pn-spinner-icon"></div> <div class="pn-spinner-icon"></div>
<span class="pn-spinner-text">加载中...</span> <span class="pn-spinner-text">加载中...</span>
</div> </div>
<el-empty v-else-if="newsList.length === 0" description="暂无数据" :image-size="80" />
<div <div
v-for="(item, index) in newsList" v-for="(item, index) in newsList"
v-show="!loading" v-show="!loading"
...@@ -52,7 +53,7 @@ ...@@ -52,7 +53,7 @@
</div> </div>
</div> </div>
<div class="pn-footer"> <div class="pn-footer" v-if="newsList.length > 0">
<span class="pn-footer-total">{{ total }}条关键新闻</span> <span class="pn-footer-total">{{ total }}条关键新闻</span>
<div class="pn-pagination"> <div class="pn-pagination">
<button <button
......
...@@ -95,6 +95,10 @@ ...@@ -95,6 +95,10 @@
</div> </div>
</template> </template>
<div class="main"> <div class="main">
<template v-if="CharacterLatestDynamic.length === 0">
<el-empty description="暂无数据" :image-size="80" />
</template>
<template v-else>
<div v-for="item in CharacterLatestDynamic" :key="item" class="main-item"> <div v-for="item in CharacterLatestDynamic" :key="item" class="main-item">
<div class="time"> <div class="time">
<div class="year">{{ item.time.split("-")[0] }}</div> <div class="year">{{ item.time.split("-")[0] }}</div>
...@@ -127,9 +131,9 @@ ...@@ -127,9 +131,9 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="line-test"></div> --> </template>
</div> </div>
<div class="pagination"> <div v-if="CharacterLatestDynamic.length > 0" class="pagination">
<div class="total">{{ `共 ${total} 项` }}</div> <div class="total">{{ `共 ${total} 项` }}</div>
<el-pagination @current-change="handleCurrentChange" :page-size="pageSize" <el-pagination @current-change="handleCurrentChange" :page-size="pageSize"
:current-page="currentPage" background layout="prev, pager, next" :total="total" :current-page="currentPage" background layout="prev, pager, next" :total="total"
...@@ -233,26 +237,22 @@ ...@@ -233,26 +237,22 @@
</div> </div>
</div> </div>
<!-- 历史提案 --> <!-- 历史提案 -->
<!-- 在 member-of-congress 同级的左侧添加标签栏 -->
<!-- 历史提案 tab 对应的内容区 --> <!-- 历史提案 tab 对应的内容区 -->
<div v-if="infoActive === '历史提案'" class="proposal-wrapper"> <div v-if="infoActive === '历史提案'" class="proposal-wrapper">
<div class="proposal-tab-switcher"> <div class="proposal-tab-switcher">
<button :class="['proposal-tab', { active: newsTab === 'history' }]" @click="newsTab = 'history'"> <button :class="['proposal-tab', { active: newsTab === 'history' }]" @click="newsTab = 'history'">
<span>历史提案</span> <span>历史提案</span>
<svg v-if="newsTab === 'history'" class="proposal-tab-arrow" width="12" height="12" <svg v-if="newsTab === 'history'" class="proposal-tab-arrow" width="8" height="8" viewBox="0 0 8 8" fill="currentColor">
viewBox="0 0 12 12" fill="currentColor"> <path d="M4 0L8 4L4 8V0Z" />
<path d="M4 2l5 4-5 4V2z" />
</svg> </svg>
</button> </button>
<button :class="['proposal-tab', { active: newsTab === 'potential' }]" @click="newsTab = 'potential'"> <button :class="['proposal-tab', { active: newsTab === 'potential' }]" @click="newsTab = 'potential'">
<span>潜在提案</span> <span>潜在提案</span>
<svg v-if="newsTab === 'potential'" class="proposal-tab-arrow" width="12" height="12" <svg v-if="newsTab === 'potential'" class="proposal-tab-arrow" width="8" height="8" viewBox="0 0 8 8" fill="currentColor">
viewBox="0 0 12 12" fill="currentColor"> <path d="M4 0L8 4L4 8V0Z" />
<path d="M4 2l5 4-5 4V2z" />
</svg> </svg>
</button> </button>
</div> </div>
<HistoricalProposal v-if="newsTab === 'history'" /> <HistoricalProposal v-if="newsTab === 'history'" />
<PotentialNews v-else /> <PotentialNews v-else />
</div> </div>
...@@ -1837,45 +1837,40 @@ const handleClickTag = async (tag) => { ...@@ -1837,45 +1837,40 @@ const handleClickTag = async (tag) => {
/* 作为定位参考 */ /* 作为定位参考 */
} }
.proposal-tab.active {
background: #055FC2;
color: #fff;
}
.proposal-tab-switcher { .proposal-tab-switcher {
position: absolute; position: absolute;
right: calc(100% + 24px); left: 24px;
top: 0; top: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 16px;
z-index: 1; align-items: center;
} }
.proposal-tab { .proposal-tab {
width: 120px; width: 112px;
height: 32px; height: 32px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: center;
padding: 0 12px; gap: 4px;
font-family: 'Source Han Sans CN', 'Noto Sans SC', sans-serif; padding: 0 16px;
border-radius: 16px;
border: none;
font-size: 16px; font-size: 16px;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: 400; font-weight: 400;
color: #8c8c8c; color: var(--text-primary-65-color);
background: none; background-color: var(--bg-white-100);
border: none; box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
border-radius: 20px;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
box-sizing: border-box;
}
.proposal-tab.active { &.active {
background: #055FC2; color: var(--bg-white-100);
color: #fff; background-color: var(--color-primary-100);
box-shadow: none;
}
} }
.proposal-tab-arrow { .proposal-tab-arrow {
......
/**
* 人物主页 - 类型配置
* type 1: 科技领袖, type 2: 国会议员, type 3: 智库研究人员
*/
export const CHARACTER_CONFIG = {
1: {
// 科技领袖
rootClass: "tech-leader",
tabs: ["人物详情", "人物关系"],
tabWidth: "50%",
useImageProxy: false,
headerTagType: "areaTag",
wordCloudTitle: "科技观点",
yearDefault: "全部时间",
yearBuildMode: "dynamic",
yearStaticOptions: [],
showFundSource: false,
resumeMode: "inline",
resumeTitle: "职业履历",
resumeHeight: "1336px",
companySectionTitle: "实体信息",
basicInfoFields: [
{ label: "出生日期:", key: "birthday", type: "text" },
{ label: "国籍:", key: "country", type: "text" },
{ label: "教育背景:", key: "educationList", type: "education", format: "school(major)" },
{ label: "净资产:", key: "assets", type: "text" },
{ label: "职业:", key: "positionTitle", type: "text" },
{ label: "婚姻状况:", key: "marital", type: "text" },
{ label: "出生地:", key: "birthPlace", type: "text" }
],
boxHeightRules: [
{ condition: "undefined", value: "605px" },
{ condition: "empty", value: "405px" },
{ condition: "lte2", value: "505px" },
{ condition: "default", value: "625px" }
],
useAreaTypeApi: true,
fieldViewMode: "areaList",
dialogTagPrefix: "#",
dialogTagSuffix: " 相关领域标签",
showRelevantSituation: false,
historicalProposalType: null
},
2: {
// 国会议员
rootClass: "member-of-congress",
tabs: ["人物详情", "历史提案", "人物关系"],
tabWidth: "auto",
useImageProxy: true,
headerTagType: "inline",
wordCloudTitle: "科技观点",
yearDefault: "全部",
yearBuildMode: "static",
yearStaticOptions: ["全部", "2025", "2024", "2023", "2022", "2021", "2020"],
showFundSource: true,
resumeMode: "inline",
resumeTitle: "职业履历",
resumeHeight: "1556px",
companySectionTitle: "社交媒体",
basicInfoFields: [
{ label: "出生日期:", key: "birthday", type: "text" },
{ label: "现任职位:", key: "positionTitle", type: "text" },
{ label: "党派归属:", key: "party", type: "text" },
{ label: "教育背景:", key: "educationList", type: "education", format: "school+major" },
{ label: "代表州/选区:", key: "state", type: "text", titleClass: "address" },
{ label: "政治立场:", key: "political", type: "text", contentClass: "long" },
{ label: "出生地:", key: "birthPlace", type: "text" }
],
boxHeightRules: [
{ condition: "undefined", value: "545px" },
{ condition: "empty", value: "495px" },
{ condition: "lte2", value: "445px" },
{ condition: "default", value: "545px" }
],
useAreaTypeApi: false,
fieldViewMode: "sessionStorage",
dialogTagPrefix: "#",
dialogTagSuffix: "相关领域标签",
showRelevantSituation: false,
historicalProposalType: "bill"
},
3: {
// 智库研究人员
rootClass: "think-tank-person",
tabs: ["人物详情", "成果报告", "人物关系"],
tabWidth: "auto",
useImageProxy: false,
headerTagType: "areaTag",
wordCloudTitle: "核心观点",
yearDefault: "全部",
yearBuildMode: "static",
yearStaticOptions: ["全部", "2026", "2025", "2024", "2023", "2022", "2021"],
showFundSource: false,
resumeMode: "card",
resumeTitle: "政治履历",
resumeHeight: null,
companySectionTitle: "社交媒体",
basicInfoFields: [
{ label: "出生日期:", key: "birthday", type: "text" },
{ label: "现任职位:", key: "positionTitle", type: "text" },
{ label: "兼任职位:", key: "sideJob", type: "text" },
{ label: "政策倾向:", key: "political", type: "text" },
{ label: "国籍:", key: "country", type: "text" },
{ label: "教育背景:", key: "educationList", type: "education", format: "school(major)" },
{ label: "研究领域:", key: "industryList", type: "industry" }
],
boxHeightRules: [
{ condition: "undefined", value: "625px" },
{ condition: "empty", value: "425px" },
{ condition: "lte2", value: "525px" },
{ condition: "default", value: "625px" }
],
useAreaTypeApi: false,
fieldViewMode: "sessionStorage",
dialogTagPrefix: "",
dialogTagSuffix: "",
showRelevantSituation: false,
historicalProposalType: "news"
}
};
<template> <template>
<div class="character-page"> <div class="character-page">
<img src="./assets/images/background.png" alt="" class="bg" /> <img src="./assets/images/background.png" alt="" class="bg" />
<ModuleHeader />
<!-- 主要内容 --> <!-- 主要内容 -->
<div class="main"> <div class="main">
<!-- 科技领袖 --> <unified-character :type="type" :person-id="personId" />
<tech-leader v-if="type == 1"/>
<!-- 国会议员 -->
<member-of-congress v-if="type == 2" />
<!-- 智库研究人员 -->
<think-tank-person v-if="type == 3" />
</div> </div>
</div> </div>
</template> </template>
...@@ -16,9 +12,8 @@ ...@@ -16,9 +12,8 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import TechLeader from './components/techLeader/index.vue'; import UnifiedCharacter from './components/unified/index.vue';
import MemberOfCongress from './components/memberOfCongress/index.vue'; import ModuleHeader from '@/components/base/moduleHeader/index.vue';
import ThinkTankPerson from './components/thinkTankPerson/index.vue';
import { getCharacterGlobalInfo } from "@/api/characterPage/characterPage.js"; import { getCharacterGlobalInfo } from "@/api/characterPage/characterPage.js";
......
...@@ -21,12 +21,11 @@ ...@@ -21,12 +21,11 @@
<div class="flex-display content-row"> <div class="flex-display content-row">
<div class="left-col"> <div class="left-col">
<AnalysisBox title="打压动态" :showAllBtn="false"> <AnalysisBox title="动态" :showAllBtn="false">
<div class="news-list-wrap"> <div class="news-list-wrap">
<div class="news-scroll-area"> <div class="news-scroll-area">
<template v-for="(item, idx) in newsList" :key="item.id">
<div <div
v-for="item in newsList"
:key="item.id"
class="news-item flex-display mouse-hover" class="news-item flex-display mouse-hover"
@click="handleNewsClick(item)" @click="handleNewsClick(item)"
> >
...@@ -34,15 +33,19 @@ ...@@ -34,15 +33,19 @@
<span class="item-thumb-text">新闻资讯</span> <span class="item-thumb-text">新闻资讯</span>
</div> </div>
<div class="item-body"> <div class="item-body">
<div class="item-top flex-display">
<p class="text-tip-1-bold text-primary-80-clor item-title">{{ item.title }}</p> <p class="text-tip-1-bold text-primary-80-clor item-title">{{ item.title }}</p>
<p class="text-tip-2 text-primary-65-clor item-content">{{ item.content }}</p>
</div>
<div class="item-meta flex-display"> <div class="item-meta flex-display">
<span class="text-tip-2 text-primary-50-clor">{{ item.date }}</span> <span class="text-tip-2 text-primary-65-clor">{{ item.date }}</span>
<span class="text-tip-2 text-primary-50-clor meta-sep">·</span> <span class="text-tip-2 text-primary-65-clor meta-sep">·</span>
<span class="text-tip-2 text-primary-50-clor">{{ item.source }}</span> <span class="text-tip-2 text-primary-65-clor">{{ item.source }}</span>
</div>
</div>
<p class="text-tip-2 text-primary-65-clor item-content">{{ item.content }}</p>
</div> </div>
</div> </div>
<div v-if="idx < newsList.length - 1" class="news-divider"></div>
</template>
</div> </div>
<div class="pagination-row flex-display"> <div class="pagination-row flex-display">
...@@ -59,7 +62,7 @@ ...@@ -59,7 +62,7 @@
@click="changePage(p)" @click="changePage(p)"
>{{ p }}</button> >{{ p }}</button>
<span v-if="pagination.page < pagination.totalPages - 2" class="page-ellipsis text-primary-50-clor">...</span> <span v-if="pagination.page < pagination.totalPages - 2" class="page-ellipsis text-primary-50-clor">...</span>
<button class="page-btn" :class="{ active: pagination.page === pagination.totalPages }" @click="changePage(pagination.totalPages)">{{ pagination.totalPages }}</button> <button v-if="pagination.totalPages > 1" class="page-btn" :class="{ active: pagination.page === pagination.totalPages }" @click="changePage(pagination.totalPages)">{{ pagination.totalPages }}</button>
<button class="page-btn" :disabled="pagination.page === pagination.totalPages" @click="changePage(pagination.page + 1)">&gt;</button> <button class="page-btn" :disabled="pagination.page === pagination.totalPages" @click="changePage(pagination.page + 1)">&gt;</button>
</div> </div>
</div> </div>
...@@ -68,7 +71,7 @@ ...@@ -68,7 +71,7 @@
</div> </div>
<div class="right-col"> <div class="right-col">
<AnalysisBox title="联盟简介" :showAllBtn="false"> <AnalysisBox title="简介" :showAllBtn="false">
<div class="intro-wrap"> <div class="intro-wrap">
<div class="intro-row flex-display"> <div class="intro-row flex-display">
<span class="text-tip-2-bold text-primary-80-clor intro-label">时间:</span> <span class="text-tip-2-bold text-primary-80-clor intro-label">时间:</span>
...@@ -104,7 +107,7 @@ import { ref, computed, onMounted } from 'vue' ...@@ -104,7 +107,7 @@ import { ref, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue' import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue'
import AreaTag from '@/components/base/AreaTag/index.vue' import AreaTag from '@/components/base/AreaTag/index.vue'
import { getNewsDetail, getNewsList } from './mock/newsDetail' import { getUnionIntroduction, getUnionDynamicList } from '@/api/ruleRestriction/index.js'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
...@@ -125,10 +128,10 @@ const newsDetail = ref({ ...@@ -125,10 +128,10 @@ const newsDetail = ref({
const newsList = ref([]) const newsList = ref([])
const pagination = ref({ const pagination = ref({
page: 5, page: 1,
pageSize: 10, pageSize: 10,
total: 153, total: 0,
totalPages: 16 totalPages: 1
}) })
const visiblePages = computed(() => { const visiblePages = computed(() => {
...@@ -145,16 +148,48 @@ const visiblePages = computed(() => { ...@@ -145,16 +148,48 @@ const visiblePages = computed(() => {
return pages return pages
}) })
const loadNewsDetail = () => { const loadNewsDetail = async () => {
newsDetail.value = getNewsDetail(newsId.value) try {
const res = await getUnionIntroduction(newsId.value)
if (res.code === 200) {
const data = res.data
newsDetail.value = {
title: data.unionNameZh || '',
subTitle: data.unionName || '',
tags: data.domains || [],
time: data.time || '',
background: data.background || '',
countries: (data.memberCountries || []).map(c => ({ name: c.countryName }))
}
}
} catch (error) {
console.error('获取联盟简介失败:', error)
}
} }
const loadNewsList = (page = 1) => { const loadNewsList = async (page = 1) => {
const data = getNewsList(page, pagination.value.pageSize) try {
newsList.value = data.list const res = await getUnionDynamicList({
pagination.value.page = data.page unionId: newsId.value,
pagination.value.total = data.total currentPage: page,
pagination.value.totalPages = data.totalPages pageSize: pagination.value.pageSize
})
if (res.code === 200) {
const data = res.data
newsList.value = (data.content || []).map(item => ({
id: item.newsId,
title: item.title || '',
content: item.summary || '',
date: item.publishedTime || '',
source: item.source || ''
}))
pagination.value.page = page
pagination.value.total = data.totalElements || 0
pagination.value.totalPages = data.totalPages || 1
}
} catch (error) {
console.error('获取联盟动态失败:', error)
}
} }
const changePage = (page) => { const changePage = (page) => {
...@@ -168,11 +203,14 @@ const handleNewsClick = (item) => { ...@@ -168,11 +203,14 @@ const handleNewsClick = (item) => {
onMounted(() => { onMounted(() => {
loadNewsDetail() loadNewsDetail()
loadNewsList(pagination.value.page) loadNewsList(1)
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.main-container1){
overflow: auto;
}
.page-wrap { .page-wrap {
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
...@@ -228,7 +266,6 @@ onMounted(() => { ...@@ -228,7 +266,6 @@ onMounted(() => {
.left-col { .left-col {
width: 1064px; width: 1064px;
height: 1284px;
flex-shrink: 0; flex-shrink: 0;
} }
...@@ -241,33 +278,32 @@ onMounted(() => { ...@@ -241,33 +278,32 @@ onMounted(() => {
.news-list-wrap { .news-list-wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: calc(100% - 48px);
padding: 0 20px; padding: 0 20px;
} }
.news-scroll-area { .news-scroll-area {
flex: 1; flex: 1;
min-height: 0;
overflow-y: auto;
} }
.news-item { .news-item {
gap: 16px; gap: 20px;
padding: 16px 0; padding: 0;
border-bottom: 1px solid #eaecee; align-items: center;
align-items: flex-start;
cursor: pointer; cursor: pointer;
transition: background 0.15s; transition: background 0.15s;
}
&:last-child { .news-divider {
border-bottom: none; width: 100%;
} height: 1px;
background: #f0f2f4;
margin: 12px 0;
} }
.item-thumb { .item-thumb {
width: 100px; width: 97px;
height: 70px; height: 72px;
border-radius: 6px; border-radius: 4px;
background: linear-gradient(135deg, #4a7bf7 0%, #6b8cff 100%); background: linear-gradient(135deg, #4a7bf7 0%, #6b8cff 100%);
flex-shrink: 0; flex-shrink: 0;
} }
...@@ -280,13 +316,24 @@ onMounted(() => { ...@@ -280,13 +316,24 @@ onMounted(() => {
.item-body { .item-body {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.item-top {
align-items: center;
width: 100%;
gap: 0;
} }
.item-title { .item-title {
margin: 0 0 6px 0; margin: 0;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
flex: 1;
min-width: 0;
} }
.item-content { .item-content {
......
...@@ -224,6 +224,19 @@ export default { ...@@ -224,6 +224,19 @@ export default {
:deep(.el-timeline) { :deep(.el-timeline) {
max-width: 688px !important; max-width: 688px !important;
padding: 0; padding: 0;
overflow: hidden;
}
:deep(.el-timeline-item) {
padding-bottom: 4px;
}
:deep(.el-timeline-item:last-child) {
padding-bottom: 0;
}
:deep(.el-timeline-item:last-child .el-timeline-item__wrapper) {
padding-bottom: 0;
} }
:deep(.el-timeline-item__wrapper) { :deep(.el-timeline-item__wrapper) {
...@@ -253,4 +266,9 @@ export default { ...@@ -253,4 +266,9 @@ export default {
text-align: justify; text-align: justify;
} }
:deep(.el-timeline-item:last-child .el-timeline-item__tail) {
visibility: hidden;
display: none;
}
</style> </style>
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
<!-- 涉华观点次数列 --> <!-- 涉华观点次数列 -->
<div class="row-col col-count"> <div class="row-col col-count">
<el-progress :percentage="getProgress(item.mediaQuoteCount)" :show-text="false" <el-progress :percentage="getProgress(item.chinaRelatedCount)" :show-text="false"
style="/* 矩形 244 */ width: 82px; height: 8px" :status="getStatus(getProgress(item.mediaQuoteCount))" /> style="/* 矩形 244 */ width: 82px; height: 8px" :status="getStatus(getProgress(item.chinaRelatedCount))" />
<span class="count-text">{{ item.mediaQuoteCount }}</span> <span class="count-text">{{ item.chinaRelatedCount }}</span>
</div> </div>
<!-- 媒体引用次数列 --> <!-- 媒体引用次数列 -->
...@@ -46,7 +46,6 @@ ...@@ -46,7 +46,6 @@
</template> </template>
<script setup> <script setup>
import { ref,onMounted,defineProps,watch } from "vue"; import { ref,onMounted,defineProps,watch } from "vue";
import personData from "../json/personData.json"; // 引入JSON数据
import {getMainCharactersView } from "@/api/technologyFigures/technologyFigures"; import {getMainCharactersView } from "@/api/technologyFigures/technologyFigures";
import { useCharacterNav } from "../utils/useCharacterNav"; import { useCharacterNav } from "../utils/useCharacterNav";
const { handleClickToCharacter } = useCharacterNav(); const { handleClickToCharacter } = useCharacterNav();
...@@ -99,14 +98,16 @@ const handlegetMainCharactersViewFn = async () => { ...@@ -99,14 +98,16 @@ const handlegetMainCharactersViewFn = async () => {
}); });
} }
} catch (error) {} } catch (error) {
console.error("获取主要人物涉华观点统计失败:", error);
}
}; };
onMounted(async () => { onMounted(async () => {
handlegetMainCharactersViewFn(); handlegetMainCharactersViewFn();
}); });
const personList = ref(); const personList = ref([]);
// 进度条状态 // 进度条状态
const getStatus = _percent => { const getStatus = _percent => {
const percent = _percent; const percent = _percent;
...@@ -120,8 +121,9 @@ const getStatus = _percent => { ...@@ -120,8 +121,9 @@ const getStatus = _percent => {
}; };
// 计算进度条宽度(基于最大值的百分比) // 计算进度条宽度(基于最大值的百分比)
const getMaxCount = () => { const getMaxCount = () => {
const counts = personList.value.flatMap(item => [item.chinaRelatedCount, item.mediaQuoteCount]); const counts = personList.value.flatMap(item => [item.chinaRelatedCount || 0, item.mediaQuoteCount || 0]);
return Math.max(...counts); if (counts.length === 0) return 1;
return Math.max(...counts, 1);
}; };
const getProgress = count => (count / getMaxCount()) * 100; const getProgress = count => (count / getMaxCount()) * 100;
</script> </script>
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<img :src="item.avatar" alt="" class="source-library-avatar" /> <img :src="item.avatar" alt="" class="source-library-avatar" />
</div> </div>
<div class="source-library-text-content"> <div class="source-library-text-content">
<div class="card-main" :class="{ 'no-title': !item.title }"> <div class="card-main" ">
<h3 class="source-library-name">{{ item.name }}</h3> <h3 class="source-library-name">{{ item.name }}</h3>
<p class="source-library-title" v-if="item.title">{{ item.title }}</p> <p class="source-library-title" v-if="item.title">{{ item.title }}</p>
<div class="taglist"> <div class="taglist">
...@@ -177,7 +177,11 @@ const handleClcikToCharacter = async id => { ...@@ -177,7 +177,11 @@ const handleClcikToCharacter = async id => {
ElMessage.warning("找不到当前人员的类型值!"); ElMessage.warning("找不到当前人员的类型值!");
return; return;
} }
} catch (error) {} } catch (error) {
if (error.name !== "AbortError") {
console.error("获取人物资源库失败:", error);
}
}
}; };
onMounted(async () => { onMounted(async () => {
...@@ -209,7 +213,7 @@ const handlePageChange = p => { ...@@ -209,7 +213,7 @@ const handlePageChange = p => {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
border: 1px solid rgba(234, 236, 238, 1); border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---10, 10px); border-radius: 10px;
padding: 20px 18px; padding: 20px 18px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
...@@ -249,9 +253,9 @@ const handlePageChange = p => { ...@@ -249,9 +253,9 @@ const handlePageChange = p => {
.source-library-avatar { .source-library-avatar {
/* 椭圆 142 */ /* 椭圆 142 */
width: 88px; width: 92px;
height: 88px; height: 121px;
border-radius: 50%; // border-radius: 50%;
} }
.source-library-text-content { .source-library-text-content {
......
...@@ -81,11 +81,6 @@ ...@@ -81,11 +81,6 @@
import * as echarts from "echarts"; import * as echarts from "echarts";
import worldJson from "@/assets/json/world.json"; import worldJson from "@/assets/json/world.json";
import { getCharacterTrends } from "@/api/technologyFigures/technologyFigures"; import { getCharacterTrends } from "@/api/technologyFigures/technologyFigures";
import { ref } from "vue";
const CharacterTrends = ref([]);
const time = ref("");
const date = ref("");
const handlegetCharacterTrendsFn = async () => { const handlegetCharacterTrendsFn = async () => {
const params = { const params = {
...@@ -104,7 +99,9 @@ const handlegetCharacterTrendsFn = async () => { ...@@ -104,7 +99,9 @@ const handlegetCharacterTrendsFn = async () => {
}; };
}); });
} }
} catch (error) {} } catch (error) {
console.error("获取人物动向失败:", error);
}
}; };
function getDateDaysAgo(days) { function getDateDaysAgo(days) {
...@@ -169,6 +166,9 @@ export default { ...@@ -169,6 +166,9 @@ export default {
tickWidth: 10, tickWidth: 10,
viewStartIndex: 0, viewStartIndex: 0,
viewDays: 120, viewDays: 120,
CharacterTrends: [],
time: "",
date: "",
}; };
}, },
computed: { computed: {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<p class="speech-stance-title">{{ item.positionTitle }}</p> <p class="speech-stance-title">{{ item.positionTitle }}</p>
</div> </div>
<p class="speech-stance-content" :title="item.remarks">{{ item.remarks }}</p> <p class="speech-stance-content" :title="item.remarks"><span class="speech-stance-text">{{ item.remarks }}</span></p>
</div> </div>
</div> </div>
...@@ -95,11 +95,11 @@ const handleClcikToCharacter = async id => { ...@@ -95,11 +95,11 @@ const handleClcikToCharacter = async id => {
const aId = ref(); const aId = ref();
const params = ref({}); const params = ref({});
watch(() => props.fieldSelected, (val) => { watch(() => [props.fieldSelected, props.areaId], (val) => {
const [fieldVal, areaVal] = val;
aId.value = props.areaId; aId.value = areaVal;
if(val !== "全部领域"){ if(fieldVal !== "全部领域"){
params.value.industryId = aId.value; params.value.industryId = areaVal;
}else{ }else{
params.value = {}; params.value = {};
} }
...@@ -117,7 +117,9 @@ const handlegetPersonRelationFn = async () => { ...@@ -117,7 +117,9 @@ const handlegetPersonRelationFn = async () => {
if (res.code === 200) { if (res.code === 200) {
PersonRelation.value = res.data; PersonRelation.value = res.data;
} }
} catch (error) {} } catch (error) {
console.error("获取重要人物言论及立场失败:", error);
}
}; };
onMounted(async () => { onMounted(async () => {
...@@ -129,15 +131,13 @@ onMounted(async () => { ...@@ -129,15 +131,13 @@ onMounted(async () => {
<style scoped> <style scoped>
.speech-stance-container { .speech-stance-container {
width: 1530px; width: 1530px;
/* max-width: 900px; */ margin: 15px 35px;
margin: 0 auto;
margin: 5px 35px;
} }
.speech-stance-grid { .speech-stance-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 30px 9px; gap: 28px 9px;
} }
/* 响应式:小屏变一列 */ /* 响应式:小屏变一列 */
...@@ -204,8 +204,16 @@ onMounted(async () => { ...@@ -204,8 +204,16 @@ onMounted(async () => {
line-height: 30px; line-height: 30px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: left; text-align: left;
/* 宽度保持不变,ellipsis 移至内部 span */
}
.speech-stance-text {
display: inline-block;
max-width: 100%;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
padding-right: 24px;
vertical-align: top;
} }
</style> </style>
\ No newline at end of file
...@@ -13,9 +13,9 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6 ...@@ -13,9 +13,9 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6
}, },
grid: { grid: {
top: "50px", top: "50px",
right: "50px", right: "24px",
bottom: "0px", bottom: "0px",
left: "40px", left: "24px",
containLabel: true containLabel: true
}, },
legend: { legend: {
...@@ -47,7 +47,13 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6 ...@@ -47,7 +47,13 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6
boundaryGap: false, boundaryGap: false,
data: dataX, data: dataX,
axisLabel: { axisLabel: {
fontSize: 14 // 设置X轴字体大小 fontSize: 14,
color: "rgba(59, 65, 75, 1)"
},
axisLine: {
lineStyle: {
color: "#E7F3FF"
}
} }
} }
], ],
...@@ -63,8 +69,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6 ...@@ -63,8 +69,7 @@ const getMultiLineChart = (dataX, dataY1, dataY2, dataY3, dataY4, dataY5, dataY6
}, },
axisLabel: { axisLabel: {
fontSize: 14 // 设置X轴字体大小 fontSize: 14 // 设置X轴字体大小
}, }
} }
], ],
series: [ series: [
......
...@@ -13,6 +13,7 @@ import { getPersonSummaryInfo } from "@/api/technologyFigures/technologyFigures ...@@ -13,6 +13,7 @@ import { getPersonSummaryInfo } from "@/api/technologyFigures/technologyFigures
// 人物类型名称 -> type 映射 // 人物类型名称 -> type 映射
const PERSON_TYPE_MAP = { const PERSON_TYPE_MAP = {
"科技企业领袖": 1, "科技企业领袖": 1,
"政府官员": 1,
"国会议员": 2, "国会议员": 2,
"智库研究人员": 3, "智库研究人员": 3,
}; };
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论