提交 e4bc83d4 authored 作者: 张伊明's avatar 张伊明

合并分支 'yp-dev' 到 'pre'

Yp dev 查看合并请求 !245
流水线 #115 已取消 于阶段
in 3 分 26 秒
<template> <template>
<div class="analysis-box-wrapper" :style="{ width: width ? width : '100%', height: height ? height : '100%' }"> <div class="analysis-box-wrapper" :style="{ width: width ? width : '100%', height: height ? height : '100%' }">
<div class="wrapper-header"> <div class="wrapper-header">
<div class="header-icon"></div> <div class="header-icon"></div>
<div class="header-title"> <div class="header-title">
<div v-if="title">{{ title }}</div> <div v-if="title">{{ title }}</div>
<slot v-else name="custom-title"></slot> <slot v-else name="custom-title"></slot>
</div> </div>
<div class="header-btn" v-if="!showAllBtn"> <div class="header-btn" v-if="!showAllBtn">
<slot name="header-btn"></slot> <slot name="header-btn"></slot>
</div> </div>
<div class="header-btn1" v-else> <div class="header-btn1" v-else>
<slot name="header-btn"></slot> <slot name="header-btn"></slot>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="header-right-btn" @click="handleSave" v-if="showAllBtn"> <div class="header-right-btn" @click="handleSave" v-if="showAllBtn">
<img src="@/assets/icons/box-header-icon1.png" alt=""> <img src="@/assets/icons/box-header-icon1.png" alt="" />
</div> </div>
<div class="header-right-btn" @click="handleDownload"> <div class="header-right-btn" @click="handleDownload">
<img src="@/assets/icons/box-header-icon2.png" alt=""> <img src="@/assets/icons/box-header-icon2.png" alt="" />
</div> </div>
<div class="header-right-btn" @click="handleCollect"> <div class="header-right-btn" @click="handleCollect">
<img src="@/assets/icons/box-header-icon3.png" alt=""> <img src="@/assets/icons/box-header-icon3.png" alt="" />
</div> </div>
</div> </div>
</div> </div>
<div class="wrapper-main"> <div class="wrapper-main">
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ElMessage } from "element-plus";
import { ref, computed } from 'vue' import { ref, computed } from "vue";
const props = defineProps({ const props = defineProps({
title: { title: {
type: String, type: String,
default: '' default: ""
}, },
width: { width: {
type: String, type: String,
default: '' default: ""
}, },
height: { height: {
type: String, type: String,
default: '' default: ""
}, },
showAllBtn: { showAllBtn: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 当业务功能尚未实现时,点击右上角图标仅弹出统一提示 // 当业务功能尚未实现时,点击右上角图标仅弹出统一提示
devTip: { devTip: {
type: Boolean, type: Boolean,
default: false default: false
} }
}) });
const handleSave = () => { const handleSave = () => {
if (props.devTip) { if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!') ElMessage.warning("当前功能正在开发中,敬请期待!");
return return;
} }
ElMessage.success('保存当前内容') ElMessage.success("保存当前内容");
// emit('save') // emit('save')
} };
const handleDownload = () => { const handleDownload = () => {
if (props.devTip) { if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!') ElMessage.warning("当前功能正在开发中,敬请期待!");
return return;
} }
ElMessage.success('下载当前内容') ElMessage.success("下载当前内容");
// emit('download') // emit('download')
};
}
const handleCollect = () => { const handleCollect = () => {
if (props.devTip) { if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!') ElMessage.warning("当前功能正在开发中,敬请期待!");
return return;
} }
ElMessage.success('收藏当前内容') ElMessage.success("收藏当前内容");
// emit('collect') // emit('collect')
};
}
const emit = defineEmits(["save", "download", "collect"]);
const emit = defineEmits(['save', 'download', 'collect'])
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.analysis-box-wrapper { .analysis-box-wrapper {
border: 1px solid rgba(234, 236, 238, 1); border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
position: relative;
.wrapper-header { .wrapper-header {
height: 45px; height: 45px;
display: flex; display: flex;
padding-right: 14px; padding-right: 14px;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
.header-icon { .header-icon {
width: 8px; width: 8px;
height: 20px; height: 20px;
background: var(--color-main-active); background: var(--color-main-active);
border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0;
margin-right: 14px; margin-right: 14px;
} }
.header-title { .header-title {
flex: auto; flex: auto;
width: 20px; width: 20px;
// color: var(--color-main-active); // color: var(--color-main-active);
// font-family: Source Han Sans CN; // font-family: Source Han Sans CN;
// font-size: 20px; // font-size: 20px;
// font-weight: 700; // font-weight: 700;
// line-height: 26px; // line-height: 26px;
// letter-spacing: 0px; // letter-spacing: 0px;
height: 100%; height: 100%;
&>div { & > div {
height: 100%; height: 100%;
color: var(--color-main-active); color: var(--color-main-active);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
font-size: 20px; font-size: 20px;
line-height: 45px; line-height: 45px;
font-weight: 700; font-weight: 700;
} }
} }
.header-btn { .header-btn {
// display: flex; // display: flex;
// justify-content: flex-end; // justify-content: flex-end;
// gap: 8px; // gap: 8px;
margin-right: 10px; margin-right: 10px;
} }
// .header-btn1 { // .header-btn1 {
// position: absolute; // position: absolute;
// top: 14px; // top: 14px;
// right: 116px; // right: 116px;
// } // }
.header-right { .header-right {
height: 28px; height: 28px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 4px; gap: 4px;
.header-right-btn { .header-right-btn {
width: 28px; width: 28px;
height: 28px; height: 28px;
cursor: pointer; cursor: pointer;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
} }
} }
.wrapper-main { .wrapper-main {
height: calc(100% - 45px); height: calc(100% - 45px);
overflow: hidden; overflow: hidden;
// overflow-y: auto; // overflow-y: auto;
padding: 5px auto; padding: 5px auto;
} }
} }
</style> </style>
...@@ -368,19 +368,29 @@ ...@@ -368,19 +368,29 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 505px; margin-top: 16px"> <el-row :gutter="20" style="width: 1600px; margin: 0 auto; height: 540px; margin-top: 16px">
<el-col :span="8"> <el-col :span="8">
<custom-container title="实体领域分布" :titleIcon="radarIcon" height="480px"> <custom-container title="实体领域分布" :titleIcon="radarIcon" height="540px">
<template #header-right> <template #header-right>
<el-checkbox v-model="domainChecked" label="50%规则" size="large" /> <el-checkbox v-model="domainChecked" label="50%规则" size="large" />
</template> </template>
<template #default> <template #default>
<EChart :option="radarOption" autoresize :style="{ height: '460px' }" /> <EChart :option="radarOption" autoresize :style="{ height: '420px' }" />
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">实体领域分布情况,数据来源:美国各行政机构官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="radarChart.loading ? '解读生成中...' : radarChart.interpretation" />
</div>
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
<custom-container title="实体清单数量增长趋势" :titleIcon="qushiIcon" height="480px"> <custom-container title="实体清单数量增长趋势" :titleIcon="qushiIcon" height="540px">
<template #header-right> <template #header-right>
<div style="display: flex; align-items: center; gap: 16px"> <div style="display: flex; align-items: center; gap: 16px">
<el-checkbox v-model="trendChecked" label="50%规则" size="large" /> <el-checkbox v-model="trendChecked" label="50%规则" size="large" />
...@@ -390,13 +400,23 @@ ...@@ -390,13 +400,23 @@
</div> </div>
</template> </template>
<template #default> <template #default>
<EChart :option="trendOption" autoresize :style="{ height: '400px' }" /> <EChart :option="trendOption" autoresize :style="{ height: '420px' }" />
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsIcon" alt="" />
</div>
<div class="data-origin-text">实体清单数量增长趋势情况,数据来源:美国各行政机构官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="trendChart.loading ? '解读生成中...' : trendChart.interpretation" />
</div>
</template> </template>
</custom-container> </custom-container>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px"> <el-row :gutter="20" style="width: 1600px; margin: 0 auto; margin-top: 39px; padding-bottom: 60px">
<CustomTitle id="position4" title="资源库" style="margin-top: 0px" /> <CustomTitle id="position4" title="资源库" style="margin-top: 0px" />
<div class="resource-tabs"> <div class="resource-tabs">
<div <div
...@@ -714,11 +734,18 @@ import { ElMessage, ElMessageBox } from "element-plus"; ...@@ -714,11 +734,18 @@ import { ElMessage, ElMessageBox } from "element-plus";
import { DArrowRight, Warning, Search } from "@element-plus/icons-vue"; import { DArrowRight, Warning, Search } from "@element-plus/icons-vue";
import EChart from "@/components/Chart/index.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 { useChartInterpretation } from "@/views/exportControl/utils/common";
const sanctionCountChart = useChartInterpretation();
import { TAGTYPE } from "@/public/constant"; import { TAGTYPE } from "@/public/constant";
import { useGotoCompanyPages } from "@/router/modules/company"; import { useGotoCompanyPages } from "@/router/modules/company";
import { useGotoNewsDetail } from "@/router/modules/news"; import { useGotoNewsDetail } from "@/router/modules/news";
const gotoCompanyPages = useGotoCompanyPages(); const gotoCompanyPages = useGotoCompanyPages();
const gotoNewsDetail = useGotoNewsDetail(); const gotoNewsDetail = useGotoNewsDetail();
const trendChart = useChartInterpretation();
const radarChart = useChartInterpretation();
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
...@@ -1026,6 +1053,7 @@ const fetchTrendData = async () => { ...@@ -1026,6 +1053,7 @@ const fetchTrendData = async () => {
}); });
if (res && res[0] && res[0].yearDomainCount) { if (res && res[0] && res[0].yearDomainCount) {
trendOption.value = processYearDomainCountData(res[0].yearDomainCount); trendOption.value = processYearDomainCountData(res[0].yearDomainCount);
trendChart.interpret({ type: "柱状图", name: "实体清单数量增长趋势", data: res[0].yearDomainCount });
} }
} catch (error) { } catch (error) {
console.error("获取趋势图数据失败:", error); console.error("获取趋势图数据失败:", error);
...@@ -1297,6 +1325,7 @@ const fetchRadarData = async checked => { ...@@ -1297,6 +1325,7 @@ const fetchRadarData = async checked => {
} }
}; };
}); });
radarChart.interpret({ type: "雷达图", name: "实体领域分布", data: data });
} }
} catch (error) { } catch (error) {
console.error("获取雷达图数据失败:", error); console.error("获取雷达图数据失败:", error);
...@@ -3573,7 +3602,7 @@ const handleMediaClick = item => { ...@@ -3573,7 +3602,7 @@ const handleMediaClick = item => {
} }
.scroll-main { .scroll-main {
height: calc(100% - 144px) !important; // height: calc(100% - 144px) !important;
} }
.center-center { .center-center {
...@@ -3596,4 +3625,50 @@ const handleMediaClick = item => { ...@@ -3596,4 +3625,50 @@ const handleMediaClick = item => {
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
} }
} }
.data-origin-box {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 22px 0;
.data-origin-icon {
width: 16px;
height: 16px;
font-size: 0px;
margin-right: 8px;
img {
width: 100%;
height: 100%;
}
}
.data-origin-text {
font-family: Source Han Sans CN;
font-size: 14px;
color: var(--text-primary-50-color);
}
}
.ai-pane {
position: absolute;
right: 0px;
bottom: 15px;
z-index: 2;
:deep(.ai-pane-wrapper) {
display: none;
}
:deep(.ai-button-wrapper) {
display: flex;
}
&:hover {
width: 100%;
bottom: 0px;
:deep(.ai-pane-wrapper) {
display: block;
}
:deep(.ai-button-wrapper) {
display: none;
}
}
}
</style> </style>
import { ref } from "vue";
export const useChartInterpretation = () => {
const loading = ref(false);
const interpretation = ref("");
const error = ref(null);
const interpret = async text => {
loading.value = true;
error.value = null;
interpretation.value = "";
try {
const response = await fetch("/aiAnalysis/chart_interpretation", {
method: "POST",
headers: {
"X-API-Key": "aircasKEY19491001",
"Content-Type": "application/json"
},
body: JSON.stringify({ text })
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.startsWith("data: ")) {
const content = line.substring(6);
const textMatch = content.match(/"解读":\s*"([^"]*)"/);
if (textMatch && textMatch[1]) {
interpretation.value = textMatch[1];
}
}
}
}
} catch (err) {
error.value = err.message || "AI 解读失败";
console.error("AI Chart Interpretation Error:", err);
} finally {
loading.value = false;
}
};
return {
loading,
interpretation,
error,
interpret
};
};
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
{{ headerTitle.department }} {{ headerTitle.department }}
</div> </div>
</div> </div>
<div class="btn"><img :src="icon01" alt="" />切换</div> <!-- <div class="btn"><img :src="icon01" alt="" />切换</div> -->
</div> </div>
<div class="header-nav"> <div class="header-nav">
<div <div
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div ref="containerRef" class="graph-container"></div> <div ref="containerRef" class="graph-container"></div>
<!-- <GraphChart v-if="currentLayoutType === 1" :nodes="graphNodes" :links="graphLinks" /> -->
<div v-if="selectedNode" class="node-popup"> <div v-if="selectedNode" class="node-popup">
<div class="popup-header"> <div class="popup-header">
<img :src="selectedNode.image || defaultIcon" alt="" class="popup-icon" /> <img :src="selectedNode.image || defaultIcon" alt="" class="popup-icon" />
...@@ -55,6 +55,8 @@ import echartsIcon02 from "../assets/echartsIcon02.png"; ...@@ -55,6 +55,8 @@ import echartsIcon02 from "../assets/echartsIcon02.png";
import echartsIcon03 from "../assets/echartsIcon03.png"; import echartsIcon03 from "../assets/echartsIcon03.png";
import defaultIcon from "../assets/echartsIcon03.png"; import defaultIcon from "../assets/echartsIcon03.png";
// import GraphChart from "@/components/base/GraphChart/index.vue";
import GraphChart from "@/views/companyPages2/component/SupplyChain/GraphChart.vue";
import { getSingleSanctionEntityInfo } from "@/api/exportControlV2.0"; import { getSingleSanctionEntityInfo } from "@/api/exportControlV2.0";
const props = defineProps({ const props = defineProps({
...@@ -107,7 +109,7 @@ const initGraph = (layoutType = 1) => { ...@@ -107,7 +109,7 @@ const initGraph = (layoutType = 1) => {
const initNormalGraph = (layoutType, width, height) => { const initNormalGraph = (layoutType, width, height) => {
const data = processGraphData(props.graphData); const data = processGraphData(props.graphData);
console.log("初始数据", props.graphData);
if (!data.nodes || data.nodes.length === 0) return; if (!data.nodes || data.nodes.length === 0) return;
const layout = { const layout = {
...@@ -653,6 +655,56 @@ const bindGraphEvents = () => { ...@@ -653,6 +655,56 @@ const bindGraphEvents = () => {
}); });
}; };
// 将传入的 graphData 转换为 GraphChart.vue 需要的格式
// GraphChart.vue 需要节点有 x, y 坐标,所以我们需要一个简单的布局算法
const graphNodes = ref([]);
const graphLinks = ref([]);
const applyStarLayout = (rawNodes, rawLinks) => {
if (!rawNodes?.length) return { nodes: [], links: [] };
const centerX = 0;
const centerY = 0;
const radius = 200; // 环绕半径
const nodes = rawNodes.map((node, index) => {
let x, y;
if (index === 0) {
// 中心节点
x = centerX;
y = centerY;
} else {
// 其他节点均匀分布在圆周上
const angle = (2 * Math.PI * (index - 1)) / (rawNodes.length - 1);
x = centerX + radius * Math.cos(angle);
y = centerY + radius * Math.sin(angle);
}
return {
id: String(node.id ?? index),
name: node.name || "",
type: node.isSanctioned ? "sanctioned" : "normal", // 用于区分样式(如果需要)
symbolSize: node.symbolSize || (index === 0 ? 60 : 40),
symbol: `image://${node.image || defaultIcon}`,
isSanctioned: node.isSanctioned,
image: node.image || defaultIcon,
x: x,
y: y,
...node // 透传其他字段
};
});
const links = (rawLinks || []).map(link => ({
source: String(link.source),
target: String(link.target),
type: link.name || "relation", // GraphChart.vue 使用 type 字段
label: {
show: true,
formatter: link.name || ""
}
}));
return { nodes, links };
};
const handleClickControlBtn = btn => { const handleClickControlBtn = btn => {
currentLayoutType.value = btn; currentLayoutType.value = btn;
emit("layoutChange", btn); emit("layoutChange", btn);
...@@ -675,6 +727,16 @@ const handleResize = () => { ...@@ -675,6 +727,16 @@ const handleResize = () => {
} }
}; };
watch(
() => props.graphData,
newVal => {
const { nodes, links } = applyStarLayout(newVal.nodes, newVal.links);
graphNodes.value = JSON.parse(JSON.stringify(nodes));
graphLinks.value = JSON.parse(JSON.stringify(links));
},
{ deep: true, immediate: true }
);
watch( watch(
() => props.graphData, () => props.graphData,
() => { () => {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
{{ headerTitle.department }} {{ headerTitle.department }}
</div> </div>
</div> </div>
<div class="btn"><img :src="icon01" alt="" />切换</div> <div class="btn" @click="openSanctionModal"><img :src="icon01" alt="" />切换</div>
</div> </div>
<div class="header-nav"> <div class="header-nav">
<div <div
...@@ -44,12 +44,46 @@ ...@@ -44,12 +44,46 @@
<deep-mining v-if="activeIndex === 2"></deep-mining> <deep-mining v-if="activeIndex === 2"></deep-mining>
<impact-analysis v-if="activeIndex === 3"></impact-analysis> <impact-analysis v-if="activeIndex === 3"></impact-analysis>
</div> </div>
<el-dialog
v-model="sanctionModalVisible"
title="制裁事件列表"
width="600px"
:close-on-click-modal="false"
@close="resetModal"
>
<div class="sanction-list">
<div
v-for="item in sanctionList"
:key="item.id"
class="sanction-item"
:class="{ active: selectedSanctionId === item.id }"
@click="selectSanction(item)"
>
{{ item.name }}
</div>
</div>
<template #footer>
<div class="pagination-footer">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="totalElements"
:page-sizes="[10]"
layout="total, prev, pager, next"
background
@current-change="handlePageChange"
/>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { ElDialog, ElPagination } from "element-plus";
import sanctionsOverview from "./components/sanctionsOverview/index.vue"; import sanctionsOverview from "./components/sanctionsOverview/index.vue";
import dataStatistics from "./components/dataStatistics/index.vue"; import dataStatistics from "./components/dataStatistics/index.vue";
import deepMining from "./components/deepMining/index.vue"; import deepMining from "./components/deepMining/index.vue";
...@@ -67,6 +101,7 @@ import icon3 from "../assets/icons/icon3.png"; ...@@ -67,6 +101,7 @@ import icon3 from "../assets/icons/icon3.png";
import icon3Active from "../assets/icons/icon3_active.png"; import icon3Active from "../assets/icons/icon3_active.png";
import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js"; import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js";
import { getSanctionProcess } from "@/api/exportControl";
import router from "@/router"; import router from "@/router";
// 处理点击实体清单原文按钮 // 处理点击实体清单原文按钮
...@@ -157,6 +192,77 @@ const handleAnalysisClick = () => { ...@@ -157,6 +192,77 @@ const handleAnalysisClick = () => {
}); });
}; };
// ========== 新增响应式状态 ==========
const sanctionModalVisible = ref(false);
const sanctionList = ref([]);
const selectedSanctionId = ref(null);
const currentPage = ref(1);
const pageSize = ref(10);
const totalElements = ref(0);
// ========== 打开弹窗并加载第一页 ==========
const openSanctionModal = async () => {
sanctionModalVisible.value = true;
console.log("制裁事件列表11:", sanctionList.value);
await fetchSanctionData();
};
// ========== 获取制裁数据 ==========
const fetchSanctionData = async () => {
try {
const res = await getSanctionProcess([1], currentPage.value, 10);
if (res && !!res.content) {
sanctionList.value = res.content || [];
totalElements.value = res.totalElements || 0;
// 如果当前选中的不在新数据中,清空选中
if (selectedSanctionId.value && !sanctionList.value.some(item => item.id === selectedSanctionId.value)) {
selectedSanctionId.value = null;
}
console.log("制裁事件列表:", sanctionList.value);
} else {
sanctionList.value = [];
totalElements.value = 0;
}
} catch (error) {
console.error("获取制裁事件失败:", error);
sanctionList.value = [];
totalElements.value = 0;
}
};
// ========== 翻页处理 ==========
const handlePageChange = async newPage => {
currentPage.value = newPage;
await fetchSanctionData();
};
// ========== 选择某项 ==========
const selectSanction = item => {
selectedSanctionId.value = item.id;
// 可在此处触发其他逻辑,如查看详情
// 替换当前路由参数,并重新加载数据
router.replace({
path: window.location.pathname,
query: {
id: item.id,
sanTypeId: item.sanTypeId
}
});
// 3. 立即重新加载数据(关键!)
getSingleSanctionOverviewData();
// 4. 可选:关闭弹窗
sanctionModalVisible.value = false;
};
// ========== 关闭弹窗时重置 ==========
const resetModal = () => {
sanctionList.value = [];
selectedSanctionId.value = null;
currentPage.value = 1;
totalElements.value = 0;
};
onMounted(() => { onMounted(() => {
getUrlParams(); getUrlParams();
getSingleSanctionOverviewData(); getSingleSanctionOverviewData();
...@@ -350,4 +456,40 @@ onMounted(() => { ...@@ -350,4 +456,40 @@ onMounted(() => {
background-color: #f7f8f9; background-color: #f7f8f9;
} }
} }
.sanction-list {
max-height: 400px;
overflow-y: auto;
padding: 0 10px;
}
.sanction-item {
padding: 7px;
margin: 6px 0;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
color: #333;
transition: background-color 0.2s;
}
.sanction-item:hover {
background-color: #f5f7fa;
}
.sanction-item.active {
background-color: #e6f0ff;
color: #1890ff;
font-weight: bold;
}
.pagination-footer {
display: flex;
justify-content: center;
margin-top: 12px;
}
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
background-color: #0e78f1;
}
</style> </style>
...@@ -14,13 +14,17 @@ ...@@ -14,13 +14,17 @@
<div class="tag">{{ value.industryName }}</div> <div class="tag">{{ value.industryName }}</div>
</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.industryName"
></AreaTag>
</div> </div>
</div> </div>
</div> </div>
<div class="header-top-right"> <div class="header-top-right">
<div class="image-name-box"> <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 class="name">{{ thinkInfo.thinkTankName }}</div>
</div> </div>
<div class="time">{{ thinkInfo.times }}</div> <div class="time">{{ thinkInfo.times }}</div>
...@@ -95,7 +99,7 @@ const router = useRouter(); ...@@ -95,7 +99,7 @@ const router = useRouter();
const route = useRoute(); const route = useRoute();
const reportUrl = ref(""); const reportUrl = ref("");
const thinkInfo = ref({}); const thinkInfo = ref({});
const reportList = ref({}) const reportList = ref({});
// 获取报告全局信息 // 获取报告全局信息
const handleGetThinkTankReportSummary = async () => { const handleGetThinkTankReportSummary = async () => {
...@@ -105,7 +109,7 @@ const handleGetThinkTankReportSummary = async () => { ...@@ -105,7 +109,7 @@ const handleGetThinkTankReportSummary = async () => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
reportUrl.value = res.data.reportUrl; reportUrl.value = res.data.reportUrl;
thinkInfo.value = res.data; thinkInfo.value = res.data;
console.log(reportUrl.value, 'reportUrl.value') console.log(reportUrl.value, "reportUrl.value");
} }
} catch (error) { } catch (error) {
console.error("获取报告全局信息error", error); console.error("获取报告全局信息error", error);
...@@ -117,9 +121,7 @@ const handleGetThinkTankReport = async () => { ...@@ -117,9 +121,7 @@ const handleGetThinkTankReport = async () => {
const res = await getThinkTankReportRelated(router.currentRoute._value.params.id); const res = await getThinkTankReportRelated(router.currentRoute._value.params.id);
console.log("报告全局信息", res); console.log("报告全局信息", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
reportList.value = res.data; reportList.value = res.data;
} }
} catch (error) { } catch (error) {
console.error("获取相关报告error", error); console.error("获取相关报告error", error);
...@@ -133,6 +135,8 @@ const toReport = () => { ...@@ -133,6 +135,8 @@ const toReport = () => {
id: router.currentRoute._value.params.id id: router.currentRoute._value.params.id
} }
}); });
console.log("route.href", route.href);
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
const tabActiveName = ref("报告分析"); const tabActiveName = ref("报告分析");
...@@ -167,7 +171,7 @@ const goToOfficialWebsite = () => { ...@@ -167,7 +171,7 @@ const goToOfficialWebsite = () => {
const downloadOnePdf = async (url, filename) => { const downloadOnePdf = async (url, filename) => {
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: "GET",
headers: { "Content-Type": "application/pdf" }, headers: { "Content-Type": "application/pdf" }
}); });
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const blob = await response.blob(); const blob = await response.blob();
...@@ -209,10 +213,9 @@ const handleDownloadDocument = async () => { ...@@ -209,10 +213,9 @@ const handleDownloadDocument = async () => {
try { try {
const { ElMessage } = await import("element-plus"); const { ElMessage } = await import("element-plus");
ElMessage.error("PDF 下载失败,请稍后重试"); ElMessage.error("PDF 下载失败,请稍后重试");
} catch (_) { } } catch (_) {}
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -305,7 +308,6 @@ const handleDownloadDocument = async () => { ...@@ -305,7 +308,6 @@ const handleDownloadDocument = async () => {
line-height: 24px; line-height: 24px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: right; text-align: right;
} }
.image { .image {
...@@ -314,12 +316,9 @@ const handleDownloadDocument = async () => { ...@@ -314,12 +316,9 @@ const handleDownloadDocument = async () => {
margin-top: 5px; margin-top: 5px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
} }
...@@ -447,4 +446,4 @@ const handleDownloadDocument = async () => { ...@@ -447,4 +446,4 @@ const handleDownloadDocument = async () => {
width: 100%; width: 100%;
} }
} }
</style> </style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论