提交 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
}) })
} }
...@@ -86,3 +89,21 @@ export function getDecreeTypeList() { ...@@ -86,3 +89,21 @@ export function getDecreeTypeList() {
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}
......
...@@ -78,3 +78,11 @@ export function getDecreeReport(params) { ...@@ -78,3 +78,11 @@ export function getDecreeReport(params) {
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
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
<img :src="tipsTcon" alt=""> <img :src="tipsTcon" alt="">
</div> </div>
<div class="date-text">近期美国各联邦政府机构发布涉华政令数量汇总</div> <div class="date-text">近期美国各联邦政府机构发布涉华政令数量汇总</div>
<TimeTabPane @time-click="handleTimeClick" /> <TimeTabPane @time-click="handleGetDepartmentList" />
</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="organization-item" v-for="(item, index) in govInsList.slice(0, 7)" :key="index" @click="handleToInstitution(item)"> <div class="organization-item" v-for="(item, index) in govInsList.slice(0, 7)" :key="index" @click="handleToInstitution(item)">
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<div class="item-right one-line-ellipsis">{{ item.orgName }}</div> <div class="item-right one-line-ellipsis">{{ item.orgName }}</div>
<div class="item-total">{{ item.total }}</div> <div class="item-total">{{ item.total }}</div>
<el-icon color="var(--color-primary-100)"><ArrowRightBold /></el-icon> <el-icon color="var(--color-primary-100)"><ArrowRightBold /></el-icon>
<div class="item-dot">+999</div> <div class="item-dot" v-if="item.totalRecent">+{{item.totalRecent}}</div>
</div> </div>
<div class="organization-item"> <div class="organization-item">
<div class="item-more">查看全部机构 ({{govInsList.length+1}}家)</div> <div class="item-more">查看全部机构 ({{govInsList.length+1}}家)</div>
...@@ -172,14 +172,24 @@ ...@@ -172,14 +172,24 @@
<div class="center-footer"> <div class="center-footer">
<div class="box5"> <div class="box5">
<div class="box5-header"> <div class="box5-header">
<div class="box5-header-left">
<div class="box5-header-icon"> <div class="box5-header-icon">
<img src="./assets/images/box3-header-icon.png" alt="" /> <img src="./assets/images/box3-header-icon.png" alt="" />
</div> </div>
<div class="box5-header-title">{{ "行政令发布频度" }}</div> <div class="box5-header-title">{{ "数量变化趋势" }}</div>
<div style="margin-right: 20px;">
<el-select @change="handleBox5" v-model="box5Params.proposeName" :empty-values="[null, undefined]" style="width:150px">
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName" :value="item.orgId" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleBox5" v-model="box5Params.domainId" :empty-values="[null, undefined]" style="width:120px">
<el-option label="全部领域" value="" />
<el-option v-for="item in areaList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div> </div>
<div class="box5-selectbox"> <div style="margin-right: 20px;">
<el-select @change="handleBox5YearChange" v-model="box5SelectedYear" placeholder="选择时间" style="width: 120px"> <el-select @change="handleBox5" v-model="box5Params.year" placeholder="选择时间" style="width:120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
...@@ -187,25 +197,49 @@ ...@@ -187,25 +197,49 @@
<div class="box5-main"> <div class="box5-main">
<div class="box5-chart" id="chart1"></div> <div class="box5-chart" id="chart1"></div>
</div> </div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政令数量变化趋势,数据来源:美国各行政机构官网</div>
</div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane aiContent="假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据" /> <AiPane aiContent="假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据" />
</div> </div>
</div> </div>
<div class="box6"> <div class="box5">
<div class="box6-header"> <div class="box5-header">
<div class="header-icon"> <div class="box5-header-icon">
<img src="./assets/images/box4-header-icon.png" alt="" /> <img src="./assets/images/box4-header-icon.png" alt="" />
</div> </div>
<div class="header-title">{{ "政令科技领域" }}</div> <div class="box5-header-title">{{ "领域分布情况" }}</div>
<div class="box6-selectbox"> <div style="margin-right: 20px;">
<el-select @change="handleBox6YearChange" v-model="box6SelectedYear" placeholder="选择时间" style="width: 120px"> <el-select @change="handleBox6YearChange" v-model="box6Params.proposeName" :empty-values="[null, undefined]" style="width:150px">
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName" :value="item.orgId" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleBox6YearChange" v-model="box6Params.year" placeholder="选择时间" style="width: 120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
</div> </div>
<div class="box6-main" id="chart2"></div> <div class="box5-main">
<div class="box5-chart" id="chart2"></div>
</div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政领领域分布情况,数据来源:美国各行政机构官网</div>
</div>
<div class="ai-pane">
<AiButton />
<AiPane aiContent="假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据假数据" />
</div>
</div> </div>
</div> </div>
<div class="center-footer1"> <div class="center-footer1">
...@@ -214,7 +248,24 @@ ...@@ -214,7 +248,24 @@
<div class="header-icon"> <div class="header-icon">
<img src="./assets/images/box5-header-icon.png" alt="" /> <img src="./assets/images/box5-header-icon.png" alt="" />
</div> </div>
<div class="header-title">{{ "关键行政令" }}</div> <div class="header-title">{{ "关键科技政令" }}</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetKeyDecree" v-model="box7Params.proposeName" :empty-values="[null, undefined]" style="width:150px">
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName" :value="item.orgId" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetKeyDecree" v-model="box7Params.domainId" :empty-values="[null, undefined]" style="width:120px">
<el-option label="全部领域" value="" />
<el-option v-for="item in areaList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetKeyDecree" v-model="box7Params.year" placeholder="选择时间" style="width:120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div> </div>
<div class="box7-main"> <div class="box7-main">
<div class="box7-list"> <div class="box7-list">
...@@ -231,7 +282,13 @@ ...@@ -231,7 +282,13 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<SimplePagination v-model:current-page="keyDecreeInfo.page" :page-size="keyDecreeInfo.size" :total="keyDecreeInfo.total" @page-change="handleGetKeyDecree" /> <SimplePagination v-model:current-page="keyDecreeInfo.page" :page-size="keyDecreeInfo.size" :total="keyDecreeInfo.total" @page-change="handleGetKeyDecree" />
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">关键科技政领列表,数据来源:美国各行政机构官网</div>
</div> </div>
</div> </div>
<div class="box8"> <div class="box8">
...@@ -239,10 +296,33 @@ ...@@ -239,10 +296,33 @@
<div class="header-icon"> <div class="header-icon">
<img src="./assets/images/box5-header-icon.png" alt="" /> <img src="./assets/images/box5-header-icon.png" alt="" />
</div> </div>
<div class="header-title">{{ "政令重点条款" }}</div> <div class="header-title">{{ "关键条款词云" }}</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetDecreeKeyInstruction" v-model="box8Params.proposeName" :empty-values="[null, undefined]" style="width:150px">
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName" :value="item.orgId" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetDecreeKeyInstruction" v-model="box8Params.domainId" :empty-values="[null, undefined]" style="width:120px">
<el-option label="全部领域" value="" />
<el-option v-for="item in areaList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
<div style="margin-right: 20px;">
<el-select @change="handleGetDecreeKeyInstruction" v-model="box8Params.year" placeholder="选择时间" style="width:120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div> </div>
<div class="box8-content" v-if="wordCloudData?.length"> </div>
<WordCloudChart :data="wordCloudData" width="100%" height="100%" /> <div class="box8-content">
<WordCloudChart v-if="wordCloudData?.length" :data="wordCloudData" width="100%" height="100%" />
</div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政领重点条款词云,数据来源:美国各行政机构官网</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -295,6 +375,10 @@ ...@@ -295,6 +375,10 @@
</div> </div>
<div class="select-main"> <div class="select-main">
<div class="checkbox-group"> <div class="checkbox-group">
<el-checkbox v-model="activeAreaList" label="all"
style="width: 100px" class="filter-checkbox" @change="checked => handleAreaChange('all', checked)">
{{ "全部领域" }}
</el-checkbox>
<el-checkbox v-for="area in areaList" :key="area.id" v-model="activeAreaList" :label="area.id" <el-checkbox v-for="area in areaList" :key="area.id" v-model="activeAreaList" :label="area.id"
style="width: 100px" class="filter-checkbox" @change="checked => handleAreaChange(area.id, checked)"> style="width: 100px" class="filter-checkbox" @change="checked => handleAreaChange(area.id, checked)">
{{ area.name }} {{ area.name }}
...@@ -385,7 +469,9 @@ import { ...@@ -385,7 +469,9 @@ import {
getDecreeKeyInstruction, getDecreeKeyInstruction,
getDecreeOrderList, getDecreeOrderList,
getDecreehylyList, getDecreehylyList,
getDecreeTypeList getDecreeTypeList,
getKeyOrganization,
getChartInterpretation,
} from "@/api/decree/home"; } from "@/api/decree/home";
import { getPersonSummaryInfo } from "@/api/common/index"; import { getPersonSummaryInfo } from "@/api/common/index";
import { getNews, getSocialMedia } from "@/api/general/index"; import { getNews, getSocialMedia } from "@/api/general/index";
...@@ -414,9 +500,14 @@ const handleCurrentChange = page => { ...@@ -414,9 +500,14 @@ const handleCurrentChange = page => {
// 机构列表 // 机构列表
const govInsList = ref([]); const govInsList = ref([]);
const checkedGovIns = ref([]); const checkedGovIns = ref([]);
const handleGetDepartmentList = async () => { const handleGetDepartmentList = async (event) => {
let day = 7
if (event?.time === '近一周') day = 7
if (event?.time === '近一月') day = 30
if (event?.time === '近一年') day = 365
try { try {
const res = await getDepartmentList(); const res = await getDepartmentList({day});
console.log("机构列表", res); console.log("机构列表", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
govInsList.value = res.data; govInsList.value = res.data;
...@@ -425,9 +516,6 @@ const handleGetDepartmentList = async () => { ...@@ -425,9 +516,6 @@ const handleGetDepartmentList = async () => {
console.error("获取机构列表error", error); console.error("获取机构列表error", error);
} }
}; };
const handleTimeClick = (time) => {
console.log("time", time);
}
// 跳转行政机构主页 // 跳转行政机构主页
const handleToInstitution = item => { const handleToInstitution = item => {
window.sessionStorage.setItem("curTabName", item.orgName); window.sessionStorage.setItem("curTabName", item.orgName);
...@@ -708,13 +796,19 @@ const chart1Data = ref({ ...@@ -708,13 +796,19 @@ const chart1Data = ref({
dataX: [], dataX: [],
dataY: [] dataY: []
}); });
const box5SelectedYear = ref(yearList[0].value); const box5Params = reactive({
year: yearList[0].value,
domainId: '',
proposeName: '',
})
const handleGetDecreeYearOrder = async () => { const handleGetDecreeYearOrder = async () => {
const params = {
year: box5SelectedYear.value
};
try { try {
const res = await getDecreeYearOrder(params); let { year, domainId, proposeName } = box5Params;
const res = await getDecreeYearOrder({
year,
domainId: domainId || undefined,
orgId: proposeName || undefined
});
console.log("行政令发布频度", res); console.log("行政令发布频度", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
chart1Data.value.dataX = res.data.map(item => { chart1Data.value.dataX = res.data.map(item => {
...@@ -723,19 +817,51 @@ const handleGetDecreeYearOrder = async () => { ...@@ -723,19 +817,51 @@ const handleGetDecreeYearOrder = async () => {
chart1Data.value.dataY = res.data.map(item => { chart1Data.value.dataY = res.data.map(item => {
return item.count; return item.count;
}); });
onChartInterpretation({type:"柱状图",name:"数量变化趋势",data:res.data})
} }
} catch (error) { } catch (error) {
console.error("行政令发布频度error", error); console.error("行政令发布频度error", error);
} }
}; };
// AI智能总结
const onChartInterpretation = async (text) => {
try {
const res = await getChartInterpretation({text});
console.log("AI智能总结", res);
const reader = res.getReader();
const decoder = new TextDecoder();
let fullText = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// 解析 chunk 中的 message 片段
const messages = chunk.split('\n\n').filter(Boolean);
for (const msg of messages) {
if (msg.startsWith('data: ')) {
const data = JSON.parse(msg.slice(6));
if (data.text) fullText += data.text;
try {
const result = JSON.parse(fullText);
renderToPage(result);
} catch (e) {}
}
}
}
console.log("fullText", fullText);
const handleBox5YearChange = val => { } catch (error) {
handleBox5(); console.error("AI智能总结error", error);
}; }
}
const handleBox5 = async () => { const handleBox5 = async () => {
await handleGetDecreeYearOrder(); await handleGetDecreeYearOrder();
let chart1 = getBarChart(chart1Data.value.dataX, chart1Data.value.dataY); let chart1 = getBarChart(chart1Data.value.dataX, chart1Data.value.dataY);
chart1.yAxis.name = "数量";
chart1.yAxis.nameTextStyle = { align: 'right' }
setChart(chart1, "chart1"); setChart(chart1, "chart1");
}; };
...@@ -752,14 +878,17 @@ const chart2Data = ref([ ...@@ -752,14 +878,17 @@ const chart2Data = ref([
]); ]);
// const colorList = ["#69B1FF", "#FFC069", "#87E8DE", "#85A5FF", "#FF7875", "#B37FEB", "#4096FF"]; // const colorList = ["#69B1FF", "#FFC069", "#87E8DE", "#85A5FF", "#FF7875", "#B37FEB", "#4096FF"];
const box6SelectedYear = ref(yearList[0].value); const box6Params = reactive({
year: yearList[0].value,
proposeName: '',
});
const handleGetDecreeArea = async () => { const handleGetDecreeArea = async () => {
const params = {
year: box6SelectedYear.value
};
try { try {
const res = await getDecreeArea(params); let { year, proposeName } = box6Params;
const res = await getDecreeArea({
year,
orgId: proposeName || undefined
});
console.log("政令科技领域", res); console.log("政令科技领域", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
chart2Data.value = res.data.map(item => { chart2Data.value = res.data.map(item => {
...@@ -788,12 +917,23 @@ const keyDecreeList = ref([]); ...@@ -788,12 +917,23 @@ const keyDecreeList = ref([]);
const keyDecreeInfo = reactive({ const keyDecreeInfo = reactive({
total: 0, total: 0,
page: 1, page: 1,
size: 5, size: 4,
})
const box7Params = reactive({
year: yearList[0].value,
domainId: '',
proposeName: '',
}) })
const handleGetKeyDecree = async () => { const handleGetKeyDecree = async () => {
try { try {
const res = await getKeyDecree({pageSize:keyDecreeInfo.size, pageNum:keyDecreeInfo.page-1}); let { year, domainId, proposeName } = box7Params;
const res = await getKeyDecree({
pageSize:keyDecreeInfo.size,
pageNum:keyDecreeInfo.page-1,
year,
domainId: domainId || undefined,
orgId: proposeName || undefined
});
console.log("关键行政令", res); console.log("关键行政令", res);
if (res.code === 200 && res.data?.total) { if (res.code === 200 && res.data?.total) {
keyDecreeInfo.total = res.data.total || 0; keyDecreeInfo.total = res.data.total || 0;
...@@ -809,13 +949,21 @@ const handleGetKeyDecree = async () => { ...@@ -809,13 +949,21 @@ const handleGetKeyDecree = async () => {
} catch (error) { } } catch (error) { }
}; };
handleGetKeyDecree();
// 政令重点条款 // 政令重点条款
const wordCloudData = ref([]); const wordCloudData = ref([]);
const box8Params = reactive({
year: yearList[0].value,
domainId: '',
proposeName: '',
})
const handleGetDecreeKeyInstruction = async () => { const handleGetDecreeKeyInstruction = async () => {
try { try {
const res = await getDecreeKeyInstruction(); let { year, domainId, proposeName } = box8Params;
const res = await getDecreeKeyInstruction({
year,
domainId: domainId || undefined,
orgId: proposeName || undefined
});
console.log("政令重点条款", res); console.log("政令重点条款", res);
wordCloudData.value = res.data.map(item => ({name: item.clause, value: item.count})); wordCloudData.value = res.data.map(item => ({name: item.clause, value: item.count}));
} catch (error) { } catch (error) {
...@@ -911,7 +1059,7 @@ const handlePubTimeChange = (id, checked) => { ...@@ -911,7 +1059,7 @@ const handlePubTimeChange = (id, checked) => {
}; };
const handleAreaChange = (id, checked) => { const handleAreaChange = (id, checked) => {
const allIds = areaList.value.filter(item => item.id !== "all").map(item => item.id); const allIds = areaList.value.map(item => item.id);
if (id === "all") { if (id === "all") {
activeAreaList.value = checked ? ["all", ...allIds] : []; activeAreaList.value = checked ? ["all", ...allIds] : [];
...@@ -940,18 +1088,9 @@ const handleGetAreaList = async () => { ...@@ -940,18 +1088,9 @@ const handleGetAreaList = async () => {
const res = await getDecreehylyList(); const res = await getDecreehylyList();
console.log("行业领域列表", res); console.log("行业领域列表", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
areaList.value = [ areaList.value = res.data;
{ name: "全部领域", id: "all" },
...res.data.map(item => {
return {
name: item.name,
id: item.id
};
})
];
// 设置默认全选 // 设置默认全选
activeAreaList.value = ["all", ...res.data.map(item => item.id)]; activeAreaList.value = ["all", ...res.data.map(item => item.id)];
console.log("areaList", areaList.value);
// 获取列表后重新查询 // 获取列表后重新查询
handleGetDecreeOrderList(); handleGetDecreeOrderList();
} }
...@@ -964,11 +1103,9 @@ const decreeList = ref([]); ...@@ -964,11 +1103,9 @@ const decreeList = ref([]);
// 修改请求方法,处理全选时不传参数的情况 // 修改请求方法,处理全选时不传参数的情况
const handleGetDecreeOrderList = async () => { const handleGetDecreeOrderList = async () => {
const p0 = checkedGovIns.value.join(",");
// 处理科技领域:如果包含 all 或全选,则 researchTypeIds 为空(不传) // 处理科技领域:如果包含 all 或全选,则 researchTypeIds 为空(不传)
let p1 = ""; let p1 = "";
const allAreaIds = areaList.value.filter(item => item.id !== "all").map(item => item.id); const allAreaIds = areaList.value.map(item => item.id);
const selectedAreaIds = activeAreaList.value.filter(id => id !== "all"); const selectedAreaIds = activeAreaList.value.filter(id => id !== "all");
if (!activeAreaList.value.includes("all") && selectedAreaIds.length > 0 && selectedAreaIds.length < allAreaIds.length) { if (!activeAreaList.value.includes("all") && selectedAreaIds.length > 0 && selectedAreaIds.length < allAreaIds.length) {
...@@ -989,11 +1126,9 @@ const handleGetDecreeOrderList = async () => { ...@@ -989,11 +1126,9 @@ const handleGetDecreeOrderList = async () => {
p2 = selectedPubTimeIds.join(","); p2 = selectedPubTimeIds.join(",");
} }
// 其他情况(包含all、长度为0、全部选中)p2保持为空,即不传years // 其他情况(包含all、长度为0、全部选中)p2保持为空,即不传years
console.log(activePubTime.value, "activePubTime.value");
const params = { const params = {
currentPage: currentPage.value, currentPage: currentPage.value,
pageSize: pageSize.value, pageSize: pageSize.value,
proposeName: p0,
researchTypeIds: p1, // 全选时不传(为空) researchTypeIds: p1, // 全选时不传(为空)
sortFun: isSort.value, sortFun: isSort.value,
isCN: isChina.value ? 1 : 0, isCN: isChina.value ? 1 : 0,
...@@ -1057,13 +1192,27 @@ const handleSearch = () => { ...@@ -1057,13 +1192,27 @@ const handleSearch = () => {
window.open(curRoute.href, "_blank"); window.open(curRoute.href, "_blank");
}; };
// 关键机构
const keyOrganizationList = ref([]);
const onKeyOrganization = async () => {
try {
const res = await getKeyOrganization();
console.log("关键机构", res);
if (res.code === 200) {
keyOrganizationList.value = res.data.map(item => ({ orgName:item.orgName, orgId:item.id }));
}
} catch (error) { }
}
onMounted(async () => { onMounted(async () => {
onKeyOrganization();
handleGetDepartmentList(); handleGetDepartmentList();
handleGetNews(); handleGetNews();
handleGetDecreeTypeList(); handleGetDecreeTypeList();
handleGetAreaList(); handleGetAreaList();
handleGetDecreeOrderList(); handleGetDecreeOrderList();
handleBox1(); // 最新科技政令 handleBox1(); // 最新科技政令
handleGetKeyDecree();
handleBox5(); handleBox5();
handleBox6(); handleBox6();
handleGetDecreeKeyInstruction(); handleGetDecreeKeyInstruction();
...@@ -1075,6 +1224,52 @@ onMounted(async () => { ...@@ -1075,6 +1224,52 @@ onMounted(async () => {
box-shadow: none; box-shadow: none;
} }
.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;
}
}
}
.data-origin-box {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 16px 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);
}
}
.home-wrapper { .home-wrapper {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -1948,56 +2143,32 @@ onMounted(async () => { ...@@ -1948,56 +2143,32 @@ onMounted(async () => {
.center-footer { .center-footer {
margin-top: 21px; margin-top: 21px;
height: 452px; height: 460px;
display: flex; display: flex;
gap: 16px;
justify-content: center; justify-content: center;
.box5 { .box5 {
width: 792px; width: 792px;
height: 452px; height: 100%;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
border-radius: 10px; border-radius: 10px;
position: relative; position: relative;
.ai-pane {
position: absolute;
right: 0px;
bottom: 30px;
z-index: 2;
:deep(.ai-pane-wrapper) {
display: none;
}
:deep(.ai-button-wrapper) {
display: flex; display: flex;
} flex-direction: column;
&:hover {
width: 100%;
bottom: 1px;
:deep(.ai-pane-wrapper) {
display: block;
}
:deep(.ai-button-wrapper) {
display: none;
}
}
}
.box5-header { .box5-header {
width: 100%;
height: 48px; height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
.box5-header-left {
display: flex; display: flex;
align-items: center;
.box5-header-icon { .box5-header-icon {
margin-top: 17px;
margin-left: 24px; margin-left: 24px;
width: 17px; width: 17px;
height: 17px; height: 17px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -2005,7 +2176,8 @@ onMounted(async () => { ...@@ -2005,7 +2176,8 @@ onMounted(async () => {
} }
.box5-header-title { .box5-header-title {
margin-top: 11px; width: 20px;
flex: auto;
margin-left: 19px; margin-left: 19px;
height: 26px; height: 26px;
color: var(--color-main-active); color: var(--color-main-active);
...@@ -2016,76 +2188,19 @@ onMounted(async () => { ...@@ -2016,76 +2188,19 @@ onMounted(async () => {
} }
} }
.box5-selectbox {
margin-right: 20px;
margin-top: 8px;
}
}
.box5-main { .box5-main {
height: 397px; flex: auto;
.box5-chart {
height: 397px;
}
}
}
.box6 {
margin-left: 16px;
width: 792px;
height: 452px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
border-radius: 10px;
.box6-header {
margin: 0 auto;
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
position: relative;
.header-icon {
margin-top: 16px;
margin-left: 22px;
width: 20px;
height: 20px; height: 20px;
.box5-chart {
img {
width: 100%;
height: 100%; height: 100%;
} }
} }
.header-title {
margin-top: 11px;
margin-left: 18px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
.box6-selectbox {
position: absolute;
right: 20px;
top: 8px;
}
}
.box6-main {
margin-top: 8px;
height: 390px;
}
} }
} }
.center-footer1 { .center-footer1 {
margin-top: 21px; margin-top: 21px;
height: 452px; height: 460px;
display: flex; display: flex;
justify-content: center; justify-content: center;
...@@ -2095,19 +2210,20 @@ onMounted(async () => { ...@@ -2095,19 +2210,20 @@ onMounted(async () => {
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2); box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
display: flex;
flex-direction: column;
.box7-header { .box7-header {
width: 792px; width: 792px;
height: 48px; height: 48px;
display: flex; display: flex;
align-items: center;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
.header-icon { .header-icon {
width: 22px; width: 22px;
height: 20px; height: 20px;
margin-left: 25px; margin-left: 25px;
margin-top: 15px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -2115,8 +2231,9 @@ onMounted(async () => { ...@@ -2115,8 +2231,9 @@ onMounted(async () => {
} }
.header-title { .header-title {
width: 20px;
flex: auto;
height: 26px; height: 26px;
margin-top: 11px;
margin-left: 13px; margin-left: 13px;
color: var(--color-main-active); color: var(--color-main-active);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
...@@ -2127,13 +2244,13 @@ onMounted(async () => { ...@@ -2127,13 +2244,13 @@ onMounted(async () => {
} }
.box7-main { .box7-main {
height: 20px;
flex: auto;
.box7-list { .box7-list {
height: 354px; padding: 10px 24px 0;
padding: 4px 24px 0;
.box7-item { .box7-item {
// border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex; display: flex;
padding: 6px 0; padding: 10px 0;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: var(--color-bg-hover); background: var(--color-bg-hover);
...@@ -2196,9 +2313,9 @@ onMounted(async () => { ...@@ -2196,9 +2313,9 @@ onMounted(async () => {
} }
} }
} }
:deep(.simple-pagination) {
padding-top: 10px;
} }
:deep(.simple-pagination) {
padding: 0;
} }
} }
...@@ -2216,14 +2333,13 @@ onMounted(async () => { ...@@ -2216,14 +2333,13 @@ onMounted(async () => {
width: 100%; width: 100%;
height: 48px; height: 48px;
display: flex; display: flex;
align-items: center;
border-bottom: 1px solid rgba(240, 242, 244, 1); border-bottom: 1px solid rgba(240, 242, 244, 1);
.header-icon { .header-icon {
width: 22px; width: 22px;
height: 20px; height: 20px;
margin-left: 25px; margin-left: 25px;
margin-top: 15px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -2231,8 +2347,9 @@ onMounted(async () => { ...@@ -2231,8 +2347,9 @@ onMounted(async () => {
} }
.header-title { .header-title {
width: 20px;
flex: auto;
height: 26px; height: 26px;
margin-top: 11px;
margin-left: 13px; margin-left: 13px;
color: var(--color-main-active); color: var(--color-main-active);
font-family: Microsoft YaHei; font-family: Microsoft YaHei;
...@@ -2247,10 +2364,6 @@ onMounted(async () => { ...@@ -2247,10 +2364,6 @@ onMounted(async () => {
height: 20px; height: 20px;
flex: auto; flex: auto;
} }
.box8-main {
height: 401px;
}
} }
} }
} }
......
<template> <template>
<div class="view-box"> <div class="view-box">
<el-empty v-if="!listData?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" /> <el-empty v-if="!listData?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div v-if="listData.length" class="main-content-main"> <div v-if="listData.length" class="main-content-main"
<div class="main-mask"
@wheel.prevent="handleWheel" @wheel.prevent="handleWheel"
@mousedown="handleMouseDown" @mousedown="handleMouseDown"
@mouseup="handleMouseUp" @mouseup="handleMouseUp"
@mouseleave="handleMouseUp" @mouseleave="handleMouseUp"
@mousemove="handleMouseMove" @mousemove="handleMouseMove"
></div> >
<div class="fishbone-container" :style="{ transform: `translate(${translateX}px, ${translateY}px) scale(${scale})`, transformOrigin: 'center center' }"> <div class="fishbone-container" :style="{ transform: `translate(${translateX}px, ${translateY}px) scale(${scale})`, transformOrigin: 'center center' }">
<!-- 主轴上的标签 --> <!-- 主轴上的标签 -->
<div class="main-line" :style="{ width: listData.length * 200 + 300 + 'px' }"> <div class="main-line" :style="{ width: listData.length * 200 + 300 + 'px' }">
...@@ -20,17 +19,25 @@ ...@@ -20,17 +19,25 @@
<div v-for="(causeGroup, groupIndex) in onFilterData(1)" :key="groupIndex" <div v-for="(causeGroup, groupIndex) in onFilterData(1)" :key="groupIndex"
class="top-bone" :style="{ left: groupIndex * 400 + 510 + 'px', height: (causeGroup.children?.length) * 22 + 100 + 'px' }"> class="top-bone" :style="{ left: groupIndex * 400 + 510 + 'px', height: (causeGroup.children?.length) * 22 + 100 + 'px' }">
<div class="left-bone"> <div class="left-bone">
<div class="left-bone-item" v-for="item in getLeftItems(causeGroup.children)" :key="item.id"> <div class="bone-item-box bone-item-end bone-item-top" v-for="item in getLeftItems(causeGroup.children)" :key="item.id">
<img :src="item.image || defaultIcon2" alt="" class="company-icon" /> <div :class="['bone-item-word', {'bone-item-back':item.back}]">
<div class="text" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div> <div class="bone-item-icon">
<div class="line"></div> <img :src="item.image || defaultIcon2" alt="" />
</div>
<div class="bone-item-text one-line-ellipsis" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div>
</div>
<div class="bone-item-line"></div>
</div> </div>
</div> </div>
<div class="right-bone"> <div class="right-bone">
<div class="right-bone-item" v-for="item in getRightItems(causeGroup.children)" :key="item.id"> <div class="bone-item-box bone-item-start bone-item-top" v-for="item in getRightItems(causeGroup.children)" :key="item.id">
<div class="line"></div> <div class="bone-item-line"></div>
<img :src="item.image || defaultIcon2" alt="" class="company-icon" /> <div :class="['bone-item-word', {'bone-item-back':item.back}]">
<div class="text" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div> <div class="bone-item-icon">
<img :src="item.image || defaultIcon2" alt="" />
</div>
<div class="bone-item-text one-line-ellipsis" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -38,17 +45,25 @@ ...@@ -38,17 +45,25 @@
<div v-for="(causeGroup, groupIndex) in onFilterData(0)" :key="groupIndex" <div v-for="(causeGroup, groupIndex) in onFilterData(0)" :key="groupIndex"
class="bottom-bone" :style="{ left: groupIndex * 400 + 310 + 'px', height: (causeGroup.children?.length) * 22 + 100 + 'px' }"> class="bottom-bone" :style="{ left: groupIndex * 400 + 310 + 'px', height: (causeGroup.children?.length) * 22 + 100 + 'px' }">
<div class="left-bone"> <div class="left-bone">
<div class="left-bone-item" v-for="item in getRightItems(causeGroup.children)" :key="item.id"> <div class="bone-item-box bone-item-end bone-item-down" v-for="item in getRightItems(causeGroup.children)" :key="item.id">
<img :src="item.image || defaultIcon2" alt="" class="company-icon" /> <div :class="['bone-item-word', {'bone-item-back':item.back}]">
<div class="text" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div> <div class="bone-item-icon">
<div class="line"></div> <img :src="item.image || defaultIcon2" alt="" />
</div>
<div class="bone-item-text one-line-ellipsis" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div>
</div>
<div class="bone-item-line"></div>
</div> </div>
</div> </div>
<div class="right-bone"> <div class="right-bone">
<div class="right-bone-item" v-for="item in getLeftItems(causeGroup.children)" :key="item.id"> <div class="bone-item-box bone-item-start bone-item-down" v-for="item in getLeftItems(causeGroup.children)" :key="item.id">
<div class="line"></div> <div class="bone-item-line"></div>
<img :src="item.image || defaultIcon2" alt="" class="company-icon" /> <div :class="['bone-item-word', {'bone-item-back':item.back}]">
<div class="text" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div> <div class="bone-item-icon">
<img :src="item.image || defaultIcon2" alt="" />
</div>
<div class="bone-item-text one-line-ellipsis" :style="{color: item.isEntity==1? '#ce4f51' : '#3b414b'}" :title="item.companyName">{{ item.companyName }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -77,12 +92,6 @@ import { ref } from "vue"; ...@@ -77,12 +92,6 @@ import { ref } from "vue";
import defaultIcon2 from "@/assets/icons/default-icon2.png"; import defaultIcon2 from "@/assets/icons/default-icon2.png";
import noticeIcon from "../assets/images/notice-icon.png"; import noticeIcon from "../assets/images/notice-icon.png";
let arr = [
{ id: 1, name: "上游" },
{ id: 2, name: "中游" },
{ id: 3, name: "下游" },
]
const props = defineProps({ const props = defineProps({
baseData: { baseData: {
type: Object, type: Object,
...@@ -174,14 +183,6 @@ const formatRate = (item, key) => { ...@@ -174,14 +183,6 @@ const formatRate = (item, key) => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
.main-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 3;
}
.fishbone-container { .fishbone-container {
position: relative; position: relative;
...@@ -210,13 +211,6 @@ const formatRate = (item, key) => { ...@@ -210,13 +211,6 @@ const formatRate = (item, key) => {
} }
} }
.company-icon {
width: 16px;
height: 16px;
margin: 0 4px;
object-fit: contain;
}
.top-bone { .top-bone {
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
...@@ -236,31 +230,6 @@ const formatRate = (item, key) => { ...@@ -236,31 +230,6 @@ const formatRate = (item, key) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
.left-bone-item {
transform: skew(-30deg);
height: 40px;
margin: 4px 0;
display: flex;
justify-content: flex-end;
align-items: center;
.text {
margin-left: 4px;
height: 25px;
line-height: 25px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
width: 30px;
height: 2px;
background: rgb(230, 231, 232);
}
}
} }
.right-bone { .right-bone {
...@@ -273,32 +242,6 @@ const formatRate = (item, key) => { ...@@ -273,32 +242,6 @@ const formatRate = (item, key) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
.right-bone-item {
transform: skew(-30deg);
height: 40px;
margin: 4px 0;
display: flex;
justify-content: flex-start;
align-items: center;
.text {
max-width: 100px;
margin-right: 4px;
height: 25px;
line-height: 25px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-right: 7px;
width: 30px;
height: 2px;
background: rgb(230, 231, 232);
}
}
} }
} }
...@@ -321,32 +264,6 @@ const formatRate = (item, key) => { ...@@ -321,32 +264,6 @@ const formatRate = (item, key) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
.left-bone-item {
transform: skew(30deg);
height: 40px;
margin: 4px 0;
display: flex;
justify-content: flex-end;
align-items: center;
.text {
margin-left: 4px;
height: 25px;
max-width: 130px;
line-height: 25px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
width: 30px;
height: 2px;
background: rgb(230, 231, 232);
}
}
} }
.right-bone { .right-bone {
...@@ -359,33 +276,70 @@ const formatRate = (item, key) => { ...@@ -359,33 +276,70 @@ const formatRate = (item, key) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
}
}
.right-bone-item { .bone-item-box {
transform: skew(30deg); transform: skew(30deg);
height: 40px; height: 40px;
margin: 4px 0; margin: 4px 0;
display: flex; display: flex;
justify-content: flex-start;
align-items: center; align-items: center;
.bone-item-line {
.line {
margin-right: 7px;
width: 30px; width: 30px;
height: 2px; height: 2px;
background: rgb(230, 231, 232); background: rgb(230, 231, 232);
} }
.bone-item-word {
.text { display: flex;
align-items: center;
height: 30px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
.bone-item-icon {
width: 16px;
height: 16px;
font-size: 0px;
position: relative;
&::after {
content: "";
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.bone-item-text {
max-width: 100px; max-width: 100px;
margin-right: 4px; line-height: 14px;
height: 25px; font-size: 14px;
line-height: 25px; }
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
.bone-item-back {
background-color: #FFF1F0;
border: 1px solid var(--color-red-100);
border-radius: 4px;
} }
.bone-item-start {
justify-content: flex-start;
}
.bone-item-end {
justify-content: flex-end;
}
.bone-item-top {
transform: skew(-30deg);
}
.bone-item-down {
transform: skew(30deg);
} }
} }
......
...@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论