提交 5d3fed98 authored 作者: 朱政's avatar 朱政

feat:风险信号页面dialog开发,智库点击风险信号列表dialog开发

上级 bc0455d7
流水线 #408 已通过 于阶段
in 1 分 29 秒
/** 从各概览页跳转风险信号管理页,并自动打开列表第一条详情的 el-dialog */
export const OPEN_FIRST_RISK_DETAIL_QUERY_KEY = "openFirstDetail";
/**
* 概览页 -> 风险信号管理页(新页面打开,不弹窗)
* @param {import('vue-router').Router} router
*/
export const navigateToViewRiskSignal = (router) => {
const route = router.resolve({
path: "/viewRiskSignal"
});
window.open(route.href, "_blank");
};
/**
* 概览页 -> 风险信号管理页(新页面打开,并自动打开第一条详情弹窗)
* @param {import('vue-router').Router} router
*/
export const navigateToViewRiskSignalOpenFirstDetail = (router) => {
const route = router.resolve({
path: "/viewRiskSignal",
query: { [OPEN_FIRST_RISK_DETAIL_QUERY_KEY]: "1" }
});
window.open(route.href, "_blank");
};
...@@ -47,8 +47,8 @@ ...@@ -47,8 +47,8 @@
<div <div
class="risk-signals-item" class="risk-signals-item"
v-for="(item, index) in warningList" v-for="(item, index) in warningList"
:key="index" :key="item.signalId || item.billId || index"
@click="handleClickToDetailO(item)" @click="handleRiskSignalItemToManage"
:class="{ highlighted: item.eventType === highlightedEventType }" :class="{ highlighted: item.eventType === highlightedEventType }"
> >
<div <div
...@@ -123,6 +123,8 @@ import { color } from "echarts"; ...@@ -123,6 +123,8 @@ import { color } from "echarts";
import { onMounted, ref, computed } from "vue"; import { onMounted, ref, computed } from "vue";
import WaveBall from "./WaveBall.vue"; import WaveBall from "./WaveBall.vue";
import { getBillRiskSignal } from "@/api/bill/billHome"; import { getBillRiskSignal } from "@/api/bill/billHome";
import router from "@/router";
import { navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const sectionTab = [ const sectionTab = [
{ {
textColor: "rgba(9, 88, 217, 1)", textColor: "rgba(9, 88, 217, 1)",
...@@ -289,12 +291,8 @@ const handleSwithCurNews = name => { ...@@ -289,12 +291,8 @@ const handleSwithCurNews = name => {
} }
}; };
// 查看详情 传递参数 const handleRiskSignalItemToManage = () => {
const handleClickToDetailO = item => { navigateToViewRiskSignalOpenFirstDetail(router);
window.sessionStorage.setItem("billId", item.billId);
window.sessionStorage.setItem("curTabName", item.name || item.signalTitle);
const route = router.resolve("/billLayout?billId=" + item.billId);
window.open(route.href, "_blank");
}; };
const highlightedEventType = ref(""); const highlightedEventType = ref("");
......
...@@ -43,9 +43,15 @@ ...@@ -43,9 +43,15 @@
<div style="display: flex"> <div style="display: flex">
<!-- 风险信号列表 --> <!-- 风险信号列表 -->
<div class="risk-signals" ref="riskSignalsRef"> <div class="risk-signals" ref="riskSignalsRef">
<div class="risk-signals-item" v-for="(item, index) in warningList" :key="index" <div
@mouseenter="onMouseEnter(item, index)" @mouseleave="onMouseLeave" class="risk-signals-item"
:class="['risk-signals-item', { 'risk-signals-item-hightLight': riskSignalActiveIndex === index }]"> v-for="(item, index) in warningList"
:key="item.signalId != null ? String(item.signalId) : 'risk-' + index"
@mouseenter="onMouseEnter(item, index)"
@mouseleave="onMouseLeave"
@click.stop="handleRiskSignalRowToManage"
:class="['risk-signals-item', { 'risk-signals-item-hightLight': riskSignalActiveIndex === index }]"
>
<div class="item-left" :class="{ <div class="item-left" :class="{
'item-status-1': item.signalLevel === '特别重大', 'item-status-1': item.signalLevel === '特别重大',
'item-status-2': item.signalLevel === '重大风险', 'item-status-2': item.signalLevel === '重大风险',
...@@ -128,6 +134,7 @@ import { onMounted, ref, onUnmounted, computed } from "vue"; ...@@ -128,6 +134,7 @@ import { onMounted, ref, onUnmounted, computed } from "vue";
import WaveBall from "./WaveBall.vue"; import WaveBall from "./WaveBall.vue";
import { getLatestRiskUpdates, getLatestRisks } from "@/api/zmOverview/risk/index.js"; import { getLatestRiskUpdates, getLatestRisks } from "@/api/zmOverview/risk/index.js";
import router from "@/router/index"; import router from "@/router/index";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import icon1 from "./icon/title-1.svg"; import icon1 from "./icon/title-1.svg";
import icon2 from "./icon/title-2.svg"; import icon2 from "./icon/title-2.svg";
import icon3 from "./icon/title-3.svg"; import icon3 from "./icon/title-3.svg";
...@@ -645,12 +652,12 @@ const filteredHotNewsList = computed(() => { ...@@ -645,12 +652,12 @@ const filteredHotNewsList = computed(() => {
return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value); return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value);
}); });
const handleRiskSignalRowToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
const handleToRiskManage = () => { const handleToRiskManage = () => {
// 这里的路由路径请根据实际情况修改 navigateToViewRiskSignal(router);
// router.push('/riskSignalManage');
const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank");
console.log("跳转到风险信号管理");
}; };
const highlightedEventType = ref(""); const highlightedEventType = ref("");
......
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
</el-carousel> </el-carousel>
</div> </div>
</OverviewMainBox> </OverviewMainBox>
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleClickToDetailO" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
riskLevel="signalLevel" postDate="signalTime" name="signalTitle" /> riskLevel="signalLevel" postDate="signalTime" name="signalTitle" />
</div> </div>
...@@ -258,6 +258,7 @@ ...@@ -258,6 +258,7 @@
import RiskSignal from "@/components/base/riskSignal/index.vue"; import RiskSignal from "@/components/base/riskSignal/index.vue";
import { onMounted, ref, onUnmounted, nextTick, watch, computed } from "vue"; import { onMounted, ref, onUnmounted, nextTick, watch, computed } from "vue";
import router from "@/router/index"; import router from "@/router/index";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import { import {
getBillIndustry, getBillIndustry,
...@@ -457,11 +458,13 @@ const handleClickToDetailO = item => { ...@@ -457,11 +458,13 @@ const handleClickToDetailO = item => {
// router.push("/billLayout?billId=" + item.billId) // router.push("/billLayout?billId=" + item.billId)
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
// router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯(新闻主页) // 查看更多新闻资讯(新闻主页)
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
</div> --> </div> -->
<RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content" <RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content"
riskLevel="title" @item-click="handleClickToDetail" /> riskLevel="title" @item-click="handleRiskSignalItemToManage" />
</div> </div>
</template> </template>
...@@ -120,6 +120,7 @@ ...@@ -120,6 +120,7 @@
import RiskSignal from "@/components/base/riskSignal/index.vue"; import RiskSignal from "@/components/base/riskSignal/index.vue";
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, computed } from "vue";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import { getCoopRestrictionTrends, getCoopRestrictionSignals } from "@/api/coopRestriction/coopRestriction.js"; import { getCoopRestrictionTrends, getCoopRestrictionSignals } from "@/api/coopRestriction/coopRestriction.js";
import defaultImg from "./assets/usImg.png"; import defaultImg from "./assets/usImg.png";
import CommonPrompt from "../../commonPrompt/index.vue"; import CommonPrompt from "../../commonPrompt/index.vue";
...@@ -210,10 +211,13 @@ const handleToRiskDetail = (item) => { ...@@ -210,10 +211,13 @@ const handleToRiskDetail = (item) => {
window.open(curRoute.href, "_blank"); window.open(curRoute.href, "_blank");
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
onMounted(() => { onMounted(() => {
......
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
</OverviewMainBox> </OverviewMainBox>
<RiskSignal :list="warningList" @item-click="onNavigateToDetail" @more-click="handleToMoreRiskSignal" <RiskSignal :list="warningList" @item-click="handleRiskSignalItemToManage" @more-click="handleToMoreRiskSignal"
riskLevel="signalLevel" postDate="signalTime" name="signalTitle"> riskLevel="signalLevel" postDate="signalTime" name="signalTitle">
</RiskSignal> </RiskSignal>
</div> </div>
...@@ -423,6 +423,7 @@ ...@@ -423,6 +423,7 @@
<script setup> <script setup>
import { onMounted, ref, watch, nextTick, reactive } from "vue"; import { onMounted, ref, watch, nextTick, reactive } from "vue";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import WordCloudChart from "@/components/base/WordCloundChart/index.vue" import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import SimplePagination from "@/components/SimplePagination.vue"; import SimplePagination from "@/components/SimplePagination.vue";
import TimeTabPane from '@/components/base/TimeTabPane/index.vue'; import TimeTabPane from '@/components/base/TimeTabPane/index.vue';
...@@ -504,10 +505,12 @@ const onNavigateTo = () => { ...@@ -504,10 +505,12 @@ const onNavigateTo = () => {
} }
// 查看更多风险信号 // 查看更多风险信号
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
// router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -602,6 +602,7 @@ const entityListReleaseFreqChart = useChartInterpretation(); ...@@ -602,6 +602,7 @@ const entityListReleaseFreqChart = useChartInterpretation();
const commerceControlListReleaseFreqChart = useChartInterpretation(); const commerceControlListReleaseFreqChart = useChartInterpretation();
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const router = useRouter(); const router = useRouter();
...@@ -673,17 +674,9 @@ const handleToPosi = id => { ...@@ -673,17 +674,9 @@ const handleToPosi = id => {
} }
}; };
// 跳转到单项制裁页面 // 风险信号:进入管理页并自动打开列表第一条详情
const handleToRiskSignalDetail = item => { const handleToRiskSignalDetail = () => {
window.sessionStorage.setItem("curTabName", item.title); navigateToViewRiskSignalOpenFirstDetail(router);
const routeData = router.resolve({
path: "/exportControl/singleSanction",
query: {
id: item.sanId
}
});
// 打开新页面
window.open(routeData.href, "_blank");
}; };
const sanctionList = ref([]); const sanctionList = ref([]);
...@@ -1649,9 +1642,7 @@ const handleSanc = item => { ...@@ -1649,9 +1642,7 @@ const handleSanc = item => {
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
// router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -693,6 +693,7 @@ const entityListReleaseFreqChart = useChartInterpretation(); ...@@ -693,6 +693,7 @@ const entityListReleaseFreqChart = useChartInterpretation();
const commerceControlListReleaseFreqChart = useChartInterpretation(); const commerceControlListReleaseFreqChart = useChartInterpretation();
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const router = useRouter(); const router = useRouter();
...@@ -775,17 +776,9 @@ const handleToPosi = id => { ...@@ -775,17 +776,9 @@ const handleToPosi = id => {
} }
}; };
// 跳转到单项制裁页面 // 风险信号:进入管理页并自动打开列表第一条详情
const handleToRiskSignalDetail = item => { const handleToRiskSignalDetail = () => {
window.sessionStorage.setItem("curTabName", item.title); navigateToViewRiskSignalOpenFirstDetail(router);
const routeData = router.resolve({
path: "/finance/singleSanction",
query: {
id: item.sanId
}
});
// 打开新页面
window.open(routeData.href, "_blank");
}; };
const sanctionList = ref([]); const sanctionList = ref([]);
...@@ -1649,9 +1642,7 @@ const handleSanc = item => { ...@@ -1649,9 +1642,7 @@ const handleSanc = item => {
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
// router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
</div> </div>
<div class="item-header-divider" /> <div class="item-header-divider" />
<div class="warning-wrap"> <div class="warning-wrap">
<div v-for="(item, index) in warningList" :key="index" class="waring-item"> <div v-for="(item, index) in warningList" :key="index" class="waring-item" @click="handleRiskSignalItemClick">
<div class="waring-row"> <div class="waring-row">
<div class="waring-status" :style="{ <div class="waring-status" :style="{
color: item.status === 0 ? '#CE4F51' : item.status === 1 ? '#FA8C16' : '#52C41A', color: item.status === 0 ? '#CE4F51' : item.status === 1 ? '#FA8C16' : '#52C41A',
...@@ -120,6 +120,7 @@ ...@@ -120,6 +120,7 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import router from '@/router'; import router from '@/router';
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from '@/utils/riskSignalOverviewNavigate';
import MeansAnalysis from './component/MeansAnalysis.vue'; import MeansAnalysis from './component/MeansAnalysis.vue';
import ResourceAnalysis from './component/ResourceAnalysis.vue' import ResourceAnalysis from './component/ResourceAnalysis.vue'
import AdvantagesAnalysis from './component/AdvantagesAnalysis.vue' import AdvantagesAnalysis from './component/AdvantagesAnalysis.vue'
...@@ -220,10 +221,13 @@ const warningList = ref([ ...@@ -220,10 +221,13 @@ const warningList = ref([
{ title: '首次提出“限制外国敏感实体获取补偿', time: '一天前', status: 0 }, { title: '首次提出“限制外国敏感实体获取补偿', time: '一天前', status: 0 },
]); ]);
const handleRiskSignalItemClick = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
</script> </script>
......
...@@ -145,8 +145,8 @@ ...@@ -145,8 +145,8 @@
<div class="text">{{ "查看更多" }}</div> <div class="text">{{ "查看更多" }}</div>
</div> </div>
</div> --> </div> -->
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" riskLevel="signalLevel" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
postDate="signalTime" name="signalTitle" /> riskLevel="signalLevel" postDate="signalTime" name="signalTitle" />
</div> </div>
<DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader>
<div class="center-center"> <div class="center-center">
...@@ -348,7 +348,8 @@ import RiskSignal from "@/components/base/riskSignal/index.vue"; ...@@ -348,7 +348,8 @@ import RiskSignal from "@/components/base/riskSignal/index.vue";
import NewsList from "@/components/base/newsList/index.vue"; import NewsList from "@/components/base/newsList/index.vue";
import { onMounted, ref, computed } from "vue"; import { onMounted, ref, computed } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
import getPieChart from "./utils/piechart"; import getPieChart from "./utils/piechart";
...@@ -514,10 +515,13 @@ const handleClickToDetail = university => { ...@@ -514,10 +515,13 @@ const handleClickToDetail = university => {
// window.open(route.href, "_blank"); // window.open(route.href, "_blank");
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
<div class="box1-right" @click="handleSwithCurSurvey('right')"> <RightBtn /> </div> <div class="box1-right" @click="handleSwithCurSurvey('right')"> <RightBtn /> </div>
</overviewMainBox> </overviewMainBox>
</div> </div>
<RiskSignal :list="box2Data" @more-click="handleToMoreRiskSignal" @item-click="onNavigateToDetail" postDate="signalTime" name="signalTitle" riskLevel="signalLevel" /> <RiskSignal :list="box2Data" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
</div> </div>
<DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader>
...@@ -278,6 +278,7 @@ import CarouselItem232 from '@/views/marketAccessRestrictions/marketAccessHome/c ...@@ -278,6 +278,7 @@ import CarouselItem232 from '@/views/marketAccessRestrictions/marketAccessHome/c
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import getMultiLineChart from "./utils/multiLineChart"; import getMultiLineChart from "./utils/multiLineChart";
import getPieChart from "./utils/piechart"; import getPieChart from "./utils/piechart";
...@@ -928,10 +929,13 @@ const handleFetchSurveyList = async () => { ...@@ -928,10 +929,13 @@ const handleFetchSurveyList = async () => {
} catch (error) { } } catch (error) { }
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
</div> </div>
<div class="item-header-divider"></div> <div class="item-header-divider"></div>
<div style="padding: 30px 23px; height: 400px"> <div style="padding: 30px 23px; height: 400px">
<div class="waring-item" v-for="(item, index) in warningList" :key="index"> <div class="waring-item" v-for="(item, index) in warningList" :key="index" @click="handleRiskSignalItemClick">
<div style="display: flex; height: 47px"> <div style="display: flex; height: 47px">
<div <div
class="waring-status" class="waring-status"
...@@ -204,6 +204,7 @@ import Thematicanalysis from "./component/Thematicanalysis.vue"; ...@@ -204,6 +204,7 @@ import Thematicanalysis from "./component/Thematicanalysis.vue";
import ResourceSupport from "./component/ResourceSupport.vue"; import ResourceSupport from "./component/ResourceSupport.vue";
import strengthComparison from "./component/strengthComparison.vue"; import strengthComparison from "./component/strengthComparison.vue";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const searchText = ref(""); const searchText = ref("");
const handleSearch = () => { const handleSearch = () => {
...@@ -223,10 +224,13 @@ const handleToSearch = () => { ...@@ -223,10 +224,13 @@ const handleToSearch = () => {
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
const handleRiskSignalItemClick = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
......
...@@ -69,8 +69,8 @@ ...@@ -69,8 +69,8 @@
查看更多 查看更多
</div> </div>
</div> --> </div> -->
<RiskSignal :list="list" @more-click="handleToMoreRiskSignal" riskLevel="signalLevel" postDate="signalTime" <RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
name="signalTitle" /> riskLevel="signalLevel" postDate="signalTime" name="signalTitle" />
</div> </div>
</template> </template>
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
import RiskSignal from "@/components/base/riskSignal/index.vue"; import RiskSignal from "@/components/base/riskSignal/index.vue";
import { ref, onBeforeMount, computed } from "vue"; import { ref, onBeforeMount, computed } from "vue";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
import { getLatestUpdates, getRiskSignal } from '@/api/ruleRestriction/index.js' import { getLatestUpdates, getRiskSignal } from '@/api/ruleRestriction/index.js'
const list = ref([ const list = ref([
...@@ -194,10 +195,13 @@ const handleToRiskDetail = (item) => { ...@@ -194,10 +195,13 @@ const handleToRiskDetail = (item) => {
// window.open(curRoute.href, "_blank"); // window.open(curRoute.href, "_blank");
// }; // };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多动态 // 查看更多动态
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
onBeforeMount(async () => { onBeforeMount(async () => {
......
...@@ -90,8 +90,8 @@ ...@@ -90,8 +90,8 @@
查看更多 查看更多
</div> </div>
</div> --> </div> -->
<RiskSignal :list="list" @more-click="handleToMoreRiskSignal" postDate="signalTime" name="signalTitle" <RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
riskLevel="signalLevel" /> postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
</div> </div>
</template> </template>
...@@ -102,6 +102,7 @@ import { ...@@ -102,6 +102,7 @@ import {
getNewProject, getRiskSignal getNewProject, getRiskSignal
} from "@/api/scientificFunding/overview"; } from "@/api/scientificFunding/overview";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const list = ref([ const list = ref([
...@@ -124,10 +125,13 @@ const formatDate = (dateStr) => { ...@@ -124,10 +125,13 @@ const formatDate = (dateStr) => {
const [y, m, d] = dateStr.split('-'); const [y, m, d] = dateStr.split('-');
return `${y}${m}${d}日`; return `${y}${m}${d}日`;
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
const box1Data = ref([]) const box1Data = ref([])
const carouselRef = ref(null); const carouselRef = ref(null);
......
...@@ -152,8 +152,8 @@ ...@@ -152,8 +152,8 @@
</div> </div>
</OverviewMainBox> </OverviewMainBox>
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" postDate="signalTime" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
name="signalTitle" riskLevel="signalLevel" /> postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
</div> </div>
<DivideHeader id="position2" class="divide-header" :titleText="'言论动态'"></DivideHeader> <DivideHeader id="position2" class="divide-header" :titleText="'言论动态'"></DivideHeader>
<div class="center-center"> <div class="center-center">
...@@ -346,10 +346,10 @@ import SpeechStance from "./component/speechStance.vue"; ...@@ -346,10 +346,10 @@ import SpeechStance from "./component/speechStance.vue";
import PersonTable from "./component/PersonTable.vue"; import PersonTable from "./component/PersonTable.vue";
import SourceLibrary from "./component/SourceLibrary.vue"; import SourceLibrary from "./component/SourceLibrary.vue";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
import { navigateToViewRiskSignal, navigateToViewRiskSignalOpenFirstDetail } from "@/utils/riskSignalOverviewNavigate";
const router = useRouter(); const router = useRouter();
const containerRef = ref(null); const containerRef = ref(null);
const { isShow } = useContainerScroll(containerRef); const { isShow } = useContainerScroll(containerRef);
...@@ -802,10 +802,13 @@ const handleClickCate = cate => { ...@@ -802,10 +802,13 @@ const handleClickCate = cate => {
typeId.value = cate.typeId; typeId.value = cate.typeId;
}; };
const handleRiskSignalItemToManage = () => {
navigateToViewRiskSignalOpenFirstDetail(router);
};
// 查看更多风险信号 // 查看更多风险信号
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
}; };
// === 图表数据 === // === 图表数据 ===
......
<template> <template>
<div class="home-wrapper"> <div class="home-wrapper">
<div class="home-main" ref="containerRef"> <div class="home-main" ref="containerRef" :class="{ 'is-risk-detail-open': isRiskDetailVisible }">
<div class="home-top-bg"></div> <div class="home-top-bg"></div>
<div class="home-main-header"> <div class="home-main-header">
<!-- <div class="home-main-header-top"> <!-- <div class="home-main-header-top">
...@@ -209,7 +209,29 @@ ...@@ -209,7 +209,29 @@
</el-carousel> </el-carousel>
</OverviewMainBox> </OverviewMainBox>
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" postDate="time" name="title" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" postDate="time" name="title"
@item-click="handleClickToDetail" /> @item-click="handleRiskSignalItemToManage" />
<el-dialog v-model="isRiskDetailVisible" class="risk-signal-detail-dialog"
modal-class="risk-signal-detail-modal" width="1280px" align-center :z-index="20000" :show-close="true"
destroy-on-close
@closed="handleCloseRiskDetail">
<template #header>
<span class="risk-signal-detail-dialog__title">{{ riskDetailItem.title }}</span>
</template>
<div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body">
<div class="risk-signal-detail-dialog__meta">
<span>{{ riskDetailItem.origin }}</span>
<span>{{ riskDetailItem.time }}</span>
<span class="risk-signal-detail-dialog__level">{{ riskDetailItem.risktype }}</span>
</div>
<div class="risk-signal-detail-dialog__desc">{{ riskDetailItem.dsc }}</div>
<div v-if="riskDetailItem.tag.length" class="risk-signal-detail-dialog__tags">
<AreaTag v-for="(tag, index) in riskDetailItem.tag" :key="'risk-detail-tag-' + index + '-' + tag"
:tagName="tag">
{{ tag }}</AreaTag>
</div>
</div>
</el-dialog>
</div> </div>
<DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader>
<div class="center-center"> <div class="center-center">
...@@ -442,6 +464,7 @@ import MessageBubble from "@/components/base/messageBubble/index.vue" ...@@ -442,6 +464,7 @@ import MessageBubble from "@/components/base/messageBubble/index.vue"
import { onBeforeUnmount, onMounted, ref, computed, reactive, nextTick } from "vue"; import { onBeforeUnmount, onMounted, ref, computed, reactive, nextTick } from "vue";
import scrollToTop from "@/utils/scrollToTop"; import scrollToTop from "@/utils/scrollToTop";
import router from "@/router"; import router from "@/router";
import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import DivideHeader from "@/components/DivideHeader.vue"; import DivideHeader from "@/components/DivideHeader.vue";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import HomeMainFooterMain from "./components/HomeMainFooterMain.vue"; import HomeMainFooterMain from "./components/HomeMainFooterMain.vue";
...@@ -511,6 +534,52 @@ import { useRouter } from 'vue-router'; ...@@ -511,6 +534,52 @@ import { useRouter } from 'vue-router';
import { useGotoNewsDetail } from '@/router/modules/news'; import { useGotoNewsDetail } from '@/router/modules/news';
const gotoNewsDetail = useGotoNewsDetail() const gotoNewsDetail = useGotoNewsDetail()
const containerRef = ref(null); const containerRef = ref(null);
const isRiskDetailVisible = ref(false);
const riskDetailItem = reactive({
title: "",
origin: "",
time: "",
risktype: "",
dsc: "",
tag: []
});
const HARD_CODED_RISK_DETAIL_MAP = {
"关于对中华人民共和国合成阿片类药物供应链...": {
origin: "智库概览 · 风险信号",
time: "一天前",
risktype: "特别重大",
dsc:
"这里先写死详情内容:该风险信号涉及合成阿片类药物供应链相关议题,可能对相关实体合规、供应链与对外合作产生影响。后续接入后端后将替换为真实详情。",
tag: ["生物科技", "供应链", "合规风险"]
},
"关于调整钢铁进口的公告": {
origin: "智库概览 · 风险信号",
time: "一天前",
risktype: "重大风险",
dsc:
"这里先写死详情内容:钢铁进口政策调整可能影响相关行业成本结构与贸易路径,需关注后续执行细则与范围变化。",
tag: ["材料", "贸易政策", "成本影响"]
},
"关于修订对中华人民共和国低价值进口商品适...": {
origin: "智库概览 · 风险信号",
time: "一天前",
risktype: "一般风险",
dsc:
"这里先写死详情内容:低价值进口商品规则修订可能带来申报、税费与物流环节的流程变化,建议提前梳理影响链路。",
tag: ["跨境电商", "关务", "流程变化"]
}
};
const handleCloseRiskDetail = () => {
isRiskDetailVisible.value = false;
riskDetailItem.title = "";
riskDetailItem.origin = "";
riskDetailItem.time = "";
riskDetailItem.risktype = "";
riskDetailItem.dsc = "";
riskDetailItem.tag = [];
};
const statCountInfo = ref([]); const statCountInfo = ref([]);
const pageSize = ref(15) const pageSize = ref(15)
const totalAllItem = ref(0) const totalAllItem = ref(0)
...@@ -2119,11 +2188,28 @@ const handleClick = tank => { ...@@ -2119,11 +2188,28 @@ const handleClick = tank => {
// router.push({ name: "ThinkTankDetail", params: { id: tank.id, name: tank.name } }) // router.push({ name: "ThinkTankDetail", params: { id: tank.id, name: tank.name } })
}; };
// 查看更多风险信号 // 风险信号 item:当前页弹窗(先写死内容,后续接后端)
const handleRiskSignalItemToManage = (item) => {
const title = item?.title || "";
const time = item?.time || "";
const risktype = item?.status || "";
const hardCoded = HARD_CODED_RISK_DETAIL_MAP[title] || {};
riskDetailItem.title = title;
riskDetailItem.origin = hardCoded.origin || "智库概览 · 风险信号";
riskDetailItem.time = hardCoded.time || time || "—";
riskDetailItem.risktype = hardCoded.risktype || risktype || "一般风险";
riskDetailItem.dsc =
hardCoded.dsc ||
"这里先写死详情内容:后续接入后端后将替换为真实详情。";
riskDetailItem.tag = hardCoded.tag || ["暂无标签"];
isRiskDetailVisible.value = true;
};
const handleToMoreRiskSignal = () => { const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal"); navigateToViewRiskSignal(router);
window.open(route.href, "_blank");
// router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯 // 查看更多新闻资讯
...@@ -4590,3 +4676,95 @@ onBeforeUnmount(() => { ...@@ -4590,3 +4676,95 @@ onBeforeUnmount(() => {
} }
} }
</style> </style>
<style lang="scss">
/* el-dialog 默认 teleport 到 body,壳子样式需非 scoped */
/* 遮罩层内水平垂直居中(勿对 .el-dialog 写 margin:0 !important,会顶到左上角) */
.risk-signal-detail-modal.el-overlay {
display: flex;
align-items: center;
justify-content: center;
z-index: 20000 !important;
}
.risk-signal-detail-modal .el-overlay-dialog {
display: flex;
align-items: center;
justify-content: center;
}
.risk-signal-detail-dialog.el-dialog {
width: 1280px !important;
max-width: calc(100vw - 32px);
height: 750px;
max-height: calc(100vh - 32px);
border-radius: 10px;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
.risk-signal-detail-dialog .el-dialog__header {
flex-shrink: 0;
padding: 16px 20px 8px;
margin: 0;
}
.risk-signal-detail-dialog .el-dialog__body {
flex: 1;
min-height: 0;
overflow: auto;
padding: 12px 20px 20px;
box-sizing: border-box;
}
.risk-signal-detail-dialog__title {
font-size: 18px;
font-weight: 700;
line-height: 24px;
color: rgba(59, 65, 75, 1);
}
.risk-signal-detail-dialog__body {
display: flex;
flex-direction: column;
gap: 12px;
}
.risk-signal-detail-dialog__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
font-size: 14px;
line-height: 22px;
color: rgba(95, 101, 108, 1);
}
.risk-signal-detail-dialog__level {
color: rgba(5, 95, 194, 1);
}
.risk-signal-detail-dialog__desc {
font-size: 16px;
line-height: 24px;
color: rgba(59, 65, 75, 1);
text-align: justify;
}
.risk-signal-detail-dialog__tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
/* 弹窗打开时,禁用轮播左右切换按钮,避免穿透点击 */
.home-main.is-risk-detail-open {
.box1-left,
.box1-right {
pointer-events: none;
}
}
</style>
...@@ -190,7 +190,8 @@ ...@@ -190,7 +190,8 @@
</div> </div>
<div class="right-main"> <div class="right-main">
<div class="itemlist" v-for="(val, idx) in riskList" :key="idx"> <div class="itemlist itemlist--clickable" v-for="(val, idx) in riskList" :key="val.rowKey"
@click="handleOpenRiskDetail(val)">
<div class="box-title"> <div class="box-title">
<div class="risktitle">{{ val.title }}</div> <div class="risktitle">{{ val.title }}</div>
<div class="risktype" :class="{ <div class="risktype" :class="{
...@@ -233,11 +234,33 @@ ...@@ -233,11 +234,33 @@
</div> </div>
</div> </div>
</div> </div>
<el-dialog v-model="isRiskDetailVisible" class="risk-signal-detail-dialog" modal-class="risk-signal-detail-modal"
width="1280px" align-center :show-close="true" destroy-on-close @closed="handleCloseRiskDetail">
<template #header>
<span class="risk-signal-detail-dialog__title">{{ riskDetailItem.title }}</span>
</template>
<div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body">
<div class="risk-signal-detail-dialog__meta">
<span>{{ riskDetailItem.origin }}</span>
<span>{{ riskDetailItem.time }}</span>
<span class="risk-signal-detail-dialog__level">{{ riskDetailItem.risktype }}</span>
</div>
<div class="risk-signal-detail-dialog__desc">{{ riskDetailItem.dsc }}</div>
<div v-if="riskDetailItem.tag.length" class="risk-signal-detail-dialog__tags">
<AreaTag v-for="(tag, index) in riskDetailItem.tag" :key="'risk-detail-tag-' + index + '-' + tag"
:tagName="tag">
{{ tag }}</AreaTag>
</div>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import { nextTick, onMounted, reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { OPEN_FIRST_RISK_DETAIL_QUERY_KEY } from "@/utils/riskSignalOverviewNavigate";
import { getCountInfo, getDailyCount, getPageQuery } from "@/api/riskSignal/index"; import { getCountInfo, getDailyCount, getPageQuery } from "@/api/riskSignal/index";
import { getHylyList } from "@/api/thinkTank/overview"; import { getHylyList } from "@/api/thinkTank/overview";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
...@@ -390,6 +413,7 @@ const handleClickBtn = item => { ...@@ -390,6 +413,7 @@ const handleClickBtn = item => {
const riskList = ref([ const riskList = ref([
{ {
rowKey: "risk-demo-1",
title: "扩大实体清单制裁范围,对中企子公司实施同等管制", title: "扩大实体清单制裁范围,对中企子公司实施同等管制",
origin: "美国商务部", origin: "美国商务部",
fileType: "实体清单", fileType: "实体清单",
...@@ -400,6 +424,7 @@ const riskList = ref([ ...@@ -400,6 +424,7 @@ const riskList = ref([
pic: "src/views/riskSignal/assets/images/origin1.png" pic: "src/views/riskSignal/assets/images/origin1.png"
}, },
{ {
rowKey: "risk-demo-2",
title: "大而美法案通过国会众议院投票,将提交至总统签署", title: "大而美法案通过国会众议院投票,将提交至总统签署",
origin: "美国国会 · 科技法案", origin: "美国国会 · 科技法案",
time: "2025年11月10日 16:14", time: "2025年11月10日 16:14",
...@@ -411,6 +436,7 @@ const riskList = ref([ ...@@ -411,6 +436,7 @@ const riskList = ref([
bgcolor: "rgba(255, 149, 77, 0.1)" bgcolor: "rgba(255, 149, 77, 0.1)"
}, },
{ {
rowKey: "risk-demo-3",
title: "兰德公司发布智库报告《中美经济竞争:复杂经济和地缘政治关系中的收益和风险》", title: "兰德公司发布智库报告《中美经济竞争:复杂经济和地缘政治关系中的收益和风险》",
origin: "兰德公司 · 科技智库", origin: "兰德公司 · 科技智库",
time: "2025年11月10日 16:14", time: "2025年11月10日 16:14",
...@@ -422,6 +448,7 @@ const riskList = ref([ ...@@ -422,6 +448,7 @@ const riskList = ref([
bgcolor: "rgba(5, 95, 194, 0.1)" bgcolor: "rgba(5, 95, 194, 0.1)"
}, },
{ {
rowKey: "risk-demo-4",
title: "美国白宫发布总统政令《关于进一步延长TikTok执法宽限期的行政令》", title: "美国白宫发布总统政令《关于进一步延长TikTok执法宽限期的行政令》",
origin: "美国白宫 · 总统政令", origin: "美国白宫 · 总统政令",
time: "2025年11月10日 16:14", time: "2025年11月10日 16:14",
...@@ -433,6 +460,7 @@ const riskList = ref([ ...@@ -433,6 +460,7 @@ const riskList = ref([
bgcolor: "rgba(5, 95, 194, 0.1)" bgcolor: "rgba(5, 95, 194, 0.1)"
}, },
{ {
rowKey: "risk-demo-5",
title: "美国财政部更新《特别指定国民清单》", title: "美国财政部更新《特别指定国民清单》",
origin: "美国财政部 · 特别指定国民清单", origin: "美国财政部 · 特别指定国民清单",
time: "2025年11月10日 16:14", time: "2025年11月10日 16:14",
...@@ -444,6 +472,7 @@ const riskList = ref([ ...@@ -444,6 +472,7 @@ const riskList = ref([
bgcolor: "rgba(206, 79, 81, 0.1)" bgcolor: "rgba(206, 79, 81, 0.1)"
}, },
{ {
rowKey: "risk-demo-6",
title: "美国FDA针对两家中国第三方检测机构的数据完整性问题采取行动", title: "美国FDA针对两家中国第三方检测机构的数据完整性问题采取行动",
origin: "美国食品药品监督管理局 · 规则限制", origin: "美国食品药品监督管理局 · 规则限制",
time: "2025年11月10日 16:14", time: "2025年11月10日 16:14",
...@@ -456,6 +485,50 @@ const riskList = ref([ ...@@ -456,6 +485,50 @@ const riskList = ref([
} }
]); ]);
const isRiskDetailVisible = ref(false);
const riskDetailItem = reactive({
title: "",
origin: "",
time: "",
dsc: "",
tag: [],
risktype: ""
});
const assignRiskDetail = (val) => {
riskDetailItem.title = val.title ?? "";
riskDetailItem.origin = val.origin ?? "";
riskDetailItem.time = val.time ?? "";
riskDetailItem.dsc = val.dsc ?? "";
riskDetailItem.tag = Array.isArray(val.tag) ? [...val.tag] : [];
riskDetailItem.risktype = val.risktype ?? "";
};
const handleOpenRiskDetail = (val) => {
assignRiskDetail(val);
isRiskDetailVisible.value = true;
};
const handleCloseRiskDetail = () => {
assignRiskDetail({ tag: [] });
};
const route = useRoute();
const router = useRouter();
const consumeOpenFirstDetailFromQuery = async () => {
if (route.query[OPEN_FIRST_RISK_DETAIL_QUERY_KEY] !== "1") {
return;
}
if (!riskList.value.length) {
return;
}
handleOpenRiskDetail(riskList.value[0]);
const nextQuery = { ...route.query };
delete nextQuery[OPEN_FIRST_RISK_DETAIL_QUERY_KEY];
await router.replace({ path: route.path, query: nextQuery });
};
const calendarData = ref([ const calendarData = ref([
]); ]);
...@@ -596,8 +669,13 @@ const handleGetPageQuery = async () => { ...@@ -596,8 +669,13 @@ const handleGetPageQuery = async () => {
console.log("按条件查询", res); console.log("按条件查询", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
totalNum.value = res.data.totalElements; totalNum.value = res.data.totalElements;
riskList.value = res.data.content.map(item => { riskList.value = res.data.content.map((item, i) => {
const stableId = item.id ?? item.riskId ?? item.riskSignalId;
return { return {
rowKey:
stableId != null
? String(stableId)
: `p${currentPage.value}-i${i}-${String(item.time ?? "")}-${String(item.titleZh ?? "")}`,
title: item.titleZh, title: item.titleZh,
origin: item.srcOrgId, origin: item.srcOrgId,
fileType: "暂无数据", fileType: "暂无数据",
...@@ -614,11 +692,24 @@ const handleGetPageQuery = async () => { ...@@ -614,11 +692,24 @@ const handleGetPageQuery = async () => {
} }
}; };
watch(
() => route.query[OPEN_FIRST_RISK_DETAIL_QUERY_KEY],
async (v) => {
if (v !== "1") {
return;
}
await nextTick();
await consumeOpenFirstDetailFromQuery();
}
);
onMounted(async () => { onMounted(async () => {
handleGetCountInfo(); handleGetCountInfo();
handleCleandarChart(); handleCleandarChart();
await handleGetHylyList(); await handleGetHylyList();
handleGetPageQuery(); await handleGetPageQuery();
await nextTick();
await consumeOpenFirstDetailFromQuery();
}); });
</script> </script>
...@@ -1063,6 +1154,10 @@ onMounted(async () => { ...@@ -1063,6 +1154,10 @@ onMounted(async () => {
} }
&.itemlist--clickable {
cursor: pointer;
}
} }
} }
...@@ -1093,4 +1188,86 @@ onMounted(async () => { ...@@ -1093,4 +1188,86 @@ onMounted(async () => {
} }
/* 复选框尺寸由 .checkbox-group 内统一控制,避免重复覆盖 */ /* 复选框尺寸由 .checkbox-group 内统一控制,避免重复覆盖 */
.risk-signal-detail-dialog__title {
font-size: 18px;
font-weight: 700;
line-height: 24px;
color: rgba(59, 65, 75, 1);
}
.risk-signal-detail-dialog__body {
display: flex;
flex-direction: column;
gap: 12px;
}
.risk-signal-detail-dialog__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
font-size: 14px;
line-height: 22px;
color: rgba(95, 101, 108, 1);
}
.risk-signal-detail-dialog__level {
color: rgba(5, 95, 194, 1);
}
.risk-signal-detail-dialog__desc {
font-size: 16px;
line-height: 24px;
color: rgba(59, 65, 75, 1);
text-align: justify;
}
.risk-signal-detail-dialog__tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
</style>
<style lang="scss">
/* el-dialog 默认 teleport 到 body,壳子样式需非 scoped */
/* 遮罩层内水平垂直居中(勿对 .el-dialog 写 margin:0 !important,会顶到左上角) */
.risk-signal-detail-modal.el-overlay {
display: flex;
align-items: center;
justify-content: center;
}
.risk-signal-detail-modal .el-overlay-dialog {
display: flex;
align-items: center;
justify-content: center;
}
.risk-signal-detail-dialog.el-dialog {
width: 1280px !important;
max-width: calc(100vw - 32px);
height: 750px;
max-height: calc(100vh - 32px);
border-radius: 10px;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
.risk-signal-detail-dialog .el-dialog__header {
flex-shrink: 0;
padding: 16px 20px 8px;
margin: 0;
}
.risk-signal-detail-dialog .el-dialog__body {
flex: 1;
min-height: 0;
overflow: auto;
padding: 12px 20px 20px;
box-sizing: border-box;
}
</style> </style>
\ No newline at end of file
import dayjs from "dayjs";
const RISK_CALENDAR_MONTH_TOTAL = 12;
const RISK_HEAT_CELL_INNER = 16;
const RISK_HEAT_CELL_GAP = 2;
const RISK_HEAT_CELL_STRIDE = RISK_HEAT_CELL_INNER + RISK_HEAT_CELL_GAP;
const RISK_HEAT_CELL_BORDER = RISK_HEAT_CELL_GAP / 2;
const getCalendarHeatChart = (data) => { const RISK_HEAT_CELL_SEPARATOR_COLOR = "rgb(255, 255, 255)";
const option = { const RISK_HEAT_EMPTY_FILL = "rgb(247, 248, 249)";
title: {
top: 30, /** 每行 5 列;日期按「左→右、上→下」排布(不再用 6 列×6 行) */
left: 'center', const COLS_PER_MONTH = 5;
text: `日历热力图`,
show: false /**
}, * 行数 = ceil(天数/5);月初补 (行数*5-天数) 个空位,使最后一行尽量排满。
tooltip: { * 常见:28–30 天为 5×6 内;31 天需 7 行(5×6=30 格装不下 31 天)。
position: 'top', * @param {number} daysInMonth
formatter: function (params) { */
const date = new Date(params.data[0]); const getMonthGridLayout = (daysInMonth) => {
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}<br/>数值: ${params.data[1]}`; const rows = Math.ceil(daysInMonth / COLS_PER_MONTH);
} const paddingSlots = rows * COLS_PER_MONTH - daysInMonth;
}, return { rows, paddingSlots };
visualMap: { };
show: false,
min: 0, const HEAT_RGB_STOPS = [
max: 20, [231, 243, 255],
calculable: true, [137, 193, 255],
orient: 'horizontal', [5, 95, 194]
left: 'center', ];
top: 65,
inRange: { const lerpChannel = (a, b, t) => Math.round(a + (b - a) * t);
color: ['rgb(231, 243, 255)', 'rgb(137, 193, 255)', 'rgb(5, 95, 194)']
}, /** v<=0 用空色;v>0 按三段渐变 */
textStyle: { const getHeatColor = (v, maxV) => {
color: 'rgba(95, 101, 108, 1)' const n = Number(v);
} if (!Number.isFinite(n) || n <= 0) {
}, return RISK_HEAT_EMPTY_FILL;
calendar: { }
top: 24, const ratio = Math.min(n / maxV, 1);
left: 30, const [c0, c1, c2] = HEAT_RGB_STOPS;
right: 30, if (ratio <= 0.5) {
cellSize: ['auto', 20], const t = ratio / 0.5;
range: '2026', return `rgb(${lerpChannel(c0[0], c1[0], t)},${lerpChannel(c0[1], c1[1], t)},${lerpChannel(c0[2], c1[2], t)})`;
splitLine: { }
show:false const t = (ratio - 0.5) / 0.5;
}, return `rgb(${lerpChannel(c1[0], c2[0], t)},${lerpChannel(c1[1], c2[1], t)},${lerpChannel(c1[2], c2[2], t)})`;
itemStyle: { };
borderWidth: 0.5,
borderColor: '#ccc' const getCalendarHeatChart = (rawData) => {
}, const now = dayjs();
yearLabel: { show: true }, const monthLabels = [];
monthLabel: { for (let offset = RISK_CALENDAR_MONTH_TOTAL - 1; offset >= 0; offset -= 1) {
nameMap: 'cn' monthLabels.push(now.subtract(offset, "month").format("YYYY-MM"));
}, }
dayLabel: {
nameMap: ['日', '一', '二', '三', '四', '五', '六'], const startBound = dayjs(`${monthLabels[0]}-01`).startOf("month");
show: false, const endBound = now.endOf("month");
} const countMap = new Map();
}, let maxVal = 0;
series: { for (const row of rawData || []) {
type: 'heatmap', if (!row || row.length < 2) continue;
coordinateSystem: 'calendar', const d = dayjs(row[0]);
data: data if (!d.isValid()) continue;
} if (d.isBefore(startBound, "day") || d.isAfter(endBound, "day")) continue;
}; const key = d.format("YYYY-MM-DD");
return option const c = Number(row[1]) || 0;
} countMap.set(key, c);
if (c > maxVal) maxVal = c;
export default getCalendarHeatChart }
\ No newline at end of file const visualMax = Math.max(1, maxVal);
const monthWidthPx = COLS_PER_MONTH * RISK_HEAT_CELL_STRIDE;
const monthLayouts = monthLabels.map((ym) => {
const daysInMonth = dayjs(`${ym}-01`).daysInMonth();
return {
ym,
daysInMonth,
...getMonthGridLayout(daysInMonth)
};
});
/** 12 个月紧挨排列;月与月之间不额外加空隙,仅靠两侧 1px 描边形成固定 2px */
const CONTENT_MARGIN_LEFT_PX = 22;
const CONTENT_MARGIN_TOP_PX = 15;
/** 月份文字在上,格子在其下(12px 字号约一行高) */
const MONTH_LABEL_TOP_PX = CONTENT_MARGIN_TOP_PX;
const GRID_TOP_PX = CONTENT_MARGIN_TOP_PX + 18;
const monthTitleGraphics = monthLayouts.map((layout, idx) => {
const monthNum = dayjs(`${layout.ym}-01`).month() + 1;
return {
type: "text",
id: `risk-heat-month-${idx}`,
left: CONTENT_MARGIN_LEFT_PX + idx * monthWidthPx,
top: MONTH_LABEL_TOP_PX,
z: 10,
silent: true,
style: {
text: `${monthNum}月`,
fill: "rgb(59, 65, 75)",
fontSize: 12,
fontWeight: 500,
fontFamily: "Source Han Sans CN, PingFang SC, Microsoft YaHei, sans-serif"
}
};
});
const grids = monthLayouts.map((layout, idx) => ({
left: CONTENT_MARGIN_LEFT_PX + idx * monthWidthPx,
top: GRID_TOP_PX,
width: monthWidthPx,
height: layout.rows * RISK_HEAT_CELL_STRIDE,
containLabel: false
}));
const xAxes = monthLabels.map((_, idx) => ({
type: "value",
gridIndex: idx,
min: 0,
max: COLS_PER_MONTH,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false }
}));
const yAxes = monthLayouts.map((layout, idx) => ({
type: "value",
gridIndex: idx,
min: 0,
max: layout.rows,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false }
}));
const series = monthLayouts.map((layout, idx) => {
const { ym, daysInMonth, rows, paddingSlots } = layout;
const m = dayjs(`${ym}-01`);
const data = [];
const totalSlots = rows * COLS_PER_MONTH;
for (let slot = 0; slot < totalSlots; slot += 1) {
const col = slot % COLS_PER_MONTH;
const row = Math.floor(slot / COLS_PER_MONTH);
if (slot < paddingSlots) {
data.push([col, row, "", null, 1]);
continue;
}
const day = slot - paddingSlots + 1;
const dateStr = m.date(day).format("YYYY-MM-DD");
const v = countMap.get(dateStr) ?? 0;
data.push([col, row, dateStr, v, 0]);
}
return {
type: "custom",
name: ym,
xAxisIndex: idx,
yAxisIndex: idx,
data,
renderItem(params, api) {
const col = api.value(0);
const row = api.value(1);
const dateStr = api.value(2);
const v = api.value(3);
const isPadding = api.value(4) === 1;
/**
* 不能用 api.coord([0,0]) 当「顶部」:默认 value 轴 y=0 在网格下方,
* 会导致整块热力画在偏下位置并从 canvas 底部被裁切。
* 用当前 grid 的 coordSys 像素框,按列/行直接铺格,保证第 0 行贴 grid 顶。
*/
const coordSys = params.coordSys;
if (!coordSys || typeof coordSys.x !== "number" || typeof coordSys.y !== "number") {
return null;
}
const xPx = coordSys.x + col * RISK_HEAT_CELL_STRIDE;
const yPx = coordSys.y + row * RISK_HEAT_CELL_STRIDE;
return {
type: "rect",
shape: {
x: xPx,
y: yPx,
width: RISK_HEAT_CELL_STRIDE,
height: RISK_HEAT_CELL_STRIDE
},
style: api.style({
fill: isPadding ? RISK_HEAT_EMPTY_FILL : getHeatColor(v, visualMax),
stroke: RISK_HEAT_CELL_SEPARATOR_COLOR,
lineWidth: RISK_HEAT_CELL_BORDER
}),
silent: isPadding
};
},
encode: {
tooltip: [2, 3]
},
tooltip: {
formatter(p) {
const dateStr = p.data?.[2] || "";
if (!dateStr) {
return "";
}
const v = p.data?.[3] ?? 0;
return `${dateStr}<br/>数值: ${v}`;
}
}
};
});
return {
tooltip: {
trigger: "item",
position: "top"
},
graphic: monthTitleGraphics,
grid: grids,
xAxis: xAxes,
yAxis: yAxes,
series
};
};
export default getCalendarHeatChart;
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论