提交 699983ea authored 作者: yanpeng's avatar yanpeng

Merge branch 'pre' of http://8.140.26.4:10003/caijian/risk-monitor into yp-dev

流水线 #287 已通过 于阶段
in 1 分 30 秒
......@@ -56,11 +56,12 @@ export function getBillDyqk(params) {
* @param {id,cRelated,currentPage,pageSize}
* @header token
*/
export function getBillBackground(params) {
export function getBillBackground(params, config = {}) {
return request({
method: 'GET',
url: `/api/billInfoBean/background/${params.id}`,
params,
signal: config.signal
})
}
// 相关事件-根据法案ID获取相关事件信息
......
......@@ -91,7 +91,7 @@ export function getHylyList() {
/**
* 智库概览/智库动态-智库报告、调查项目
* 智库概览/智库动态-智库报告
* GET /api/thinkTankOverview/report
* 常用 query:pageNum, pageSize, sortFun, domainIds, startDate, endDate, category(调查项目), thinkTankId(详情页), keyword(动态搜索)
*/
......@@ -103,6 +103,68 @@ export function getThinkTankReport(params) {
})
}
//智库概览调查项目
export function getThinkTankProjects(params) {
return request({
method: 'GET',
url: `/api/think-tank/projects`,
params
})
}
//智库概览页国会听证会
export function getThinkTankTestimonies() {
return request({
method: 'GET',
url: `/api/think-tank/testimonies`,
})
}
//智库调查项目详情主页
export function getThinkTankProjectsInfo(params) {
return request({
method: 'GET',
url: `/api/think-tank/projects/${params.id}`,
})
}
//智库国会听证会详情主页
export function getThinkTankHearingInfo(params) {
return request({
method: 'GET',
url: `/api/think-tank/testimonies/${params.id}`,
})
}
// 智库详情-调查项目(按智库 id)
export function getThinkTankProjectsByThinkTankId(params) {
return request({
method: 'GET',
url: `/api/think-tank/${params.thinkTankId}/projects`,
params: {
pageNum: params.pageNum,
pageSize: params.pageSize,
}
})
}
// 智库动态-国会听证会(按智库 id)
export function getThinkTankTestimoniesByThinkTankId(params) {
return request({
method: 'GET',
url: `/api/think-tank/${params.thinkTankId}/testimonies`,
params: {
pageNum: params.pageNum,
pageSize: params.pageSize,
}
})
}
//智库调查项目详情作者
export function getThinkTankProjectsAuthors(params) {
return request({
method: 'GET',
url: `/api/think-tank/projects/${params.id}/team`,
})
}
// 智库概览:政策建议(资源库-政策建议)
export function getThinkTankOverviewPolicy(params) {
return request({
......@@ -378,6 +440,15 @@ export const getThinkTankReportRelated = (params) => {
}
);
}
//调查项目:获取项目报告
export const getThinkTankProjectRelated = (params) => {
return request(
{
method: 'GET',
url: `/api/think-tank/projects/${params}/reports`,
}
);
}
//获取报告原文
export const getThinkTankReportcontentUrl = (params) => {
......@@ -424,7 +495,21 @@ export function getThinkTankReportViewpoint(params) {
}
})
}
// 获取报告核心论点(支持关键字搜索)
export function getThinkTankHearingViewpoint(params) {
const { testimonyId, pageSize, keyword = '',pageNum } = params
return request({
method: 'GET',
url: `/api/think-tank/testimonies/qa`,
params: {
pageSize,
keyword,
pageNum,
testimonyId
}
})
}
//获取涉及科技领域
export function getThinkTankReportIndustry(params) {
return request({
......@@ -440,6 +525,20 @@ export function getThinkTankReportIndustryCloud(params) {
url: `/api/thinkTankReport/keyword/${params.id}`,
})
}
//获取调查项目词云
export function getThinkTankProjectCloud(params) {
return request({
method: 'GET',
url: `/api/think-tank/projects/${params.id}/word-cloud`,
})
}
//获取国会听证会词云
export function getThinkTankHearingCloud(params) {
return request({
method: 'GET',
url: `/api/think-tank/testimonies/${params.id}/wordcloud`,
})
}
//获取政策建议落实情况
export function getThinkTankReportPolicy(params) {
......
......@@ -41,17 +41,31 @@ const thinktankRoutes = [
path: "/thinkTank/reportOriginal/:id",
name: "ReportOriginal",
component: ReportOriginal,
meta: {
title: "报告原文",
dynamicTitle: true,
titleStorageKey: "reportOriginalTabName"
}
},
{
path: "/thinkTank/SurveyProjectView/:id",
name: "SurveyProjectView",
component: SurveyProjectView,
meta: {
title: "调查项目",
dynamicTitle: true,
titleStorageKey: "surveyProjectTabName"
}
},
{
path: "/thinkTank/CongressHearingView/:id",
name: "CongressHearingView",
component: CongressHearingView,
meta: {
title: "国会听证会",
dynamicTitle: true,
titleStorageKey: "congressHearingTabName"
}
},
{
path: "/thinkTank/allThinkTank",
......
......@@ -8,7 +8,7 @@
<el-button :type="box1Btn2Type" plain @click="handleClickBox1Btn(2)">全部背景</el-button>
</div>
</template>
<div class="box1-main">
<div class="box1-main" v-loading="backgroundLoading">
<div class="box1-main-center">
<div class="box1-main-item" v-for="item in backgroundDisplayList" :key="item.id">
<div class="id">{{ item.displayIndex }}</div>
......@@ -198,6 +198,8 @@ const handleClickBox2Btn = index => {
const aboutUserList = ref([]);
const backgroundList = ref([]);
const backgroundLoading = ref(false);
let backgroundAbortController = null;
const eventList = ref([]);
......@@ -244,6 +246,12 @@ const nextIconColor = computed(() => (currentIndex.value < personList.value.leng
// 获取立法背景内容
const handleGetBillBackground = async () => {
if (backgroundAbortController) {
backgroundAbortController.abort();
}
const controller = new AbortController();
backgroundAbortController = controller;
const cRelated = box1BtnActive.value === 1 ? "Y" : "N";
const params = {
cRelated: cRelated,
......@@ -251,11 +259,21 @@ const handleGetBillBackground = async () => {
currentPage: currentPage.value - 1,
pageSize: 10
};
backgroundLoading.value = true;
try {
const res = await getBillBackground(params);
const res = await getBillBackground(params, { signal: controller.signal });
backgroundList.value = res.data.content;
total.value = res.data.totalElements; // 假设API返回totalElements
} catch (error) { }
} catch (error) {
if (error?.name !== "AbortError" && error?.code !== "ERR_CANCELED") {
console.error(error);
}
} finally {
if (backgroundAbortController === controller) {
backgroundLoading.value = false;
backgroundAbortController = null;
}
}
};
// 获取相关事件
......
......@@ -153,7 +153,7 @@
class="item-box"
v-for="item in sharedEvents"
:key="`shared-${item.id}`"
style="width: 280px; flex-shrink: 0;"
:style="{ width: TIMELINE_ITEM_WIDTH_PX + 'px', flexShrink: 0 }"
>
<div class="item-time">{{ item.actionDate }}</div>
<div class="item-box-dot"></div>
......@@ -211,7 +211,15 @@ const dialogPos = ref({ left: "0px", top: "0px" });
const ORG_SENATE = "参议院";
const ORG_HOUSE = "众议院";
const PRESIDENT_KEYWORD = "呈递给总统";
/** 卡片宽度;同一条时间线上相邻圆点间距 ≥ 此值,避免同轨卡片横向重叠 */
const TIMELINE_ITEM_WIDTH_PX = 280;
/** 时间线锚点为圆点;与样式 .item-box-dot { left: 10px } 一致 */
const DOT_LEFT_IN_ITEM_BOX_PX = 10;
/** 与 scoped 样式中 .top/.bottom .content-box 的 margin-left 一致,用于跨轨圆点与「两密点中点」对齐 */
const SENATE_CONTENT_BOX_MARGIN_LEFT_PX = 134;
const HOUSE_CONTENT_BOX_MARGIN_LEFT_PX = 30;
/** 双轨主线在最后一个圆点/卡片之后保留的空白(原先用整卡宽 W 导致尾部过长) */
const TIMELINE_LINE_TAIL_PADDING_PX = 48;
const ARROW_SEGMENT_TOTAL_PX = 16;
const DIAGONAL_LINE_WIDTH_PX = 127;
......@@ -281,50 +289,98 @@ const dualLaneTimeline = computed(() => {
.filter((item) => !isDualEvent(item));
});
const senateEventsPositioned = computed(() => {
const list = [];
let slotIndex = 0;
dualLaneTimeline.value.forEach((item) => {
if (!isSenateEvent(item)) return;
list.push({
key: item.id,
/**
* 双轨事件已按时间排好(dualLaneTimeline)。依次放置:与前一条同轨则绝对 x + W,
* 异轨则以前一条圆点绝对位置为锚 + 0.5W(方案 C)。单轨(灰线)仍用 sharedEvents 顺序定宽。
*/
const dualLaneLayout = computed(() => {
const U = dualLaneTimeline.value;
const W = TIMELINE_ITEM_WIDTH_PX;
const chamberRowSenate = (item) => {
const s = isSenateEvent(item);
const h = isHouseEvent(item);
if (s && !h) return true;
if (h && !s) return false;
return Boolean(s);
};
const idxInU = (item) => U.indexOf(item);
const toSlots = (list, posByItem) =>
list.map((item) => ({
key: `${idxInU(item)}-${item.id ?? "e"}`,
item,
left: slotIndex * TIMELINE_ITEM_WIDTH_PX
});
slotIndex += 1;
});
return list;
});
left: (posByItem.get(item) ?? 0) - DOT_LEFT_IN_ITEM_BOX_PX
}));
const houseEventsPositioned = computed(() => {
const list = [];
let slotIndex = 0;
dualLaneTimeline.value.forEach((item) => {
if (!isHouseEvent(item)) return;
list.push({
key: item.id,
item,
left: slotIndex * TIMELINE_ITEM_WIDTH_PX
});
slotIndex += 1;
});
return list;
if (!U.length) {
return { senateSlots: [], houseSlots: [], maxDualLaneRightPx: 0 };
}
const posByItem = new Map();
let prevAbs = null;
let prevSenate = null;
for (const item of U) {
const sen = chamberRowSenate(item);
const margin = sen ? SENATE_CONTENT_BOX_MARGIN_LEFT_PX : HOUSE_CONTENT_BOX_MARGIN_LEFT_PX;
const absDot =
prevAbs === null ? margin : prevAbs + (sen === prevSenate ? W : W / 2);
posByItem.set(item, absDot - margin);
prevAbs = absDot;
prevSenate = sen;
}
/** 相对 content-box 左缘:卡片右边界 = layoutDotX - DOT_LEFT + W */
let maxDualLaneRightPx = 0;
for (const item of U) {
const layoutDotX = posByItem.get(item) ?? 0;
const rightPx = layoutDotX - DOT_LEFT_IN_ITEM_BOX_PX + W;
maxDualLaneRightPx = Math.max(maxDualLaneRightPx, rightPx);
}
return {
senateSlots: toSlots(
U.filter((item) => isSenateEvent(item)),
posByItem
),
houseSlots: toSlots(
U.filter((item) => isHouseEvent(item)),
posByItem
),
maxDualLaneRightPx
};
});
const senateEventsPositioned = computed(() => dualLaneLayout.value.senateSlots);
const houseEventsPositioned = computed(() => dualLaneLayout.value.houseSlots);
const sharedEvents = computed(() => {
return sortedTimeline.value.filter((item, idx) => {
return isDualEvent(item) || idx >= mergeIndexExclusive.value;
});
});
const dualLaneCount = computed(() => {
return Math.max(senateEventsPositioned.value.length, houseEventsPositioned.value.length);
/** 双轨 content-box 内所需宽度:最后卡片右缘 + 尾部留白(与 maxLineWidth 中的 extent 一致) */
const dualLaneContentExtentPx = computed(() => {
if (!dualLaneTimeline.value.length) return 0;
return (
dualLaneLayout.value.maxDualLaneRightPx + TIMELINE_LINE_TAIL_PADDING_PX
);
});
/**
* 双轨横线(top-line / bottom-line 共用)宽度:起点 left:110,需画到与卡片区右缘对齐。
* 右缘 x = 120 + 参院 margin + extent = 254 + extent,故宽度 = 254 + extent - 110 = 144 + extent。
* 旧式 254+extent 多 110px,导致上下线都偏长。
*/
const maxLineWidth = computed(() => {
const senateWidth = 254 + dualLaneCount.value * TIMELINE_ITEM_WIDTH_PX;
const houseWidth = 150 + dualLaneCount.value * TIMELINE_ITEM_WIDTH_PX;
return Math.max(senateWidth, houseWidth);
if (!dualLaneTimeline.value.length) {
return Math.max(254, 150);
}
const extent = dualLaneContentExtentPx.value;
return 120 + SENATE_CONTENT_BOX_MARGIN_LEFT_PX + extent - 110;
});
const lineWidth = computed(() => `${maxLineWidth.value}px`);
......@@ -350,22 +406,41 @@ const rightArrowCount = computed(() => {
return Math.max(1, Math.ceil(sharedLineWidth.value / ARROW_SEGMENT_TOTAL_PX));
});
/** 参院 content 起点更靠右(margin 134 vs 30),众院 box 需多 104px 才能与参院右缘对齐 */
const CONTENT_BOX_WIDTH_DELTA_SENATE_HOUSE_PX =
SENATE_CONTENT_BOX_MARGIN_LEFT_PX - HOUSE_CONTENT_BOX_MARGIN_LEFT_PX;
const senateBoxStyle = computed(() => ({
width: `${maxLineWidth.value + 110 - 254}px`,
width: `${
dualLaneTimeline.value.length
? dualLaneContentExtentPx.value
: 110
}px`,
justifyContent: "flex-start"
}));
const houseBoxStyle = computed(() => ({
width: `${maxLineWidth.value + 110 - 150}px`,
width: `${
dualLaneTimeline.value.length
? dualLaneContentExtentPx.value + CONTENT_BOX_WIDTH_DELTA_SENATE_HOUSE_PX
: 214
}px`,
justifyContent: "flex-start"
}));
const rightPos = computed(() => `${maxLineWidth.value + 90}px`);
const sharedBoxStyle = computed(() => ({
left: `${maxLineWidth.value + 219}px`,
width: `${Math.max(sharedLineWidth.value, sharedEvents.value.length * TIMELINE_ITEM_WIDTH_PX)}px`
}));
const sharedBoxStyle = computed(() => {
const rightTopPx = Number.parseFloat(String(rightTop.value)) || 0;
const rightCenterY = rightTopPx + 12;
return {
left: `${maxLineWidth.value + 230}px`,
top: `${rightCenterY}px`,
transform: "translateY(-100%)",
width: `${Math.max(sharedLineWidth.value, sharedEvents.value.length * TIMELINE_ITEM_WIDTH_PX)}px`
};
});
const topLineEndRef = ref(null);
const bottomLineEndRef = ref(null);
......@@ -547,13 +622,18 @@ const updateRightTop = () => {
}
}
/* .top 高 260px,时间线 top:242px;卡片下缘应对齐线顶:距容器底 18px */
.content-box {
position: relative;
margin-left: 134px;
margin-bottom: 19px;
margin-bottom: 0;
align-self: stretch;
min-height: 0;
.item-box {
position: absolute;
top: auto;
bottom: 18px;
}
}
}
......@@ -808,7 +888,7 @@ const updateRightTop = () => {
.shared-content-box {
position: absolute;
top: 170px;
top: 254px;
display: flex;
.item-box {
......
......@@ -834,6 +834,7 @@ onMounted(() => {
flex-shrink: 1;
max-width: 170px;
text-align: center;
margin-bottom: 10px;
}
.nameItemActive {
......
......@@ -46,7 +46,7 @@
<div class="chart-ai-wrap">
<div :class="['right-box2-main', { 'right-box-main--full': !domainFooterText }]" id="chart2"></div>
<div class="overview-tip-row">
<TipTab class="overview-tip" />
<TipTab class="overview-tip" :text="'涉华科技法案数量及通过率变化趋势,数据来源:美国国会官网'"/>
<AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('domain')" />
</div>
<div v-if="aiPaneVisible.domain" class="overview-ai-pane"
......@@ -72,7 +72,7 @@
<div class="chart-ai-wrap">
<div :class="['right-box1-main', { 'right-box-main--full': !limitFooterText }]" id="chart1"></div>
<div class="overview-tip-row">
<TipTab class="overview-tip" />
<TipTab class="overview-tip" :text="'涉华科技法案数量及通过率变化趋势,数据来源:美国国会官网'"/>
<AiButton class="overview-tip-action" @mouseenter="handleShowAiPane('limit')" />
</div>
<div v-if="aiPaneVisible.limit" class="overview-ai-pane"
......
......@@ -4,18 +4,18 @@
<div class="header">
<div class="header-top">
<div class="header-top-left">
<img src="../assets/images/box1-logo.png" alt="" />
<img :src=thinkInfo.coverImgUrl alt="" />
<div>
<div class="title">{{ thinkInfo.name }}</div>
<div class="title">{{ thinkInfo.titleZh }}</div>
<div class="en-title">
{{ thinkInfo.ename }}.{{ thinkInfo.times }}
{{ thinkInfo.testimonyDate }}·{{ "国会听证会" }}·{{ thinkInfo.committeeZh }}
</div>
<div class="tag-box">
<!-- <div class="tag-box" v-for="value,index in thinkInfo.tags" :key="index">
<div class="tag">{{ value.industryName }}</div>
</div> -->
<AreaTag v-for="(value, index) in thinkInfo.tags" :key="index" :tagName="value.industryName"></AreaTag>
<AreaTag v-for="(value, index) in thinkInfo.tags" :key="index" :tagName="value.domainName"></AreaTag>
</div>
</div>
</div>
......@@ -155,7 +155,7 @@
<div class="box3-main">
<AiSummary>
<template #summary-content>
{{ box1Data }}
{{ thinkInfo.descriptionZh }}
</template>
</AiSummary>
......@@ -180,10 +180,9 @@
{{ index + 1 }}
</div>
<div class="center">
<div class="title" v-html="highlightOpinionText(item.titleZh)"></div>
<div class="title" v-html="highlightOpinionText(item.questionZh)"></div>
<div>
<img src="./images/image-open.png" alt="" class="center-image"
@click="handleOpenReportOriginal(item)" />
<img src="./images/image-open.png" alt="" class="center-image" />
</div>
<div>
<img v-if="!isOpinionExpanded(item, index)" src="./images/image-down.png" alt=""
......@@ -194,7 +193,7 @@
</div>
</div>
<div v-if="isOpinionExpanded(item, index)" class="desc"
v-html="highlightOpinionText(item.contentZh)">
v-html="highlightOpinionText(item.answerZh)">
</div>
<!-- <div class="right"> -->
<!-- <div class="tag" v-for="(val, idx) in item.hylyList" :key="idx">
......@@ -239,7 +238,10 @@ import {
getThinkTankReportContent,
getThinkTankReportIndustry,
getThinkTankReportIndustryCloud,
getThinkTankReportViewpoint
getThinkTankReportViewpoint,
getThinkTankHearingInfo,
getThinkTankHearingCloud,
getThinkTankHearingViewpoint
} from "@/api/thinkTank/overview";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import { useRouter } from "vue-router";
......@@ -270,15 +272,39 @@ const props = defineProps({
}
});
const thinkInfo = ref({
name: "探讨中国开发和管理的跨大陆电网的安全影响",
ename: "调查项目",
tags: [{ industryName: "深海" }, { industryName: "人工智能" }],
thinkTankName: "兰德科技智库",
thinkTankLogoUrl: "http://8.140.26.4:10010/kjb-files/images/org/land.webp",
times: "2024-05-28"
})
const applyCongressHearingDocumentTitle = (title) => {
const text = String(title || "").trim();
if (!text) return;
window.sessionStorage.setItem("congressHearingTabName", text);
document.title = text;
};
const handleGetThinkTankHearingInfo = async () => {
try {
const id = router.currentRoute._value.params.id;
if (!id) return;
const res = await getThinkTankHearingInfo({ id });
if (res?.code === 200 && res?.data) {
const info = res.data;
thinkInfo.value = {
...info,
// 保持模板现有字段:titleZh / testimonyDate / committeeZh / coverImgUrl ...
tags: Array.isArray(info.domains) ? info.domains : thinkInfo.value.tags,
};
console.log("thinkInfo", thinkInfo.value);
applyCongressHearingDocumentTitle(info.titleZh || info.title || "");
if (info.summaryZh || info.summary) {
box1Data.value = info.summaryZh || info.summary;
}
projectBackground.value = info.backgroundZh || info.background || projectBackground.value;
}
} catch (error) {
console.error("获取调查项目详情error", error);
}
};
const REPORT_ANALYSIS_TIP_BOX5 =
"智库报告关键词云,数据来源:美国兰德公司官网";
"国会听证会关键词云,数据来源:美国兰德公司官网";
// 刷新后默认展示「报告关键词云」AI 总结
const isShowAiContentBox5 = ref(true);
const aiContentBox5 = ref("");
......@@ -464,19 +490,19 @@ const hasBox5ChartData = computed(() => Array.isArray(box5Data.value) && box5Dat
/** 词云子组件不 watch 数据,每次接口成功有数据时递增 key,强制重新挂载以触发 onMounted */
const box5WordCloudKey = ref(0);
//获取科技领域词云
//获取国会听证会词云
const handleGetThinkTankReportIndustryCloud = async () => {
try {
const params = {
id: router.currentRoute._value.params.id
// industryId: activeArea.value
};
const res = await getThinkTankReportIndustryCloud(params);
const res = await getThinkTankHearingCloud(params);
console.log("科技领域词云", res);
if (res.code === 200 && res.data) {
const data = (res.data || []).map(item => ({
name: item.clause,
value: item.count
name: item.name,
value: item.value
}));
// 该接口数据用于「报告关键词云」
box5Data.value = data;
......@@ -566,18 +592,17 @@ const handleCurrentChange = page => {
handleGetThinkTankReportViewpoint();
};
// 获取报告核心论点(支持搜索)
// 获取报告听证会(支持搜索)
const handleGetThinkTankReportViewpoint = async () => {
try {
const params = {
reportId: router.currentRoute._value.params.id,
currentPage: currentPage.value - 1,
testimonyId: router.currentRoute._value.params.id,
pageNum: currentPage.value,
pageSize: pageSize.value,
keyword: (searchOpinions.value || "").trim(),
orgIds: ""
};
const res = await getThinkTankReportViewpoint(params);
console.log("核心论点", res.data);
const res = await getThinkTankHearingViewpoint(params);
console.log("听证会内容", res.data);
if (res.code === 200 && res.data) {
const nextOpinions = res.data.content || [];
majorOpinions.value = nextOpinions;
......@@ -672,6 +697,7 @@ onMounted(() => {
handleGetThinkTankReportIndustry();
handleGetThinkTankReportIndustryCloud();
handleGetThinkTankHearingInfo();
});
</script>
......@@ -1484,6 +1510,7 @@ onMounted(() => {
height: 24px;
margin-top: 12px;
margin-left: 18px;
cursor: pointer;
}
......@@ -1533,8 +1560,8 @@ onMounted(() => {
padding-top: 22px;
padding-bottom: 23px;
padding-left: 56px; // 24(left) + 13(center margin) + 一点间距
padding-left: 56px;
padding-right: 56px;
color: rgb(59, 65, 75);
font-family: "Source Han Sans CN";
font-weight: 400;
......
......@@ -32,7 +32,7 @@ const getPieChart = (data) => {
const value = params.value ?? "";
const percent = params.percent != null ? Math.round(params.percent) : 0;
return `{name|${name}}\n{time|${value} ${percent}%}`;
return `{name|${name}}\n{time| ${percent}%}`;
},
minMargin: 10,
edgeDistance: 20,
......
......@@ -21,10 +21,7 @@
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchIds"
@change="handleAreaGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchIds" @change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox>
......@@ -40,9 +37,7 @@
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchTimeIds"
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchTimeIds"
@change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }}
......@@ -62,9 +57,7 @@
<div class="title">{{ "听证会部门" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchHearingIds"
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchHearingIds"
@change="handleDeptGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_DEPT">
{{ RESOURCE_FILTER_ALL_DEPT }}
......@@ -82,31 +75,33 @@
<div class="right">
<div class="card-box">
<div class="card-content">
<div v-for="(item, index) in displayList" :key="item.id">
<div class="card-item">
<img class="card-item-img" src="../images/img congress.png" alt="report image" />
<div v-for="(item, index) in hearingData" :key="item.id">
<div class="card-item" @click="handleToReportDetail(item)">
<img class="card-item-img" :src="item.coverImgUrl" alt="report image" />
<div class="card-item-text">
<div class="card-item-title">
<span v-html="highlightText(item.title)"></span>
</div>
<div class="card-item-time">
<span v-html="highlightText(item.time + ' · ' + item.content)"></span>
<img src="../images/image open.png" alt="open icon" class="card-open-image" />
<img src="../images/image open.png" alt="open icon" class="card-open-image"
@click.stop="handleToReportDetail(item)" />
</div>
<div class="card-item-category"> {{ item.category }}</div>
<div class="card-item-category" v-if="item.category"> {{ item.category }}</div>
</div>
</div>
<div class="divider" v-if="index !== displayList.length - 1"></div>
<div class="divider" v-if="index !== hearingData.length - 1"></div>
</div>
</div>
</div>
<div class="right-footer">
<div class="info">
{{ filteredHearingData.length }} 篇智库报告
{{ total }} 篇智库报告
</div>
<div class="page-box">
<el-pagination :page-size="10" background layout="prev, pager, next" :total="filteredHearingData.length"
<el-pagination :page-size="10" background layout="prev, pager, next" :total="total"
@current-change="handleCurrentChange" :current-page="currentPage" />
</div>
</div>
......@@ -262,42 +257,9 @@ const handleDeptGroupChange = (val) => {
handleGetThinkDynamicsReport();
};
const filteredHearingData = computed(() => {
const areaSel = stripAllAreaForRequest(selectedResearchIds.value);
const timeSel = stripAllTimeForRequest(selectedResearchTimeIds.value);
const deptSel = stripAllDeptForRequest(selectedResearchHearingIds.value);
const rangeStart = getDateYearsAgo(selectedYear.value || 1);
return (hearingData.value || []).filter(item => {
const itemDate = parseChineseDate(item.time);
const matchTopRange = itemDate ? itemDate >= rangeStart : true;
const matchYear =
timeSel.length === 0 ||
timeSel.some(sel => {
if (sel === RESOURCE_FILTER_EARLIER) {
return matchesEarlierChineseDate(item.time);
}
return String(item.time || "").startsWith(String(sel));
});
const matchDepartment =
deptSel.length === 0 ||
deptSel.some(department =>
String(item.content || "").includes(department) || String(item.title || "").includes(department)
);
const matchType =
areaSel.length === 0 ||
areaSel.some(typeId =>
String(item.category || "").includes(String(typeId)) || String(item.title || "").includes(String(typeId))
);
return matchTopRange && matchYear && matchDepartment && matchType;
});
});
// 只展示当前页的数据
const displayList = computed(() => {
const list = filteredHearingData.value || [];
const start = (currentPage.value - 1) * pageSize;
return list.slice(start, start + pageSize);
});
// 保持模板里的方法名不变,但改成通知父组件,直接传入当前选中值避免时序问题
const handleGetThinkDynamicsReport = () => {
......@@ -542,7 +504,7 @@ const handleToReportDetail = item => {
width: 100%;
height: 77px;
display: flex;
align-items: center;
......@@ -555,11 +517,9 @@ const handleToReportDetail = item => {
}
.card-item-text {
flex: 1;
min-width: 0;
flex-direction: column;
justify-content: space-between;
display: flex;
......@@ -592,6 +552,7 @@ const handleToReportDetail = item => {
height: 16px;
margin-left: 9px;
margin-top: 3px;
cursor: pointer;
}
}
......
......@@ -21,10 +21,7 @@
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchIds"
@change="handleAreaGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchIds" @change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox>
......@@ -40,9 +37,7 @@
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchTimeIds"
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchTimeIds"
@change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }}
......@@ -62,14 +57,14 @@
<div class="footer-card" v-for="(item, index) in curFooterList" :key="index"
@click="handleToReportDetail(item)">
<div class="footer-card-top">
<img :src="item.imageUrl" alt="" />
<img :src=item.projectCoverImgUrl alt="" />
</div>
<div class="footer-card-title">
<span v-html="highlightText(item.name)"></span>
<span v-html="highlightText(item.projectNameZh)"></span>
</div>
<div class="footer-card-footer">
<div class="time">{{ item.times }}</div>
<div class="from">{{ item.thinkTankName }}</div>
<div class="time">{{ formatDate(item.startDate) }}</div>
<div class="from">{{ item.thinktankName }}</div>
</div>
</div>
</div>
......@@ -94,7 +89,11 @@ import {
stripAllAreaForRequest,
stripAllTimeForRequest
} from "../../../utils/resourceLibraryFilters";
const formatDate = (str) => {
if (!str) return ''
const [y, m, d] = str.split('T')[0].split('-')
return `${y}${+m}${+d}日`
};
const props = defineProps({
researchTypeList: {
type: Array,
......
......@@ -21,10 +21,7 @@
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchIds"
@change="handleAreaGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchIds" @change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox>
......@@ -40,9 +37,7 @@
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchTimeIds"
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchTimeIds"
@change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }}
......@@ -68,7 +63,7 @@
<span v-html="highlightText(item.name)"></span>
</div>
<div class="footer-card-footer">
<div class="time">{{ item.times }}</div>
<div class="time">{{ formatDate(item.times) }}</div>
<div class="from">{{ item.thinkTankName }}</div>
</div>
</div>
......@@ -94,7 +89,11 @@ import {
stripAllAreaForRequest,
stripAllTimeForRequest
} from "../../../utils/resourceLibraryFilters";
const formatDate = (str) => {
if (!str) return ''
const [y, m, d] = str.split('T')[0].split('-')
return `${y}${+m}${+d}日`
};
const props = defineProps({
researchTypeList: {
type: Array,
......
......@@ -47,7 +47,7 @@
{{ item.name }}
</div>
<div class="footer-card-footer">
<div class="time">{{ item.times }}</div>
<div class="time">{{ formatDate(item.times) }}</div>
<div class="from">{{ item.thinkTankName }}</div>
</div>
</div>
......@@ -97,6 +97,11 @@ const handleTimeGroupChange = (val) => {
emit("update:selectedPubTimeList", normalizeExclusiveAllOption(val, RESOURCE_FILTER_ALL_TIME));
emit("filter-change");
};
const formatDate = (str) => {
if (!str) return ''
const [y, m, d] = str.split('T')[0].split('-')
return `${y}${+m}${+d}日`
};
</script>
<style lang="scss" scoped>
......
......@@ -8,18 +8,11 @@
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedAreaList"
@change="handleAreaGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedAreaList" @change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox>
<el-checkbox
v-for="research in areaList"
:key="research.id"
class="filter-checkbox"
:label="research.id">
<el-checkbox v-for="research in areaList" :key="research.id" class="filter-checkbox" :label="research.id">
{{ research.name }}
</el-checkbox>
</el-checkbox-group>
......@@ -32,18 +25,11 @@
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedPubTimeList"
@change="handleTimeGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedPubTimeList" @change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }}
</el-checkbox>
<el-checkbox
v-for="time in pubTimeList"
:key="time.id"
class="filter-checkbox"
:label="time.id">
<el-checkbox v-for="time in pubTimeList" :key="time.id" class="filter-checkbox" :label="time.id">
{{ time.name }}
</el-checkbox>
</el-checkbox-group>
......@@ -56,14 +42,14 @@
<div class="footer-card" v-for="(item, index) in curFooterList" :key="index"
@click="emit('report-click', item)">
<div class="footer-card-top">
<img :src="item.imageUrl" alt="" />
<img :src="item.projectCoverImgUrl" alt="" />
</div>
<div class="footer-card-title">
{{ item.name }}
{{ item.projectNameZh }}
</div>
<div class="footer-card-footer">
<div class="time">{{ item.times }}</div>
<div class="from">{{ item.thinkTankName }}</div>
<div class="time">{{ formatDate(item.startDate) }}</div>
<div class="from">{{ item.thinktankName }}</div>
</div>
</div>
</div>
......@@ -94,7 +80,11 @@ defineProps({
total: { type: Number, default: 0 },
currentPage: { type: Number, default: 1 }
});
const formatDate = (str) => {
if (!str) return ''
const [y, m, d] = str.split('T')[0].split('-')
return `${y}${+m}${+d}日`
};
const emit = defineEmits([
"update:selectedAreaList",
"update:selectedPubTimeList",
......@@ -190,12 +180,14 @@ const handleTimeGroupChange = (val) => {
flex-wrap: wrap;
gap: 16px 16px;
.footer-card {
width: 398px;
height: 300px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
cursor: pointer;
.footer-card-top {
width: 364px;
......
......@@ -7,18 +7,11 @@
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchIds"
@change="handleAreaGroupChange">
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchIds" @change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox>
<el-checkbox
v-for="type in researchTypeList"
:key="type.id"
class="filter-checkbox"
:label="type.id">
<el-checkbox v-for="type in researchTypeList" :key="type.id" class="filter-checkbox" :label="type.id">
{{ type.name }}
</el-checkbox>
</el-checkbox-group>
......@@ -31,18 +24,12 @@
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group"
:model-value="selectedResearchTimeIds"
<el-checkbox-group class="checkbox-group" :model-value="selectedResearchTimeIds"
@change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }}
</el-checkbox>
<el-checkbox
v-for="type in researchTimeList"
:key="type.id"
class="filter-checkbox"
:label="type.id">
<el-checkbox v-for="type in researchTimeList" :key="type.id" class="filter-checkbox" :label="type.id">
{{ type.name }}
</el-checkbox>
</el-checkbox-group>
......@@ -55,18 +42,12 @@
<div class="title">{{ "听证会部门" }}</div>
</div>
<div class="select-main">
<el-checkbox-group
class="checkbox-group hearing-grid"
:model-value="selectedResearchHearingIds"
<el-checkbox-group class="checkbox-group hearing-grid" :model-value="selectedResearchHearingIds"
@change="handleDeptGroupChange">
<el-checkbox class="filter-checkbox" :label="RESOURCE_FILTER_ALL_DEPT">
{{ RESOURCE_FILTER_ALL_DEPT }}
</el-checkbox>
<el-checkbox
v-for="type in researchHearingList"
:key="type.id"
class="filter-checkbox"
:label="type.id">
<el-checkbox v-for="type in researchHearingList" :key="type.id" class="filter-checkbox" :label="type.id">
{{ type.name }}
</el-checkbox>
</el-checkbox-group>
......@@ -77,35 +58,34 @@
<div class="right">
<div class="card-box">
<div class="card-content">
<div v-for="(item, index) in displayList" :key="item.id ?? index">
<div class="card-item" @click="emit('report-click', item)">
<img class="card-item-img" src="../ThinkTankDetail/thinkDynamics/images/img congress.png"
alt="report image" />
<div v-for="(item, index) in hearingData" :key="item.id ?? index">
<div class="card-item">
<img class="card-item-img" :src=item.coverImgUrl alt="report image" />
<div class="card-item-text">
<div class="card-item-title">
{{ item.title }}
{{ item.titleZh }}
</div>
<div class="card-item-time">
{{ item.time + ' · ' + item.content }}
{{ item.testimonyDate + ' · ' + item.committeeZh }}
<img src="../ThinkTankDetail/thinkDynamics/images/image open.png" alt="open icon"
class="card-open-image" />
class="card-open-image" @click="emit('report-click', item)" />
</div>
<div class="card-item-category">
<div class="card-item-category" v-if="item.category">
<AreaTag :key="`cat-${item.id}`" :tagName="item.category" />
</div>
</div>
</div>
<div class="divider" v-if="index !== displayList.length - 1"></div>
<div class="divider" v-if="index !== hearingData.length - 1"></div>
</div>
</div>
</div>
<div class="right-footer">
<div class="info">
{{ filteredHearingData.length }} 篇智库报告
{{ hearingData.length }} 篇智库报告
</div>
<div class="page-box">
<el-pagination :page-size="pageSize" background layout="prev, pager, next" :total="filteredHearingData.length"
<el-pagination :page-size="pageSize" background layout="prev, pager, next" :total="total"
@current-change="handlePageChange" :current-page="currentPage" />
</div>
</div>
......@@ -131,6 +111,8 @@ import {
const props = defineProps({
researchTypeList: { type: Array, default: () => [] },
researchTimeList: { type: Array, default: () => [] },
hearingData: { type: Array, default: () => [] },
total: { type: Number, default: 0 }
});
const emit = defineEmits(["report-click"]);
......@@ -142,25 +124,10 @@ const selectedResearchIds = ref([RESOURCE_FILTER_ALL_AREA]);
const selectedResearchTimeIds = ref([RESOURCE_FILTER_ALL_TIME]);
const selectedResearchHearingIds = ref([RESOURCE_FILTER_ALL_DEPT]);
const hearingData = ref([
{ id: 1, title: "美国国会听证会:人工智能与国家安全", content: "美中经济与安全审查委员会", category: "人工智能", time: "2025年7月8日" },
{ id: 2, title: "美国国会听证会:先进制造供应链韧性", content: "国会-行政部门中国委员会", category: "先进制造", time: "2025年6月15日" },
{ id: 3, title: "美国国会听证会:半导体出口管制与产业政策", content: "美国商务部", category: "半导体", time: "2025年5月22日" },
{ id: 4, title: "美国国会听证会:人工智能与国家安全", content: "美中经济与安全审查委员会", category: "人工智能", time: "2025年7月8日" },
{ id: 5, title: "美国国会听证会:先进制造供应链韧性", content: "国会-行政部门中国委员会", category: "先进制造", time: "2025年6月15日" },
{ id: 6, title: "美国国会听证会:半导体出口管制与产业政策", content: "美国商务部", category: "半导体", time: "2025年5月22日" },
{ id: 7, title: "美国国会听证会:人工智能与国家安全", content: "美中经济与安全审查委员会", category: "人工智能", time: "2025年7月8日" },
{ id: 8, title: "美国国会听证会:先进制造供应链韧性", content: "国会-行政部门中国委员会", category: "先进制造", time: "2025年6月15日" },
{ id: 9, title: "美国国会听证会:半导体出口管制与产业政策", content: "美国商务部", category: "半导体", time: "2025年5月22日" },
{ id: 10, title: "美国国会听证会:人工智能与国家安全", content: "美中经济与安全审查委员会", category: "人工智能", time: "2025年7月8日" },
{ id: 11, title: "美国国会听证会:先进制造供应链韧性", content: "国会-行政部门中国委员会", category: "先进制造", time: "2025年6月15日" },
{ id: 12, title: "美国国会听证会:半导体出口管制与产业政策", content: "美国商务部", category: "半导体", time: "2025年5月22日" },
]);
const researchHearingList = ref([
{ id: "美中经济与安全审查委员会", name: "美中经济与安全审查委员会" },
{ id: "国会-行政部门中国委员会", name: "国会-行政部门中国委员会" },
{ id: "美国商务部", name: "美国商务部" },
]);
const handleAreaGroupChange = (val) => {
......@@ -178,39 +145,9 @@ const handleDeptGroupChange = (val) => {
currentPage.value = 1;
};
const filteredHearingData = computed(() => {
const areaSel = stripAllAreaForRequest(selectedResearchIds.value);
const timeSel = stripAllTimeForRequest(selectedResearchTimeIds.value);
const deptSel = stripAllDeptForRequest(selectedResearchHearingIds.value);
return (hearingData.value || []).filter(item => {
const matchYear =
timeSel.length === 0 ||
timeSel.some(sel => {
if (sel === RESOURCE_FILTER_EARLIER) {
return matchesEarlierChineseDate(item.time);
}
return String(item.time || "").startsWith(String(sel));
});
const matchDepartment =
deptSel.length === 0 ||
deptSel.some(department =>
String(item.content || "").includes(department) || String(item.title || "").includes(department)
);
const matchType =
areaSel.length === 0 ||
areaSel.some(typeId =>
String(item.category || "").includes(String(typeId)) || String(item.title || "").includes(String(typeId))
);
return matchYear && matchDepartment && matchType;
});
});
const displayList = computed(() => {
const list = filteredHearingData.value || [];
const start = (currentPage.value - 1) * pageSize;
return list.slice(start, start + pageSize);
});
const handlePageChange = page => {
currentPage.value = page;
......@@ -354,7 +291,7 @@ const handlePageChange = page => {
width: 100%;
height: 77px;
display: flex;
align-items: center;
.card-item-img {
width: 56px;
......@@ -364,8 +301,8 @@ const handlePageChange = page => {
}
.card-item-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
......@@ -394,6 +331,7 @@ const handlePageChange = page => {
height: 16px;
margin-left: 9px;
margin-top: 3px;
cursor: pointer;
}
}
......
......@@ -47,7 +47,8 @@
<div class="card-item-text">
<div class="card-item-title">{{ item.name }}</div>
<div class="card-item-time">
<span class="info-text">{{ item.times }} · {{ item.thinkTankName }} · {{ item.reportName }}</span>
<span class="info-text">{{ formatDate(item.times) }} · {{ item.thinkTankName }} · {{ item.reportName
}}</span>
<div class="card-open-image" @click.stop="handleOpenReportOriginal(item)">
<img src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open.png" alt="" />
</div>
......@@ -125,7 +126,11 @@ const handleOpenReportOriginal = (item) => {
})
window.open(route.href, "_blank")
}
const formatDate = (str) => {
if (!str) return ''
const [y, m, d] = str.split('T')[0].split('-')
return `${y}${+m}${+d}日`
};
/** 政策建议关联法案:新标签页打开法案介绍页,billId 随接口 id 变化 */
const handleBillMoreClick = (bill) => {
const billId = bill?.id;
......
......@@ -365,15 +365,17 @@
v-model:selectedAreaList="surveySelectedAreaList" :pub-time-list="pubTimeList"
v-model:selectedPubTimeList="surveySelectedPubTimeList" @filter-change="handleSurveyFilterChange"
:cur-footer-list="surveyFooterList" :total="surveyTotal" :current-page="surveyCurrentPage"
@report-click="handleToReportDetail" @page-change="handleSurveyCurrentChange" />
@report-click="handleToSurveyProjectView" @page-change="handleSurveyCurrentChange" />
<ThinkTankCongressHearingOverview v-else-if="activeCate === '国会听证会'" :key="`congress-${resourceTabResetKey}`"
:research-type-list="areaList" :research-time-list="pubTimeList" @report-click="handleToReportDetail" />
:hearing-data="hearingData" :research-type-list="areaList" :research-time-list="pubTimeList"
:total="projectTotal" @report-click="handleToHearingDetail" />
<ThinkTankPolicyAdviceOverview v-else :key="`policy-${resourceTabResetKey}`" :research-type-list="areaList"
:research-time-list="pubTimeList" :list="policyFooterList" :total="policyTotal"
:current-page="policyCurrentPage" :page-size="7" @filter-change="handlePolicyFilterChange"
@page-change="handlePolicyCurrentChange" />
</div>
</div>
</div>
......@@ -407,7 +409,9 @@ import {
getThinkTankReport,
getThinkTankOverviewPolicy,
getThinkTankReportNews,
getThinkTankReportRemarks
getThinkTankReportRemarks,
getThinkTankProjects,
getThinkTankTestimonies
} from "@/api/thinkTank/overview";
import { getPersonSummaryInfo } from "@/api/common/index";
import getMultiLineChart from "./utils/multiLineChart";
......@@ -432,7 +436,8 @@ import {
stripAllTimeForRequest,
buildNumericYearsQueryString,
isSelectionCoveringAllOptions,
getResourceLibraryReportDateRangeFromTimeSelection
getResourceLibraryReportDateRangeFromTimeSelection,
} from "./utils/resourceLibraryFilters";
import Message1 from "./assets/images/message-icon1.png";
......@@ -552,6 +557,8 @@ const getStatCountInfo = async () => {
};
//国会听证会数据
const hearingData = ref([]);
const searchThinktankText = ref(""); //搜索科技人物及观点
// 智库列表
const cardList = ref([
......@@ -1662,6 +1669,7 @@ const handleClickCate = cate => {
handleGetThinkTankPolicyAdvice();
} else if (cate === "国会听证会") {
resetResourceTabCommon()
handleGetThinkTankHearings();
}
};
......@@ -1811,6 +1819,8 @@ const surveySort = ref(null);
const surveyFooterList = ref([]);
const surveyCurrentPage = ref(1);
const surveyTotal = ref(0);
//国会听证会总数
const projectTotal = ref(0);
const handleSurveyFilterChange = () => {
surveyCurrentPage.value = 1;
......@@ -1827,24 +1837,9 @@ const handleGetThinkTankSurvey = async () => {
stripAllTimeForRequest(surveySelectedPubTimeList.value),
(pubTimeList.value || []).map((x) => x.id)
);
const params = {
pageNum: surveyCurrentPage.value,
pageSize: 12,
sortFun: surveySort.value === true,
domainIds: (() => {
const areas = stripAllAreaForRequest(surveySelectedAreaList.value);
const allAreaIds = (areaList.value || []).map((a) => a.id);
if (isSelectionCoveringAllOptions(areas, allAreaIds)) {
return "";
}
return arrayToString(areas);
})(),
startDate,
endDate,
category: "调查项目"
};
try {
const res = await getThinkTankReport(params);
const res = await getThinkTankProjects();
if (res.code === 200 && res.data) {
surveyFooterList.value = res.data.content;
surveyTotal.value = res.data.totalElements;
......@@ -1856,7 +1851,22 @@ const handleGetThinkTankSurvey = async () => {
console.error("获取调查项目 error", error);
}
};
const handleGetThinkTankHearings = async () => {
try {
const res = await getThinkTankTestimonies();
if (res.code === 200 && res.data) {
hearingData.value = res.data.content;
projectTotal.value = res.data.totalElements;
} else {
hearingData.value = [];
projectTotal.value = 0;
}
} catch (error) {
console.error("获取调查项目 error", error);
}
};
// ===== 政策建议:独立状态(不影响智库报告/调查项目)=====
const policyFooterList = ref([]);
const policyCurrentPage = ref(1);
......@@ -2097,7 +2107,26 @@ const handleToReportDetail = item => {
});
window.open(route.href, "_blank");
};
const handleToHearingDetail = item => {
window.sessionStorage.setItem("curTabName", item.titleZh);
const route = router.resolve({
name: "CongressHearingView",
params: {
id: item.id
}
});
window.open(route.href, "_blank");
};
const handleToSurveyProjectView = item => {
window.sessionStorage.setItem("curTabName", item.name);
const route = router.resolve({
name: "SurveyProjectView",
params: {
id: item.id
}
});
window.open(route.href, "_blank");
};
const handleSearch = () => {
window.sessionStorage.setItem("curTabName", `搜索-${searchThinktankText.value}`);
const curRoute = router.resolve({
......
......@@ -14,7 +14,7 @@
</div>
<div class="header-top-right">
<div class="image-name-box">
<div class="image"> <img :src=thinkInfo.thinkTankLogoUrl alt="" /></div>
<div class="image"><img :src="thinkInfo.thinkTankLogoUrl" alt="" /></div>
<div class="name">{{ thinkInfo.thinkTankName }}</div>
</div>
<div class="time">{{ thinkInfo.times }}</div>
......@@ -242,14 +242,38 @@ const handleDownload = async () => {
}
}
/** 接口可能返回对象或数组;字段名兼容 snake/camel */
const normalizeReportSummaryRow = (row) => {
if (!row || typeof row !== "object") return {};
return {
...row,
name: row.name || "",
imageUrl: row.imageUrl || row.image || "",
ename: row.ename || row.nameEn || row.englishName || "",
times: row.times || row.postDate || "",
thinkTankName: row.thinkTankName || row.thinktankName || "",
thinkTankLogoUrl: row.thinkTankLogoUrl || row.thinktankLogo || ""
};
};
const applyReportOriginalDocumentTitle = (title) => {
const text = String(title || "").trim();
if (!text) return;
window.sessionStorage.setItem("reportOriginalTabName", text);
document.title = text;
};
// 获取报告全局信息
const handleGetThinkTankReportSummary = async () => {
try {
const res = await getThinkTankReportSummary(router.currentRoute._value.params.id);
console.log("报告全局信息", res);
if (res.code === 200 && res.data) {
thinkInfo.value = res.data
const raw = res.data;
const first = Array.isArray(raw) ? raw[0] : raw;
const normalized = normalizeReportSummaryRow(first);
thinkInfo.value = normalized;
applyReportOriginalDocumentTitle(normalized.name);
}
} catch (error) {
console.error("获取报告全局信息error", error);
......
......@@ -40,7 +40,7 @@ const getPieChart = (data) => {
const value = params.value ?? "";
const percent = params.percent != null ? Math.round(params.percent) : 0;
return `{name|${name}}\n{time|${value} ${percent}%}`;
return `{name|${name}}\n{time| ${percent}%}`;
},
minMargin: 5,
edgeDistance: 10,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论