提交 ab4c3907 authored 作者: coderBryanFu's avatar coderBryanFu

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

流水线 #127 已失败 于阶段
in 27 秒
......@@ -335,99 +335,79 @@ export function getProgressPrediction(billId) {
* @returns {Promise<Object>} 相似法案列表
*/
export function getSimiBills(params = {}) {
// domains 如果是数组则用逗号拼接
const domains = Array.isArray(params.domains)
? params.domains.join(',')
: params.domains
return request('/api/BillProgressPrediction/simiBills', {
method: 'GET',
params: {
billIds: params.billIds,
domains: params.domains,
patternType: params.patternType ,
proposalType: params.proposalType ,
...params
domains: domains,
patternType: params.patternType,
proposalType: params.proposalType
}
})
}
/**
* 格式化日期 YYYY-MM-DD -> YYYY年M月D日
* @param {string} dateStr - 日期字符串
* @returns {string} 格式化后的日期
*/
function formatDate(dateStr) {
if (!dateStr) return ''
const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/)
if (match) {
return `${match[1]}${parseInt(match[2])}${parseInt(match[3])}日`
}
return dateStr
}
/**
* 转换相似法案 API 返回的数据为组件所需格式
* @param {Object} apiData - API 返回的原始数据
* @returns {Object} 转换后的统计数据和法案列表
*/
export function transformSimiBillsData(apiData) {
if (!apiData || !apiData.data || !Array.isArray(apiData.data)) {
if (!apiData || !apiData.data) {
return { stats: null, bills: [] }
}
const bills = apiData.data
// 计算统计数据
let becameLaw = 0
let notPassedOrShelved = 0
let totalDays = 0
let completedBills = 0
bills.forEach(bill => {
const actions = bill.bill_actions || []
const hasBecameLaw = actions.some(a =>
a.action_type === 'BecameLaw' ||
a.action_type === 'President' && a.action_desc?.includes('签署')
)
if (hasBecameLaw) {
becameLaw++
// 计算耗时
if (actions.length >= 2) {
const firstDate = new Date(actions[0].action_date)
const lastDate = new Date(actions[actions.length - 1].action_date)
const days = Math.ceil((lastDate - firstDate) / (1000 * 60 * 60 * 24))
if (days > 0) {
totalDays += days
completedBills++
}
}
} else {
notPassedOrShelved++
}
})
const medianDays = completedBills > 0 ? Math.round(totalDays / completedBills) : 223
const passRate = bills.length > 0 ? ((becameLaw / bills.length) * 100).toFixed(1) : '0'
const data = apiData.data
const simiBills = data.simi_bills || []
// 直接使用 API 返回的统计数据
const stats = {
totalBills: bills.length,
becameLaw,
notPassedOrShelved,
medianDays,
passRate
totalBills: data.count || simiBills.length,
becameLaw: data.become_law || 0,
notPassedOrShelved: (data.count || simiBills.length) - (data.become_law || 0),
medianDays: data.become_law_avg_days || 0,
passRate: data.become_law_prop ? (data.become_law_prop * 100).toFixed(1) : '0'
}
// 转换法案列表格式
const transformedBills = bills.map(bill => ({
const transformedBills = simiBills.map(bill => ({
id: bill.bill_id,
title: bill.bill_name || bill.bill_id,
proposalDate: extractProposalDate(bill.poli_pattern_desc),
areas: bill.domain_name ? (Array.isArray(bill.domain_name) ? bill.domain_name : [bill.domain_name]) : [],
proposer: extractProposer(bill.bill_sponsors),
coProposers: bill.bill_proposal_desc || '',
proposalDate: bill.proposed_date ? formatDate(bill.proposed_date) : '',
areas: bill.bill_domain ? (Array.isArray(bill.bill_domain) ? bill.bill_domain : [bill.bill_domain]) : [],
proposer: bill.key_sponsor_name || extractProposer(bill.bill_sponsors),
proposerParty: bill.key_sponsor_party || '',
coProposers: bill.co_sponsor_desc || bill.bill_proposal_desc || '',
proposalType: bill.bill_proposal_type || '',
governmentType: formatGovernmentType(bill.poli_pattern_type, bill.poli_pattern_desc),
passDays: calculateTotalDays(bill.bill_actions),
patternType: bill.poli_pattern_type || '',
billStatus: bill.bill_status || '',
passDays: bill.bill_action_days || calculateTotalDays(bill.bill_actions),
yearsDifference: bill.years_difference || 0,
billActions: bill.bill_actions || [],
billSponsors: bill.bill_sponsors || [],
selected: true // 默认全选
}))
return { stats, bills: transformedBills }
}
/**
* 从政治格局描述中提取提案时间
* @param {string} desc - 政治格局描述
* @returns {string} 提案时间
*/
function extractProposalDate(desc) {
if (!desc) return ''
const match = desc.match(/(\d{4})-(\d{2})-(\d{2})/)
if (match) {
return `${match[1]}${parseInt(match[2])}${parseInt(match[3])}日`
}
return ''
}
/**
* 从提案人列表中提取主提案人
* @param {Array} sponsors - 提案人列表
......@@ -504,9 +484,9 @@ export function transformProposalInfo(apiData) {
return {
// 提案标题
title: data.bill_title || 'H.R.1-大而美法案',
title: data.bill_name_zh ,
// 提案时间 - 从政治格局描述中提取
date: extractDateFromDesc(data.poli_pattern_desc) || '2025年5月20日',
date: extractDateFromDesc(data.poli_pattern_desc) ,
// 涉及领域 TAG - 使用 domain_name
areas: Array.isArray(data.domain_name) ? data.domain_name : (data.domain_name ? [data.domain_name] : []),
// 政策领域完整选项列表 - 使用 bill_domain(用于筛选下拉框)
......
......@@ -62,7 +62,6 @@
<div class="field-content">
<el-select
v-model="localValues.oppositionProposer"
multiple
placeholder="请选择"
style="width: 420px"
@change="handleChange"
......@@ -83,7 +82,6 @@
<div class="field-content">
<el-select
v-model="localValues.proposalTime"
multiple
placeholder="请选择"
style="width: 420px"
@change="handleChange"
......@@ -115,18 +113,18 @@ const props = defineProps<{
const localValues = ref({
policyArea: [] as string[],
governmentType: [] as string[],
oppositionProposer: [] as string[],
proposalTime: [] as string[],
oppositionProposer: '' as string,
proposalTime: '' as string,
})
// 根据 proposalInfo 计算初始筛选值(即"设置为当前提案"的目标状态)
function buildInitialValues(info?: ProposalInfo | null): Record<string, string[]> {
if (!info) return { policyArea: [], governmentType: [], oppositionProposer: [], proposalTime: [] }
function buildInitialValues(info?: ProposalInfo | null): Record<string, string[] | string> {
if (!info) return { policyArea: [], governmentType: [], oppositionProposer: '', proposalTime: '' }
return {
policyArea: info.defaultDomains?.length ? [...info.defaultDomains] : [...(info.areas || [])],
governmentType: info.patternType ? [info.patternType] : [],
oppositionProposer: [],
proposalTime: [],
oppositionProposer: '',
proposalTime: '',
}
}
......@@ -144,8 +142,8 @@ function reset() {
localValues.value = {
policyArea: [],
governmentType: [],
oppositionProposer: [],
proposalTime: [],
oppositionProposer: '',
proposalTime: '',
}
}
......
......@@ -2,8 +2,8 @@
<div v-if="phase" class="phase-card" :class="borderColorClass">
<div class="phase-header flex-display-start">
<div>
<h3 class="phase-title main-color text-title-2-bold">{{ phase.title }}</h3>
<p class="text-tip-2 text-primary-50-clor">{{ phase.description }}</p>
<div class="phase-title main-color text-title-2-bold">{{ phase.title }}</div>
<div class="text-tip-2 text-primary-50-clor">{{ phase.description }}</div>
</div>
<div class="phase-status">
<span class="risk-badge" :class="riskColorClass">{{ riskLabel }}</span>
......@@ -18,32 +18,41 @@
<p v-if="phase.riskLevel !== 'passed'" class="text-tip-2 text-primary-50-clor">{{ phase.estimatedDays }}</p>
</div>
</div>
<div style="display: flex;">
<div class="box-title-row"><img src="../assets/input.svg" />
<span class="text-compact-bold text-primary-80-clor" style="margin-left: 8px;">预测模型数据输入</span></div>
<div class="box-hint flex-display-center text-tip-2 text-primary-50-clor" style="margin-left: auto;">
<img src="../assets/importent.svg"/>
<span>此阶段预测基于以下多维特征</span>
</div>
</div>
<div class="model-inputs-box">
<div class="box-header flex-display-start">
<div class="box-title-row flex-display-center">
<img src="../assets/input.svg" />
<span class="text-compact-bold">预测模型数据输入</span>
</div>
<div class="box-hint flex-display-center text-tip-2 text-primary-50-clor">
<img src="../assets/importent.svg"/>
<span>此阶段预测基于以下多维特征</span>
</div>
</div>
<div class="model-inputs">
<p
<div
v-for="(input, index) in phase.modelInputs"
:key="index"
class="text-tip-2 text-primary-65-clor"
>
{{ input }}
</p>
</div>
</div>
</div>
<div v-if="phase.predictionBasis" class="facts-section">
<div class="box-header flex-display-start">
<div class="box-title-row flex-display-center">
<img src="../assets/icon1.svg"/>
<span class="text-compact-bold">通过性预测依据</span>
<span class="text-compact-bold text-primary-80-clor">通过性预测依据</span>
</div>
<div class="box-hint flex-display-center text-tip-2 text-primary-50-clor">
<img src="../assets/importent.svg"/>
......@@ -51,9 +60,11 @@
</div>
</div>
<div class="prediction-basis-content">
<p class="text-tip-2 text-primary-65-clor">{{ phase.predictionBasis }}</p>
<div class="text-tip-2 text-primary-65-clor">{{ phase.predictionBasis }}</div>
</div>
</div>
<!-- 底部虚线分隔 -->
<div class="phase-divider"></div>
</div>
</template>
......@@ -144,7 +155,8 @@ const riskLabel = computed(() => {
<style scoped>
.phase-card {
padding-left: 24px;
padding-bottom: 32px;
/* padding-bottom: 16px; */
/* padding-top: 16px; */
}
.border-primary {
......@@ -166,7 +178,7 @@ const riskLabel = computed(() => {
.phase-header {
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
/* margin-bottom: 16px; */
}
.phase-header > div:first-child {
......@@ -288,4 +300,10 @@ const riskLabel = computed(() => {
height: 16px;
color: var(--text-primary-65-color);
}
/* 底部虚线分隔 */
.phase-divider {
margin-top: 16px;
border-bottom: 1px solid var(--bg-black-10, #e5e5e5);
}
</style>
......@@ -125,7 +125,7 @@ async function loadData() {
// 使用真实 API,传入 billIds、domains、patternType、proposalType
const params = {
billIds: filterParams?.value.billIds,
domains:JSON.stringify(filterParams?.value.domains) || [],
domains: filterParams?.value.domains || [],
patternType: filterParams?.value.patternType || '统一政府',
proposalType: filterParams?.value.proposalType || '两党共同提案'
}
......@@ -133,14 +133,15 @@ async function loadData() {
const response = await getSimiBills(params)
if (response && response.data) {
// 保存原始数据
rawBillsData.value = response.data
// 保存原始数据(新 API 返回结构中 simi_bills 是法案数组)
rawBillsData.value = response.data.simi_bills || []
const { stats: apiStats, bills: apiBills } = transformSimiBillsData(response)
stats.value = apiStats
bills.value = apiBills
} else {
stats.value = null
bills.value = []
rawBillsData.value = []
}
} catch (error) {
console.error('获取相似法案失败:', error)
......
......@@ -90,7 +90,7 @@ onMounted(async () => {
// 获取提案信息
const predictionData = await getProgressPrediction(billId.value).catch(err => {
console.error('[v0] 获取预测数据失败:', err)
console.error(' 获取预测数据失败:', err)
return null
})
......@@ -341,7 +341,7 @@ async function handleStep2Next(selectedBills: any[]) {
predictionResult.value = transformPredictionResult(response.data)
}
} catch (error) {
console.error('[v0] 获取预测分析失败:', error)
console.error(' 获取预测分析失败:', error)
} finally {
predictionLoading.value = false
}
......@@ -370,9 +370,11 @@ function transformPredictionResult(data: any) {
const factors = data?.factor_analysis || []
// 转换阶段分析
const phases = stages.map((stage: any, index: number) => ({
const chineseNum = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
const phases = stages.map((stage: any, index: number) => ({
id: index + 1,
title: `阶段${index + 1}${stage.stage}`,
title: `阶段${chineseNum[index] || index + 1}${stage.stage}`,
description: stage.stage,
riskLevel: probabilityToRisk(stage.predicted_pass_probability),
progressLevel: probabilityToProgressLevel(stage.predicted_pass_probability),
......@@ -387,7 +389,7 @@ function transformPredictionResult(data: any) {
]
},
predictionBasis: stage.prediction_basis
}))
}))
return {
title: '立法进展阶段预测分析',
......
......@@ -228,11 +228,9 @@
<div v-for="(sv, svIdx) in item.sourceViewDetails" :key="`${sv.report_id}-${sv.view_id}-${svIdx}`"
class="source-view-detail">
<div class="source-view-detail-title">
<span
class="source-view-detail-title-text"
<span class="source-view-detail-title-text"
:class="{ 'is-clickable-report': hasReportLinkForSourceView(sv) }"
@click.stop="handleOpenReportOriginalFromSource(sv)"
>{{ getSourceViewDisplayTitle(sv) }}</span>
@click.stop="handleOpenReportOriginalFromSource(sv)">{{ getSourceViewDisplayTitle(sv) }}</span>
<span class="source-view-detail-org" v-if="sv.thinktankName || sv.thinktankLogoUrl">
<img v-if="sv.thinktankLogoUrl" :src="sv.thinktankLogoUrl" alt="" />
<span class="source-view-detail-org-text">{{ sv.thinktankName }}</span>
......@@ -278,11 +276,10 @@
<div v-for="(sv, svIdx) in item.sourceViewDetails"
:key="`${sv.report_id}-${sv.view_id}-${svIdx}`" class="source-view-detail">
<div class="source-view-detail-title">
<span
class="source-view-detail-title-text"
<span class="source-view-detail-title-text"
:class="{ 'is-clickable-report': hasReportLinkForSourceView(sv) }"
@click.stop="handleOpenReportOriginalFromSource(sv)"
>{{ getSourceViewDisplayTitle(sv) }}</span>
@click.stop="handleOpenReportOriginalFromSource(sv)">{{ getSourceViewDisplayTitle(sv)
}}</span>
<span class="source-view-detail-org" v-if="sv.thinktankName || sv.thinktankLogoUrl">
<img v-if="sv.thinktankLogoUrl" :src="sv.thinktankLogoUrl" alt="" />
<span class="source-view-detail-org-text">{{ sv.thinktankName }}</span>
......@@ -1007,6 +1004,8 @@ onMounted(async () => {
flex-direction: column;
align-items: center;
overflow-y: auto;
height: 100vh;
.header {
......@@ -1197,7 +1196,9 @@ onMounted(async () => {
margin-top: 16px;
width: 1600px;
height: 1094px;
height: 950px;
margin-bottom: 100px;
.box1 {
width: 480px;
......
......@@ -39,10 +39,10 @@
</div>
</div>
<div class="box4-main-footer">
<div class="info">{{ opinionsTotal }}核心论点</div>
<div class="info">{{ opinionsTotal }}政策建议</div>
<div class="page-box">
<el-pagination :page-size="pageSize" background layout="prev, pager, next" :total="opinionsTotal"
@current-change="handleCurrentChange" :current-page="currentPage" />
<el-pagination :pager-count="4" :page-size="pageSize" background layout="prev, pager, next"
:total="opinionsTotal" @current-change="handleCurrentChange" :current-page="currentPage" />
</div>
</div>
</div>
......@@ -368,7 +368,7 @@ onMounted(async () => {
.left {
width: 506px;
gap: 12px;
gap: 8px;
display: flex;
flex-direction: column;
......@@ -406,19 +406,32 @@ onMounted(async () => {
width: 506px;
gap: 8px;
display: flex;
height: 28px;
align-items: flex-start;
margin-top: 4px;
padding-bottom: 0;
box-sizing: border-box;
overflow-x: auto;
overflow-y: hidden;
scrollbar-gutter: stable;
.tag {
height: 28px;
margin-bottom: 4px;
border: 1px solid rgb(230, 231, 232);
border-radius: 4px;
width: auto !important;
flex-shrink: 0;
/* 不被压缩 */
padding: 0px 8px;
display: flex;
cursor: pointer;
white-space: nowrap;
/* 强制不换行 */
&.tag-active {
border-color: rgb(5, 95, 194);
......@@ -426,6 +439,7 @@ onMounted(async () => {
.tag-text {
color: rgb(5, 95, 194);
}
}
......@@ -515,6 +529,8 @@ onMounted(async () => {
.box4-main-footer {
display: flex;
justify-content: space-between;
/* empty */
}
}
......
......@@ -73,8 +73,13 @@
<template v-else>
<div class="box5Chart">
<!-- 有数据后再挂载子组件:子组件仅在 onMounted 初始化,异步数据到达后需 v-if + key 强制重新挂载 -->
<WordCloudChart v-if="box5Data.length" :key="box5WordCloudKey" :data="box5Data" width="100%"
height="100%" />
<WordCloudChart
v-if="box5Data.length"
:key="box5WordCloudKey"
:data="box5Data"
width="432px"
height="272px"
/>
</div>
<div class="box5-footer">
<TipTab :text="REPORT_ANALYSIS_TIP_BOX5" />
......@@ -356,54 +361,21 @@ const handleClickReportAuthor = async (author) => {
const personId = author?.id;
if (!personId) return;
let personTypeList = [];
try {
personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList") || "[]");
} catch (e) {
personTypeList = [];
}
const params = { personId };
try {
const res = await getPersonSummaryInfo(params);
if (res.code !== 200 || !res.data) return;
const arr = personTypeList.filter((t) => {
const typeIdNum = Number(t.typeId);
const personTypeNum = Number(res.data.personType);
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
});
if (!arr.length) {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
const personTypeName = arr[0]?.typeName || "";
let type = 0;
if (personTypeName === "科技企业领袖") {
type = 1;
} else if (personTypeName === "国会议员") {
type = 2;
} else if (personTypeName === "智库研究人员") {
type = 3;
} else {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
window.sessionStorage.setItem("curTabName", author?.name || "");
const route = router.resolve({
path: "/characterPage",
query: {
type,
personId
}
});
window.open(route.href, "_blank");
} catch (error) {
console.error("点击报告作者头像跳转失败", error);
}
};
const riskSignal = computed(() => {
const info = props.thinkInfo || {};
......@@ -921,10 +893,7 @@ onMounted(() => {
.box5-main {
width: 480px;
height: 361px;
padding-left: 31px;
padding-right: 32px;
padding-top: 26px;
padding-bottom: 43px;
padding: 24px 24px 65px 24px;
display: flex;
flex-direction: column;
box-sizing: border-box;
......@@ -932,8 +901,8 @@ onMounted(() => {
position: relative;
.box5Chart {
width: 418px;
height: 292px;
width: 100%;
height: 100%;
margin: 0 auto;
overflow: hidden;
}
......@@ -1023,6 +992,15 @@ onMounted(() => {
line-height: 24px;
letter-spacing: 0;
text-align: left;
/* 👇 下面是 两行文本超出省略 核心代码 */
display: -webkit-box;
-webkit-line-clamp: 2;
/* 限制显示 2 行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.report-footer {
......@@ -1385,9 +1363,14 @@ onMounted(() => {
height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px 5px;
box-sizing: border-box;
overflow: hidden;
.info {
flex: 1 1 auto;
min-width: 0;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
......@@ -1396,6 +1379,23 @@ onMounted(() => {
letter-spacing: 0px;
text-align: left;
}
.page-box {
/* 最大 300px:允许变小,但绝不变大 */
flex: 0 1 300px;
width: 100%;
max-width: 300px;
min-width: 0;
display: flex;
justify-content: flex-end;
overflow: hidden;
}
.page-box :deep(.el-pagination) {
max-width: 100%;
min-width: 0;
overflow: hidden;
}
}
}
......
......@@ -237,7 +237,7 @@
<div class="title" v-html="highlightPolicyText(item.titleZh)"></div>
<div class="info">
<div class="text">
<span v-html="highlightPolicyText(`${item.reportDateDisplay}·${item.contentZh}`)"></span>
<span v-html="highlightPolicyText(`${item.reportDateDisplay}·${item.reportName}`)"></span>
</div>
<div class="more" @click="toDetail(item)">
......@@ -346,7 +346,7 @@ const getAreaTagColor = (name, idx = 0) =>
const POLICY_TRACKING_TIP_BOX1 =
"智库报告中政策建议的领域分布情况,数据来源:美国兰德公司官网";
const POLICY_TRACKING_TIP_BOX2 =
"智库报告中政策建议涉及领域分布情况,数据来源:美国兰德公司官网";
"智库报告中政策建议部门分布情况,数据来源:美国兰德公司官网";
const POLICY_TRACKING_TIP_BOX3 =
"智库报告热门研究领域变化趋势,数据来源:美国兰德公司官网";
......@@ -1221,7 +1221,7 @@ function mapPolicyRowToView(row) {
const toDetail = item => {
window.sessionStorage.setItem("curTabName", item.contentZh ?? item.content ?? "");
const route = router.resolve({
name: "ReportDetail",
name: "ReportOriginal",
params: {
id: item.reportId
}
......@@ -2110,7 +2110,7 @@ onMounted(async () => {
line-height: 22px;
letter-spacing: 0px;
text-align: left;
width: 1020px;
white-space: nowrap;
/* 强制不换行,保持一行 */
overflow: hidden;
......
......@@ -726,36 +726,15 @@ const handleClickPerson = async item => {
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
});
console.log("arr", arr);
if (arr && arr.length > 0) {
personTypeName = arr[0].typeName;
console.log("personTypeName", personTypeName);
if (personTypeName === "科技企业领袖") {
type = 1;
} else if (personTypeName === "国会议员") {
type = 2;
} else if (personTypeName === "智库研究人员") {
type = 3;
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
window.sessionStorage.setItem("curTabName", item.name);
const route = router.resolve({
path: "/characterPage",
query: {
type: type, // type=1为科技企业领袖,2为国会议员,3为智库研究人员
personId: item.personId
}
});
window.open(route.href, "_blank");
} else {
personTypeName = "";
ElMessage.warning("找不到当前人员的类型值!");
return;
}
} else {
ElMessage.warning("找不到当前人员的类型值!");
return;
......@@ -779,9 +758,10 @@ onMounted(() => {
<style lang="scss" scoped>
.wrap {
width: 100%;
height: 100%;
height: 100vh;
display: flex;
gap: 16px;
overflow-y: auto;
.left {
margin-top: 16px;
......@@ -910,7 +890,7 @@ onMounted(() => {
.right {
width: 1104px;
/* 三栏:390 + 390 + 900,间距 16×2 */
height: 1712px;
height: 1732px;
margin-top: 16px;
display: flex;
flex-direction: column;
......@@ -1461,6 +1441,7 @@ onMounted(() => {
.box.box-core-researchers {
width: 1104px;
height: 900px;
margin-bottom: 90px;
flex-shrink: 0;
}
......
......@@ -48,7 +48,7 @@
<div class="card-item-title">{{ item.name }}</div>
<div class="card-item-time">
<span class="info-text">{{ item.times }} · {{ item.thinkTankName }} · {{ item.reportName }}</span>
<div class="card-open-image">
<div class="card-open-image" @click.stop="handleOpenReportOriginal(item)">
<img src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image open.png" alt="" />
</div>
</div>
......@@ -115,6 +115,17 @@ const emit = defineEmits(["filter-change", "page-change", "item-click"]);
const router = useRouter();
/** 打开报告原文:新标签页打开 /thinkTank/reportOriginal/:id(优先 reportId) */
const handleOpenReportOriginal = (item) => {
const reportId = item?.reportId || item?.report_id || item?.id
if (!reportId) return
const route = router.resolve({
name: "ReportOriginal",
params: { id: String(reportId) }
})
window.open(route.href, "_blank")
}
/** 政策建议关联法案:新标签页打开法案介绍页,billId 随接口 id 变化 */
const handleBillMoreClick = (bill) => {
const billId = bill?.id;
......
......@@ -200,7 +200,7 @@
<div id="box5Chart" class="box5-chart-canvas"></div>
</div>
<div class="source">
<TipTab :text="'智库报告数量变化趋势,数据来源:美国各智库官网'" />
<TipTab :text="'智库报告数量变化趋势,数据来源美国各智库官网'" />
</div>
<div class="chart-box">
<div class="btn-box" v-if="!isShowAiContentBox5" @mouseenter="handleSwitchAiContentShowBox5(true)">
......
......@@ -65,12 +65,8 @@
<pdf ref="leftPdfRef" :pdfUrl="reportUrlEnWithPage" class="pdf-pane-inner" />
</div>
<div class="pdf-pane-wrap" :class="{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key="`right-pdf-${valueSwitch ? 'split' : 'full'}`"
ref="rightPdfRef"
:pdfUrl="reportUrlWithPage"
class="pdf-pane-inner"
/>
<pdf :key="`right-pdf-${valueSwitch ? 'split' : 'full'}`" ref="rightPdfRef" :pdfUrl="reportUrlWithPage"
class="pdf-pane-inner" />
</div>
</div>
</div>
......@@ -287,7 +283,8 @@ onMounted(async () => {
<style lang="scss" scoped>
.wrap {
overflow-y: auto;
height: 100vh;
.header {
......@@ -340,6 +337,14 @@ onMounted(async () => {
line-height: 24px;
letter-spacing: 0px;
text-align: left;
/* 👇 下面是 两行文本超出省略 核心代码 */
display: -webkit-box;
-webkit-line-clamp: 1;
/* 限制显示 2 行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.tag-box {
......@@ -469,8 +474,9 @@ onMounted(async () => {
margin: 0 auto;
background: rgb(255, 255, 255);
width: 1600px;
height: 928px;
overflow: hidden;
height: 948px;
margin-bottom: 86px;
border: 1px, solid, rgb(234, 236, 238);
box-shadow: 0 0 20px 0 rgba(25, 69, 130, 0.1);
......
......@@ -12,8 +12,8 @@
<script>
import { ref, shallowRef, nextTick, watch } from 'vue';
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';
import pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';
import pdfWorkerUrl from 'pdfjs-dist/legacy/build/pdf.worker.min?url';
// 通过 Vite 的 ?url 产出静态资源地址,确保线上/线下都能正确加载 worker
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerUrl;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论