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

合并分支 'zy-dev' 到 'master'

Zy dev 查看合并请求 !190
const baseUrl = `http://8.140.26.4:9085/`
\ No newline at end of file
const baseUrl = `http://8.140.26.4:9085`
\ No newline at end of file
......@@ -251,7 +251,7 @@ body {
/* 可点击文本 鼠标悬浮样式 */
#app .text-click-hover:hover {
text-decoration: underline;
color: rgb(5, 95, 194);
color: var(--color-primary-100);
cursor: pointer;
}
/* #endregion 公共样式类名 */
......
......@@ -5,6 +5,7 @@ export function getDepartmentList(params) {
return request({
method: 'GET',
url: `/api/administrativeDict/department`,
params
})
}
......@@ -27,34 +28,36 @@ export function getDecreeRiskSignal(params) {
// 行政令发布频度
export function getDecreeYearOrder(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderOverview/yearOrder/${params.year}`,
params
method: 'POST',
url: `/api/administrativeOrderOverview/yearOrder`,
data: params
})
}
// 政令涉及领域
export function getDecreeArea(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderOverview/industry/${params.year}`,
params
method: 'POST',
url: `/api/administrativeOrderOverview/industry`,
data: params
})
}
// 关键行政令
export function getKeyDecree(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderOverview/action?pageSize=${params.pageSize}&pageNum=${params.pageNum}`,
method: 'POST',
url: `/api/administrativeOrderOverview/action`,
data: params
})
}
// 政令重点条款
export function getDecreeKeyInstruction() {
export function getDecreeKeyInstruction(params) {
return request({
method: 'GET',
method: 'POST',
url: `/api/administrativeOrderOverview/instruction`,
data: params
})
}
......@@ -85,4 +88,22 @@ export function getDecreeTypeList() {
method: 'GET',
url: `/api/administrativeDict/type`,
})
}
// 关键机构
export function getKeyOrganization() {
return request({
method: 'GET',
url: `/api/commonFeature/keyOrganization`,
})
}
// AI智能总结
export function getChartInterpretation(params) {
return request({
method: 'POST',
url: `/aiAnalysis/chart_interpretation`,
headers: {"X-API-Key": "aircasKEY19491001"},
data: params
})
}
\ No newline at end of file
......@@ -20,6 +20,39 @@ export function getDecreehylyList() {
})
}
// 获取受影响实体列表
export function getDecreeEntities(params) {
return request({
method: 'POST',
url: `/api/administrativeOrderInfo/relatedEntities`,
data: params
})
}
// 获取实体产业链列表
export function getDecreeRelatedChain(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/relatedChain/${params.id}`,
})
}
// 获取产业链节点列表
export function getDecreeChainNodes(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/relatedChainNodes/${params.id}`,
})
}
// 获取实体关系节点列表
export function getDecreeRelatedEntitie(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/listRelatedEntitie/${params.id}`,
})
}
// 根据政行业领域ID获取公司列表
/**
* @param {cRelated, id}
......
......@@ -77,4 +77,20 @@ export function getDecreeReport(params) {
method: 'GET',
url: `/api/administrativeOrderInfo/contentUrl/${params.id}`,
})
}
// 政令关键词云
export function getKeyWordUp() {
return request({
method: 'GET',
url: `/api/element/getKeyWordUp/2025-01-01`,
})
}
// 报告内容摘要
export function getOverview(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/overview/${params.id}`,
})
}
\ No newline at end of file
......@@ -326,9 +326,8 @@ watch(isTranslate, () => {
.report-main {
flex: auto;
min-height: 0;
box-sizing: border-box;
padding-top: 10px;
height: 20px;
padding: 10px 0;
:deep(.el-scrollbar) {
height: 100%;
......
......@@ -5,10 +5,11 @@
</template>
<script setup>
import { onMounted, nextTick } from 'vue';
import { onMounted, onBeforeUnmount } from 'vue';
import setChart from '@/utils/setChart';
import getGraphChart from './graphChart';
const emits = defineEmits(["handleClickNode"])
const props = defineProps({
nodes: {
type: Array,
......@@ -27,16 +28,22 @@ const props = defineProps({
default: 'force'
},
height: {
type: String,
type: String,
default: 'force'
}
})
let chart = null
onMounted(() => {
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>
......
......@@ -3,7 +3,7 @@
<div class="box1">
<AnalysisBox title="相关政令" :showAllBtn="false">
<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>
<div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickDecree(item)">
<div class="item-head">
......@@ -18,9 +18,9 @@
</div>
<div class="box2">
<AnalysisBox title="政令关系挖掘" :showAllBtn="false">
<el-empty v-if="!siderList?.length" style="padding-top: 20%;" description="暂无数据" :image-size="100" />
<div class="box2-main">
<div ref="containerRef" class="graph-container"></div>
<el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div class="box2-main" v-if="graphData.nodes?.length">
<GraphChart :nodes="graphData.nodes" :links="graphData.links" layoutType="force" @handleClickNode="handleClickNode" />
</div>
</AnalysisBox>
</div>
......@@ -48,12 +48,13 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { ref, onMounted, onBeforeUnmount, reactive } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import * as G6 from '@antv/g6';
import { getDecreeRelatedOrder } from "@/api/decree/deepdig";
import { getDecreeSummary } from "@/api/decree/introduction";
import GraphChart from "@/components/base/GraphChart/index.vue";
import icon1628 from "./assets/icons/icon1628.png";
import icon1629 from "./assets/icons/icon1629.png";
......@@ -64,7 +65,7 @@ const route = useRoute();
const dialogVisible = ref(false);
// 基本信息
const mainInfo = ref({});
const mainInfo = ref({ label: "", time: "", id: "" });
const nodeInfo = ref({});
const onDecreeSummaryData = async () => {
try {
......@@ -74,10 +75,9 @@ const onDecreeSummaryData = async () => {
mainInfo.value.label = res.data.name;
mainInfo.value.time = res.data.postDate;
mainInfo.value.id = route.query.id;
mainInfo.value.isCenter = true
}
} catch (error) {
mainInfo.value = {};
mainInfo.value = { label: "", time: "", id: "" };
console.log("获取基本信息数据失败:", error);
}
};
......@@ -109,8 +109,53 @@ const handleGetRelateOrder = async () => {
};
// 政令关系挖掘
const containerRef = ref();
let graphInstance = null;
const graphData = reactive({
nodes: [],
links: [],
})
// 节点点击处理
const handleClickNode = ({data}) => {
if (data.target) {
let node = siderList.value.find(item => item.id==data.target)
if (node) handleClickSider(node)
} else {
let node = siderList.value.find(item => item.id==data.id)
if (node) handleClickDecree(node)
}
}
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))
}
})
}
const onFormatLink = (item, index) => {
return {
id: `link-${index+1}`,
source: route.query.id, target: item.id+'',
label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.relation },
lineStyle: { color: '#B9DCFF', type: "solid", opacity: 1 }
}
}
const onFormatNode = (item) => {
let leader = item.id == mainInfo.value.id;
return {
id: item.id+'',
name: onWordWrap(item.label, 8),
label: {
show: true,
color: leader ? "#055fc2" : "#3b414b",
fontSize: leader ? 16 : 14,
fontWeight: leader ? 700 : 400,
fontFamily: 'Source Han Sans CN',
},
symbolSize: leader ? 60 : 40,
symbol: `image://${leader ? icon1628 : icon1629}`
}
}
// 文本插入换行符
const onWordWrap = (word, num) => {
const list = word.split('');
......@@ -123,132 +168,6 @@ const onWordWrap = (word, num) => {
}
return label;
}
const onFormatNode = (item) => {
let isCenter = item.isCenter || false
return {
id: item.id+'', label:onWordWrap(item.label, 15), isCenter,
img: isCenter ? icon1628 : icon1629,
clipCfg: { r: isCenter ? 40 : 30 },
labelCfg: {
style: {
fill: isCenter ? "#1459BB" : "#333333",
fontSize: isCenter ? 13 : 13,
fontWeight: isCenter ? "bold" : "normal"
}
}
}
}
const onFormatEdge = (item, index) => {
return {
id: `edge-${index+1}`,
target: item.id+'',
source: route.query.id,
// label: ["", "相似", "继承", "冲突"][1],
label: item.relation,
style: {
stroke: ["", "#B9DCFF", "#87E8DE", "#FFCCC7"][1],
},
labelCfg: {
style: {
fill: ["", "#055FC2", "#13A8A8", "#CE4F51"][1],
background: {
fill: ["", "#E7F3FF", "#E6FFFB", "#FFE0E0"][1],
}
}
}
}
}
const initChart = () => {
let edgeList = siderList.value.map(onFormatEdge)
let nodeList = siderList.value.map(onFormatNode)
nodeList.unshift(onFormatNode(mainInfo.value))
console.log(nodeList)
const width = containerRef.value.offsetWidth || 800
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] }
}
}
}
})
// 节点点击处理
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 => {
window.sessionStorage.setItem("curTabName", decree.name);
......@@ -293,9 +212,9 @@ const onRelationChart = () => {
},
labelCfg: {
style: {
fill: ["", "#055FC2", "#13A8A8", "#CE4F51"][1],
fill: ["", "#055fc2", "#13A8A8", "#CE4F51"][1],
background: {
fill: ["", "#E7F3FF", "#E6FFFB", "#FFE0E0"][1],
fill: ["", "#eef7ff", "#E6FFFB", "#FFE0E0"][1],
}
}
}
......@@ -363,13 +282,10 @@ const onRelationChart = () => {
}
onMounted(() => {
Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => {
if (mainInfo.value.id && siderList.value.length) initChart()
})
initGraphChart()
});
onBeforeUnmount(() => {
graphInstance?.destroy()
graph?.destroy()
})
</script>
......@@ -462,10 +378,6 @@ onBeforeUnmount(() => {
.box2-main {
height: 100%;
padding: 10px;
.graph-container {
width: 100%;
height: 600px;
}
}
}
}
......
......@@ -79,7 +79,7 @@ onMounted(() => {
.left {
position: absolute;
left: -160px;
top: 0px;
top: 25px;
width: 160px;
padding: 0px 16px;
}
......
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论