提交 cfd54036 authored 作者: 张烨's avatar 张烨

feat:政令增加实体关系图

上级 250ad91d
...@@ -9,6 +9,6 @@ ...@@ -9,6 +9,6 @@
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
<script src="./config.js"></script> <script src="./js/config.js"></script>
</body> </body>
</html> </html>
\ No newline at end of file
const baseUrl = `http://8.140.26.4:9085/` const baseUrl = `http://8.140.26.4:9085`
\ No newline at end of file \ No newline at end of file
...@@ -5,6 +5,7 @@ export function getDepartmentList(params) { ...@@ -5,6 +5,7 @@ export function getDepartmentList(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/administrativeDict/department`, url: `/api/administrativeDict/department`,
params
}) })
} }
...@@ -27,34 +28,36 @@ export function getDecreeRiskSignal(params) { ...@@ -27,34 +28,36 @@ export function getDecreeRiskSignal(params) {
// 行政令发布频度 // 行政令发布频度
export function getDecreeYearOrder(params) { export function getDecreeYearOrder(params) {
return request({ return request({
method: 'GET', method: 'POST',
url: `/api/administrativeOrderOverview/yearOrder/${params.year}`, url: `/api/administrativeOrderOverview/yearOrder`,
params data: params
}) })
} }
// 政令涉及领域 // 政令涉及领域
export function getDecreeArea(params) { export function getDecreeArea(params) {
return request({ return request({
method: 'GET', method: 'POST',
url: `/api/administrativeOrderOverview/industry/${params.year}`, url: `/api/administrativeOrderOverview/industry`,
params data: params
}) })
} }
// 关键行政令 // 关键行政令
export function getKeyDecree(params) { export function getKeyDecree(params) {
return request({ return request({
method: 'GET', method: 'POST',
url: `/api/administrativeOrderOverview/action?pageSize=${params.pageSize}&pageNum=${params.pageNum}`, url: `/api/administrativeOrderOverview/action`,
data: params
}) })
} }
// 政令重点条款 // 政令重点条款
export function getDecreeKeyInstruction() { export function getDecreeKeyInstruction(params) {
return request({ return request({
method: 'GET', method: 'POST',
url: `/api/administrativeOrderOverview/instruction`, url: `/api/administrativeOrderOverview/instruction`,
data: params
}) })
} }
...@@ -85,4 +88,22 @@ export function getDecreeTypeList() { ...@@ -85,4 +88,22 @@ export function getDecreeTypeList() {
method: 'GET', method: 'GET',
url: `/api/administrativeDict/type`, 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
...@@ -45,6 +45,14 @@ export function getDecreeChainNodes(params) { ...@@ -45,6 +45,14 @@ export function getDecreeChainNodes(params) {
}) })
} }
// 获取实体关系节点列表
export function getDecreeRelatedEntitie(params) {
return request({
method: 'GET',
url: `/api/administrativeOrderInfo/listRelatedEntitie/${params.id}`,
})
}
// 根据政行业领域ID获取公司列表 // 根据政行业领域ID获取公司列表
/** /**
* @param {cRelated, id} * @param {cRelated, id}
......
...@@ -77,4 +77,12 @@ export function getDecreeReport(params) { ...@@ -77,4 +77,12 @@ export function getDecreeReport(params) {
method: 'GET', method: 'GET',
url: `/api/administrativeOrderInfo/contentUrl/${params.id}`, url: `/api/administrativeOrderInfo/contentUrl/${params.id}`,
}) })
}
// 政令关键词云
export function getKeyWordUp() {
return request({
method: 'GET',
url: `/api/element//getKeyWordUp/2025-01-01`,
})
} }
\ No newline at end of file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<el-empty v-if="!entityInfo.list?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" /> <el-empty v-if="!entityInfo.list?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-scrollbar height="100%" always> <el-scrollbar height="100%" always>
<div class="list-data"> <div class="list-data">
<div class="list-item" v-for="item in entityInfo.list" :key="item.id" :class="{ 'item-active': entityInfo.id==item.id }" @click="onDecreeRelatedChain(item)"> <div class="list-item" v-for="item in entityInfo.list" :key="item.id" :class="{ 'item-active': entityInfo.id==item.id }" @click="headerChartData(item)">
<div class="item-icon"> <div class="item-icon">
<img :src="defaultIcon2" alt="" class="item-img" /> <img :src="defaultIcon2" alt="" class="item-img" />
</div> </div>
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
<div class="graph-box" v-if="contentType==1"> <div class="graph-box" v-if="contentType==1">
<ChartChain :listData="fishbone.list" :baseData="fishbone.base" /> <ChartChain :listData="fishbone.list" :baseData="fishbone.base" />
</div> </div>
<div class="graph-box" v-if="contentType==2"> <div class="graph-box" v-if="contentType==2 && graphInfo.nodes.length">
<GraphChart :nodes="graphInfo.nodes" :links="graphInfo.links" layoutType="force" /> <GraphChart :nodes="graphInfo.nodes" :links="graphInfo.links" layoutType="force" />
</div> </div>
</div> </div>
...@@ -92,7 +92,13 @@ ...@@ -92,7 +92,13 @@
import { ref, onMounted, reactive } from "vue"; import { ref, onMounted, reactive } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { getDecreehylyList, getDecreeEntities, getDecreeRelatedChain, getDecreeChainNodes } from "@/api/decree/influence"; import {
getDecreehylyList,
getDecreeEntities,
getDecreeRelatedChain,
getDecreeChainNodes,
getDecreeRelatedEntitie
} from "@/api/decree/influence";
import ChartChain from "./com/ChartChain.vue"; import ChartChain from "./com/ChartChain.vue";
import AiTips from "./com/AiTips.vue"; import AiTips from "./com/AiTips.vue";
import GraphChart from "@/components/base/GraphChart/index.vue"; import GraphChart from "@/components/base/GraphChart/index.vue";
...@@ -133,18 +139,25 @@ const entityInfo = reactive({ ...@@ -133,18 +139,25 @@ const entityInfo = reactive({
total: 0, total: 0,
list: [], list: [],
id: '', id: '',
node: {id: '', companyName: ''},
}) })
const onDecreeEntities = async (page=1) => { const onDecreeEntities = async (page=1) => {
entityInfo.pageNum = page; entityInfo.pageNum = page;
try { try {
let { pageSize, pageNum, keyword } = entityInfo; let params = {
const res = await getDecreeEntities({id: route.query.id, pageSize, pageNum:pageNum-1, keyword}); id: route.query.id,
pageSize: entityInfo.pageSize,
pageNum: entityInfo.pageNum - 1,
keyword: entityInfo.keyword,
domainId: areaInfo.id || undefined
}
const res = await getDecreeEntities(params);
console.log("受影响实体:", res); console.log("受影响实体:", res);
if (res.code === 200) { if (res.code === 200) {
entityInfo.list = res.data.companyInfos; entityInfo.list = res.data.companyInfos;
entityInfo.total = res.data.total; entityInfo.total = res.data.total;
if (entityInfo.total && entityInfo.list.every(item => item.id!=entityInfo.id)) { if (entityInfo.total && entityInfo.list.every(item => item.id!=entityInfo.id)) {
onDecreeRelatedChain(entityInfo.list[0]) headerChartData(entityInfo.list[0])
} }
} }
} catch (error) { } catch (error) {
...@@ -155,20 +168,39 @@ const onDecreeEntities = async (page=1) => { ...@@ -155,20 +168,39 @@ const onDecreeEntities = async (page=1) => {
const contentType = ref(1); const contentType = ref(1);
const headerContentType = (type) => { const headerContentType = (type) => {
contentType.value = type; contentType.value = type;
headerChartData(entityInfo.node)
}; };
const headerChartData = (row) => {
entityInfo.id = row.id;
entityInfo.node = row;
industryChain.id = "";
fishbone.list = []
fishbone.base = []
graphInfo.nodes = [];
graphInfo.links = [];
switch (contentType.value) {
case 1:
onDecreeRelatedChain(row.id)
break;
case 2:
onDecreeRelatedEntitie(row.id)
break;
}
}
// 产业链 // 产业链
const industryChain = reactive({ const industryChain = reactive({
list: [], list: [],
id: "", id: "",
}) })
const onDecreeRelatedChain = async ({ id }) => { const onDecreeRelatedChain = async (id) => {
entityInfo.id = id;
try { try {
const res = await getDecreeRelatedChain({ id }); const res = await getDecreeRelatedChain({ id });
console.log("产业链:", res); console.log("产业链:", res);
if (res.code === 200) { if (res.code === 200) {
industryChain.id = "";
industryChain.list = res.data; industryChain.list = res.data;
if (industryChain.list.length) onDecreeChainNodes(industryChain.list[0].id) if (industryChain.list.length) onDecreeChainNodes(industryChain.list[0].id)
} }
...@@ -184,8 +216,6 @@ const fishbone = reactive({ ...@@ -184,8 +216,6 @@ const fishbone = reactive({
}) })
const onDecreeChainNodes = async (id) => { const onDecreeChainNodes = async (id) => {
industryChain.id = id; industryChain.id = id;
fishbone.list = []
fishbone.base = []
try { try {
const res = await getDecreeChainNodes({ id }); const res = await getDecreeChainNodes({ id });
console.log("产业链鱼骨图:", res); console.log("产业链鱼骨图:", res);
...@@ -195,11 +225,14 @@ const onDecreeChainNodes = async (id) => { ...@@ -195,11 +225,14 @@ const onDecreeChainNodes = async (id) => {
return result; return result;
}, {}); }, {});
res.data.children.forEach(item => { res.data.children.forEach(item => {
if (obj['chain-'+item.chainId]?.children?.length < 10) { if (item.companyId== entityInfo.id) {
obj['chain-'+item.chainId].children.push({ ...item, back:true})
} else if (obj['chain-'+item.chainId]?.children?.length < 10) {
obj['chain-'+item.chainId].children.push(item) obj['chain-'+item.chainId].children.push(item)
} }
}) })
fishbone.list = Object.values(obj); fishbone.list = Object.values(obj);
console.log("fishbone.list:", fishbone.list);
fishbone.base = res.data.levelInfos.map((item, index) => { fishbone.base = res.data.levelInfos.map((item, index) => {
return {...item, name: ['上游', '中游', '下游'][index]} return {...item, name: ['上游', '中游', '下游'][index]}
}); });
...@@ -211,38 +244,35 @@ const onDecreeChainNodes = async (id) => { ...@@ -211,38 +244,35 @@ const onDecreeChainNodes = async (id) => {
// 实体关系 // 实体关系
const graphInfo = reactive({ const graphInfo = reactive({
leader: { id: 0, name: "泰丰先行" },
list: [
{ id: 1, name: "国轩高科", formatter: '持股' },
{ id: 2, name: "智方纳米", formatter: '持股' },
{ id: 3, name: "香百科技", formatter: '合作' },
{ id: 4, name: "格林滨", formatter: '从属' },
{ id: 5, name: "江西紫宸", formatter: '合作' },
{ id: 6, name: "紫江企业", formatter: '持股' },
{ id: 7, name: "大而美法案", formatter: '合作' },
{ id: 8, name: "比亚迪", formatter: '合作' },
],
nodes: [], nodes: [],
links: [], links: [],
}); });
const initGraphChart = () => { const onDecreeRelatedEntitie = async (id) => {
graphInfo.links = graphInfo.list.map(onFormatLink) try {
graphInfo.nodes = graphInfo.list.map(onFormatNode) const res = await getDecreeRelatedEntitie({ id });
graphInfo.nodes.unshift(onFormatNode(graphInfo.leader)) console.log("实体关系:", res);
if (res.code === 200) {
graphInfo.links = res.data.map(onFormatLink)
graphInfo.nodes = res.data.map(onFormatNode)
graphInfo.nodes.unshift(onFormatNode(entityInfo.node))
}
} catch (error) {
console.log("获取实体关系失败", error);
}
} }
const onFormatLink = (item, index) => { const onFormatLink = (item, index) => {
return { return {
id: `link-${index+1}`, id: `link-${index+1}`,
source: item.id+'', target: '0', source: item.id+'', target: entityInfo.id+'',
label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.formatter }, label: { show: true, color: "#055fc2", backgroundColor: "#eef7ff", borderWidth: 0, offset: [0, 15], formatter: item.relation },
lineStyle: { color: '#B9DCFF', type: "solid", opacity: 1 } lineStyle: { color: '#B9DCFF', type: "solid", opacity: 1 }
} }
} }
const onFormatNode = (item) => { const onFormatNode = (item) => {
let leader = item.id == '0'; let leader = item.id == entityInfo.id;
return { return {
id: item.id+'', id: item.id+'',
name: onWordWrap(item.name, 7), name: onWordWrap(item.companyName, 7),
label: { label: {
show: true, show: true,
color: "#3b414b", color: "#3b414b",
...@@ -269,7 +299,6 @@ const onWordWrap = (word, num) => { ...@@ -269,7 +299,6 @@ const onWordWrap = (word, num) => {
onMounted(() => { onMounted(() => {
onDecreeEntities(); onDecreeEntities();
initGraphChart()
handleGetHylyList(); handleGetHylyList();
}); });
</script> </script>
......
...@@ -98,8 +98,8 @@ ...@@ -98,8 +98,8 @@
<div class="box4"> <div class="box4">
<AnalysisBox title="政令关键词云" :showAllBtn="false"> <AnalysisBox title="政令关键词云" :showAllBtn="false">
<div class="box4-main"> <div class="box4-main">
<el-empty v-if="false" description="暂无数据" :image-size="100" /> <el-empty v-if="!wordCloudData.length" description="暂无数据" :image-size="100" />
<WordCloudChart :data="wordCloudData" width="100%" height="100%" /> <WordCloudChart v-if="wordCloudData.length" :data="wordCloudData" width="100%" height="100%" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -166,7 +166,8 @@ import WordCloudChart from "@/components/base/WordCloundChart/index.vue" ...@@ -166,7 +166,8 @@ import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import { import {
getDecreeBasicInfo, getDecreeBasicInfo,
getDecreeRiskSignal, getDecreeRiskSignal,
getDecreeIssueOrganization getDecreeIssueOrganization,
getKeyWordUp,
} from "@/api/decree/introduction"; } from "@/api/decree/introduction";
import { getDecreeRelatedEvent } from "@/api/decree/background"; import { getDecreeRelatedEvent } from "@/api/decree/background";
import AiSummary from '@/components/base/Ai/AiSummary/index.vue' import AiSummary from '@/components/base/Ai/AiSummary/index.vue'
...@@ -230,19 +231,28 @@ const handleGetBasicInfo = async () => { ...@@ -230,19 +231,28 @@ const handleGetBasicInfo = async () => {
handleGetBasicInfo(); handleGetBasicInfo();
// 政令关键词云 // 政令关键词云
const wordCloudData = ref([ const wordCloudData = ref([])
{ name: "与马斯克公开冲突", value: 100 }, const onKeyWordUp = async () => {
{ name: "传统能源", value: 5 }, try {
{ name: "共和党财政鹰派", value: 77 }, const res = await getKeyWordUp();
{ name: "未实现赤字控制目标", value: 35 }, console.log("政令关键词云", res);
{ name: "得克萨斯州", value: 88 }, wordCloudData.value = res.data.slice(0, 10).map(item => ({name: item.name, value: item.count}));
{ name: "选举压力", value: 57 }, } catch (error) {
{ name: "主张财政紧缩", value: 72 }, console.error("获取政令关键词云数据失败", error);
{ name: "财政保守", value: 18 }, }
]) };
// 报告内容摘要 // 报告内容摘要
const box1Data = ref(`包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。为了应对这一挑战,兰德大学的研究人员对美中竞争进行了经济和制度分析,进行了参与式的远见练习,以了解确保美国经济健康的长期路径,并创建了两个经济竞争游戏,探索多个国家在相互交流的同时确保经济健康的动态...`); const box1Data = ref(`包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。为了应对这一挑战,兰德大学的研究人员对美中竞争进行了经济和制度分析,进行了参与式的远见练习,以了解确保美国经济健康的长期路径,并创建了两个经济竞争游戏,探索多个国家在相互交流的同时确保经济健康的动态...`);
// const handleGetDecreeKeyInstruction = async () => {
// try {
// const res = await getDecreeKeyInstruction();
// console.log("报告内容摘要", res);
// box1Data.value = res.data;
// } catch (error) {
// console.error("获取报告内容摘要数据失败", error);
// }
// };
// 相关事件 // 相关事件
const relatedData = ref([]); const relatedData = ref([]);
...@@ -332,6 +342,7 @@ const handleClickUser = item => { ...@@ -332,6 +342,7 @@ const handleClickUser = item => {
}; };
onMounted(() => { onMounted(() => {
onKeyWordUp()
onRiskSignalData() onRiskSignalData()
handleGetRelateEvents(); handleGetRelateEvents();
handleGetOrgnization(); handleGetOrgnization();
......
...@@ -41,10 +41,11 @@ import { getDecreeReport } from "@/api/decree/introduction"; ...@@ -41,10 +41,11 @@ import { getDecreeReport } from "@/api/decree/introduction";
import BaseDecreeOriginal from "@/components/base/DecreeOriginal/index.vue"; import BaseDecreeOriginal from "@/components/base/DecreeOriginal/index.vue";
const route = useRoute(); const route = useRoute();
let pdfUrl = "";
const handleDownload = async () => { const handleDownload = async () => {
if (summaryInfo.value?.url) { if (pdfUrl) {
try { try {
const response = await fetch(summaryInfo.value.url, { const response = await fetch(pdfUrl, {
method: 'GET', method: 'GET',
headers: { 'Content-Type': 'application/pdf' }, headers: { 'Content-Type': 'application/pdf' },
}); });
...@@ -103,6 +104,7 @@ const handleGetReport = async () => { ...@@ -103,6 +104,7 @@ const handleGetReport = async () => {
const res = await getDecreeReport({id: route.query.id}); const res = await getDecreeReport({id: route.query.id});
console.log("报告原文", res); console.log("报告原文", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
pdfUrl = res.data.pdfUrl;
const originData = []; const originData = [];
let num = Math.max(res.data.content.length, res.data.contentEn.length) let num = Math.max(res.data.content.length, res.data.contentEn.length)
for (let i = 0; i < num; i++) { for (let i = 0; i < num; i++) {
......
...@@ -44,16 +44,14 @@ export default defineConfig({ ...@@ -44,16 +44,14 @@ export default defineConfig({
port: 3000, port: 3000,
open: true, open: true,
proxy: { proxy: {
'/reportData': { '/reportData': {
target: 'http://8.140.26.4:10022/', target: 'http://8.140.26.4:10022/',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/reportData/, '') rewrite: (path) => path.replace(/^\/reportData/, '')
}, },
'/api': { '/api': {
// target: 'http://8.140.26.4:9085/', // target: 'http://8.140.26.4:9085/',
target: 'http://172.20.10.3:28080/', target: 'http://192.168.0.4:28080/',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') rewrite: (path) => path.replace(/^\/api/, '')
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论