提交 c590eff1 authored 作者: yanpeng's avatar yanpeng

bugfix-2

上级 dc88159f
......@@ -147,7 +147,7 @@ const router = createRouter({
// 2)登录成功回跳带 ?token=:先 setToken 并同步 bootId,再去掉 URL 中的 token(须先于 clearTokenIfNewDevBoot,避免误清刚写入的登录态)
// 3)已有本地 token:正常走前端路由
router.beforeEach((to, from, next) => {
// 【新增】在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 在每次路由跳转开始前,取消上一个页面所有未完成的请求
// 这能防止旧页面的数据回来覆盖新页面,也能减少服务器压力
cancelAllRequests();
if (import.meta.env.DEV) {
......
<template>
<div class="chart-ai-analysis" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
<!-- 触发按钮 -->
<div class="ai-button-wrapper">
<AiButton />
</div>
<!-- AI 内容面板 -->
<transition name="ai-fade">
<div v-if="isVisible" class="ai-pane-wrapper">
<div class="ai-pane-content">
<!-- 1. Loading 状态 -->
<div v-if="hookInstance.loading" class="ai-loading-text">智能总结生成中...</div>
<!-- 2. 错误状态 -->
<div v-else-if="hookInstance.error" class="ai-error-text">
{{ hookInstance.error }}
</div>
<!-- 3. 成功状态 -->
<div v-else-if="hookInstance.interpretation" class="ai-success-content">
<AiPane :aiContent="hookInstance.interpretation" />
</div>
<!-- 4. 初始空状态 (可选,防止闪烁) -->
<div v-else class="ai-empty-text">暂无数据</div>
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { ref } from "vue";
import AiButton from "@/components/base/Ai/AiButton/index.vue";
import AiPane from "@/components/base/Ai/AiPane/index.vue";
const props = defineProps({
// 接收由 useChartInterpretation() 创建的实例对象
hookInstance: {
type: Object,
required: true
},
// 图表数据 Payload,用于首次请求
payload: {
type: Object,
required: true
}
});
const isVisible = ref(false);
const hasRequested = ref(false);
const handleMouseEnter = () => {
isVisible.value = true;
// 如果还没有请求过数据,则发起请求
if (!hasRequested.value && !props.hookInstance.loading) {
hasRequested.value = true;
// 调用 Hook 中的 interpret 方法
props.hookInstance.interpret(props.payload);
}
};
const handleMouseLeave = () => {
isVisible.value = false;
};
</script>
<style lang="scss" scoped>
.chart-ai-analysis {
position: absolute;
right: 0px;
bottom: 15px;
z-index: 999;
width: auto;
height: auto;
.ai-button-wrapper {
display: flex;
justify-content: flex-end;
}
.ai-pane-wrapper {
position: absolute;
bottom: 0;
right: 0;
width: 100%;
// 确保面板出现在按钮上方或覆盖区域,根据原有 CSS 调整
// 原有 .ai-pane:hover 逻辑是宽度变宽,这里我们直接显示完整面板
background: #fff;
border-radius: 4px;
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
padding: 10px;
box-sizing: border-box;
.ai-pane-content {
min-height: 40px;
.ai-loading-text,
.ai-error-text,
.ai-empty-text {
font-size: 14px;
color: var(--text-primary-50-color);
display: flex;
align-items: center;
justify-content: center;
min-height: 40px;
}
.ai-error-text {
color: var(--color-red-100);
}
}
}
}
// 复用原有的淡入淡出动画
.ai-fade-enter-active,
.ai-fade-leave-active {
transition: opacity 0.3s ease;
}
.ai-fade-enter-from,
.ai-fade-leave-to {
opacity: 0;
}
</style>
......@@ -43,8 +43,8 @@ defineProps({
.title-text {
color: rgba(10, 18, 30, 1);
font-size: 32px;
font-family: $base-font-family;
font-weight: bold;
font-family: "Microsoft YaHei";
font-weight: 700;
margin-left: 20px;
white-space: nowrap;
}
......
......@@ -149,48 +149,6 @@
</custom-container>
</el-col>
<el-col :span="8" style="padding-right: 0px">
<!-- <custom-container
titleType="danger"
title="风险信号"
:headerNum="warningList.length"
:titleIcon="dangerIcon"
height="450px"
>
<template #default>
<div class="box2-main">
<div
class="box2-main-item"
v-for="(item, index) in warningList"
:key="index"
@click="handleToRiskSignalDetail"
>
<div
class="item-left"
:class="{
itemLeftStatus1: item.status === '一般风险',
itemLeftStatus2: item.status === '重大风险'
}"
>
{{ item.status }}
</div>
<div class="item-right">
<div class="text">
<CommonPrompt :content="item.title">
{{ item.title }}
</CommonPrompt>
</div>
<div class="time">{{ item.time }}</div>
</div>
</div>
<div class="box2-footer" @click="handleToMoreRiskSignal">
<div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" />
</div>
<div class="text">{{ "查看更多" }}</div>
</div>
</div>
</template>
</custom-container> -->
<RiskSignal
:list="warningList"
@item-click="handleToRiskSignalDetail"
......@@ -205,21 +163,7 @@
<el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 50px; margin-top: 64px">
<CustomTitle id="position2" title="资讯要闻" />
</el-row>
<!-- <el-col :span="12">
<custom-container title="新闻资讯" :titleIcon="newsIcon" height="450px">
<template #header-right>
<el-button type="primary" link @click="handleToMoreNews">
{{ "更多 +" }}
</el-button>
</template>
<template #default>
<div class="news-list">
<NewsList :list-data="newsList" @item-click="item => handleNewsInfoClick(item)" />
</div>
</template>
</custom-container>
</el-col> -->
<div class="center-center">
<NewsList
:newsList="newsList"
......@@ -234,15 +178,6 @@
imageUrl="avatar"
@more-click="handleToSocialDetail"
/>
<!-- <custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px">
<template #default>
<div class="dialog-list">
<MessageBubble v-for="(item, index) in socialMediaList" @click="handlePerClick(item)"
@info-click="handleMediaClick(item)" :key="index" :avatar="item.avatar" :name="item.name"
:time="item.time" :source="item.source" :content="item.content" />
</div>
</template>
</custom-container> -->
</div>
<el-row :gutter="16" style="width: 1600px; margin: 0 auto; height: 510px; margin-top: 64px">
......@@ -278,9 +213,6 @@
style="display: flex; justify-content: center; align-items: center; gap: 5px"
>
<AreaTag v-for="tag in scope.row.tags" :key="tag" :tagName="tag" />
<!-- <el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{
tag
}}</el-tag> -->
</div>
</template>
</el-table-column>
......@@ -289,11 +221,15 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">美国商务部发布实体清单的频次,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="entityListReleaseFreqChart.interpretation" />
<AiButton @mouseenter="handleShowAiPane('entityListReleaseFreqChart')" />
<AiPane
v-if="aiPaneVisible?.entityListReleaseFreqChart"
:aiContent="overviewAiContent.entityListReleaseFreqChart"
@mouseleave="handleHideAiPane('entityListReleaseFreqChart')"
/>
</div>
</div>
<div class="box3-content">
......@@ -323,9 +259,6 @@
style="display: flex; justify-content: center; align-items: center; gap: 5px"
>
<AreaTag v-for="tag in scope.row.tags" :key="tag" :tagName="tag" />
<!-- <el-tag v-for="tag in scope.row.tags" :key="tag" :type="getTagType(tag)">{{
tag
}}</el-tag> -->
</div>
</template>
</el-table-column>
......@@ -334,13 +267,15 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">
美国商务部发布商业管制清单的频次,数据来源:美国商务部官网
</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="commerceControlListReleaseFreqChart.interpretation" />
<AiButton @mouseenter="handleShowAiPane('commerceControlListReleaseFreqChart')" />
<AiPane
v-if="aiPaneVisible?.commerceControlListReleaseFreqChart"
:aiContent="overviewAiContent.commerceControlListReleaseFreqChart"
@mouseleave="handleHideAiPane('commerceControlListReleaseFreqChart')"
/>
</div>
</div>
<div class="box3-content" style="display: none">
......@@ -393,11 +328,11 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入实体清单的中国实体领域分布情况,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="radarChart.interpretation" />
<AiButton @mouseenter="handleShowAiPane('radarChart')" />
<AiPane :aiContent="overviewAiContent.radarChart" @mouseleave="handleHideAiPane('radarChart')" />
</div>
</template>
</custom-container>
......@@ -428,11 +363,15 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入实体清单的中国实体数量变化趋势,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="trendChart.interpretation" />
<AiButton @mouseenter="handleShowAiPane('trendChart')" />
<AiPane
v-if="aiPaneVisible?.trendChart"
:aiContent="overviewAiContent.trendChart"
@mouseleave="handleHideAiPane('trendChart')"
/>
</div>
</template>
</custom-container>
......@@ -558,25 +497,6 @@
</template>
</el-table-column>
<!-- <el-table-column prop="strength" label="制裁强度" width="120" align="center">
<template #default="scope">
<div class="sanction-strength">
<div :class="['strength-bar', `strength-${scope.row.strength}`]"></div>
<span>{{ strengthLabels[scope.row.strength] }}</span>
</div>
</template>
</el-table-column> -->
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="140" align="right">
<template #default="scope">
<span
:class="['revenue-cell', scope.row.revenue === '无营收数据' ? 'no-revenue' : '']"
>
{{ scope.row.revenue }}
</span>
</template>
</el-table-column> -->
<el-table-column prop="revenue" label="50%规则子企业" width="280" align="right">
<template #default="scope">
<div class="num-item" v-if="scope.row.ruleOrgCount > 0">
......@@ -729,24 +649,21 @@
//这是一个备注
import NewsList from "@/components/base/newsList/index.vue";
import RiskSignal from "@/components/base/riskSignal/index.vue";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import RiskSignalOverviewDetailDialog from "@/components/base/RiskSignalOverviewDetailDialog/index.vue";
import { onMounted, ref, computed, reactive, shallowRef, watch, nextTick } from "vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
import * as echarts from "echarts";
import setChart from "@/utils/setChart";
import { ElMessage, ElMessageBox } from "element-plus";
import listPage from "./v2.0CommercialControlList/components/sanctionsOverview/components/listPage/index.vue";
import { DArrowRight, Warning, Search, Message } from "@element-plus/icons-vue";
import EChart from "@/components/Chart/index.vue";
import tipsIcon from "./assets/icons/info-icon.png";
import AiButton from "@/components/base/Ai/AiButton/index.vue";
import AiPane from "@/components/base/Ai/AiPane/index.vue";
import AreaTag from "@/components/base/AreaTag/index.vue";
import { useChartInterpretation } from "@/views/exportControl/utils/common";
const sanctionCountChart = useChartInterpretation();
import { TAGTYPE } from "@/public/constant";
import { useGotoCompanyPages } from "@/router/modules/company";
import { useGotoNewsDetail } from "@/router/modules/news";
......@@ -1017,11 +934,12 @@ onMounted(async () => {
tags: item.domain
};
});
entityListReleaseFreqChart.interpret({
type: "柱状图",
name: "美国商务部发布实体清单的频次",
data: entityListReleaseFreq.value
});
entityListReleaseFreqChartData.value = entityListReleaseFreq.value;
// entityListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布实体清单的频次",
// data: entityListReleaseFreq.value
// });
commerceControlListReleaseFreq.value = _.map(cclList1, item => {
return {
year: item.year,
......@@ -1030,11 +948,12 @@ onMounted(async () => {
tags: item.domain
};
});
commerceControlListReleaseFreqChart.interpret({
type: "柱状图",
name: "美国商务部发布商业管制清单的频次",
data: commerceControlListReleaseFreq.value
});
commerceControlListReleaseFreqChartData.value = commerceControlListReleaseFreq.value;
// commerceControlListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布商业管制清单的频次",
// data: commerceControlListReleaseFreq.value
// });
// 获取趋势图数据
await fetchTrendData();
......@@ -1075,13 +994,136 @@ const fetchTrendData = async () => {
});
if (res && res[0] && res[0].yearDomainCount) {
trendOption.value = processYearDomainCountData(res[0].yearDomainCount);
trendChart.interpret({ type: "柱状图", name: "制裁清单数量增长趋势", data: res[0].yearDomainCount });
trendChartData.value = res[0].yearDomainCount;
// trendChart.interpret({ type: "柱状图", name: "制裁清单数量增长趋势", data: res[0].yearDomainCount });
}
} catch (error) {
console.error("获取趋势图数据失败:", error);
}
};
const requestAiPaneContent = async key => {
if (!key || aiPaneLoading.value[key] || aiPaneFetched.value[key]) return;
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: true };
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成中..." };
try {
const payload = buildAiChartPayload(key);
const res = await getChartAnalysis(
{ text: JSON.stringify(payload) },
{
onChunk: chunk => {
const current = overviewAiContent.value[key];
const base = current === "智能总结生成中..." ? "" : current;
overviewAiContent.value = {
...overviewAiContent.value,
[key]: base + chunk
};
}
}
);
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
const interpretation = first?.解读 || first?.["解读"];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if (interpretation) {
overviewAiContent.value = {
...overviewAiContent.value,
[key]: interpretation
};
}
aiPaneFetched.value = { ...aiPaneFetched.value, [key]: true };
} catch (error) {
console.error("获取图表解读失败", error);
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成失败" };
} finally {
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: false };
}
};
const trendChartData = ref([]);
const radarChartData = ref([]);
const entityListReleaseFreqChartData = ref([]);
const commerceControlListReleaseFreqChartData = ref([]);
const aiPaneVisible = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const overviewAiContent = ref({
trendChart: "智能总结生成中...",
radarChart: "智能总结生成中...",
entityListReleaseFreqChart: "智能总结生成中...",
commerceControlListReleaseFreqChart: "智能总结生成中..."
});
const aiPaneFetched = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const aiPaneLoading = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const chartLoading = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const buildAiChartPayload = key => {
if (key === "trendChart") {
return { type: "柱状图", name: "制裁清单数量增长趋势", data: trendChartData.value };
}
if (key === "radarChart") {
return { type: "雷达图", name: "实体清单领域分布情况", data: radarChartData.value };
}
if (key === "entityListReleaseFreqChart") {
return {
type: "柱状图",
name: "美国商务部发布实体清单的频次",
data: entityListReleaseFreqChartData.value
};
}
if (key === "commerceControlListReleaseFreqChart") {
return {
type: "柱状图",
name: "美国商务部发布商业管制清单的频次",
data: commerceControlListReleaseFreqChartData.value
};
}
return { type: "", name: "", data: [] };
};
const handleShowAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: true
};
requestAiPaneContent(key);
};
const handleHideAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: false
};
};
watch(
() => [trendChecked.value, selectedEntityId.value],
() => {
......@@ -1096,6 +1138,7 @@ const processYearDomainCountData = yearDomainCountData => {
// 提取所有领域名称
const allDomains = [...new Set(yearDomainCountData.flatMap(item => item.domainCountInfo.map(domain => domain.name)))];
console.log("不同领域的数据 =>", allDomains);
// 构造 getMultipleBarChart_m 所需的数据结构
const chartData = {
......@@ -1122,7 +1165,7 @@ const processYearDomainCountData = yearDomainCountData => {
};
})
};
console.log("不同领域的数据 chartData", chartData);
// 使用 getMultipleBarChart_m 生成图表配置
return getMultipleBarChart_m(chartData);
};
......@@ -1349,7 +1392,8 @@ const fetchRadarData = async checked => {
}
};
});
radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
radarChartData.value = data;
// radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
}
} catch (error) {
console.error("获取雷达图数据失败:", error);
......
......@@ -26,7 +26,7 @@
</div>
</div>
</div>
<div class="nav-item" @click="handleToDataLibrary2">
<div class="nav-item" @click="handleToDataLibrary2">
<div class="item-position"></div>
<div class="content">
<div class="info">
......@@ -54,107 +54,94 @@
</div>
</div>
</div> -->
<EChart :option="domainChartOption" autoresize :style="{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary3" />
<EChart
:option="domainChartOption"
autoresize
:style="{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary3"
/>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入本次实体清单的中国实体领域分布情况,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="domainChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="domainChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('domainChart')" />
<AiPane
v-if="aiPaneVisible?.domainChart"
:aiContent="overviewAiContent.domainChart"
@mouseleave="handleHideAiPane('domainChart')"
/>
</div>
</AnalysisBox>
</div>
<div class="main-item">
<!-- <div class="title-com">
<div class="box"></div>
<div class="text">制裁实体类型分布情况</div>
<div class="right-group">
<div class="btn">
<img src="../../assets/数据库按钮.png" alt="" />
<img src="../../assets/下载按钮.png" alt="" />
<img src="../../assets/收藏按钮.png" alt="" />
</div>
</div>
</div>
<div class="echarts" ref="typeChartRef"></div>
<div class="bottom">
<div class="ai">
<div class="left">
<img :src="ai" alt="" class="icon1" />
<div class="text">我国被制裁实体以企业、科研院所和高校为主。</div>
</div>
<div class="right">
<img :src="right" alt="" class="icon2" />
</div>
</div>
</div> -->
<AnalysisBox title="制裁实体类型分布情况">
<!-- <div class="echarts" ref="typeChartRef"></div>
<div class="bottom">
<div class="ai">
<div class="left">
<img :src="ai" alt="" class="icon1" />
<div class="text">我国被制裁实体以企业、科研院所和高校为主。</div>
</div>
<div class="right">
<img :src="right" alt="" class="icon2" />
</div>
</div>
</div> -->
<EChart :option="typeChartOption" autoresize :style="{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary4" />
<EChart
:option="typeChartOption"
autoresize
:style="{ height: '300px', padding: '0 20px' }"
@chart-click="handleToDataLibrary4"
/>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入本次实体清单的中国实体类型分布情况,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="typeChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="typeChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('typeChart')" />
<AiPane
v-if="aiPaneVisible?.typeChart"
:aiContent="overviewAiContent.typeChart"
@mouseleave="handleHideAiPane('typeChart')"
/>
</div>
</AnalysisBox>
</div>
<div class="main-item">
<AnalysisBox title="制裁实体国家地区分布情况">
<div class="country-list">
<div class="list-item" v-for="(item, index) in countryDistribution" :key="index"
@click="handleToDataLibrary5(item)">
<div
class="list-item"
v-for="(item, index) in countryDistribution"
:key="index"
@click="handleToDataLibrary5(item)"
>
<img :src="flag" alt="" class="flag" />
<div class="country-name">{{ item.name }}</div>
<div class="progress-bar-container">
<div class="progress-bar" :style="{
width: item.width,
background: item.gradient
}"></div>
<div
class="progress-bar"
:style="{
width: item.width,
background: item.gradient
}"
></div>
</div>
<div class="count" :class="{ highlight: index === 0 }">{{ item.count }}</div>
</div>
</div>
<!-- <div class="bottom">
<div class="ai">
<div class="left">
<img :src="ai" alt="" class="icon1" />
<div class="text">美国对中国的制裁近年来呈现显著增长趋势。</div>
</div>
<div class="right">
<img :src="right" alt="" class="icon2" />
</div>
</div>
</div> -->
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入本次实体清单的实体国家地区分布情况,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="countryDistributionChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="countryDistributionChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('countryDistributionChart')" />
<AiPane
v-if="aiPaneVisible?.countryDistributionChart"
:aiContent="overviewAiContent.countryDistributionChart"
@mouseleave="handleHideAiPane('countryDistributionChart')"
/>
</div>
</AnalysisBox>
</div>
......@@ -163,40 +150,42 @@
<div class="map-wrapper">
<div class="map-chart" ref="mapChartRef"></div>
<div class="rank-list">
<div class="rank-item" v-for="(item, index) in regionDistribution" :key="index"
@click="handleToDataLibrary6(item)">
<div
class="rank-item"
v-for="(item, index) in regionDistribution"
:key="index"
@click="handleToDataLibrary6(item)"
>
<div class="rank-index" :class="'rank-' + (index + 1)">{{ index + 1 }}</div>
<div class="rank-name">{{ item.name }}</div>
<div class="rank-bar-bg">
<div class="rank-bar-fill" :style="{
width: (maxRegionCount > 0 ? (item.count / maxRegionCount) * 100 : 0) + '%',
background: getBarColor(index)
}"></div>
<div
class="rank-bar-fill"
:style="{
width: (maxRegionCount > 0 ? (item.count / maxRegionCount) * 100 : 0) + '%',
background: getBarColor(index)
}"
></div>
</div>
<div class="rank-value">{{ item.count }}</div>
</div>
</div>
</div>
<!-- <div class="bottom">
<div class="ai">
<div class="left">
<img :src="ai" alt="" class="icon1" />
<div class="text">我国被制裁实体多分布于沿海经济活跃省份。</div>
</div>
<div class="right">
<img :src="right" alt="" class="icon2" />
</div>
</div>
</div> -->
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">进入本次实体清单的中国实体各省分布情况,数据来源:美国商务部官网</div>
<div class="data-origin-text">数据来源:美国商务部官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="regionDistributionChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="regionDistributionChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('regionDistributionChart')" />
<AiPane
v-if="aiPaneVisible?.regionDistributionChart"
:aiContent="overviewAiContent.regionDistributionChart"
@mouseleave="handleHideAiPane('regionDistributionChart')"
/>
</div>
</AnalysisBox>
</div>
......@@ -223,9 +212,9 @@ import {
getSingleSanctionEntityCountryCount,
getSingleSanctionEntityRegionCount
} from "@/api/exportControlV2.0";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import { useChartInterpretation } from "@/views/exportControl/utils/common";
const sanctionCountChart = useChartInterpretation();
const domainChart = useChartInterpretation();
const typeChart = useChartInterpretation();
const countryDistributionChart = useChartInterpretation();
......@@ -248,8 +237,9 @@ const getRegionData = async () => {
if (res.code === 200) {
regionDistribution.value = res.data || [];
maxRegionCount.value = Math.max(...regionDistribution.value.map(item => item.count), 0);
regionDistributionChartData.value = res.data || [];
initMapChart();
regionDistributionChart.interpret({ type: "柱状图", name: "进入本次实体清单的中国实体各省分布情况", data: res.data });
// regionDistributionChart.interpret({ type: "柱状图", name: "进入本次实体清单的中国实体各省分布情况", data: res.data });
}
} catch (error) {
console.log(error);
......@@ -289,11 +279,12 @@ const getCountryCount = async () => {
gradient
};
});
countryDistributionChart.interpret({
type: "柱状图",
name: "进入本次实体清单的实体国家地区分布情况",
data: res.data
});
countryDistributionChartData.value = res.data || [];
// countryDistributionChart.interpret({
// type: "柱状图",
// name: "进入本次实体清单的实体国家地区分布情况",
// data: res.data
// });
}
} catch (error) {
console.log(error);
......@@ -314,7 +305,7 @@ const getEntityTypeCount = async () => {
const res = await getSingleSanctionEntityTypeCount(params);
if (res.code === 200) {
entityTypeCount.value = res.data || [];
typeChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体类型分布情况", data: entityTypeCount.value });
// typeChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体类型分布情况", data: entityTypeCount.value });
initTypeChart();
}
} catch (error) {
......@@ -337,7 +328,7 @@ const getDomainCount = async () => {
if (res.code === 200) {
domainCount.value = res.data || [];
initDomainChart();
domainChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体领域分布情况", data: domainCount.value });
// domainChart.interpret({ type: "饼图", name: "进入本次实体清单的中国实体领域分布情况", data: domainCount.value });
}
} catch (error) {
console.log(error);
......@@ -351,7 +342,7 @@ const getTotalCount = async () => {
if (!sanRecordId.value) return;
try {
const res = await getSingleSanctionTotalCount(route.query.sanTypeId, sanRecordId.value);
console.log('统计', res);
console.log("统计", res);
if (res.code === 200) {
totalCount.value = res.data || {};
......@@ -389,14 +380,6 @@ for (let i = 2025; i >= 2000; i--) {
timeOptions.push({ label: `${i}年`, value: `${i}` });
}
// const countryDistribution = [
// { name: "中国", count: 24, width: "80%", gradient: "linear-gradient(90deg, rgba(205, 66, 70, 0) 0%, rgba(205, 66, 70, 1) 100%)" },
// { name: "沙特阿拉伯", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "伊朗", count: 2, width: "60%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "俄罗斯", count: 2, width: "55%", gradient: "linear-gradient(90deg, rgba(255, 172, 77, 0) 0%, rgba(255, 172, 77, 1) 100%)" },
// { name: "中国香港", count: 1, width: "40%", gradient: "linear-gradient(90deg, rgba(5, 95, 194, 0) 0%, rgba(5, 95, 194, 1) 100%)" }
// ];
const mapChartRef = ref(null);
const domainChartRef = ref(null);
const typeChartRef = ref(null);
......@@ -474,13 +457,13 @@ const initMapChart = () => {
chart.setOption(option);
chart.on('click', function (params) {
chart.on("click", function (params) {
const param = {
selectedProvince: params.name,
selectedDate: JSON.stringify([route.query.date, route.query.date])
}
};
const curRoute = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: param
});
window.open(curRoute.href, "_blank");
......@@ -855,67 +838,67 @@ const initTypeChart = () => {
const sanTypeId = ref("");
// 制裁实体领域分布情况
const handleToDataLibrary3 = (val) => {
const handleToDataLibrary3 = val => {
// console.log('val', val);
const params = {
domains: val.name,
isCnEntityOnly: true,
selectedDate: JSON.stringify([route.query.date, route.query.date])
}
};
const curRoute = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: params
});
window.open(curRoute.href, "_blank");
}
};
// 制裁实体类型分布情况
const handleToDataLibrary4 = (val) => {
const handleToDataLibrary4 = val => {
// console.log('val', val);
const params = {
selectedEntityType: val.name,
isCnEntityOnly: true,
selectedDate: JSON.stringify([route.query.date, route.query.date])
}
};
const curRoute = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: params
});
window.open(curRoute.href, "_blank");
}
};
// 制裁实体国家地区分布情况
const handleToDataLibrary5 = (item) => {
const handleToDataLibrary5 = item => {
const params = {
selectedCountryId: item.id,
isCnEntityOnly: true,
selectedDate: JSON.stringify([route.query.date, route.query.date])
}
};
const curRoute = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: params
});
window.open(curRoute.href, "_blank");
}
};
// 制裁实体各省分布情况
const handleToDataLibrary6 = (item) => {
console.log('item', item);
const handleToDataLibrary6 = item => {
console.log("item", item);
const params = {
selectedProvince: item.name,
isCnEntityOnly: true,
selectedDate: JSON.stringify([route.query.date, route.query.date])
}
};
const curRoute = router.resolve({
path: '/dataLibrary/dataEntityList',
path: "/dataLibrary/dataEntityList",
query: params
});
window.open(curRoute.href, "_blank");
}
};
// 跳转到数据资源库
const handleToDataLibrary = () => {
const dateStr = route.query.date ? route.query.date : ''
const dateStr = route.query.date ? route.query.date : "";
const curRoute = router.resolve({
path: "/dataLibrary/dataEntityList",
query: {
......@@ -924,10 +907,10 @@ const handleToDataLibrary = () => {
}
});
window.open(curRoute.href, "_blank");
}
};
const handleToDataLibrary1 = () => {
const dateStr = route.query.date ? route.query.date : ''
const dateStr = route.query.date ? route.query.date : "";
const curRoute = router.resolve({
path: "/dataLibrary/dataEntityList",
query: {
......@@ -937,10 +920,10 @@ const handleToDataLibrary1 = () => {
}
});
window.open(curRoute.href, "_blank");
}
};
const handleToDataLibrary2 = () => {
const dateStr = route.query.date ? route.query.date : ''
const dateStr = route.query.date ? route.query.date : "";
const curRoute = router.resolve({
path: "/dataLibrary/dataEntityList",
query: {
......@@ -949,8 +932,129 @@ const handleToDataLibrary2 = () => {
}
});
window.open(curRoute.href, "_blank");
}
};
const requestAiPaneContent = async key => {
if (!key || aiPaneLoading.value[key] || aiPaneFetched.value[key]) return;
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: true };
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成中..." };
try {
const payload = buildAiChartPayload(key);
const res = await getChartAnalysis(
{ text: JSON.stringify(payload) },
{
onChunk: chunk => {
const current = overviewAiContent.value[key];
const base = current === "智能总结生成中..." ? "" : current;
overviewAiContent.value = {
...overviewAiContent.value,
[key]: base + chunk
};
}
}
);
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
const interpretation = first?.解读 || first?.["解读"];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if (interpretation) {
overviewAiContent.value = {
...overviewAiContent.value,
[key]: interpretation
};
}
aiPaneFetched.value = { ...aiPaneFetched.value, [key]: true };
} catch (error) {
console.error("获取图表解读失败", error);
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成失败" };
} finally {
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: false };
}
};
const domainChartData = ref([]);
const typeChartData = ref([]);
const countryDistributionChartData = ref([]);
const regionDistributionChartData = ref([]);
const aiPaneVisible = ref({
domainChart: false,
typeChart: false,
countryDistributionChart: false,
regionDistributionChart: false
});
const overviewAiContent = ref({
domainChart: "智能总结生成中...",
typeChart: "智能总结生成中...",
countryDistributionChart: "智能总结生成中...",
regionDistributionChart: "智能总结生成中..."
});
const aiPaneFetched = ref({
domainChart: false,
typeChart: false,
countryDistributionChart: false,
regionDistributionChart: false
});
const aiPaneLoading = ref({
domainChart: false,
typeChart: false,
countryDistributionChart: false,
regionDistributionChart: false
});
const chartLoading = ref({
domainChart: false,
typeChart: false,
countryDistributionChart: false,
regionDistributionChart: false
});
const buildAiChartPayload = key => {
if (key === "domainChart") {
return { type: "饼图", name: "进入本次实体清单的中国实体领域分布情况", data: domainCount.value };
}
if (key === "typeChart") {
return { type: "饼图", name: "进入本次实体清单的中国实体类型分布情况", data: entityTypeCount.value };
}
if (key === "countryDistributionChart") {
return {
type: "柱状图",
name: "进入本次实体清单的实体国家地区分布情况",
data: countryDistributionChartData.value
};
}
if (key === "regionDistributionChart") {
return {
type: "柱状图",
name: "进入本次实体清单的中国实体各省分布情况",
data: regionDistributionChartData.value
};
}
return { type: "", name: "", data: [] };
};
const handleShowAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: true
};
requestAiPaneContent(key);
};
const handleHideAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: false
};
};
onMounted(() => {
// 获取路由参数id
......
......@@ -8,7 +8,7 @@
<div class="info-row">
<div class="label">发布机构:</div>
<div class="value link">
<img :src="title" alt="" class="icon" />
<img :src="formattedData.postOrgLogoUrl || title" alt="" class="icon" />
<span @click="handleClickDp">{{ formattedData.postOrgName }} ></span>
</div>
</div>
......@@ -45,15 +45,22 @@
<div class="left-top-content">
<div class="content-title">制裁实体分布:</div>
<div class="distribution-list">
<div class="list-item" v-for="(item, index) in entityDistribution" :key="index"
@click="handleToDataLibrary(item)">
<div
class="list-item"
v-for="(item, index) in entityDistribution"
:key="index"
@click="handleToDataLibrary(item)"
>
<img :src="item.imageUrl || flag" alt="" class="flag" />
<div class="country-name">{{ item.name }}</div>
<div class="progress-bar-container">
<div class="progress-bar" :style="{
width: item.width,
background: item.gradient
}"></div>
<div
class="progress-bar"
:style="{
width: item.width,
background: item.gradient
}"
></div>
</div>
<div class="count" :class="{ highlight: index === 0 }">{{ item.count }}</div>
</div>
......@@ -96,13 +103,25 @@
</div>
<div class="filter-right">
<el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select v-model="filterField" placeholder="选择领域" style="width: 150px; margin: 0 12px 0 16px">
<el-select
v-model="filterField"
placeholder="选择领域"
style="width: 150px; margin: 0 12px 0 16px"
>
<!-- <el-option label="全部领域" value="" /> -->
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" />
<el-option
v-for="item in domainOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input v-model="searchKeyword" placeholder="搜索实体"
<el-input
v-model="searchKeyword"
placeholder="搜索实体"
style="width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon="Search" />
:suffix-icon="Search"
/>
</div>
</div>
<div class="stats-row">
......@@ -117,14 +136,21 @@
<div class="stats-info">
<div class="stat-item">
<span class="dot red"></span>
<span class="text">新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
addRuleCount
}}</span>家)</span>
<span class="text"
>新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
addRuleCount
}}</span
>家)</span
>
</div>
<div class="stat-item">
<span class="dot green"></span>
<span class="text">移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span
class="num green">{{ removeRuleCount }}</span>家)</span>
<span class="text"
>移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span
class="num green"
>{{ removeRuleCount }}</span
>家)</span
>
</div>
</div>
</div>
......@@ -156,7 +182,11 @@
>{{ item }}</span
> -->
<div class="domain-box">
<AreaTag v-for="(domain, index) in scope.row.fields" :key="index" :tagName="domain" />
<AreaTag
v-for="(domain, index) in scope.row.fields"
:key="index"
:tagName="domain"
/>
</div>
</template>
</el-table-column>
......@@ -165,8 +195,11 @@
<el-table-column prop="revenue" label="营收(亿元)" width="110" align="center" />
<el-table-column label="50%规则子企业" width="180" align="center">
<template #default="scope">
<span v-if="scope.row.subsidiaryCount" class="subsidiary-link"
@click="handleSubsidiaryClick(scope.row)">
<span
v-if="scope.row.subsidiaryCount"
class="subsidiary-link"
@click="handleSubsidiaryClick(scope.row)"
>
{{ scope.row.subsidiaryText }}
<span class="blue-text">{{ scope.row.subsidiaryCount }}家 ></span>
</span>
......@@ -184,8 +217,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog v-model="subsidiaryDialogVisible" :company-name="currentSubsidiaryCompanyName"
:total-count="currentSubsidiaryCount" :data-list="currentSubsidiaryList" />
<RuleSubsidiaryDialog
v-model="subsidiaryDialogVisible"
:company-name="currentSubsidiaryCompanyName"
:total-count="currentSubsidiaryCount"
:data-list="currentSubsidiaryList"
/>
</div>
</template>
......@@ -310,8 +347,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText:
org.ruleOrgList && org.ruleOrgList.length > 0
? (org.ruleOrgList[0].orgName.length > 10
? org.ruleOrgList[0].orgName.slice(0, 10) + "..."
: org.ruleOrgList[0].orgName) + "...等"
? org.ruleOrgList[0].orgName.slice(0, 10) + "..."
: org.ruleOrgList[0].orgName) + "...等"
: ""
}))
}));
......@@ -445,7 +482,8 @@ const formattedData = computed(() => {
administrativeOrderId: info.administrativeOrderId ? `No. ${info.administrativeOrderId}` : "",
postPersonName: info.postPersonName,
domains: info.domainNames,
avartar: info.postPersonAvatarUrl
avartar: info.postPersonAvatarUrl,
postOrgLogoUrl: info.postOrgLogoUrl
};
});
......@@ -525,23 +563,22 @@ const entityDistribution = ref([
const sanTypeId = ref("");
// 跳转到数据资源库
const handleToDataLibrary = (item) => {
console.log('item', item);
const dateStr = formattedData.value.postDate.replace(/(\d{4})(\d{1,2})(\d{1,2})日/, (_, y, m, d) =>
`${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`
const handleToDataLibrary = item => {
console.log("item", item);
const dateStr = formattedData.value.postDate.replace(
/(\d{4})(\d{1,2})(\d{1,2})日/,
(_, y, m, d) => `${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`
);
const route = router.resolve({
const route = router.resolve({
path: "/dataLibrary/dataEntityList",
query:{
selectedDate: JSON.stringify([dateStr,dateStr]),
query: {
selectedDate: JSON.stringify([dateStr, dateStr]),
selectedCountryId: item.id
}
});
window.open(route.href, "_blank");
}
};
onMounted(() => {
// 获取路由参数中的sanTypeId
......
......@@ -68,7 +68,7 @@
"
/>
</div>
<div class="translate-text">{{ "显示文" }}</div>
<div class="translate-text">{{ "显示文" }}</div>
</div>
<div class="btn" @click="handleDownload">
<div class="icon">
......@@ -79,9 +79,6 @@
</div>
</div>
<div class="report-box">
<div class="pdf-pane-wrap" v-if="valueSwitch && reportUrlEnWithPage">
<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'}`"
......@@ -90,6 +87,9 @@
class="pdf-pane-inner"
/>
</div>
<div class="pdf-pane-wrap" v-if="valueSwitch && reportUrlEnWithPage">
<pdf ref="leftPdfRef" :pdfUrl="reportUrlEnWithPage" class="pdf-pane-inner" />
</div>
</div>
</div>
</div>
......
......@@ -43,8 +43,8 @@ defineProps({
.title-text {
color: rgba(10, 18, 30, 1);
font-size: 32px;
font-family: $base-font-family;
font-weight: bold;
font-family: "Microsoft YaHei";
font-weight: 700;
margin-left: 20px;
white-space: nowrap;
}
......
......@@ -234,13 +234,17 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">
美国商务部发布实体清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div class="data-origin-text">数据来源:美国财政部海外资产管理办公室官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="entityListReleaseFreqChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="entityListReleaseFreqChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('entityListReleaseFreqChart')" />
<AiPane
v-if="aiPaneVisible?.entityListReleaseFreqChart"
:aiContent="overviewAiContent.entityListReleaseFreqChart"
@mouseleave="handleHideAiPane('entityListReleaseFreqChart')"
/>
</div>
</div>
<div class="box3-content">
......@@ -280,13 +284,17 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">
美国商务部发布商业管制清单的频次,数据来源:美国财政部海外资产管理办公室官网
</div>
<div class="data-origin-text">数据来源:美国财政部海外资产管理办公室官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="commerceControlListReleaseFreqChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="commerceControlListReleaseFreqChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('commerceControlListReleaseFreqChart')" />
<AiPane
v-if="aiPaneVisible?.commerceControlListReleaseFreqChart"
:aiContent="overviewAiContent.commerceControlListReleaseFreqChart"
@mouseleave="handleHideAiPane('commerceControlListReleaseFreqChart')"
/>
</div>
</div>
<div class="box3-content" style="display: none">
......@@ -339,13 +347,13 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">
进入SDN清单的中国实体领域分布情况,数据来源:美国财政部海外资产管理办公室官网
</div>
<div class="data-origin-text">数据来源:美国财政部海外资产管理办公室官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="radarChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="radarChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('radarChart')" />
<AiPane :aiContent="overviewAiContent.radarChart" @mouseleave="handleHideAiPane('radarChart')" />
</div>
</template>
</custom-container>
......@@ -371,13 +379,17 @@
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">
进入SDN清单的中国实体数量变化趋势,数据来源:美国财政部海外资产管理办公室官网
</div>
<div class="data-origin-text">数据来源:美国财政部海外资产管理办公室官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="trendChart.interpretation" />
<!-- <AiButton />
<AiPane :aiContent="trendChart.interpretation" /> -->
<AiButton @mouseenter="handleShowAiPane('trendChart')" />
<AiPane
v-if="aiPaneVisible?.trendChart"
:aiContent="overviewAiContent.trendChart"
@mouseleave="handleHideAiPane('trendChart')"
/>
</div>
</template>
</custom-container>
......@@ -705,6 +717,7 @@ import RiskSignal from "@/components/base/riskSignal/index.vue";
import RiskSignalOverviewDetailDialog from "@/components/base/RiskSignalOverviewDetailDialog/index.vue";
import { onMounted, ref, computed, reactive, shallowRef, watch, nextTick } from "vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
import * as echarts from "echarts";
......@@ -1265,7 +1278,8 @@ const fetchRadarData = async checked => {
};
});
console.log("图例 =>", radarOption.value);
radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
radarChartData.value = data;
// radarChart.interpret({ type: "雷达图", name: "实体清单领域分布情况", data: data });
}
} catch (error) {
console.error("获取雷达图数据失败:", error);
......@@ -1824,6 +1838,128 @@ const handleToDataLibrary = item => {
window.open(route.href, "_blank");
};
const requestAiPaneContent = async key => {
if (!key || aiPaneLoading.value[key] || aiPaneFetched.value[key]) return;
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: true };
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成中..." };
try {
const payload = buildAiChartPayload(key);
const res = await getChartAnalysis(
{ text: JSON.stringify(payload) },
{
onChunk: chunk => {
const current = overviewAiContent.value[key];
const base = current === "智能总结生成中..." ? "" : current;
overviewAiContent.value = {
...overviewAiContent.value,
[key]: base + chunk
};
}
}
);
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
const interpretation = first?.解读 || first?.["解读"];
// 流式已渲染过内容,最终用解析出的解读覆盖(保证显示格式统一)
if (interpretation) {
overviewAiContent.value = {
...overviewAiContent.value,
[key]: interpretation
};
}
aiPaneFetched.value = { ...aiPaneFetched.value, [key]: true };
} catch (error) {
console.error("获取图表解读失败", error);
overviewAiContent.value = { ...overviewAiContent.value, [key]: "智能总结生成失败" };
} finally {
aiPaneLoading.value = { ...aiPaneLoading.value, [key]: false };
}
};
const trendChartData = ref([]);
const radarChartData = ref([]);
const entityListReleaseFreqChartData = ref([]);
const commerceControlListReleaseFreqChartData = ref([]);
const aiPaneVisible = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const overviewAiContent = ref({
trendChart: "智能总结生成中...",
radarChart: "智能总结生成中...",
entityListReleaseFreqChart: "智能总结生成中...",
commerceControlListReleaseFreqChart: "智能总结生成中..."
});
const aiPaneFetched = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const aiPaneLoading = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const chartLoading = ref({
trendChart: false,
radarChart: false,
entityListReleaseFreqChart: false,
commerceControlListReleaseFreqChart: false
});
const buildAiChartPayload = key => {
if (key === "trendChart") {
return { type: "柱状图", name: "制裁清单数量增长趋势", data: trendChartData.value };
}
if (key === "radarChart") {
return { type: "雷达图", name: "实体清单领域分布情况", data: radarChartData.value };
}
if (key === "entityListReleaseFreqChart") {
return {
type: "柱状图",
name: "美国商务部发布实体清单的频次",
data: entityListReleaseFreqChartData.value
};
}
if (key === "commerceControlListReleaseFreqChart") {
return {
type: "柱状图",
name: "美国商务部发布商业管制清单的频次",
data: commerceControlListReleaseFreqChartData.value
};
}
return { type: "", name: "", data: [] };
};
const handleShowAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: true
};
requestAiPaneContent(key);
};
const handleHideAiPane = key => {
aiPaneVisible.value = {
...aiPaneVisible.value,
[key]: false
};
};
onMounted(async () => {
console.log("finance 页面 mounted");
try {
......@@ -1886,11 +2022,12 @@ onMounted(async () => {
await fetchRadarData(domainChecked.value);
// 获取出口管制制裁措施
await fetchSanctionList();
entityListReleaseFreqChart.interpret({
type: "柱状图",
name: "美国商务部发布实体清单的频次",
data: entityListReleaseFreq.value
});
entityListReleaseFreqChartData.value = entityListReleaseFreq.value;
// entityListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布实体清单的频次",
// data: entityListReleaseFreq.value
// });
commerceControlListReleaseFreq.value = _.map(cclList1, item => {
return {
year: item.year,
......@@ -1899,11 +2036,12 @@ onMounted(async () => {
tags: item.domain
};
});
commerceControlListReleaseFreqChart.interpret({
type: "柱状图",
name: "美国商务部发布商业管制清单的频次",
data: commerceControlListReleaseFreq.value
});
commerceControlListReleaseFreqChartData.value = commerceControlListReleaseFreq.value;
// commerceControlListReleaseFreqChart.interpret({
// type: "柱状图",
// name: "美国商务部发布商业管制清单的频次",
// data: commerceControlListReleaseFreq.value
// });
} catch (err) {
console.log("此处报错?");
console.log(err);
......
......@@ -8,7 +8,7 @@
<div class="info-row">
<div class="label">发布机构:</div>
<div class="value link">
<img :src="title" alt="" class="icon" />
<img :src="formattedData.postOrgLogoUrl || title" alt="" class="icon" />
<span @click="handleClickDp">{{ formattedData.postOrgName }} ></span>
</div>
</div>
......@@ -46,17 +46,24 @@
<div class="left-top-content">
<div class="content-title">制裁实体分布:</div>
<div class="distribution-list">
<div class="list-item" v-for="(item, index) in entityDistribution" :key="index"
@click="handleToDataLibrary(item)">
<div
class="list-item"
v-for="(item, index) in entityDistribution"
:key="index"
@click="handleToDataLibrary(item)"
>
<img :src="item.imageUrl || flag" alt="" class="flag" />
<div class="country-name">{{ item.name }}</div>
<div class="progress-bar-container">
<div class="progress-bar" :style="{
width: item.width,
background: item.gradient
}"></div>
<div
class="progress-bar"
:style="{
width: item.width,
background: item.gradient
}"
></div>
</div>
<div class="count" :class="{ highlight: index === 0 }">{{ item.count }}</div>
<div class="count" :class="{ highlight: item.name === '中国' }">{{ item.count }}</div>
</div>
</div>
</div>
......@@ -97,13 +104,25 @@
</div>
<div class="filter-right">
<el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select v-model="filterField" placeholder="全部领域" style="width: 150px; margin: 0 12px 0 16px">
<el-select
v-model="filterField"
placeholder="全部领域"
style="width: 150px; margin: 0 12px 0 16px"
>
<el-option label="全部领域" value="" />
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" />
<el-option
v-for="item in domainOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input v-model="searchKeyword" placeholder="搜索实体"
<el-input
v-model="searchKeyword"
placeholder="搜索实体"
style="width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon="Search" />
:suffix-icon="Search"
/>
</div>
</div>
<div class="stats-row">
......@@ -118,14 +137,21 @@
<div class="stats-info">
<div class="stat-item">
<span class="dot red"></span>
<span class="text">新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
addRuleCount
}}</span>家)</span>
<span class="text"
>新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
addRuleCount
}}</span
>家)</span
>
</div>
<div class="stat-item">
<span class="dot green"></span>
<span class="text">移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span
class="num green">{{ removeRuleCount }}</span>家)</span>
<span class="text"
>移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span
class="num green"
>{{ removeRuleCount }}</span
>家)</span
>
</div>
</div>
</div>
......@@ -157,7 +183,11 @@
>{{ item }}</span
> -->
<div class="domain-box">
<AreaTag v-for="(domain, index) in scope.row.fields" :key="index" :tagName="domain" />
<AreaTag
v-for="(domain, index) in scope.row.fields"
:key="index"
:tagName="domain"
/>
</div>
</template>
</el-table-column>
......@@ -166,20 +196,26 @@
<el-table-column prop="entityTypeId" label="类型" width="120" align="center">
<template #default="scope">
<div style="display: flex; gap: 4px; justify-content: center">
<AreaTag :tagName="scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
" />
<AreaTag
:tagName="
scope.row.entityType === 1
? '个人'
: scope.row.entityType === 2
? '实体'
: '公司'
"
/>
</div>
</template>
</el-table-column>
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="110" align="center" /> -->
<el-table-column label="50%规则子企业" width="180" align="center">
<template #default="scope">
<span v-if="scope.row.subsidiaryCount" class="subsidiary-link"
@click="handleSubsidiaryClick(scope.row)">
<span
v-if="scope.row.subsidiaryCount"
class="subsidiary-link"
@click="handleSubsidiaryClick(scope.row)"
>
{{ scope.row.subsidiaryText }}
<span class="blue-text">{{ scope.row.subsidiaryCount }}家 ></span>
</span>
......@@ -223,8 +259,12 @@
</div>
</div>
<!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog v-model="subsidiaryDialogVisible" :company-name="currentSubsidiaryCompanyName"
:total-count="currentSubsidiaryCount" :data-list="currentSubsidiaryList" />
<RuleSubsidiaryDialog
v-model="subsidiaryDialogVisible"
:company-name="currentSubsidiaryCompanyName"
:total-count="currentSubsidiaryCount"
:data-list="currentSubsidiaryList"
/>
</div>
</template>
......@@ -333,8 +373,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText:
org.ruleOrgList && org.ruleOrgList.length > 0
? (org.ruleOrgList[0].orgName.length > 10
? org.ruleOrgList[0].orgName.slice(0, 10) + "..."
: org.ruleOrgList[0].orgName) + "...等"
? org.ruleOrgList[0].orgName.slice(0, 10) + "..."
: org.ruleOrgList[0].orgName) + "...等"
: ""
}))
}));
......@@ -481,7 +521,8 @@ const formattedData = computed(() => {
administrativeOrderId: info.administrativeOrderId ? `No. ${info.administrativeOrderId}` : "",
postPersonName: info.postPersonName,
domains: info.domainNames,
avartar: info.postPersonAvatarUrl
avartar: info.postPersonAvatarUrl,
postOrgLogoUrl: info.postOrgLogoUrl
};
});
......@@ -590,10 +631,11 @@ const getReasonHistoryList = async () => {
const sanTypeId = ref("");
// 跳转到数据资源库
const handleToDataLibrary = (item) => {
console.log('item', item);
const dateStr = formattedData.value.postDate.replace(/(\d{4})(\d{1,2})(\d{1,2})日/, (_, y, m, d) =>
`${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`
const handleToDataLibrary = item => {
console.log("item", item);
const dateStr = formattedData.value.postDate.replace(
/(\d{4})(\d{1,2})(\d{1,2})日/,
(_, y, m, d) => `${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`
);
const route = router.resolve({
......@@ -604,7 +646,7 @@ const handleToDataLibrary = (item) => {
}
});
window.open(route.href, "_blank");
}
};
onMounted(() => {
// 获取路由参数中的sanTypeId
......
......@@ -28,7 +28,7 @@
</div>
<div class="original-text-btn" @click="handleClickOriginalText">
<img :src="icon1" alt="" />
<span>实体清单原文</span>
<span>SDN清单原文</span>
</div>
<div class="btn3" @click="handleAnalysisClick">
<div class="icon">
......
......@@ -68,7 +68,7 @@
"
/>
</div>
<div class="translate-text">{{ "显示文" }}</div>
<div class="translate-text">{{ "显示文" }}</div>
</div>
<div class="btn" @click="handleDownload">
<div class="icon">
......@@ -79,9 +79,6 @@
</div>
</div>
<div class="report-box">
<div class="pdf-pane-wrap" v-if="valueSwitch && reportUrlEnWithPage">
<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'}`"
......@@ -90,6 +87,9 @@
class="pdf-pane-inner"
/>
</div>
<div class="pdf-pane-wrap" v-if="valueSwitch && reportUrlEnWithPage">
<pdf ref="leftPdfRef" :pdfUrl="reportUrlEnWithPage" class="pdf-pane-inner" />
</div>
</div>
</div>
</div>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论