提交 58932509 authored 作者: 朱政's avatar 朱政

feat:多智库分析开始分析功能完善

上级 c675e799
......@@ -167,22 +167,25 @@
</div>
</AnalysisBox>
<AnalysisResultBox title="核心观点分析" :showAllBtn="false" v-if="!isBox2" @tab-change="handleOpinionTabChange">
<div class="box2-main-Consensus" v-if="activeOpinionTab === 'consensus'">
<div v-if="isAnalysisLoading" class="analysis-loading">
正在加载
</div>
<div class="box2-main-Consensus" v-if="!isAnalysisLoading && activeOpinionTab === 'consensus'">
<div class="box2-main-Consensus-header">
<div class="tech-development-view">{{ "科技发展观点 1条" }}</div>
<div class="criticism-view">{{ "对我打压观点 1条" }}</div>
<div class="tech-development-view">{{ `共识观点 ${consensusList.length}条` }}</div>
<div class="criticism-view">{{ `分歧观点 ${differenceList.length}条` }}</div>
<div class="thinkTank-view">
<div class="thinkTank-image">
<img src="../assets/images/rand-image.png" alt="" />
</div>
{{ "兰德公司 2条" }}
{{ `${domainName} ${consensusList.length + differenceList.length}条` }}
</div>
</div>
<div class="box2-main-Consensus-content">
<template v-for="(item, index) in mockConsensusList" :key="item.id">
<template v-for="(item, index) in consensusList" :key="item.id">
<div class="Consensus-item">
<div class="Consensus-item-number">{{ index + 1 }}</div>
<div class="Consensus-item-title">{{ item.title }}</div>
<div class="Consensus-item-title">{{ item.consensusContent }}</div>
<div class="Consensus-item-right">{{ `${item.reportCount}篇报告提及` }}</div>
<div class="Consensus-item-image" @click="toggleConsensusItem(item.id)">
<img src="../assets/images/down.png" alt="" v-if="!openConsensusIds.has(item.id)" />
......@@ -194,36 +197,35 @@
<div class="Consensus-expand-row">
<div class="Consensus-expand-title">
{{ item.childrenTitle }}
{{ "来源观点列表" }}
</div>
<div class="Consensus-expand-subtitle">
<div class="Consensus-expand-image">
<img src="../assets/images/rand-image.png" alt="" />
</div>
{{ item.thinkTankName }}
{{ `${item.reportCount}条来源观点` }}
</div>
</div>
<div class="Consensus-expand-content">
{{ item.childrenContent }}
{{ item.sourceViewText }}
</div>
<div class="Consensus-expand-badge"
:class="{ 'is-tech-development': item.view === '科技发展观点', 'is-criticism': item.view === '对我打压观点' }">
{{ item.view }}
<div class="Consensus-expand-badge is-tech-development">
{{ "共识观点" }}
</div>
</div>
</template>
</div>
</div>
<div class="box2-main-Differences" v-else>
<div class="box2-main-Differences" v-else-if="!isAnalysisLoading">
<div class="box2-main-Differences-content">
<template v-for="(item, index) in mockDifferenceList" :key="item.id">
<template v-for="(item, index) in differenceList" :key="item.id">
<div class="Differences-item">
<div class="Differences-item-number">{{ index + 1 }}</div>
<div class="Differences-item-title">
<div class="Differences-item-title-left"> {{ item.divergent_views[0].view }}</div>
<div class="Differences-item-title-right">{{ item.divergent_views[1].view }}</div>
<div class="Differences-item-title-left"> {{ item.disagreementContent }}</div>
<div class="Differences-item-title-right">{{ `${item.reportCount}篇报告提及` }}</div>
</div>
......@@ -237,37 +239,22 @@
<div class="Differences-expand-content">
<div class="Differences-expand-top-content">
<div class="Differences-expand-top-content-left">
<div class="content-left-title">{{ item.divergent_views[0].Consensus.title }}</div>
<div class="content-left-title">{{ "来源观点列表" }}</div>
<div class="content-left-img-name">
<div class="content-left-img">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="content-left-name">
{{ item.divergent_views[0].Consensus.institution }}
</div>
</div>
<div class="content-left-text">{{ item.divergent_views[0].Consensus.quote }}</div>
</div>
<div class="Differences-expand-top-content-right">
<div class="content-right-title">{{ item.divergent_views[1].Differences.title }}</div>
<div class="content-right-img-name">
<div class="content-right-img">
<img src="../assets/images/rand-image.png" alt="" />
{{ `${item.reportCount}条来源观点` }}
</div>
<div class="content-right-name">
{{ item.divergent_views[1].Differences.institution }}
</div>
</div>
<div class="content-right-text">{{ item.divergent_views[1].Differences.quote }}</div>
<div class="content-left-text">{{ item.sourceViewText }}</div>
</div>
</div>
<div class="Differences-expand-view">
<div class="Differences-expand-view-left">
<div class="left-tag">{{ item.divergent_views[0].Consensus.tag }}</div>
</div>
<div class="Differences-expand-view-right">
<div class="right-tag">{{ item.divergent_views[1].Differences.tag }}</div>
<div class="left-tag">{{ "分歧观点" }}</div>
</div>
</div>
</div>
......@@ -286,10 +273,11 @@ import router from '@/router';
import { onMounted, ref, computed, reactive, nextTick } from "vue";
import AnalysisBox from "@/components/base/boxBackground/analysisBox.vue"
import AnalysisResultBox from "./boxBackground/analysisBox.vue"
import { getThinkTankReport, getHylyList, postReportDomainViewAnalysis } from "@/api/thinkTank/overview";
import { getThinkTankReport, getHylyList, getThinkTankReportViewpoint, postReportDomainViewAnalysis } from "@/api/thinkTank/overview";
const sort = ref("");
const searchPolicy = ref("");
const isBox2 = ref(true)
const isAnalysisLoading = ref(false)
const activeOpinionTab = ref('consensus')
const handleOpinionTabChange = type => {
activeOpinionTab.value = type
......@@ -369,6 +357,69 @@ const domainName = computed(() => {
return hit?.name || '全部领域'
})
const domainViewAnalysisRes = ref(null)
const tryParseAnswerFromStreamText = (text) => {
const lines = String(text || "")
.split(/\r?\n/)
.map((x) => x.trim())
.filter(Boolean)
let lastAnswerData = null
for (const line of lines) {
if (!line.startsWith("data:")) continue
const jsonText = line.slice(5).trim()
if (!jsonText || jsonText === "[DONE]") continue
try {
const parsed = JSON.parse(jsonText)
if (parsed?.type === "answer" && parsed?.data) {
lastAnswerData = parsed.data
}
} catch (e) {
// 忽略非 JSON 片段,继续解析后续行
}
}
return lastAnswerData
}
const getAnalysisAnswerData = (raw) => {
if (!raw) return null
if (typeof raw === "string") {
return tryParseAnswerFromStreamText(raw)
}
if (typeof raw?.data === "string") {
return tryParseAnswerFromStreamText(raw.data)
}
if (raw.type === "answer" && raw.data) return raw.data
if (raw.data?.type === "answer" && raw.data?.data) return raw.data.data
if (raw.data?.consensus || raw.data?.differences) return raw.data
if (raw.consensus || raw.differences) return raw
return null
}
const consensusList = computed(() => {
const answer = getAnalysisAnswerData(domainViewAnalysisRes.value)
const list = Array.isArray(answer?.consensus) ? answer.consensus : []
return list.map((item, index) => {
const sourceViews = Array.isArray(item?.source_views) ? item.source_views : []
const sourceViewText = sourceViews.map((v) => `report_id: ${v.report_id}, view_id: ${v.view_id}`).join(";")
return {
id: `consensus-${index + 1}`,
consensusContent: item?.consensus_content || "",
reportCount: sourceViews.length,
sourceViewText
}
})
})
const differenceList = computed(() => {
const answer = getAnalysisAnswerData(domainViewAnalysisRes.value)
const list = Array.isArray(answer?.differences) ? answer.differences : []
return list.map((item, index) => {
const sourceViews = Array.isArray(item?.source_views) ? item.source_views : []
const sourceViewText = sourceViews.map((v) => `report_id: ${v.report_id}, view_id: ${v.view_id}`).join(";")
return {
id: `difference-${index + 1}`,
disagreementContent: item?.disagreement_content || "",
reportCount: sourceViews.length,
sourceViewText
}
})
})
// 近N年发布(用于 startDate)
const selectedYears = ref(5);
const yearsOptions = [
......@@ -400,12 +451,14 @@ const handleChoseItem = () => {
if (!canProceed.value) return
isChoseItem.value = false
}
const handleAnalysis = () => {
const handleAnalysis = async () => {
if (!canProceed.value) return
isBox2.value = false
isAnalysisLoading.value = true
domainViewAnalysisRes.value = null
// 每次进入“开始分析”默认回到共识观点,避免上次状态残留导致内容错位
activeOpinionTab.value = 'consensus'
handlePostReportDomainViewAnalysis()
await handlePostReportDomainViewAnalysis()
}
const handleBack = () => {
isChoseItem.value = true
......@@ -446,39 +499,68 @@ const normalizeViewList = raw => {
const list = Array.isArray(raw) ? raw : []
return list
.filter(v => v && (v.view_id || v.viewId || v.id))
.map(v => ({
view_id: v.view_id ?? v.viewId ?? v.id,
view_text: v.view_text ?? v.viewText ?? v.text ?? ''
}))
.map(v => {
const viewId = v.view_id ?? v.viewId ?? v.id
const viewTextCandidate =
v.view_text ??
v.viewText ??
v.contentZh ??
v.contentEn ??
v.content ??
v.textZh ??
v.textEn ??
v.text ??
v.titleZh ??
v.titleEn ??
v.title ??
""
const viewText = String(viewTextCandidate ?? "").trim()
return {
view_id: viewId,
view_text: viewText
}
})
.filter(v => v.view_id != null && v.view_text !== "")
}
const handlePostReportDomainViewAnalysis = async () => {
try {
// 临时:先按接口文档示例写死,确保链路打通
const payload = {
domain: '人工智能',
report_view_list: [
{
report_id: 'Rand_RRA2877-1',
view_list: [
{
view_id: 35959,
view_text:
"We assess that metaverse technologies—that is, AI systems, immersive technologies, and enabling digital technologies—will continue to be integrated across new markets and create new demands for immersive experiences. We anticipate this will drive an increased TAV. We also assess that the metaverse concept is at an inflection point. As a result, uncertainties exist in assessing how the metaverse will expand, how large it will become, and whether it will become an expansive virtual world that directly competes with the physical world. We do assess that by the end of our study’s ten-year horizon, the conceptualization of this internet-like, immersive environment with virtual spaces will probably feel functionally more like an enhanced version of the IoT today than the metaverse that some have aspirationally described."
const selectedReports = selectedReportList.value || []
const reportViewListRaw = await Promise.all(
selectedReports.map(async (report) => {
const reportId = report?.id
if (!reportId) return null
try {
const viewpointRes = await getThinkTankReportViewpoint({
reportId,
currentPage: 0,
pageSize: 100,
keyword: "",
orgIds: ""
})
if (viewpointRes?.code !== 200 || !viewpointRes?.data) return null
const views = normalizeViewList(viewpointRes.data.content ?? viewpointRes.data)
if (!views.length) return null
return {
report_id: reportId,
view_list: views
}
]
},
{
report_id: 'Rand_RRA3124-1',
view_list: [
{
view_id: 35826,
view_text:
"But even successful generalization to other present-day models cannot guarantee that the method will generalize well to future models, which may differ from the models of today in unexpected ways"
} catch (error) {
console.error(`获取报告核心论点失败: ${reportId}`, error)
return null
}
]
})
)
const reportViewList = reportViewListRaw.filter(Boolean)
if (!reportViewList.length) {
domainViewAnalysisRes.value = null
return
}
]
const payload = {
domain: domainName.value || "全部领域",
report_view_list: reportViewList
}
const res = await postReportDomainViewAnalysis(payload)
......@@ -487,6 +569,8 @@ const handlePostReportDomainViewAnalysis = async () => {
} catch (e) {
console.error('智库领域观点分析接口调用失败', e)
domainViewAnalysisRes.value = null
} finally {
isAnalysisLoading.value = false
}
}
function arrayToString(value) {
......@@ -651,6 +735,18 @@ onMounted(async () => {
cursor: pointer;
}
.analysis-loading {
width: 100%;
height: 100%;
min-height: 320px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 700;
}
.box {
display: flex;
gap: 16px;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论