提交 42c74a3f authored 作者: 张烨's avatar 张烨

科技政令-深度挖掘增加冲突关系弹出框

上级 9b312ae7
......@@ -364,7 +364,7 @@
</div>
</div>
</div>
<div class="select-box">
<!-- <div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "涉及领域" }}</div>
......@@ -378,7 +378,7 @@
</el-checkbox>
</div>
</div>
</div>
</div> -->
</div>
<div class="right">
<div class="content-header">
......@@ -521,6 +521,7 @@ const pageSize = ref(10);
// 处理页码改变事件
const handleCurrentChange = page => {
currentPage.value = page;
handleToPosi('position4')
handleGetDecreeOrderList();
};
......@@ -1235,7 +1236,8 @@ const handleGetDecreeOrderList = async () => {
watch([activePubTime, activeAreaList, checkedGovIns, isSort, isChina, searchType], val => {
// 切换页码到第一页
handleCurrentChange(1);
currentPage.value = 1;
handleGetDecreeOrderList();
});
// 切换当前政令
......@@ -3182,11 +3184,11 @@ onMounted(async () => {
border-radius: 10px;
.select-box {
margin-top: 21px;
margin-top: 16px;
.select-box-header {
display: flex;
gap: 17px;
gap: 16px;
.icon {
margin-top: 4px;
......
......@@ -5,12 +5,12 @@
<div class="box1-main">
<el-empty v-if="siderList.length===0" style="padding-top: 30%" description="暂无数据" :image-size="100" />
<el-scrollbar height="100%" always>
<div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickSider(index)">
<div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickSider(item)">
<div class="item-head">
<div class="itme-name one-line-ellipsis">{{ item.proposeOrgName }}</div>
<div class="itme-name one-line-ellipsis">{{ item.label }}</div>
<div class="item-tag">政令</div>
</div>
<div class="itme-time one-line-ellipsis">{{ item.postDate }} · {{ item.name }} </div>
<div class="itme-time one-line-ellipsis">{{ item.time }} · {{ item.name }} </div>
</div>
</el-scrollbar>
</div>
......@@ -24,14 +24,23 @@
</AnalysisBox>
</div>
<el-dialog v-model="dialogVisible" width="1280px" class="viewpoint-dialog" top="8vh">
<el-dialog v-model="dialogVisible" width="1000px" class="viewpoint-dialog">
<template #header>
<div class="viewpoint-header">
<div class="viewpoint-title">冲突关系</div>
</div>
</template>
<div class="viewpoint-body">
<div class="graph-tag">
<div class="icon1"></div>
<div class="title">
{{ `${mainInfo.time}-${mainInfo.label} ${nodeInfo.relation} ${nodeInfo.time}-${nodeInfo.label}, 属于${nodeInfo.relation}关系` }}
</div>
<div class="icon2Wrap">
<div class="icon2"></div>
</div>
</div>
<div ref="graphContainer" class="graph-container"></div>
</div>
</el-dialog>
</div>
......@@ -53,44 +62,41 @@ const route = useRoute();
const dialogVisible = ref(false);
// 基本信息
const basicInfo = ref({
id: '',
proposeOrgName: '',
isCenter: true
});
const mainInfo = ref({});
const nodeInfo = ref({});
const onDecreeSummaryData = async () => {
// basicInfo.value = {proposeOrgName: "是事儿也就来一回儿一会儿就完事儿", id: route.query.id, isCenter: true}
try {
const res = await getDecreeSummary({id: route.query.id});
console.log("基本信息", res);
if (res.code===200 && res.data) {
basicInfo.value.proposeOrgName = res.data.name;
basicInfo.value.id = route.query.id;
mainInfo.value.label = res.data.name;
mainInfo.value.time = res.data.postDate;
mainInfo.value.id = route.query.id;
mainInfo.value.isCenter = true
}
} catch (error) {
basicInfo.value = {};
mainInfo.value = {};
console.log("获取基本信息数据失败:", error);
}
};
// 相关政令
const siderList = ref([]);
const siderActive = ref(0);
const handleClickSider = async index => {
siderActive.value = index;
const handleClickSider = async (item) => {
dialogVisible.value = true;
nodeInfo.value = item
setTimeout(onRelationChart, 300)
};
const handleGetRelateOrder = async () => {
// siderList.value = [
// {id: 1, proposeOrgName: '天空飘来五个字那都不是事儿天空飘来五个字那都不是事儿1', postDate: '2022-01-01', name: 'name'},
// {id: 2, proposeOrgName: '天空飘来五个字那都不是事儿天空飘来五个字那都不是事儿2', postDate: '2022-01-02', name: 'name'},
// {id: 3, proposeOrgName: '天空飘来五个字那都不是事儿天空飘来五个字那都不是事儿3', postDate: '2022-01-03', name: 'name'},
// ]
try {
const res = await getDecreeRelatedOrder({id: route.query.id});
console.log("相关政令", res);
if (res.code===200 && res.data?.length) {
siderList.value = res.data
siderList.value.forEach(item => {
item.label = item.proposeOrgName;
item.time = item.postDate;
})
} else {
siderList.value = []
}
......@@ -103,19 +109,22 @@ const handleGetRelateOrder = async () => {
// 政令关系挖掘
const containerRef = ref();
let graphInstance = null;
const onFormatNode = (item) => {
let isCenter = item.isCenter || false
// 判断文字每达到8个字插入换行符
const text = item.proposeOrgName.split('');
// 文本插入换行符
const onWordWrap = (word, num) => {
const list = word.split('');
let label = "";
for (let i = 0; i < text.length; i++) {
if (i % 8 === 0 && i !== 0) {
for (let i = 0; i < list.length; i++) {
if (i % num === 0 && i !== 0) {
label += "\n";
}
label += text[i];
label += list[i];
}
return label;
}
const onFormatNode = (item) => {
let isCenter = item.isCenter || false
return {
id: item.id+'', label, isCenter, size: 40,
id: item.id+'', label:onWordWrap(item.label, 15), isCenter, size: 40,
img: isCenter ? icon1628 : icon1629,
clipCfg: { show: true, type: "circle", r: isCenter ? 30 : 20 }, style: { cursor: "pointer" },
labelCfg: {
......@@ -161,7 +170,8 @@ const onFormatEdge = (item, index) => {
const initChart = () => {
let edgeList = siderList.value.map(onFormatEdge)
let nodeList = siderList.value.map(onFormatNode)
nodeList.unshift(onFormatNode(basicInfo.value))
nodeList.unshift(onFormatNode(mainInfo.value))
console.log(nodeList)
const width = containerRef.value.offsetWidth || 800
const height = containerRef.value.offsetHeight || 600
......@@ -184,9 +194,6 @@ const initChart = () => {
centerNode.fy = centerY
}
console.log("节点列表", nodeList)
console.log("边列表", edgeList)
graphInstance = new G6.Graph({
container: containerRef.value,
width,
......@@ -235,21 +242,115 @@ const initChart = () => {
// 节点点击处理
graphInstance.on('node:click', (evt) => {
console.log('节点详情:', evt.item);
console.log('节点详情:', evt.item._cfg.model.id);
let node = siderList.value.find(item => item.id==evt.item._cfg.model.id)
handleClickSider(node)
});
graphInstance.data({nodes: nodeList, edges: edgeList})
graphInstance.render()
}
// 冲突关系
const graphContainer = ref(null);
let graph = null;
const onRelationChart = () => {
const container = graphContainer.value;
const nodeWidth = 180;
const width = container.clientWidth;
const height = container.clientHeight;
const centerX = width / 2
const centerY = height / 2 - 40
const leftNodeX = centerX - nodeWidth;
const rightNodeX = centerX + nodeWidth;
const data = {
nodes: [
{
id: 'node-left', label: `${mainInfo.value.time}\n${onWordWrap(mainInfo.value.label, 15)}`,
size: [250, 80],
x: leftNodeX,
y: centerY,
},
{
id: 'node-right', label: `${nodeInfo.value.time}\n${onWordWrap(nodeInfo.value.label, 15)}`,
size: [250, 80],
x: rightNodeX,
y: centerY,
},
],
edges: [
{
id: `edge-1`,
target: 'node-right',
source: 'node-left',
type: "line",
label: nodeInfo.value.relation,
style: {
stroke: ["", "#B9DCFF", "#87E8DE", "#FFCCC7"][1],
lineWidth: 1,
endArrow: true,
},
labelCfg: {
autoRotate: true,
style: {
fill: ["", "#055FC2", "#13A8A8", "#CE4F51"][1],
fontSize: 14,
fontFamily: 'Microsoft YaHei',
background: {
fill: ["", "#E7F3FF", "#E6FFFB", "#FFE0E0"][1],
padding: [6, 4, 4, 4],
}
}
}
}
]
};
// 创建图实例
if (!graph) {
graph = new G6.Graph({
container: container,
width,
height,
defaultNode: {
type: 'rect',
anchorPoints: [[0, 0.5], [1, 0.5]],
style: {
radius: 4,
fill: '#f6faff',
stroke: '#B9DCFF',
},
labelCfg: {
style: {
fill: "#333333",
fontSize: 15,
fontWeight: "bold",
fontFamily: "Microsoft YaHei",
textAlign: "center",
}
}
},
layout: null,
modes: { default: [] },
fitView: false,
});
}
// 加载数据并渲染
graph.data(data);
graph.render();
}
onMounted(() => {
Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => {
if (basicInfo.value.id && siderList.value.length) initChart()
if (mainInfo.value.id && siderList.value.length) initChart()
})
});
onBeforeUnmount(() => {
graphInstance?.destroy()
graph?.destroy()
})
</script>
......@@ -351,7 +452,6 @@ onBeforeUnmount(() => {
// 修改element-plus弹出框样式
:deep(.viewpoint-dialog) {
height: 750px;
padding: 0;
border-radius: 4px;
.el-dialog__body {
......@@ -369,7 +469,6 @@ onBeforeUnmount(() => {
right: 12px;
}
.viewpoint-header {
width: 761px;
height: 48px;
display: flex;
align-items: center;
......@@ -383,70 +482,60 @@ onBeforeUnmount(() => {
line-height: 24px;
}
.viewpoint-body {
padding: 24px 24px 35px 24px;
height: calc(669px - 48px);
box-sizing: border-box;
padding: 16px;
height: 560px;
overflow: hidden;
.viewpoint-body-title {
font-size: 28px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 37px;
color: rgb(59, 65, 75);
margin-bottom: 32px;
}
.viewpoint-item {
width: 713px;
min-height: 81px;
display: block;
margin-bottom: 12px;
position: relative;
padding-left: 48px;
.viewpoint-item-img {
position: absolute;
top: 0;
left: -10px;
width: 42px;
height: 42px;
}
.viewpoint-item-content {
width: 665px;
min-height: 81px;
background-image: url("./assets/bg01.png");
background-size: 100% 100%;
position: relative;
top: 0;
left: 0;
padding-left: 23px;
padding-top: 12px;
padding-bottom: 13px;
box-sizing: border-box;
.viewpoint-item-name {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
margin-bottom: 5px;
}
.viewpoint-item-desc {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
}
.viewpoint-item-job {
position: absolute;
top: 8px;
right: 22px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 30px;
color: rgb(132, 136, 142);
}
}
display: flex;
flex-direction: column;
.graph-tag {
display: flex;
align-items: center;
padding: 7px 12px;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
margin-bottom: 9px;
.icon1 {
width: 19px;
height: 20px;
background-image: url("../assets/icons/ai.png");
background-size: 100% 100%;
flex-shrink: 0;
}
.title {
color: rgb(5, 95, 194);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-left: 13px;
flex: 1;
}
.icon2Wrap {
width: 24px;
height: 24px;
background-color: rgba(231, 243, 255, 1);
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
margin-left: 20px;
flex-shrink: 0;
.icon2 {
width: 24px;
height: 24px;
background-image: url("../assets/icons/right.png");
background-size: 100% 100%;
}
}
}
.graph-container {
width: 100%;
height: 20px;
flex: auto;
}
}
}
......
......@@ -328,7 +328,7 @@ onMounted(() => {
.icon1 {
width: 19px;
height: 20px;
background-image: url("./assets/images/ai.png");
background-image: url("../assets/icons/ai.png");
background-size: 100% 100%;
flex-shrink: 0;
}
......@@ -356,7 +356,7 @@ onMounted(() => {
.icon2 {
width: 24px;
height: 24px;
background-image: url("./assets/images/right.png");
background-image: url("../assets/icons/right.png");
background-size: 100% 100%;
}
}
......
......@@ -11,20 +11,22 @@
</div>
</div>
</template>
<div class="box1-main">
<el-empty v-if="backgroundList.length === 0" style="padding-top: 60px;" description="暂无数据" :image-size="100" />
<div class="box1-item" v-for="(item, index) in backgroundList" :key="index">
<div class="id">{{ index + 1 }}</div>
<div class="title">{{ item.content }}</div>
<div class="open">
<img src="./assets/images/open-icon.png" alt="" />
<div class="box1-container">
<div class="box1-main">
<el-empty v-if="backgroundList.length === 0" style="padding-top: 60px;" description="暂无数据" :image-size="100" />
<div class="box1-item" v-for="(item, index) in backgroundList" :key="index">
<div class="id">{{ index + 1 }}</div>
<div class="title">{{ item.content }}</div>
<div class="open">
<img src="./assets/images/open-icon.png" alt="" />
</div>
</div>
</div>
</div>
<div class="box1-footer">
<div class="box1-footer-left">{{ `共 ${backgroundListNum} 项` }}</div>
<div class="box1-footer-right">
<el-pagination :page-size="10" @current-change="handleCurrentChange" :current-page="currentPage" background layout="prev, pager, next" :total="backgroundListNum" />
<div class="box1-footer">
<div class="box1-footer-left">{{ `共 ${backgroundListNum} 项` }}</div>
<div class="box1-footer-right">
<el-pagination :page-size="10" @current-change="handleCurrentChange" :current-page="currentPage" background layout="prev, pager, next" :total="backgroundListNum" />
</div>
</div>
</div>
</AnalysisBox>
......@@ -203,7 +205,6 @@ onMounted(() => {
.introduction-wrap {
display: flex;
width: 1600px;
height: 901px;
padding: 16px 0;
gap: 16px;
......@@ -215,8 +216,7 @@ onMounted(() => {
gap: 16px;
.box1 {
height: 50%;
flex: auto;
height: 690px;
.header-btn-box {
display: flex;
......@@ -245,12 +245,17 @@ onMounted(() => {
}
}
.box1-container {
height: 100%;
display: flex;
flex-direction: column;
padding: 16px;
}
.box1-main {
margin-top: 16px;
margin-left: 22px;
width: 1034px;
height: 290px;
overflow: hidden;
height: 20px;
flex: auto;
overflow-y: auto;
.box1-item {
......@@ -300,7 +305,6 @@ onMounted(() => {
}
.box1-footer {
margin: 20px 22px;
display: flex;
justify-content: space-between;
......@@ -316,14 +320,11 @@ onMounted(() => {
}
.box2 {
height: 50%;
flex: auto;
height: 500px;
.box2-main {
margin-top: 3px;
margin-left: 31px;
height: 330px;
width: 1004px;
padding: 16px 20px;
height: 100%;
overflow: hidden;
overflow-y: auto;
.custom-collapse {
......@@ -335,6 +336,9 @@ onMounted(() => {
:deep(.el-collapse-item__header) {
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
:deep(.el-collapse-item__content) {
padding-bottom: 16px;
}
.custom-collapse-title {
position: relative;
.custom-collapse-index {
......
......@@ -114,7 +114,6 @@ onMounted(() => {
}
.main {
width: 1760px;
height: 901px;
}
}
</style>
\ No newline at end of file
......@@ -29,19 +29,19 @@
</div>
<!-- 渲染一级列表 -->
<div class="numbered-list">
<div v-for="(item, itemIndex) in section.children" :key="itemIndex" class="list-item">
<div v-for="(item, itemIndex) in section.slaver" :key="itemIndex" class="list-item">
<div class="list-item-dot">{{itemIndex+1}}.</div>
<div class="list-item-word">{{ item.content }}</div>
<!-- 渲染二级列表 -->
<div v-if="item.children" class="sub-list">
<div v-for="(subItem, subIndex) in item.children" :key="subIndex" class="sub-item">
<div v-if="item.slaver" class="sub-list">
<div v-for="(subItem, subIndex) in item.slaver" :key="subIndex" class="sub-item">
<div class="sub-item-dot">({{subIndex+1}})</div>
<div class="sub-item-word">{{ subItem.content }}</div>
<!-- 渲染三级列表 -->
<div v-if="subItem.children" class="sub-sub-list">
<div v-for="(subSubItem, subSubIndex) in subItem.children" :key="subSubIndex" class="sub-sub-item">
<div v-if="subItem.slaver" class="sub-sub-list">
<div v-for="(subSubItem, subSubIndex) in subItem.slaver" :key="subSubIndex" class="sub-sub-item">
<div class="sub-sub-item-dot">{{ALPHABET[subSubIndex%26]}}.</div>
<div class="sub-sub-item-word">{{ subSubItem.content }}</div>
</div>
......@@ -149,16 +149,16 @@ const commandWord = ref("");
const contentList = ref([
// {
// content: "建立美国人工智能出口计划建立美国人工智能出口计划建立美国人工智能出口计划建立美国人工智能出口计划建立美国人工智能出口计划",
// children: [
// slaver: [
// {
// content: '在本命令发布之日起 90 天内,商务部长应与国务卿及科学技术政策办公室(OSTP)主任协商,建立并实施美国人工智能出口计划(计划),以支持美国全栈人工智能出口软件包的开发和部署。'
// },
// {
// content: '商务部长应公开征集由行业主导的联盟提案,以纳入该计划。公开征集要求每项提案必须:',
// children: [
// slaver: [
// {
// content: '包含一套全栈人工智能技术包,涵盖:',
// children: [
// slaver: [
// {
// content: 'AI 优化的计算机硬件(如芯片、服务器和加速器)、数据中心存储、云服务和网络,以及这些设备是否以及在多大程度上在美国制造的描述;'
// },
......@@ -200,7 +200,7 @@ const contentList = ref([
// },
// {
// content: "动员联邦融资工具",
// children: [
// slaver: [
// {
// content: '经济外交行动小组(EDAG),于 2024 年 6 月 21 日总统备忘录中成立,由国务卿主持,并与商务部长和美国贸易代表协商,并根据 2019 年《通过外交倡导美国企业法案》(公共法 116-94 J 部分第七章)第 708 条(CABDA)所述,应协调联邦融资工具的动员,以支持优先的人工智能出口方案。'
// },
......@@ -319,7 +319,6 @@ onMounted(() => {
.introduction-wrap {
display: flex;
width: 1600px;
height: 100%;
padding: 16px 0;
gap: 16px;
......@@ -723,8 +722,7 @@ onMounted(() => {
}
.box4 {
height: 20px;
flex: auto;
height: 610px;
.left-bottom-main {
padding: 20px 20px 0;
......
......@@ -60,6 +60,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { getDecreeSummary } from "@/api/decree/introduction";
import { getDecreeReport } from "@/api/decree/introduction";
......@@ -72,8 +73,44 @@ const summaryInfo = ref({});
// 政令原文操作
const isHighlight = ref(true);
const isTranslate = ref(true);
const handleDownload = () => {
const handleDownload = async () => {
if (summaryInfo.value?.url) {
try {
const response = await fetch(summaryInfo.value.url, {
method: 'GET',
headers: { 'Content-Type': 'application/pdf' },
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
// 创建 Blob URL
const blobUrl = window.URL.createObjectURL(blob);
// 创建隐藏的下载链接
const link = document.createElement('a');
link.href = blobUrl;
link.download = `${summaryInfo.value.name}.pdf`;
// 添加到文档中并点击
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.error('下载失败:', error);
// 可以在这里提示用户下载失败
alert('PDF 下载失败,请稍后重试');
}
} else {
ElMessage.warning("暂无下载链接!");
}
}
const handleFindWord = () => {
......
......@@ -53,7 +53,7 @@ export default defineConfig({
'/api': {
// target: 'http://8.140.26.4:9085/',
target: 'http://192.168.0.6:28080/',
target: 'http://192.168.0.5:28080/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论