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

fix:科技政令-处理部分bug以及性能优化

上级 ba800b15
......@@ -265,12 +265,13 @@ const onHighlightedText = (text, nounList) => {
const key = `${searchInterval.start}-${searchInterval.end}`;
if (end === searchInterval.end && !pointAdded.has(key)) {
findWordMax.value++;
list.push({ point: true, class: "" });
list.push({ point: findWordMax.value, class: "" });
pointAdded.add(key);
break;
}
}
}
console.log('看看格式', list);
return list;
};
......
......@@ -19,7 +19,7 @@ const decreeRoutes = [
name: "Decree",
component: Decree,
meta: {
title: "科技政令概",
title: "科技政令概",
isShowHeader: true
}
},
......
......@@ -6,19 +6,19 @@
<div class="hard-num text-title-2-show">{{ organizationInfo.total }}</div>
<div style="width: 0px; flex: auto;"></div>
<div class="hard-input">
<el-input v-model="organizationInfo.keyWord" @keyup.enter="onAllOrganization()"
style="width:100%; height:100%;" :suffix-icon="Search" placeholder="搜索机构" />
<el-input v-model="organizationInfo.keyWord" @keyup.enter="onAllOrganization()" @clear="onAllOrganization()"
style="width:100%; height:100%;" :prefix-icon="Search" placeholder="搜索机构" clearable />
</div>
<div class="hard-time">
<el-select v-model="organizationInfo.isSort" @change="onAllOrganization()" placeholder="发布时间"
style="width:160px; margin-left:8px;">
<el-select v-model="organizationInfo.isSort" @change="onAllOrganization()" style="width:100%;">
<template #prefix>
<div class="icon1">
<img v-if="isSort" src="@/assets/icons/shengxu1.png" alt="" />
<img v-else src="@/assets/icons/jiangxu1.png" alt="" />
<img src="@/assets/icons/jiangxu1.png" alt="" />
<!-- <img src="@/assets/icons/shengxu1.png" alt="" /> -->
</div>
</template>
<el-option label="政令数量" :value="1" />
<el-option label="政令数据总量" :value="1" />
<el-option label="政令新增数量" :value="2" />
</el-select>
</div>
</div>
......@@ -30,13 +30,12 @@
<TimeTabPane @time-click="handleDateChange" activeTime="近一年" />
</div>
<div class="organization-list" ref="refOrganization" v-loading="organizationInfo.loading">
<div class="organization-item" v-for="(item, index) in organizationInfo.list" :key="index"
@click="handleToInstitution(item)">
<div class="organization-item" v-for="item in organizationInfo.list" :key="item.orgId">
<div class="item-left">
<img :src="item.orgImage || DefaultIcon2" alt="" />
</div>
<div class="item-right one-line-ellipsis">{{ item.orgName }}</div>
<div class="item-total">{{ item.total }}项</div>
<div class="item-right one-line-ellipsis text-click-hover" @click="handleToInstitution(item)">{{ item.orgName }}</div>
<div class="item-total text-click-hover" @click="handleToDataLibrary(item)">{{ item.total }}项</div>
<el-icon color="var(--color-primary-100)">
<ArrowRightBold />
</el-icon>
......@@ -50,17 +49,15 @@
</div>
</div>
<div class="back-bnt" @click="router.back()">
<el-icon>
<Back />
</el-icon>
<el-icon> <Back /> </el-icon>
<div style="margin-left: 6px;">返回</div>
</div>
</div>
</template>
<script setup name="index">
<script setup>
import { onMounted, reactive, ref } from "vue"
import { Search } from '@element-plus/icons-vue'
import { Search, Back } from '@element-plus/icons-vue'
import router from "@/router";
import TimeTabPane from '@/components/base/TimeTabPane/index.vue';
......@@ -112,9 +109,7 @@ const handleToInstitution = item => {
window.sessionStorage.setItem("curTabName", item.orgName);
const curRoute = router.resolve({
path: "/institution",
query: {
id: item.orgId
}
query: { id: item.orgId }
});
window.open(curRoute.href, "_blank");
// router.push({
......@@ -125,6 +120,15 @@ const handleToInstitution = item => {
// })
};
// 下钻至数据资源库
const handleToDataLibrary = (item) => {
const route = router.resolve({
path: "/dataLibrary/dataDecree",
query: { orgnizationName: item.orgName }
});
window.open(route.href, "_blank");
}
const refOrganization = ref()
onMounted(() => {
// 根据元素的高度决定分页显示的机构数量
......@@ -196,15 +200,14 @@ onMounted(() => {
background-color: var(--el-fill-color-blank);
border-radius: var(--el-border-radius-base);
box-shadow: 0 0 0 1px var(--el-border-color) inset;
box-sizing: border-box;
margin-left: 20px;
width: 160px;
height: 32px;
}
.hard-time {
height: 42px;
padding: 5px 0;
height: 32px;
width: 160px;
margin-left: 8px;
.icon1 {
width: 11px;
......@@ -269,7 +272,6 @@ onMounted(() => {
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
position: relative;
......
......@@ -51,7 +51,7 @@
<div class="center-top">
<OverviewMainBox class="box1" title="最新科技政令" @toDetail="handleClickOrder">
<template #header-icon>
<img style="width: 100%; height: 100%" src="./assets/images/box1-header-icon.png" alt="" />
<img style="width: 100%; height: 100%" src="./assets/images/icon_1596.png" alt="" />
</template>
<div class="box1-left" @click="handleSwithCurDecree('left')">
<div class="icon">
......@@ -77,25 +77,20 @@
</div>
</div>
<div class="box1-main-right">
<div class="box1-main-right-title">
{{ item.name }}
</div>
<div class="box1-main-right-info">
<div class="box1-main-right-title">{{ item.name }}</div>
<div class="box1-main-right-info" v-if="item.industryList?.length">
<AreaTag v-for="(tag, index) in item.industryList" :key="index" :tagName="tag.industryName" />
</div>
<div class="box1-main-right-center">
{{ item.describe }}
</div>
<div class="box1-main-right-center">{{ item.describe }}</div>
<div style="height: 0; flex: auto;"></div>
<div class="box1-main-right-footer">
<div class="footer-left">{{ item.postDate }}</div>
<div class="footer-right">
<div class="footer-right-item1">
{{ item.officialUrl }}
</div>
<!-- <div class="footer-right">
<div class="footer-right-item1">{{ item.officialUrl }}</div>
<div class="footer-right-item2">
<img src="./assets/images/open-icon.png" alt="" />
</div>
</div>
</div> -->
</div>
</div>
</div>
......@@ -114,77 +109,66 @@
</div>
<DivideHeader id="position3" class="divide3" :titleText="'数据总览'"></DivideHeader>
<div class="center-footer">
<div class="box5">
<div class="box5" v-loading="box5Params.loading">
<div class="box5-header">
<div class="box5-header-icon">
<img src="./assets/images/box3-header-icon.png" alt="" />
</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-select @change="getBox5Data" v-model="box5Params.proposeName" :empty-values="[null, undefined]" style="width:150px" filterable>
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName"
:value="item.orgId" />
<el-option v-for="item in govInsList" :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-select @change="getBox5Data" 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 style="margin-right: 20px;">
<el-select @change="handleBox5" v-model="box5Params.year" placeholder="选择时间" style="width:120px">
<el-select @change="getBox5Data" 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-select>
</div>
</div>
<div class="box5-main" v-loading="box5Params.loading">
<div class="box5-chart" id="chart1"></div>
<div class="box5-main" style="padding-top: 16px;">
<div v-if="chart1Data.dataX?.length" class="box5-chart" id="chart1"></div>
<el-empty v-else style="padding-top: 100px;" description="暂无数据" :image-size="100" />
</div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政令数量变化趋势,数据来源:美国各行政机构官网</div>
<div style="padding: 14px 22px;">
<TipTab text="数据来源:美国各行政机构官网" />
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content1" />
</div>
</div>
<div class="box5">
<div class="box5" v-loading="box6Params.loading">
<div class="box5-header">
<div class="box5-header-icon">
<img src="./assets/images/box4-header-icon.png" alt="" />
</div>
<div class="box5-header-title">{{ "领域分布情况" }}</div>
<div style="margin-right: 20px;">
<el-select @change="handleBox6YearChange" v-model="box6Params.proposeName"
:empty-values="[null, undefined]" style="width:150px">
<el-select @change="getBox6Data" v-model="box6Params.proposeName" :empty-values="[null, undefined]" style="width:150px" filterable>
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName"
:value="item.orgId" />
<el-option v-for="item in govInsList" :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-select @change="getBox6Data" 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-select>
</div>
</div>
<div class="box5-main" v-loading="box6Params.loading">
<div class="box5-chart" id="chart2"></div>
<div class="box5-main" style="padding: 16px 22px 0;">
<div v-if="chart2Data.length" class="box5-chart" ref="box6Ref"></div>
<el-empty v-else style="padding-top: 100px;" description="暂无数据" :image-size="100" />
</div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政令领域分布情况,数据来源:美国各行政机构官网</div>
<div style="padding: 14px 22px;">
<TipTab text="数据来源:美国各行政机构官网" />
</div>
<div class="ai-pane">
<AiButton />
......@@ -193,121 +177,111 @@
</div>
</div>
<div class="center-footer1">
<div class="box7">
<div class="box7" v-loading="box7Params.loading">
<div class="box7-header">
<div class="header-icon">
<img src="./assets/images/box5-header-icon.png" alt="" />
</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-select @change="getBox7Data(1)" v-model="box7Params.proposeName" :empty-values="[null, undefined]" style="width:150px" filterable>
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName"
:value="item.orgId" />
<el-option v-for="item in govInsList" :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-select @change="getBox7Data(1)" 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-select @change="getBox7Data(1)" 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 class="box7-main" v-loading="box7Params.loading">
<div class="box7-list">
<div class="box7-item" v-for="(item, index) in keyDecreeList" :key="index"
@click="onNavigateToDetail(item)">
<div class="box7-main">
<div v-if="keyDecreeList?.length" class="box7-list">
<div class="box7-item" v-for="(item, index) in keyDecreeList" :key="index" @click="onNavigateToDetail(item)">
<div class="icon">
<img src="./assets/images/warning.png" alt="" />
</div>
<div class="info">
<div class="info-header">
<div class="title one-line-ellipsis">{{ item.title }}</div>
<div class="time">{{ item.time }}</div>
<div class="title one-line-ellipsis">{{ item.name }}</div>
<div class="time">{{ item.postDate }}</div>
</div>
<div class="info-content one-line-ellipsis">{{ item.content || "暂无数据" }}</div>
<div class="info-content one-line-ellipsis">{{ item.describe || "暂无数据" }}</div>
</div>
</div>
</div>
<el-empty v-else style="padding-top: 100px;" description="暂无数据" :image-size="100" />
</div>
<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>
<SimplePagination v-if="keyDecreeList?.length" v-model:current-page="keyDecreeInfo.page" :page-size="keyDecreeInfo.size" :total="keyDecreeInfo.total" @page-change="getBox7Data" />
<div style="padding: 14px 22px;">
<TipTab text="数据来源:美国各行政机构官网" />
</div>
</div>
<div class="box8">
<div class="box8" v-loading="box8Params.loading">
<div class="box8-header">
<div class="header-icon">
<img src="./assets/images/box5-header-icon.png" alt="" />
</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-select @change="getBox8Data" v-model="box8Params.proposeName" :empty-values="[null, undefined]" style="width:150px" filterable>
<el-option label="全部政府部门" value="" />
<el-option v-for="item in keyOrganizationList" :key="item.orgId" :label="item.orgName"
:value="item.orgId" />
<el-option v-for="item in govInsList" :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-select @change="getBox8Data" 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-select @change="getBox8Data" 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-loading="box8Params.loading">
<div class="box8-content">
<WordCloudChart v-if="wordCloudData?.length" :data="wordCloudData" width="100%" height="100%" />
<el-empty v-else style="padding-top: 100px;" description="暂无数据" :image-size="100" />
</div>
<div class="data-origin-box">
<div class="data-origin-icon">
<img :src="tipsTcon" alt="">
</div>
<div class="data-origin-text">科技政令重点条款词云,数据来源:美国各行政机构官网</div>
<div style="padding: 14px 22px;">
<TipTab text="数据来源:美国各行政机构官网" />
</div>
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content3" />
</div>
</div>
</div>
</div>
<div class="home-main-footer">
<DivideHeader id="position4" class="divide4" :titleText="'科技政令库'"></DivideHeader>
<div class="home-main-footer-header">
<DivideHeader id="position4" class="divide4" :titleText="'科技政令数据库'"></DivideHeader>
<div class="home-main-footer-head">
<div class="search-box">
<el-select v-model="searchType" :empty-values="[null, undefined]" style="width: 100%" filterable>
<el-select v-model="box9Params.name" :empty-values="[null, undefined]" @change="getDecreeList(1)" style="width: 100%" filterable>
<el-option label="全部政府部门" value="" />
<el-option v-for="item in govInsList" :key="item.orgId" :label="item.orgName" :value="item.orgId" />
</el-select>
</div>
<div style="flex: auto;"></div>
<el-checkbox v-model="isChina">只看涉华政令</el-checkbox>
<el-checkbox @change="getDecreeList(1)" v-model="box9Params.isCN">只看涉华政令</el-checkbox>
<div class="select-box">
<el-select v-model="isSort" placeholder="发布时间" style="width:120px; margin-left:8px;">
<el-select v-model="box9Params.sortFun" @change="getDecreeList(1)" style="width:166px; margin-left:8px;">
<template #prefix>
<div class="icon1">
<img v-if="isSort" src="@/assets/icons/shengxu1.png" alt="" />
<img v-if="box9Params.sortFun=='1'" src="@/assets/icons/shengxu1.png" alt="" />
<img v-else src="@/assets/icons/jiangxu1.png" alt="" />
</div>
</template>
<el-option label="正序" :value="true" />
<el-option label="倒序" :value="false" />
<el-option label="发布时间正序" value="1" />
<el-option label="发布时间倒序" value="0" />
</el-select>
</div>
</div>
......@@ -319,13 +293,12 @@
<div class="title">{{ "政令类型" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox v-for="type in decreeTypeList" :key="type.id" v-model="checkedDecreeType"
:label="type.typeId" style="width: 180px" class="filter-checkbox"
@change="handleChangeCheckedDecreeType">
{{ type.typeName }}
<el-checkbox-group class="checkbox-group" v-model="checkedDecreeType" @change="handleTypeChange">
<el-checkbox label="" class="filter-checkbox">{{ "全部类型" }}</el-checkbox>
<el-checkbox v-for="item in decreeTypeList" :key="item.typeId" :label="item.typeId" class="filter-checkbox">
{{ item.typeName }}
</el-checkbox>
</div>
</el-checkbox-group>
</div>
</div>
<div class="select-box">
......@@ -349,6 +322,7 @@
</div>
<div class="select-main">
<el-checkbox-group class="checkbox-group" v-model="activePubTime" @change="handlePubTimeChange">
<el-checkbox label="" class="filter-checkbox">{{ "全部时间" }}</el-checkbox>
<el-checkbox v-for="time in pubTime" :key="time.id" :label="time.id" class="filter-checkbox">
{{ time.name }}
</el-checkbox>
......@@ -356,36 +330,36 @@
</div>
</div>
</div>
<div class="right">
<div class="right" v-loading="box9Params.loading">
<div class="content-header">
<div class="icon">
<img src="./assets/images/footer-header-icon.png" alt="" />
</div>
<div class="title">{{ "政令库" }}</div>
</div>
<div class="content-box" v-show="decreeList">
<div class="content-box">
<el-empty v-if="!decreeList.length" style="padding-top: 160px;" description="暂无数据" :image-size="100" />
<div class="main-item" v-for="(item, index) in decreeList" :key="index" @click="onNavigateToDetail(item)">
<div class="main-item-left">
<div class="left-left">
{{ item.time?.split("-")[0] }}<br />{{ item.time?.split("-")[1] }}月{{
item.time?.split("-")[2]
}}日
{{ item.postDate?.split("-")[0] }}<br />
{{ item.postDate?.split("-")[1] }}月{{ item.postDate?.split("-")[2] }}日
</div>
<div class="left-right">
<div class="icon">
<img :src="item.img ? item.img : DefaultIcon2" alt="" />
<img :src="item.orgImage || DefaultIcon2" alt="" />
</div>
<div class="line" v-if="index !== 9 && index !== totalDecreesNum - 1"></div>
</div>
</div>
<div class="main-item-center">
<div class="center-header">
<div class="title">{{ item.title }}</div>
<!-- <div class="type-box type1">{{ item.type }}</div> -->
<div class="title one-line-ellipsis">{{ `${item.proposeOrgName}: ${item.name}` }}</div>
<div class="type-box type1">{{ '行政命令' }}</div>
</div>
<div class="desc">{{ item.desc }}</div>
<div class="desc">{{ item.describe }}</div>
<div class="tag-box">
<AreaTag v-for="(tag, index) in item.tagList" :key="index" :tagName="tag.industryName" />
<AreaTag v-for="(tag, index) in item.industryList" :key="index" :tagName="tag.industryName" />
</div>
</div>
</div>
......@@ -395,7 +369,7 @@
{{ `共 ${totalDecreesNum} 项` }}
</div>
<div class="footer-right">
<el-pagination @current-change="handleCurrentChange" :pageSize="10" :current-page="currentPage"
<el-pagination @current-change="handleCurrentChange" :pageSize="box9Params.size" :current-page="box9Params.page"
background layout="prev, pager, next" :total="totalDecreesNum" />
</div>
</div>
......@@ -414,7 +388,7 @@
</template>
<script setup>
import { onMounted, ref, watch, nextTick, reactive, computed } from "vue";
import { onMounted, ref, nextTick, reactive, computed } from "vue";
import router from "@/router";
import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import RiskSignalOverviewDetailDialog from "@/components/base/RiskSignalOverviewDetailDialog/index.vue";
......@@ -423,6 +397,7 @@ import SimplePagination from "@/components/SimplePagination.vue";
import SummaryCardsPanel from "@/components/base/SummaryCardsPanel/index.vue";
import AiButton from '@/components/base/Ai/AiButton/index.vue';
import AiPane from '@/components/base/Ai/AiPane/index.vue';
import TipTab from "@/components/base/TipTab/index.vue"
import {
getDepartmentList,
getLatestDecree,
......@@ -441,8 +416,7 @@ import { getNews, getSocialMedia } from "@/api/general/index";
import DivideHeader from "@/components/DivideHeader.vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
import getBarChart from "./utils/barChart";
import getPieChart from "./utils/piechart";
import createPieChart from "@/views/marketAccessRestrictions/utils/basePiechart.js";
import setChart from "@/utils/setChart";
import DefaultIcon2 from "@/assets/icons/default-icon2.png";
......@@ -454,18 +428,14 @@ import { useGotoNewsDetail } from '@/router/modules/news';
const containerRef = ref(null);
const { isShow } = useContainerScroll(containerRef);
const currentPage = ref(1);
const pageSize = ref(10);
// 处理页码改变事件
const handleCurrentChange = page => {
currentPage.value = page;
handleToPosi('position4')
handleGetDecreeOrderList();
getDecreeList(page);
};
// 机构列表
const govInsList = ref([]);
const checkedGovIns = ref([]);
const handleGetDepartmentList = async () => {
try {
const res = await getDepartmentList({});
......@@ -473,9 +443,7 @@ const handleGetDepartmentList = async () => {
if (res.code === 200) {
govInsList.value = res.data.orgList;
}
} catch (error) {
console.error("获取机构列表error", error);
}
} catch (error) {}
};
// 跳转行政机构主页
const handleToInstitution = item => {
......@@ -540,9 +508,7 @@ const handleGetLatestDecree = async () => {
if (res.code === 200 && res.data) {
box1DataList.value = res.data;
}
} catch (error) {
console.error("最新科技政令error", error);
}
} catch (error) {}
};
const handleBox1 = async () => {
......@@ -588,9 +554,7 @@ const handlegetDecreeRiskSignal = async () => {
if (res.code === 200 && res.data) {
warningList.value = res.data.map(item => ({ ...item, id: item.orderId }));
}
} catch (error) {
console.error("风险信号error", error);
}
} catch (error) {}
};
handlegetDecreeRiskSignal();
......@@ -624,9 +588,7 @@ const handleGetNews = async () => {
};
});
}
} catch (error) {
console.error("新闻资讯error", error);
}
} catch (error) {}
};
// 点击新闻条目,跳转到新闻分析页
const gotoNewsDetail = useGotoNewsDetail()
......@@ -738,13 +700,14 @@ const yearList = getNearYearList();
const aiContent = reactive({
content1: "正在生成...",
content2: "正在生成...",
content3: "正在生成...",
})
const onAIReport = (data, key) => {
getAIReport(data).then(res => { aiContent[key] = res })
}
// 行政令发布频度
const chart1Data = ref({
// 数量变化趋势
const chart1Data = reactive({
dataX: [],
dataY: []
});
......@@ -757,121 +720,88 @@ const box5Params = reactive({
const handleGetDecreeYearOrder = async () => {
box5Params.loading = true
try {
let { year, domainId, proposeName } = box5Params;
const res = await getDecreeYearOrder({
year,
domainId: domainId || undefined,
orgId: proposeName || undefined
year: box5Params.year,
domainId: box5Params.domainId || null,
orgId: box5Params.proposeName || null
});
console.log("行政令发布频度", res);
console.log("数量变化趋势", res);
if (res.code === 200 && res.data) {
chart1Data.value.dataX = res.data.map(item => {
return item.year;
});
chart1Data.value.dataY = res.data.map(item => {
return item.count;
});
onAIReport({ type: "柱状图", name: "数量变化趋势", data: res.data }, "content1")
chart1Data.dataX = res.data.map(item => item.year);
chart1Data.dataY = res.data.map(item => item.count);
onAIReport({ type: "柱状图", name: "数量变化趋势", data: chart1Data }, "content1")
} else {
chart1Data.value.dataX = [];
chart1Data.value.dataY = [];
aiContent.content1 = ""
chart1Data.dataX = [];
chart1Data.dataY = [];
aiContent.content1 = "数据为空"
}
} catch (error) {
console.error("行政令发布频度error", error);
chart1Data.dataX = [];
chart1Data.dataY = [];
aiContent.content1 = "数据为空"
}
box5Params.loading = false
};
const handleBox5 = async () => {
const getBox5Data = async () => {
await handleGetDecreeYearOrder();
let chart1 = getBarChart(chart1Data.value.dataX, chart1Data.value.dataY);
let chart1 = getBarChart(chart1Data.dataX, chart1Data.dataY);
chart1.yAxis.name = "数量";
chart1.yAxis.nameTextStyle = { align: 'right' }
let org = '全部机构'
if (box5Params.proposeName) {
org = keyOrganizationList.value.filter(item => {
return item.orgId === box5Params.proposeName
})[0].orgName
}
let domain = '全部领域'
if (box5Params.domainId) {
domain = areaList.value.filter(item => {
return item.id === box5Params.domainId
})[0].name
}
const selectParam = {
moduleType: '政令',
orgnizationName: org,
domains: domain,
selectDate: box5Params.year
}
setChart(chart1, "chart1", true, selectParam);
let org = govInsList.value.find(item => item.orgId === box5Params.proposeName)?.orgName || '全部机构'
let domain = areaList.value.find(item => item.id === box5Params.domainId)?.name || '全部领域'
nextTick(() => {
setChart(chart1, "chart1", true, {
moduleType: '政令',
orgnizationName: org,
domains: domain,
selectDate: box5Params.year
})
})
};
// 政令科技领域
const chart2Data = ref([
// {
// name: "集成电路",
// value: 50
// },
// {
// name: "人工智能",
// value: 46
// },
]);
// const colorList = ["#69B1FF", "#FFC069", "#87E8DE", "#85A5FF", "#FF7875", "#B37FEB", "#4096FF"];
// 领域分布情况
const chart2Data = ref([]);
const box6Params = reactive({
year: yearList[0].value,
proposeName: '',
loading: false,
});
const handleGetDecreeArea = async () => {
const box6Ref = ref(null);
const getBox6Data = async () => {
box6Params.loading = true
try {
let { year, proposeName } = box6Params;
const res = await getDecreeArea({
year,
orgId: proposeName || undefined
year: box6Params.year,
orgId: box6Params.proposeName || null
});
console.log("政令科技领域", res);
console.log("领域分布情况", res);
if (res.code === 200 && res.data) {
chart2Data.value = res.data.map(item => {
return {
name: item.industry,
value: item.count
};
});
onAIReport({ type: "环形图", name: "领域分布情况", data: res.data }, "content2")
chart2Data.value = res.data.map(item => ({name: item.industry, value: item.count}));
onAIReport({ type: "环形图", name: "领域分布情况", data: chart2Data.value }, "content2")
} else {
chart2Data.value = []
aiContent.content2 = ""
aiContent.content2 = "数据为空"
}
} catch (error) {
console.error("政令科技领域error", error);
chart2Data.value = []
aiContent.content2 = "数据为空"
}
box6Params.loading = false
};
const handleBox6 = async () => {
await handleGetDecreeArea();
let org = '全部机构'
if (box6Params.proposeName) {
org = keyOrganizationList.value.filter(item => {
return item.orgId === box6Params.proposeName
})[0].orgName
}
const selectParam = {
moduleType: '政令',
orgnizationName: org,
selectedDate: JSON.stringify([box6Params.year + '-01-01', box6Params.year + '-12-31'])
}
let chart2 = getPieChart(chart2Data.value);
setChart(chart2, "chart2", true, selectParam);
};
const handleBox6YearChange = () => {
handleBox6();
nextTick(() => {
let box6Chart = createPieChart(box6Ref, chart2Data.value)
box6Chart.on('click', (node) => {
const route = router.resolve({
path: "/dataLibrary/dataDecree",
query: {
moduleType: '政令',
orgnizationName: govInsList.value.find(item => item.orgId === box6Params.proposeName)?.orgName || '全部机构',
domains: node.name,
selectedDate: JSON.stringify([`${box6Params.year}-01-01`, `${box6Params.year}-12-31`])
}
});
window.open(route.href, "_blank");
})
})
};
// 关键行政令
......@@ -887,7 +817,8 @@ const box7Params = reactive({
proposeName: '',
loading: false,
})
const handleGetKeyDecree = async () => {
const getBox7Data = async (page) => {
if (page) keyDecreeInfo.page = page
box7Params.loading = true
try {
let { year, domainId, proposeName } = box7Params;
......@@ -899,22 +830,21 @@ const handleGetKeyDecree = async () => {
orgId: proposeName || undefined
});
console.log("关键行政令", res);
if (res.code === 200 && res.data?.total) {
if (res.code === 200 && res.data.list) {
keyDecreeInfo.total = res.data.total || 0;
keyDecreeList.value = res.data.list.map(item => {
return {
title: item.name,
content: item.describe,
time: item.postDate,
id: item.orderId
};
});
keyDecreeList.value = res.data.list;
} else {
keyDecreeInfo.total = 0
keyDecreeList.value = []
}
} catch (error) { }
} catch (error) {
keyDecreeInfo.total = 0
keyDecreeList.value = []
}
box7Params.loading = false
};
// 政令重点条款
// 关键条款词云
const wordCloudData = ref([]);
const box8Params = reactive({
year: yearList[0].value,
......@@ -922,7 +852,7 @@ const box8Params = reactive({
proposeName: '',
loading: false,
})
const handleGetDecreeKeyInstruction = async () => {
const getBox8Data = async () => {
box8Params.loading = true
wordCloudData.value = []
try {
......@@ -932,24 +862,21 @@ const handleGetDecreeKeyInstruction = async () => {
domainId: domainId || undefined,
orgId: proposeName || undefined
});
console.log("政令重点条款", res);
console.log("关键条款词云", res);
if (res.code == 200) {
wordCloudData.value = res.data.map(item => ({ name: item.clause, value: item.count }));
onAIReport({ type: "词云图", name: "关键条款词云", data: wordCloudData.value }, "content3")
} else {
wordCloudData.value = []
aiContent.content1 = "数据为空"
}
} catch (error) {
wordCloudData.value = []
console.error("政令重点条款error", error);
aiContent.content1 = "数据为空"
}
box8Params.loading = false
};
// 资源库
const searchType = ref("");
const isChina = ref(false);
const isSort = ref(false); // true 升序 false 降序
const handleToPosi = id => {
const element = document.getElementById(id);
if (element && containerRef.value) {
......@@ -975,8 +902,7 @@ const handleToPosi = id => {
// 政令类型
const decreeTypeList = ref([]);
const checkedDecreeType = ref([]);
const checkedDecreeType = ref(['']);
const handleGetDecreeTypeList = async () => {
try {
const res = await getDecreeTypeList();
......@@ -984,8 +910,17 @@ const handleGetDecreeTypeList = async () => {
if (res.code === 200 && res.data) {
decreeTypeList.value = res.data;
}
} catch (error) { }
} catch (error) {}
};
const handleTypeChange = async (event) => {
if (event.length && event[event.length-1] !== "") {
checkedDecreeType.value = event.filter(item => item !== "");
} else {
checkedDecreeType.value = [""];
}
getDecreeList(1);
}
// 查看社交媒体详情
const handleToSocialDetail = item => {
const route = router.resolve({
......@@ -996,12 +931,8 @@ const handleToSocialDetail = item => {
});
window.open(route.href, "_blank");
};
const handleChangeCheckedDecreeType = () => {
handleGetDecreeOrderList();
};
const pubTime = ref([
{ id: "", name: "全部时间" },
{ id: "2026", name: "2026年" },
{ id: "2025", name: "2025年" },
{ id: "2024", name: "2024年" },
......@@ -1018,8 +949,7 @@ const handlePubTimeChange = (event) => {
} else {
activePubTime.value = [""];
}
currentPage.value = 1;
handleGetDecreeOrderList();
getDecreeList(1);
};
const activeAreaList = ref([""]);
......@@ -1029,18 +959,10 @@ const handleAreaChange = (event) => {
} else {
activeAreaList.value = [""];
}
currentPage.value = 1;
handleGetDecreeOrderList();
getDecreeList(1);
};
const areaList = ref([
// { id: "人工智能", name: "人工智能" },
// { id: "集成电路", name: "集成电路" },
// { id: "通信网络", name: "通信网络" },
// { id: "量子科技", name: "量子科技" }
]);
const areaList = ref([]);
// 修改获取科技领域列表,添加全选选项
const handleGetAreaList = async () => {
try {
......@@ -1048,8 +970,6 @@ const handleGetAreaList = async () => {
console.log("行业领域列表", res);
if (res.code === 200 && res.data) {
areaList.value = res.data;
// 获取列表后重新查询
handleGetDecreeOrderList();
}
} catch (error) { }
};
......@@ -1058,51 +978,46 @@ const totalDecreesNum = ref(0);
const decreeList = ref([]);
// 修改请求方法,处理全选时不传参数的情况
const handleGetDecreeOrderList = async () => {
// 科技政令库
const box9Params = reactive({
page: 1,
size: 10,
name: '',
sortFun: '0',
isCN: false,
loading: false,
})
const getDecreeList = async (page) => {
if (page) box9Params.page = page;
box9Params.loading = true;
const params = {
currentPage: currentPage.value,
pageSize: pageSize.value,
currentPage: box9Params.page,
pageSize: box9Params.size,
proposeName: box9Params.name,
isCN: box9Params.isCN ? 1 : 0,
sortFun: box9Params.sortFun,
typeIds: checkedDecreeType.value.join(',') || null,
researchTypeIds: activeAreaList.value.join(',') || null,
sortFun: isSort.value,
isCN: isChina.value ? 1 : 0,
proposeName: searchType.value,
years: activePubTime.value.join(',') || null,
typeIds: checkedDecreeType.value.toString()
};
try {
const res = await getDecreeOrderList(params);
console.log("资源库列表", res);
console.log("科技政令库", res);
if (res.code === 200 && res.data) {
totalDecreesNum.value = res.data.totalElements;
decreeList.value = res.data.content.map(item => {
return {
id: item.id,
time: item.postDate,
title: item.proposeOrgName + ": " + item.name,
desc: item.describe,
img: item.orgImage,
tagList: item.industryList
};
});
decreeList.value = res.data.content;
} else {
decreeList.value = [];
totalDecreesNum.value = 0;
}
} catch (error) {
console.error("资源库列表error", error);
decreeList.value = [];
totalDecreesNum.value = 0;
}
box9Params.loading = false;
};
watch([checkedGovIns, isSort, isChina, searchType], val => {
// 切换页码到第一页
currentPage.value = 1;
handleGetDecreeOrderList();
});
// 切换当前政令
const handleSwithCurDecree = name => {
if (name === "left") {
......@@ -1160,12 +1075,12 @@ onMounted(async () => {
handleGetNews();
handleGetDecreeTypeList();
handleGetAreaList();
handleGetDecreeOrderList();
handleBox1(); // 最新科技政令
handleGetKeyDecree();
handleBox5();
handleBox6();
handleGetDecreeKeyInstruction();
getBox5Data();
getBox6Data();
getBox7Data();
getBox8Data();
getDecreeList();
});
</script>
......@@ -1202,31 +1117,6 @@ onMounted(async () => {
}
}
.data-origin-box {
width: 100%;
display: flex;
align-items: center;
padding: 16px 22px;
.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 {
width: 100%;
height: 100%;
......@@ -1638,17 +1528,14 @@ onMounted(async () => {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
.box1-main-right-title {
width: 100%;
// height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
font-weight: bold;
line-height: 30px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
......@@ -1657,10 +1544,10 @@ onMounted(async () => {
}
.box1-main-right-info {
margin-top: 14px;
margin-top: 10px;
display: flex;
height: 24px;
gap: 8px;
flex-wrap: wrap;
}
.box1-main-right-center {
......@@ -1990,8 +1877,6 @@ onMounted(async () => {
.box5-main {
flex: auto;
height: 20px;
padding-top: 16px;
.box5-chart {
height: 100%;
}
......@@ -2136,6 +2021,7 @@ onMounted(async () => {
background: rgba(255, 255, 255, 1);
display: flex;
flex-direction: column;
position: relative;
.box8-header {
width: 100%;
......@@ -2189,10 +2075,10 @@ onMounted(async () => {
margin-bottom: 6px;
}
.home-main-footer-header {
.home-main-footer-head {
width: 1600px;
height: 50px;
margin: 0 auto 16px;
margin: 16px auto 22px;
display: flex;
align-items: center;
......@@ -2334,6 +2220,7 @@ onMounted(async () => {
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
border-radius: 10px;
overflow: hidden;
.content-header {
height: 48px;
......@@ -2365,25 +2252,16 @@ onMounted(async () => {
}
.content-box {
border-bottom: 1px solid rgba(234, 236, 238, 1);
overflow: hidden;
min-height: 790px;
box-sizing: border-box;
min-height: 480px;
.main-item {
display: flex;
width: 100%;
min-height: 100px;
// height: 136px;
// background: orange;
box-sizing: border-box;
padding: 16px 36px 0px 0px;
cursor: pointer;
// &:hover {
// background: var(--color-bg-hover);
// }
.main-item-left {
display: flex;
gap: 18px;
......@@ -2440,30 +2318,29 @@ onMounted(async () => {
gap: 16px;
.title {
width: 20px;
flex: auto;
height: 26px;
line-height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
font-weight: bold;
letter-spacing: 0px;
flex: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.type-box {
flex: none;
height: 28px;
line-height: 28px;
padding: 0 8px;
padding: 0 12px;
text-align: center;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
letter-spacing: 0px;
border-radius: 4px;
border-radius: 14px;
margin-left: 20px;
}
.type1 {
......@@ -2504,8 +2381,8 @@ onMounted(async () => {
}
.footer-box {
margin: 20px 30px;
height: 32px;
border-top: 1px solid rgba(234, 236, 238, 1);
padding: 20px 30px;
display: flex;
justify-content: space-between;
......
......@@ -3,7 +3,7 @@
<div class="box1">
<AnalysisBox title="相关政令" :showAllBtn="false">
<div class="box1-main" v-loading="isLoading">
<el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-empty v-if="!siderList?.length" style="height: 100%;" 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">
......@@ -19,7 +19,7 @@
<div class="box2">
<AnalysisBox title="政令关系挖掘" :showAllBtn="false">
<div style="height: 100%; width: 100%;" v-loading="isLoading">
<el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-empty v-if="!siderList?.length" style="height: 100%;" 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>
......
......@@ -2,7 +2,7 @@
<div class="layout-container">
<!-- 导航菜单 -->
<div class="layout-main">
<div class="header-main">
<div class="layout-head">
<div class="layout-main-header">
<div class="layout-main-header-container">
<div class="layout-main-header-left-box">
......@@ -65,7 +65,7 @@
</div>
</div>
</div>
<div class="layout-main-center">
<div class="layout-down">
<router-view />
</div>
</div>
......@@ -208,68 +208,7 @@ onMounted(() => {
height: 100%;
overflow: hidden;
overflow-y: auto;
.report {
padding: 10px 150px;
position: absolute;
left: 0;
top: 0;
z-index: 999999;
width: 100%;
height: 100%;
background: #f7f8f9;
.report-header {
width: 100%;
height: 50px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 50px;
letter-spacing: 0px;
text-align: left;
padding-left: 30px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.report-main {
display: flex;
height: calc(100% - 100px);
justify-content: space-between;
.left {
width: 800px;
.noContent {
height: 100px;
line-height: 100px;
text-align: center;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
}
}
.right {
width: 800px;
.noContent {
height: 100px;
line-height: 100px;
text-align: center;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
}
}
}
}
background-color: white;
.layout-main {
width: 100%;
......@@ -277,10 +216,10 @@ onMounted(() => {
display: flex;
flex-direction: column;
.header-main {
.layout-head {
width: 100%;
border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
border-bottom: 1px solid rgb(234, 236, 238);
box-shadow: 0 0 20px #1945821a;
background: rgba(255, 255, 255, 1);
}
......@@ -288,7 +227,6 @@ onMounted(() => {
width: 1600px;
height: 137px;
margin: 0 auto;
background: rgba(255, 255, 255, 1);
display: flex;
justify-content: space-between;
position: sticky;
......@@ -526,10 +464,9 @@ onMounted(() => {
}
}
.layout-main-center {
.layout-down {
height: 20px;
flex: auto;
background-color: #f7f8f9;
}
}
}
......
<template>
<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="height: 100%;" description="暂无数据" :image-size="100" />
<div
v-if="listData.length"
class="main-content-main"
......@@ -10,12 +10,8 @@
@mouseleave="handleMouseUp"
@mousemove="handleMouseMove"
>
<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})` }"
>
<!-- 主轴上的标签 -->
<div class="main-line" :style="{ width: listData.length * 200 + 300 + 'px' }">
......@@ -152,18 +148,18 @@
</template>
<script setup name="ChartChain">
import { ref } from "vue";
import { onBeforeUnmount, ref, watch } from "vue";
import defaultIcon2 from "@/assets/icons/default-icon2.png";
import noticeIcon from "../assets/images/notice-icon.png";
const props = defineProps({
baseData: {
type: Object,
default: () => []
type: Array,
default: () => ([])
},
listData: {
type: Array,
default: () => []
default: () => ([])
}
});
......@@ -211,6 +207,15 @@ const handleMouseUp = () => {
};
// #endregion 移动功能处理
// 初始化布局
const watchInfo = watch(() => props.listData, val => {
scale.value = 1;
translateX.value = 0;
translateY.value = 0;
})
onBeforeUnmount(() => { watchInfo() });
// 奇数索引的数据组放在上方, 偶数索引的数据组放在下方
const onFilterData = num => {
return props.listData.filter((_, index) => index % 2 === num);
......@@ -247,9 +252,14 @@ const formatRate = (item, key) => {
align-items: center;
justify-content: center;
overflow: hidden;
-webkit-user-select: none; /* Safari/Chrome */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none; /* 标准语法 */
.fishbone-container {
position: relative;
transform-origin: center center;
.main-line {
height: 3px;
......
......@@ -16,7 +16,7 @@
</div>
<div class="data-title">实体名称</div>
<div style="height: 20px; flex: auto;">
<el-empty v-if="!entityInfo.list?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-empty v-if="!entityInfo.list?.length" style="height: 100%;" description="暂无数据" :image-size="100" />
<el-scrollbar height="100%" always>
<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="headerChartData(item)">
......@@ -81,7 +81,7 @@
</div>
<div class="graph-box" v-if="contentType==2">
<GraphChart v-if="graphInfo.nodes?.length" :nodes="graphInfo.nodes" :links="graphInfo.links" layoutType="force" />
<el-empty v-else style="padding: 60px 0" description="暂无数据" :image-size="100" />
<el-empty v-else style="height: 100%;" description="暂无数据" :image-size="100" />
</div>
</div>
</AnalysisBox>
......@@ -234,7 +234,6 @@ const onDecreeChainNodes = async (id) => {
}
})
fishbone.list = Object.values(obj);
console.log("fishbone.list:", fishbone.list);
fishbone.base = res.data.levelInfos.map((item, index) => {
return {...item, name: ['上游', '中游', '下游'][index]}
});
......@@ -448,10 +447,13 @@ onMounted(() => {
:deep(.header-icon) {
display: none;
}
:deep(.wrapper-header) {
padding-top: 8px;
}
.custom-title {
display: flex;
justify-content: space-between;
align-items: flex-end;
align-items: center;
width: 100%;
height: 100%;
padding: 0 20px;
......
......@@ -5,10 +5,8 @@
<AnalysisBox title="提出背景" :showAllBtn="false">
<template #header-btn>
<div class="header-btn-box">
<div class="btn" :class="{ btnActive: box1ActiveBtn === item }" v-for="(item, index) in box1BtnList"
:key="index" @click="handleClickBox1Btn(item)">
{{ item }}
</div>
<div class="btn" :class="{ btnActive: box1ActiveBtn === 1 }" @click="handleClickBox1Btn(1)">涉华背景</div>
<div class="btn" :class="{ btnActive: box1ActiveBtn === 2 }" @click="handleClickBox1Btn(2)">全部背景</div>
</div>
</template>
<div class="box1-container">
......@@ -17,9 +15,9 @@
<div class="box1-item" v-for="(item, index) in backgroundList" :key="index">
<div class="id">{{ index + 1 }}</div>
<div class="title text-align-justify">{{ item.content }}</div>
<div class="open">
<!-- <div class="open">
<img src="./assets/images/open-icon.png" alt="" />
</div>
</div> -->
</div>
</div>
<div class="box1-footer" v-if="backgroundListNum > 10">
......@@ -32,15 +30,15 @@
</div>
</AnalysisBox>
</div>
<div class="box2">
<div class="box2" v-loading="box2Params.loading">
<AnalysisBox title="法律依据" :showAllBtn="false">
<el-empty v-if="!dependList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-empty v-if="!box2Params.list?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div class="box2-main">
<div class="custom-collapse">
<el-collapse v-model="dependActive">
<el-collapse-item v-for="(item, index) in dependList" :key="item.billId" :name="item.billId">
<el-collapse v-model="box2Params.active">
<el-collapse-item v-for="(item, index) in box2Params.list" :key="item.billId" :name="item.billId">
<template #icon>
<el-icon v-if="dependActive.includes(item.billId)">
<el-icon v-if="box2Params.active.includes(item.billId)">
<ArrowDownBold />
</el-icon>
<el-icon v-else>
......@@ -92,7 +90,7 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import { getDecreeBackground, getDecreeDepend, getDecreePrev } from "@/api/decree/background";
import router from "@/router";
......@@ -104,8 +102,7 @@ const route = useRoute();
const decreeId = ref(route.query.id);
// 提出背景
const box1BtnList = ref(["涉华背景", "全部背景"]);
const box1ActiveBtn = ref("涉华背景");
const box1ActiveBtn = ref(1);
const handleClickBox1Btn = btn => {
box1ActiveBtn.value = btn;
handleCurrentChange(1)
......@@ -126,7 +123,7 @@ const handleCurrentChange = page => {
};
const handleGetBackground = async () => {
const params = {
cRelated: box1ActiveBtn.value === "涉华背景" ? true : false,
cRelated: box1ActiveBtn.value === 1,
currentPage: currentPage.value - 1,
pageSize: 10,
id: decreeId.value
......@@ -144,7 +141,6 @@ const handleGetBackground = async () => {
} catch (error) {
backgroundListNum.value = 0;
backgroundList.value = [];
console.error("获取提出背景数据失败", error);
}
};
......@@ -167,7 +163,6 @@ const handleGetPrev = async () => {
}
} catch (error) {
prevList.value = [];
console.error("获取前序政令数据失败", error);
}
};
// 跳转行政机构主页
......@@ -205,22 +200,25 @@ const handleClickDecree = item => {
};
// 法律依据
const dependList = ref([]);
const dependActive = ref([]);
const box2Params = reactive({
loading: false,
active: [],
list: [],
})
const handleGetLaws = async () => {
box2Params.loading = true;
try {
const res = await getDecreeDepend({ id: decreeId.value });
console.log("法律依据", res);
if (res.code === 200 && res.data) {
dependList.value = res.data;
// dependActive.value = res.data.map(item => item.billId);
box2Params.list = res.data;
} else {
dependList.value = [];
box2Params.list = [];
}
} catch (error) {
dependList.value = [];
console.error("获取法律依据数据失败", error);
box2Params.list = [];
}
box2Params.loading = false;
};
// 跳转科技法案详情页
const handleClickBull = decree => {
......@@ -290,10 +288,11 @@ onMounted(() => {
}
.box1-main {
width: 1034px;
width: 100%;
padding: 0 6px;
.box1-item {
width: 1015px;
width: 100%;
min-height: 48px;
font-family: Microsoft YaHei;
font-size: var(--font-size-base);
......@@ -302,11 +301,10 @@ onMounted(() => {
background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
padding: 18px 0;
padding: 18px 16px;
.id {
margin-right: 16px;
margin-left: 15px;
width: 24px;
height: 24px;
text-align: center;
......@@ -357,9 +355,8 @@ onMounted(() => {
}
.box2 {
.box2-main {
padding: 16px 20px;
padding: 10px 24px 16px;
.custom-collapse {
padding-left: 32px;
......@@ -371,6 +368,8 @@ onMounted(() => {
:deep(.el-collapse-item__header) {
border-bottom: 1px solid rgba(234, 236, 238, 1);
min-height: 50px;
line-height: 50px;
}
:deep(.el-collapse-item__content) {
......@@ -384,18 +383,19 @@ onMounted(() => {
font-family: Microsoft YaHei;
font-size: var(--font-size-base);
position: absolute;
top: 12px;
top: 13px;
left: -32px;
width: 24px;
height: 24px;
text-align: center;
line-height: 24px;
text-align: center;
border-radius: 50%;
background: #e7f3ff;
color: #0a57a6;
}
.custom-collapse-name {
padding-left: 6px;
font-weight: 600;
font-size: 18px;
color: var(--el-collapse-header-text-color);
......
......@@ -70,7 +70,6 @@ onMounted(() => {
.decree-overview-wrap {
width: 100%;
overflow: hidden;
background: rgba(247, 248, 249, 1);
.main {
position: relative;
width: 1600px;
......
......@@ -145,7 +145,7 @@
<div class="box3-bottom-main">
<el-timeline style="max-width: 500px">
<el-timeline-item :timestamp="item.postDate" placement="top" v-for="(item, index) in eventList?.slice(0, 3)" :key="index">
<div class="timeline-content">{{ item.describe }}</div>
<div class="timeline-content text-align-justify">{{ item.describe }}</div>
</el-timeline-item>
</el-timeline>
</div>
......@@ -789,6 +789,12 @@ onMounted(() => {
font-size: 14px;
font-weight: 400;
line-height: 26px;
max-height: 78px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
......
<template>
<div class="introduction-wrap">
<div class="page-left">
<div class="box1">
<div class="box1" v-loading="box1Params.loading">
<AnalysisBox title="主要指令" :showAllBtn="false">
<template #header-btn>
<div class="mind-bnt" @click="onDecreeMindMap()">
......@@ -23,18 +23,18 @@
<div style="margin-left: 6px;">高亮实体</div> -->
<div class="select-input">
<el-input v-model="commandWord" @keyup.enter="onMainContentData()" style="width: 100%; height: 100%;"
:suffix-icon="Search" placeholder="指令搜索" />
:prefix-icon="Search" placeholder="指令搜索" clearable @clear="onMainContentData()" />
</div>
</div>
<div class="analysis-content">
<!-- 遍历文档章节 -->
<el-empty v-if="!contentList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-empty v-if="!contentList?.length" style="padding: 80px 0 96px;" description="暂无数据" :image-size="100" />
<div v-for="(section, index) in contentList" :key="index" class="section">
<div class="section-header">
<div class="section-title text-align-justify" v-html="section.content"></div>
<div class="section-icon">
<!-- <div class="section-icon">
<img src="./assets/images/open-icon.png" alt="" />
</div>
</div> -->
</div>
<!-- 渲染一级列表 -->
<div class="numbered-list">
......@@ -67,22 +67,21 @@
</div>
</div>
<div class="page-right">
<div class="box3">
<div class="box3" v-loading="orgInfo.loading">
<AnalysisBox title="执行机构" :showAllBtn="false">
<div class="box3-top">
<div class="box3-top" v-if="orgInfo.list?.length">
<div class="organization-list">
<div class="organization-item" v-for="item in organizationInfo.list" :key="item.id">
<ActionButton @click="handleOrganization(item)" :name="item.obb"
:type="item.id == organizationInfo.node.id ? 'active' : 'normal'" />
<div class="organization-item" v-for="item in orgInfo.list" :key="item.id">
<ActionButton @click="handleOrganization(item)" :name="item.obb" :type="item.id == orgInfo.node.id ? 'active' : 'normal'" />
</div>
</div>
<div class="box3-top-top" @click="handleToInstitution()">
<div class="box3-top-top">
<div class="left">
<img :src="organizationInfo.node.logo || DefaultIcon2" alt="" />
<img :src="orgInfo.node.logo || DefaultIcon2" alt="" />
</div>
<div class="right">
<div class="name">{{ organizationInfo.node.name + " >" }}</div>
<div class="ename">{{ organizationInfo.node.ename }}</div>
<div class="name text-click-hover" @click="handleToInstitution()">{{ orgInfo.node.name + " >" }}</div>
<div class="ename">{{ orgInfo.node.ename }}</div>
</div>
</div>
<div class="box3-top-bottom">
......@@ -93,7 +92,8 @@
<div class="text">{{ "关键人物" }}</div>
</div>
<div class="box3-top-bottom-main">
<div class="box3-top-bottom-item" v-for="(item, index) in organizationInfo.node.leaders" :key="index">
<el-empty v-if="!orgInfo.node.leaders?.length" style="width:100%; height:100%;" description="暂无数据" :image-size="60" />
<div class="box3-top-bottom-item" v-for="(item, index) in orgInfo.node.leaders" :key="index">
<div class="box3-top-bottom-item-left">
<img :src="item.avatar || DefaultIcon1" alt="" />
</div>
......@@ -112,32 +112,34 @@
</el-icon>
</div>
</div>
<el-empty v-else style="padding: 98px 0 138px;" description="暂无数据" :image-size="100" />
</AnalysisBox>
</div>
<div class="box4">
<div class="box4" v-loading="box4Params.loading">
<AnalysisBox title="相关实体" :showAllBtn="false">
<el-empty v-if="!entityList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<div class="left-bottom-main">
<div v-for="(item, index) in entityList" :key="index" class="main-box">
<div v-if="box4Params.list?.length" class="left-bottom-main">
<div v-for="(item, index) in box4Params.list" :key="index" class="main-box">
<div class="icon">
<img style="width: 100%; height: 100%;" :src="item.imgUrl || defaultCom" alt="" />
</div>
<div class="name one-line-ellipsis">{{ item.name }}</div>
<div class="name one-line-ellipsis text-click-hover" @click="handleSearch(item)">{{ item.name }}</div>
<div class="type">{{ item.entityType }}</div>
</div>
</div>
<el-empty v-else style="padding: 60px 0 100px;" description="暂无数据" :image-size="100" />
</AnalysisBox>
</div>
</div>
<el-dialog v-model="isTreeDialog" width="1400px" top="8vh" class="viewpoint-dialog" destroy-on-close>
<el-dialog v-model="mindParams.isShow" width="1400px" top="8vh" class="viewpoint-dialog" destroy-on-close>
<template #header>
<div class="viewpoint-header">
<div class="viewpoint-title">政令举措思维导图</div>
</div>
</template>
<div class="viewpoint-body">
<MindGraph ref="refMindGraph"></MindGraph>
<div class="viewpoint-body" v-loading="mindParams.loading">
<MindGraph v-if="mindParams.isData" ref="refMindGraph"></MindGraph>
<el-empty v-else style="height: 100%;" description="暂无数据" :image-size="100" />
</div>
</el-dialog>
</div>
......@@ -179,7 +181,6 @@ const handleGetAreaList = async () => {
}
} catch (error) {
areaList.value = []
console.error("获取科技领域数据失败:", error);
}
};
......@@ -188,7 +189,11 @@ const isHighlight = ref(false);
const commandWord = ref("");
const contentList = ref([]);
const ALPHABET = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
const box1Params = reactive({
loading: false,
})
const onMainContentData = async () => {
box1Params.loading = true;
try {
const keyword = commandWord.value;
const res = await getDecreeMainContent({ id: route.query.id, keyword, domainId: areaType.value });
......@@ -205,8 +210,8 @@ const onMainContentData = async () => {
}
} catch (error) {
contentList.value = []
console.error("获取主要指令数据失败:", error);
}
box1Params.loading = false;
};
// 搜索高亮效果
const onHighlight = (word, row) => {
......@@ -219,9 +224,15 @@ const onHighlight = (word, row) => {
}
// 思维导图
const isTreeDialog = ref(false);
const refMindGraph = ref(null);
const mindParams = reactive({
isData: true,
loading: false,
isShow: false,
})
const onDecreeMindMap = async () => {
mindParams.loading = true;
mindParams.isData = true;
let labelCfg = {
position: 'left',
offset: -20,
......@@ -232,11 +243,11 @@ const onDecreeMindMap = async () => {
autoWrap: true
}
}
isTreeDialog.value = true;
mindParams.isShow = true;
try {
let res = await getDecreeMindMap({ id: route.query.id });
console.log("思维导图", res);
if (res.code === 200) {
if (res.code === 200 && res.data) {
let nodes = []
let edges = []
let obj = {}
......@@ -256,12 +267,14 @@ const onDecreeMindMap = async () => {
edges.push({ id: `edge-${index}`, source: `root-virtual`, target: `node-${item.orderNum}-${item.sectionId}` })
}
})
setTimeout(() => { refMindGraph.value.onMindGraphData(nodes, edges) }, 100)
} else {
mindParams.isData = false
}
} catch (error) {
console.error("获取思维导图数据失败:", error);
mindParams.isData = false
}
mindParams.loading = false
}
// 对象数组去重
......@@ -277,50 +290,64 @@ const onUniqueArray = (list, key = 'id') => {
}
// 相关实体
const entityList = ref([]);
const box4Params = reactive({
loading: false,
list: [],
})
const onRelatedEntityData = async () => {
box4Params.loading = true;
try {
const res = await getDecreeRelatedEntity({ id: route.query.id });
console.log("相关实体", res);
if (res && res.code === 200) {
entityList.value = res.data;
box4Params.list = res.data;
} else {
entityList.value = []
box4Params.list = []
}
} catch (error) {
entityList.value = []
console.error("获取相关实体数据失败:", error);
box4Params.list = []
}
box4Params.loading = false;
};
const handleSearch = (item) => {
window.sessionStorage.setItem("curTabName", `搜索-${item.name}`);
const curRoute = router.resolve({
path: "/searchResults",
query: {searchText: item.name}
});
window.open(curRoute.href, "_blank");
};
// 执行机构
const organizationInfo = reactive({
const orgInfo = reactive({
loading: false,
list: [],
node: { id: "", obb: "", logo: "", name: "", ename: "", leaders: [] },
})
const handleGetOrgnization = async () => {
orgInfo.loading = true
try {
const res = await getDecreeOrganization({ id: route.query.id });
console.log("执行机构", res);
if (res.code === 200 && res.data?.length) {
organizationInfo.list = res.data;
organizationInfo.node = res.data[0];
orgInfo.list = res.data;
orgInfo.node = res.data[0];
}
} catch (error) {
organizationInfo.node = { id: "", obb: "", logo: "", name: "", ename: "", leaders: [] };
console.error("获取执行机构数据失败", error);
orgInfo.node = { id: "", obb: "", logo: "", name: "", ename: "", leaders: [] };
}
orgInfo.loading = false;
};
// 切换执行机构
const handleOrganization = (node) => {
organizationInfo.node = node;
orgInfo.node = node;
};
// 跳转机构主页
const handleToInstitution = () => {
const curRoute = router.resolve({
path: "/institution",
query: {
id: organizationInfo.node.id
id: orgInfo.node.id
}
});
window.open(curRoute.href, "_blank");
......@@ -450,6 +477,9 @@ onMounted(() => {
letter-spacing: 1px;
width: 20px;
flex: auto;
font-weight: bold;
color: var(--text-primary-80-color);
font-family: Source Han Sans CN;
}
.section-icon {
......@@ -468,7 +498,9 @@ onMounted(() => {
margin-bottom: 12px;
font-size: 16px;
line-height: 1.7;
color: #374151;
color: var(--text-primary-80-color);
font-family: Source Han Sans CN;
font-size: 16px;
.list-item {
position: relative;
......@@ -542,7 +574,7 @@ onMounted(() => {
display: flex;
flex-wrap: wrap;
margin-bottom: 16px;
gap: 8px 16px;
gap: 8px 8px;
}
.organization-button {
......@@ -571,7 +603,6 @@ onMounted(() => {
background: rgba(247, 248, 249, 1);
display: flex;
align-items: center;
cursor: pointer;
.left {
width: 64px;
......@@ -798,7 +829,6 @@ onMounted(() => {
padding: 0 15px;
display: flex;
align-items: center;
cursor: pointer;
.icon {
width: 24px;
......
......@@ -80,7 +80,7 @@
<DivideHeader id="position3" class="divide-header" :titleText="'数据总览'"></DivideHeader>
<div class="center-footer">
<div class="box5">
<OverviewNormalBox title="数量变化趋势">
<OverviewNormalBox title="数量变化趋势" width="auto" height="100%">
<template #header-icon>
<img style="width: 100%; height: 100%;" src="./assets/icons/icon2.svg" alt="" />
</template>
......@@ -97,7 +97,7 @@
<el-empty v-if="!box5ChartData.title.length" description="暂无数据" style="padding: 100px 0 0;" :image-size="100" />
<div v-if="box5ChartData.title.length" style="width: 100%; height: 100%;" ref="box5Ref"></div>
</div>
<TipTab text="美对华发起调查案件数量变化趋势,数据来源:美国国际贸易委员会、商务部、贸易代表办公室官网" style="margin-top: 16px;" />
<TipTab text="数据来源:美国国际贸易委员会、商务部、贸易代表办公室官网" style="margin-top: 16px;" />
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content5" />
......@@ -106,7 +106,7 @@
</OverviewNormalBox>
</div>
<div class="box6">
<OverviewNormalBox title="领域分布情况" width="521px">
<OverviewNormalBox title="领域分布情况" width="auto" height="100%">
<template #header-icon>
<img style="width: 100%; height: 100%;" src="./assets/icons/icon3.svg" alt="" />
</template>
......@@ -122,7 +122,7 @@
<el-empty v-if="!box6Data.title.length" description="暂无数据" style="padding: 100px 0 0;" :image-size="100" />
<div v-if="box6Data.title.length" style="width: 100%; height: 100%;" id="box6Chart"></div>
</div>
<TipTab text="美对华发起调查案件领域分布情况,数据来源:美国国际贸易委员会、商务部、贸易代表办公室官网" ellipsis style="padding-right: 50px;" />
<TipTab text="数据来源:美国国际贸易委员会、商务部、贸易代表办公室官网" style="padding-right: 50px;" />
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content6" />
......@@ -133,7 +133,7 @@
</div>
<div class="center-footer1">
<div class="box7">
<OverviewNormalBox title="国家分布情况" width="1064px">
<OverviewNormalBox title="国家分布情况" width="auto" height="100%">
<template #header-icon>
<img style="width: 100%; height: 100%;" src="./assets/icons/icon4.svg" alt="" />
</template>
......@@ -154,7 +154,7 @@
<el-empty v-if="!box7Data.data.length" description="暂无数据" style="padding: 100px 0 0;" :image-size="100" />
<div v-if="box7Data.data.length" style="width: 100%; height: 100%;" id="box7Chart"></div>
</div>
<TipTab :text="`美发起调查案件的被调查国家分布情况,数据来源:${box7TipText}`" style="margin-top: 10px;" />
<TipTab :text="`数据来源:${box7TipText}`" style="margin-top: 10px;" />
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content7" />
......@@ -163,7 +163,7 @@
</OverviewNormalBox>
</div>
<div class="box8">
<OverviewNormalBox title="结果分布情况" width="521px">
<OverviewNormalBox title="结果分布情况" width="auto" height="100%">
<template #header-icon>
<img style="width: 100%; height: 100%" src="./assets/icons/icon5.svg" alt="" />
</template>
......@@ -179,7 +179,7 @@
<el-empty v-if="!box8Data.length" description="暂无数据" style="padding: 100px 0 0;" :image-size="100" />
<div v-if="box8Data.length" style="width: 100%; height: 100%;" ref="box8Ref"></div>
</div>
<TipTab :text="`美发起调查案件的被调查国家分布情况,数据来源:${box8TipText}`" ellipsis style="padding-right: 50px;" />
<TipTab :text="`数据来源:${box8TipText}`" style="padding-right: 50px;" />
<div class="ai-pane">
<AiButton />
<AiPane :aiContent="aiContent.content8" />
......@@ -304,7 +304,7 @@ import { getDateBefore, getAIReport, getNearYearList } from "@/views/marketAcces
import router from "@/router";
import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import createLineChart from "@/views/marketAccessRestrictions/utils/baseLineChart";
import createLineChart from "@/views/marketAccessRestrictions/utils/baseLineChart.js";
import createPieChart from "@/views/marketAccessRestrictions/utils/basePiechart.js";
import getRadarChart from "./utils/radarChart";
import getBarChart from "./utils/barChart1";
......@@ -863,7 +863,7 @@ const handleGetBox8Data = async () => {
box8Data.value = []
aiContent.content8 = "";
}
nextTick(() => { createPieChart(box8Ref, box8Data.value, {labelType:1}) })
nextTick(() => { createPieChart(box8Ref, box8Data.value) })
};
// 资源库
......@@ -1814,15 +1814,16 @@ onMounted(async () => {
}
.center-footer {
margin-top: 21px;
margin: 21px auto 0;
height: 460px;
display: flex;
justify-content: center;
gap: 15px;
gap: 16px;
width: 1600px;
.box5 {
width: 1064px;
height: 460px;
width: 20px;
flex: auto;
height: 100%;
.box-header-right {
height: 48px;
......@@ -1865,8 +1866,9 @@ onMounted(async () => {
}
.box6 {
width: 521px;
height: 460px;
width: 20px;
flex: auto;
height: 100%;
.box-header-right {
height: 48px;
......@@ -1879,14 +1881,16 @@ onMounted(async () => {
}
.center-footer1 {
margin-top: 16px;
margin: 16px auto 0;
height: 460px;
display: flex;
justify-content: center;
gap: 15px;
gap: 16px;
width: 1600px;
.box7 {
width: 1064px;
height: 460px;
width: 20px;
flex: auto;
height: 100%;
.box-header-right {
height: 48px;
......@@ -1903,8 +1907,9 @@ onMounted(async () => {
}
.box8 {
width: 521px;
height: 460px;
width: 20px;
flex: auto;
height: 100%;
.box-header-right {
height: 48px;
......
import * as echarts from 'echarts'
import { MUTICHARTCOLORS } from '@/common/constant'
const truncateLabel = (value, maxLen = 6) => {
if (value === null || value === undefined) return ''
const str = String(value)
const chars = Array.from(str)
if (chars.length <= maxLen) return str
return `${chars.slice(0, maxLen).join('')}...`
}
const formatLabel = (node, type) => {
if (type==1) {
const name = truncateLabel(node.name, 6)
return `{name|${name}}\n{time|${ node.percent||0}%}`
}
return `{name|${node.name}} {time| ${node.value}${ node.percent||0}%}\n`
}
const createPieChart = (chartDom, data=[], option={}) => {
if (!chartDom.value) return;
......@@ -46,21 +30,21 @@ const createPieChart = (chartDom, data=[], option={}) => {
},
label: {
alignTo: 'edge',
formatter: (node) => formatLabel(node, option.labelType),
formatter: (node) => `{name|${node.name}}\n{time|${node.value}${ node.percent||0}%}`,
minMargin: 5,
edgeDistance: 10,
lineHeight: 22,
lineHeight: 26,
rich: {
name: {
color: 'rgba(59, 65, 75, 1)',
fontFamily: 'Microsoft YaHei',
fontFamily: 'Source Han Sans CN',
fontSize: 16,
fontWeight: 'bold',
padding: [10, 0, 10, 0]
},
time: {
color: 'rgba(95, 101, 108, 1)',
fontFamily: 'Microsoft YaHei',
fontFamily: 'Source Han Sans CN',
fontSize: 16,
padding: [10, 0, 10, 0]
}
......
......@@ -61,51 +61,51 @@ export const getNearYearList = (num=6) => {
* AI智能总结
* @param data 需要分析的数据
*/
export const getAIReport = async (data:any) => {
let word = ""
export const getAIReport = async (data: any, timeoutMs: number = 10000): Promise<string> => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
// 👇 新增:超时 + 终止请求(只加这一段)
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10*1000); // 10秒超时
try {
const res = await fetch('/aiAnalysis/chart_interpretation', {
method: 'POST',
headers: {
"X-API-Key": "aircasKEY19491001",
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: JSON.stringify(data) }),
signal: controller.signal
});
try {
const res = await fetch('/aiAnalysis/chart_interpretation', {
method: 'POST',
headers: {
"X-API-Key": "aircasKEY19491001",
'Content-Type': 'application/json',
},
body: JSON.stringify({text: JSON.stringify(data)}),
signal: controller.signal // 👇 新增:绑定中断信号
});
clearTimeout(timeoutId);
clearTimeout(timeout); // 👇 新增:请求成功清除定时器
if (!res.ok) throw new Error(`HTTP 错误 ${res.status}`);
const reader = res.body.getReader();
const decoder = new TextDecoder();
if (!res.ok) throw new Error(`HTTP ${res.status}`);
let buffer = '';
let summarize = '';
const reader = res.body.getReader();
const decoder = new TextDecoder();
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 添加读取超时
const readTimeout = setTimeout(() => {
reader.cancel();
}, timeoutMs);
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const content = line.substring(6);
const textMatch = content.match(/"解读":\s*"([^"]*)"/);
if (textMatch && textMatch[1]) summarize = textMatch[1];
}
}
}
word = summarize
} catch (err) {
word = "系统异常,生成失败";
}
return word
}
\ No newline at end of file
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const match = chunk.match(/"解读":\s*"([^"]*)"/);
if (match?.[1]) {
result = match[1];
break; // 获取到结果立即结束
}
}
} finally {
clearTimeout(readTimeout);
reader.cancel(); // 确保连接关闭
}
return result || "未获取到解读内容";
} catch (err) {
return "系统异常,生成失败";
}
};
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论