提交 7bc0a1ee authored 作者: 张烨's avatar 张烨

fix:政令模块细节优化

上级 3042de15
...@@ -5,10 +5,11 @@ ...@@ -5,10 +5,11 @@
</template> </template>
<script setup> <script setup>
import { onMounted, nextTick } from 'vue'; import { onMounted, onBeforeUnmount } from 'vue';
import setChart from '@/utils/setChart'; import setChart from '@/utils/setChart';
import getGraphChart from './graphChart'; import getGraphChart from './graphChart';
const emits = defineEmits(["handleClickNode"])
const props = defineProps({ const props = defineProps({
nodes: { nodes: {
type: Array, type: Array,
...@@ -32,11 +33,17 @@ const props = defineProps({ ...@@ -32,11 +33,17 @@ const props = defineProps({
} }
}) })
let chart = null
onMounted(() => { onMounted(() => {
const graph = getGraphChart(props.nodes, props.links, props.layoutType) const graph = getGraphChart(props.nodes, props.links, props.layoutType)
setChart(graph, 'graph') chart = setChart(graph, 'graph')
chart.on("click", (event) => { emits("handleClickNode", event) })
}) })
onBeforeUnmount(() => {
chart.off("click")
chart.dispose()
})
</script> </script>
......
...@@ -6,14 +6,6 @@ ...@@ -6,14 +6,6 @@
<div class="home-main-header-center"> <div class="home-main-header-center">
<SearchContainer style="margin-bottom: 0; margin-top: 48px; height: fit-content" v-if="containerRef" <SearchContainer style="margin-bottom: 0; margin-top: 48px; height: fit-content" v-if="containerRef"
placeholder="搜索政令" :containerRef="containerRef" areaName="政令" /> placeholder="搜索政令" :containerRef="containerRef" areaName="政令" />
<!-- <el-input v-model="searchDecreeText" @keyup.enter="handleSearch" style="width: 838px; height: 100%"
placeholder="搜索政令" />
<div class="search">
<div class="search-icon">
<img src="./assets/images/search-icon.png" alt="" />
</div>
<div class="search-text" @click="handleSearch">搜索</div>
</div> -->
</div> </div>
<!-- <div class="home-main-header-footer" v-show="!isShow"> <!-- <div class="home-main-header-footer" v-show="!isShow">
<div class="home-main-header-footer-item"> <div class="home-main-header-footer-item">
...@@ -37,32 +29,13 @@ ...@@ -37,32 +29,13 @@
<div class="item-footer">分析报告</div> <div class="item-footer">分析报告</div>
</div> </div>
</div> --> </div> -->
<!-- <div class="home-main-header-btn-box" v-show="!isShow"> <div class="date-box" v-if="govInsList.length">
<div class="btn" @click="handleToPosi('position1')"> <div class="date-icon">
<div class="btn-text">{{ "最新动态" }}</div> <img :src="tipsTcon" alt="">
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">{{ "资讯要闻" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">{{ "数据总览" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div> </div>
<div class="btn" @click="handleToPosi('position4')"> <div class="date-text">近期美国各联邦政府机构发布涉华政令数量汇总</div>
<div class="btn-text">{{ "资源库" }}</div> <TimeTabPane @time-click="handleTimeClick" />
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div> </div>
</div>
</div> -->
<div class="home-main-header-item-box" v-if="govInsList.length"> <div class="home-main-header-item-box" v-if="govInsList.length">
<div class="item" v-for="(item, index) in govInsList.slice(0, 7)" :key="index" @click="handleToInstitution(item)"> <div class="item" v-for="(item, index) in govInsList.slice(0, 7)" :key="index" @click="handleToInstitution(item)">
<div class="item-left"> <div class="item-left">
...@@ -396,6 +369,7 @@ import { onMounted, ref, watch, nextTick, reactive } from "vue"; ...@@ -396,6 +369,7 @@ import { onMounted, ref, watch, nextTick, reactive } from "vue";
import router from "@/router"; import router from "@/router";
import WordCloudChart from "@/components/base/WordCloundChart/index.vue" import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import SimplePagination from "@/components/SimplePagination.vue"; import SimplePagination from "@/components/SimplePagination.vue";
import TimeTabPane from '@/components/base/TimeTabPane/index.vue'
import { import {
getDepartmentList, getDepartmentList,
getLatestDecree, getLatestDecree,
...@@ -418,21 +392,9 @@ import getPieChart from "./utils/piechart"; ...@@ -418,21 +392,9 @@ import getPieChart from "./utils/piechart";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import DefaultIcon2 from "@/assets/icons/default-icon2.png"; import DefaultIcon2 from "@/assets/icons/default-icon2.png";
import tipsTcon from "./assets/images/tips-icon.png";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
// 跳转行政机构主页
const handleToInstitution = item => {
window.sessionStorage.setItem("curTabName", item.orgName);
const curRoute = router.resolve({
path: "/institution",
query: {
id: item.orgId
}
});
window.open(curRoute.href, "_blank");
};
const containerRef = ref(null); const containerRef = ref(null);
const { isShow } = useContainerScroll(containerRef); const { isShow } = useContainerScroll(containerRef);
const currentPage = ref(1); const currentPage = ref(1);
...@@ -444,10 +406,9 @@ const handleCurrentChange = page => { ...@@ -444,10 +406,9 @@ const handleCurrentChange = page => {
handleGetDecreeOrderList(); handleGetDecreeOrderList();
}; };
// 页面 header // 机构列表
const govInsList = ref([]); const govInsList = ref([]);
const checkedGovIns = ref([]); const checkedGovIns = ref([]);
const handleGetDepartmentList = async () => { const handleGetDepartmentList = async () => {
try { try {
const res = await getDepartmentList(); const res = await getDepartmentList();
...@@ -459,7 +420,20 @@ const handleGetDepartmentList = async () => { ...@@ -459,7 +420,20 @@ const handleGetDepartmentList = async () => {
console.error("获取机构列表error", error); console.error("获取机构列表error", error);
} }
}; };
handleGetDepartmentList(); const handleTimeClick = (time) => {
console.log("time", time);
}
// 跳转行政机构主页
const handleToInstitution = item => {
window.sessionStorage.setItem("curTabName", item.orgName);
const curRoute = router.resolve({
path: "/institution",
query: {
id: item.orgId
}
});
window.open(curRoute.href, "_blank");
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
...@@ -486,16 +460,6 @@ const box1DataList = ref([ ...@@ -486,16 +460,6 @@ const box1DataList = ref([
} }
]); ]);
// const curBox1Data = ref({
// id: 89,
// name: "",
// postDate: "",
// describe: null,
// imageUrl: null,
// officialUrl: null,
// industryList: null
// });
const handleGetLatestDecree = async () => { const handleGetLatestDecree = async () => {
try { try {
const res = await getLatestDecree(); const res = await getLatestDecree();
...@@ -1131,6 +1095,7 @@ const handleSearch = () => { ...@@ -1131,6 +1095,7 @@ const handleSearch = () => {
}; };
onMounted(async () => { onMounted(async () => {
handleGetDepartmentList();
handleGetNews(); handleGetNews();
handleGetDecreeTypeList(); handleGetDecreeTypeList();
handleGetAreaList(); handleGetAreaList();
...@@ -1389,8 +1354,34 @@ onMounted(async () => { ...@@ -1389,8 +1354,34 @@ onMounted(async () => {
} }
} }
.date-box {
display: flex;
align-items: center;
width: 1600px;
margin-top: 48px;
.date-icon {
width: 16px;
height: 16px;
font-size: 0px;
margin-right: 6px;
img {
width: 100%;
height: 100%;
}
}
.date-text {
width: 20px;
flex: auto;
font-size: 18px;
line-height: 18px;
font-family: Source Han Sans CN;
color: var(--text-primary-80-color);
}
}
.home-main-header-item-box { .home-main-header-item-box {
margin: 48px 0 64px; margin: 20px 0 64px;
width: 1600px; width: 1600px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="box1"> <div class="box1">
<AnalysisBox title="相关政令" :showAllBtn="false"> <AnalysisBox title="相关政令" :showAllBtn="false">
<div class="box1-main"> <div class="box1-main">
<el-empty v-if="!siderList?.length" style="padding-top: 40%;" description="暂无数据" :image-size="100" /> <el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-scrollbar height="100%" always> <el-scrollbar height="100%" always>
<div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickDecree(item)"> <div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickDecree(item)">
<div class="item-head"> <div class="item-head">
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
</div> </div>
<div class="box2"> <div class="box2">
<AnalysisBox title="政令关系挖掘" :showAllBtn="false"> <AnalysisBox title="政令关系挖掘" :showAllBtn="false">
<el-empty v-if="!siderList?.length" style="padding-top: 20%;" description="暂无数据" :image-size="100" /> <el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div class="box2-main"> <div class="box2-main" v-if="graphData.nodes?.length">
<div ref="containerRef" class="graph-container"></div> <GraphChart :nodes="graphData.nodes" :links="graphData.links" layoutType="force" @handleClickNode="handleClickNode" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -48,12 +48,13 @@ ...@@ -48,12 +48,13 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"; import { ref, onMounted, onBeforeUnmount, reactive } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import router from "@/router"; import router from "@/router";
import * as G6 from '@antv/g6'; import * as G6 from '@antv/g6';
import { getDecreeRelatedOrder } from "@/api/decree/deepdig"; import { getDecreeRelatedOrder } from "@/api/decree/deepdig";
import { getDecreeSummary } from "@/api/decree/introduction"; import { getDecreeSummary } from "@/api/decree/introduction";
import GraphChart from "@/components/base/GraphChart/index.vue";
import icon1628 from "./assets/icons/icon1628.png"; import icon1628 from "./assets/icons/icon1628.png";
import icon1629 from "./assets/icons/icon1629.png"; import icon1629 from "./assets/icons/icon1629.png";
...@@ -64,7 +65,7 @@ const route = useRoute(); ...@@ -64,7 +65,7 @@ const route = useRoute();
const dialogVisible = ref(false); const dialogVisible = ref(false);
// 基本信息 // 基本信息
const mainInfo = ref({}); const mainInfo = ref({ label: "", time: "", id: "" });
const nodeInfo = ref({}); const nodeInfo = ref({});
const onDecreeSummaryData = async () => { const onDecreeSummaryData = async () => {
try { try {
...@@ -74,10 +75,9 @@ const onDecreeSummaryData = async () => { ...@@ -74,10 +75,9 @@ const onDecreeSummaryData = async () => {
mainInfo.value.label = res.data.name; mainInfo.value.label = res.data.name;
mainInfo.value.time = res.data.postDate; mainInfo.value.time = res.data.postDate;
mainInfo.value.id = route.query.id; mainInfo.value.id = route.query.id;
mainInfo.value.isCenter = true
} }
} catch (error) { } catch (error) {
mainInfo.value = {}; mainInfo.value = { label: "", time: "", id: "" };
console.log("获取基本信息数据失败:", error); console.log("获取基本信息数据失败:", error);
} }
}; };
...@@ -109,145 +109,64 @@ const handleGetRelateOrder = async () => { ...@@ -109,145 +109,64 @@ const handleGetRelateOrder = async () => {
}; };
// 政令关系挖掘 // 政令关系挖掘
const containerRef = ref(); const graphData = reactive({
let graphInstance = null; nodes: [],
// 文本插入换行符 links: [],
const onWordWrap = (word, num) => { })
const list = word.split(''); // 节点点击处理
let label = ""; const handleClickNode = ({data}) => {
for (let i = 0; i < list.length; i++) { if (data.target) {
if (i % num === 0 && i !== 0) { let node = siderList.value.find(item => item.id==data.target)
label += "\n"; if (node) handleClickSider(node)
} else {
let node = siderList.value.find(item => item.id==data.id)
if (node) handleClickDecree(node)
} }
label += list[i]; }
const initGraphChart = () => {
Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => {
if (mainInfo.value.id && siderList.value.length) {
graphData.links = siderList.value.map(onFormatLink)
graphData.nodes = siderList.value.map(onFormatNode)
graphData.nodes.unshift(onFormatNode(mainInfo.value))
} }
return label; })
} }
const onFormatNode = (item) => { const onFormatLink = (item, index) => {
let isCenter = item.isCenter || false
return { return {
id: item.id+'', label:onWordWrap(item.label, 15), isCenter, id: `link-${index+1}`,
img: isCenter ? icon1628 : icon1629, source: route.query.id, target: item.id+'',
clipCfg: { r: isCenter ? 40 : 30 }, label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.relation },
labelCfg: { lineStyle: { color: '#B9DCFF', type: "solid", opacity: 1 }
style: {
fill: isCenter ? "#1459BB" : "#333333",
fontSize: isCenter ? 13 : 13,
fontWeight: isCenter ? "bold" : "normal"
}
}
} }
} }
const onFormatEdge = (item, index) => { const onFormatNode = (item) => {
let leader = item.id == mainInfo.value.id;
return { return {
id: `edge-${index+1}`, id: item.id+'',
target: item.id+'', name: onWordWrap(item.label, 8),
source: route.query.id, label: {
// label: ["", "相似", "继承", "冲突"][1], show: true,
label: item.relation, color: leader ? "#055fc2" : "#3b414b",
style: { fontSize: leader ? 16 : 14,
stroke: ["", "#B9DCFF", "#87E8DE", "#FFCCC7"][1], fontWeight: leader ? 700 : 400,
fontFamily: 'Source Han Sans CN',
}, },
labelCfg: { symbolSize: leader ? 60 : 40,
style: { symbol: `image://${leader ? icon1628 : icon1629}`
fill: ["", "#055FC2", "#13A8A8", "#CE4F51"][1],
background: {
fill: ["", "#E7F3FF", "#E6FFFB", "#FFE0E0"][1],
}
}
}
} }
} }
const initChart = () => { // 文本插入换行符
let edgeList = siderList.value.map(onFormatEdge) const onWordWrap = (word, num) => {
let nodeList = siderList.value.map(onFormatNode) const list = word.split('');
nodeList.unshift(onFormatNode(mainInfo.value)) let label = "";
console.log(nodeList) for (let i = 0; i < list.length; i++) {
if (i % num === 0 && i !== 0) {
const width = containerRef.value.offsetWidth || 800 label += "\n";
const height = containerRef.value.offsetHeight || 600
const centerX = width / 2
const centerY = height / 2
const radius = Math.min(width, height) / 2 - 120
const otherNodes = nodeList.filter(n => !n.isCenter)
const nodeCount = otherNodes.length
otherNodes.forEach((node, index) => {
const angle = (2 * Math.PI * index) / nodeCount - Math.PI / 2
node.x = centerX + radius * Math.cos(angle)
node.y = centerY + radius * Math.sin(angle)
})
const centerNode = nodeList.find(n => n.isCenter)
if (centerNode) {
centerNode.x = centerX
centerNode.y = centerY
centerNode.fx = centerX
centerNode.fy = centerY
}
graphInstance = new G6.Graph({
container: containerRef.value,
width,
height,
fitView: false,
fitCenter: false,
animate: true,
animateCfg: {
duration: 300,
easing: 'easeLinear'
},
minZoom: 0.1,
maxZoom: 10,
modes: {
default: [ 'drag-canvas', 'zoom-canvas', 'drag-node' ]
},
defaultNode: {
type: 'image',
size: 50,
style: { cursor: "pointer" },
clipCfg: { show: true, type: 'circle' },
labelCfg: {
position: "bottom", offset: 12,
style: {
fill: '#333',
fontSize: 11,
fontFamily: 'Microsoft YaHei',
textAlign: 'center',
background: {
fill: 'rgba(255, 255, 255, 0.95)',
padding: [4, 6, 4, 6],
radius: 4
}
}
}
},
defaultEdge: {
type: "line",
style: { lineWidth: 1, endArrow: true },
labelCfg: {
autoRotate: true,
style: {
cursor: "pointer",
fontSize: 12,
fontFamily: 'Microsoft YaHei',
background: { padding: [4, 4, 4, 4] }
}
} }
label += list[i];
} }
}) return label;
// 节点点击处理
graphInstance.on('node:click', (evt) => {
let node = siderList.value.find(item => item.id==evt.item._cfg.model.id)
if (node) handleClickDecree(node)
});
graphInstance.on('edge:click', (evt) => {
let node = siderList.value.find(item => item.id==evt.item._cfg.model.target)
if (node) handleClickSider(node)
});
graphInstance.data({nodes: nodeList, edges: edgeList})
graphInstance.render()
} }
const handleClickDecree = decree => { const handleClickDecree = decree => {
...@@ -293,9 +212,9 @@ const onRelationChart = () => { ...@@ -293,9 +212,9 @@ const onRelationChart = () => {
}, },
labelCfg: { labelCfg: {
style: { style: {
fill: ["", "#055FC2", "#13A8A8", "#CE4F51"][1], fill: ["", "#055fc2", "#13A8A8", "#CE4F51"][1],
background: { background: {
fill: ["", "#E7F3FF", "#E6FFFB", "#FFE0E0"][1], fill: ["", "#eef7ff", "#E6FFFB", "#FFE0E0"][1],
} }
} }
} }
...@@ -363,13 +282,10 @@ const onRelationChart = () => { ...@@ -363,13 +282,10 @@ const onRelationChart = () => {
} }
onMounted(() => { onMounted(() => {
Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => { initGraphChart()
if (mainInfo.value.id && siderList.value.length) initChart()
})
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
graphInstance?.destroy()
graph?.destroy() graph?.destroy()
}) })
</script> </script>
...@@ -462,10 +378,6 @@ onBeforeUnmount(() => { ...@@ -462,10 +378,6 @@ onBeforeUnmount(() => {
.box2-main { .box2-main {
height: 100%; height: 100%;
padding: 10px; padding: 10px;
.graph-container {
width: 100%;
height: 600px;
}
} }
} }
} }
......
<template> <template>
<div class="view-box"> <div class="view-box">
<el-empty v-if="!dataList?.length" style="padding-top: 15%;" description="暂无数据" :image-size="100" /> <el-empty v-if="!dataList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div v-if="dataList.length" class="main-content-main"> <div v-if="dataList.length" class="main-content-main">
<div class="main-mask" <div class="main-mask"
@wheel.prevent="handleWheel" @wheel.prevent="handleWheel"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论