提交 fcaf505e authored 作者: 朱政's avatar 朱政

feat:智库和风险列表加入加载状态,同时修改智库数量变化趋势图表调用

上级 2a5ddbf3
流水线 #448 已通过 于阶段
in 1 分 44 秒
...@@ -113,7 +113,7 @@ service.interceptors.response.use( ...@@ -113,7 +113,7 @@ service.interceptors.response.use(
} }
// 特殊处理:风险信号管理页面接口偶发 500,不弹出提示 // 特殊处理:风险信号管理页面接口偶发 500,不弹出提示
// 覆盖接口:/api/riskSignal/getCountInfo | /api/riskSignal/getDailyCount | /api/riskSignal/pageQuery // 覆盖接口:/api/riskSignal/baseInfo | /api/riskSignal/pageQuery
try { try {
const errUrl = String(error?.config?.url || '') const errUrl = String(error?.config?.url || '')
if (error?.response?.status === 500 && errUrl.includes('/api/riskSignal/')) { if (error?.response?.status === 500 && errUrl.includes('/api/riskSignal/')) {
......
import request from "@/api/request.js"; import request from "@/api/request.js";
// 基本统计信息 /** 风险信号管理页:统计 + 日历热力数据(原 getCountInfo + getDailyCount) */
export function getCountInfo() { export function getRiskSignalBaseInfo() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/riskSignal/getCountInfo`, url: `/api/riskSignal/baseInfo`
}) });
}
// 每日统计信息
export function getDailyCount() {
return request({
method: 'GET',
url: `/api/riskSignal/getDailyCount`,
})
} }
// 按条件分页查询风险信号信息 // 按条件分页查询风险信号信息
export function getPageQuery(data) { export function getPageQuery(data) {
return request({ return request({
method: 'POST', method: 'POST',
url: `/api/riskSignal/pageQuery`, url: `/api/riskSignal/PageLimit`,
data: data data: data
}) })
} }
...@@ -53,6 +53,25 @@ export function getThinkTankPolicyIndustryChange(params) { ...@@ -53,6 +53,25 @@ export function getThinkTankPolicyIndustryChange(params) {
}); });
} }
/**
* 智库概览-数量变化趋势(按领域统计)
* GET /thinkTankReport/domainStats
* @param {{ startDate: string, endDate: string }} params
*/
export function getThinkTankReportDomainStats(params) {
return request({
method: "GET",
url: `/api/thinkTankReport/domainStats`,
params: {
startDate: params.startDate,
endDate: params.endDate
},
// 与 policyIndustryChange 一致:无数据年份可能返回 400/500,避免走全局错误提示
validateStatus: (status) =>
(status >= 200 && status < 300) || status === 400 || status === 500
});
}
// 政策建议领域分布 // 政策建议领域分布
export function getThinkTankPolicyIndustry(params) { export function getThinkTankPolicyIndustry(params) {
return request({ return request({
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<span class="tag-text"> {{ tag }} ({{ tagCountMap[tag] || 0 }} 项)</span> <span class="tag-text"> {{ tag }} ({{ tagCountMap[tag] || 0 }} 项)</span>
</span> </span>
</div> </div>
<div class="item-box"> <div class="item-box" v-loading="loading">
<div class="item" v-for="(item, index) in filteredOpinions" :key="item.id || index" <div class="item" v-for="(item, index) in filteredOpinions" :key="item.id || index"
:class="{ 'item-active': index === activeItemIndex }" @click=" :class="{ 'item-active': index === activeItemIndex }" @click="
() => { () => {
...@@ -138,6 +138,7 @@ import { ...@@ -138,6 +138,7 @@ import {
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import DefaultNewsImg from '@/assets/images/default-icon-news.png' import DefaultNewsImg from '@/assets/images/default-icon-news.png'
const router = useRouter(); const router = useRouter();
const loading = ref(false);
const searchOpinions = ref(""); const searchOpinions = ref("");
// 政策建议相关情况(当前页/当前筛选展示的数据) // 政策建议相关情况(当前页/当前筛选展示的数据)
const box1Data = ref([]); const box1Data = ref([]);
...@@ -277,6 +278,7 @@ const updateTagsFromAllData = () => { ...@@ -277,6 +278,7 @@ const updateTagsFromAllData = () => {
const handleGetThinkTankReportPolicyAction = async () => { const handleGetThinkTankReportPolicyAction = async () => {
try { try {
loading.value = true;
const params = { const params = {
reportId: router.currentRoute._value.params.id, reportId: router.currentRoute._value.params.id,
// 这里请求全量数据,前端自行分页 // 这里请求全量数据,前端自行分页
...@@ -312,6 +314,8 @@ const handleGetThinkTankReportPolicyAction = async () => { ...@@ -312,6 +314,8 @@ const handleGetThinkTankReportPolicyAction = async () => {
box1Data.value = []; box1Data.value = [];
allTags.value = []; allTags.value = [];
tagCountMap.value = {}; tagCountMap.value = {};
} finally {
loading.value = false;
} }
}; };
// 按当前标签与页码从全量数据中截取一页 // 按当前标签与页码从全量数据中截取一页
...@@ -765,6 +769,7 @@ onMounted(async () => { ...@@ -765,6 +769,7 @@ onMounted(async () => {
width: 20px; width: 20px;
height: 20px; height: 20px;
margin-left: auto; margin-left: auto;
cursor: pointer;
img { img {
width: 100%; width: 100%;
...@@ -815,6 +820,7 @@ onMounted(async () => { ...@@ -815,6 +820,7 @@ onMounted(async () => {
width: 20px; width: 20px;
height: 20px; height: 20px;
margin-left: auto; margin-left: auto;
cursor: pointer;
img { img {
width: 100%; width: 100%;
......
...@@ -1284,6 +1284,7 @@ onMounted(() => { ...@@ -1284,6 +1284,7 @@ onMounted(() => {
height: 24px; height: 24px;
margin-top: 12px; margin-top: 12px;
margin-left: 18px; margin-left: 18px;
cursor: pointer;
} }
......
...@@ -226,7 +226,7 @@ ...@@ -226,7 +226,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="right"> <div class="right" v-loading="loading">
<div class="right-main"> <div class="right-main">
<div class="right-main-item" v-for="item in policyList" :key="item.id"> <div class="right-main-item" v-for="item in policyList" :key="item.id">
<div class="item-left"> <div class="item-left">
...@@ -308,6 +308,8 @@ import { useRouter } from "vue-router"; ...@@ -308,6 +308,8 @@ import { useRouter } from "vue-router";
const router = useRouter(); const router = useRouter();
const loading = ref(false);
/** 与 AreaTag 一致的领域色(取 tag 的文字色) */ /** 与 AreaTag 一致的领域色(取 tag 的文字色) */
const AREA_TAG_COLOR_BY_NAME = { const AREA_TAG_COLOR_BY_NAME = {
"人工智能": "rgba(245, 34, 45, 1)", // tag1 "人工智能": "rgba(245, 34, 45, 1)", // tag1
...@@ -1247,6 +1249,7 @@ const handleCurrentChange = page => { ...@@ -1247,6 +1249,7 @@ const handleCurrentChange = page => {
const handleGetThinkPolicy = async () => { const handleGetThinkPolicy = async () => {
try { try {
loading.value = true;
const thinkTankId = router.currentRoute._value.params.id; const thinkTankId = router.currentRoute._value.params.id;
const domainIds = selectedAreaList.value const domainIds = selectedAreaList.value
.filter((id) => id != null && id !== "" && id !== POLICY_FILTER_ALL_AREA) .filter((id) => id != null && id !== "" && id !== POLICY_FILTER_ALL_AREA)
...@@ -1292,6 +1295,8 @@ const handleGetThinkPolicy = async () => { ...@@ -1292,6 +1295,8 @@ const handleGetThinkPolicy = async () => {
console.error("获取智库政策error", error); console.error("获取智库政策error", error);
policyList.value = []; policyList.value = [];
total.value = 0; total.value = 0;
} finally {
loading.value = false;
} }
}; };
......
...@@ -456,7 +456,7 @@ import ThinkTankPolicyAdviceOverview from "./components/ThinkTankPolicyAdviceOve ...@@ -456,7 +456,7 @@ import ThinkTankPolicyAdviceOverview from "./components/ThinkTankPolicyAdviceOve
import { import {
getThinkTankList, getThinkTankList,
getThinkTankRiskSignal, getThinkTankRiskSignal,
getThinkTankPolicyIndustryChange, getThinkTankReportDomainStats,
getThinkTankPolicyIndustry, getThinkTankPolicyIndustry,
getThinkTankDonation, getThinkTankDonation,
getAllThinkTankList, getAllThinkTankList,
...@@ -1003,10 +1003,25 @@ const changeBox5Data = year => { ...@@ -1003,10 +1003,25 @@ const changeBox5Data = year => {
// 政策建议趋势分布 // 政策建议趋势分布
const handleGetThinkTankPolicyIndustryChange = async range => { const handleGetThinkTankPolicyIndustryChange = async range => {
try { try {
const res = await getThinkTankPolicyIndustryChange(range); const res = await getThinkTankReportDomainStats(range);
console.log("政策建议趋势分布", res); console.log("智库报告数量变化趋势(domainStats)", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const originalData = res.data; const rawList = Array.isArray(res.data) ? res.data : [];
// domainStats:季度维度 + areaList;旧逻辑使用 industryList,这里做字段归一(不改图表样式/结构)
const originalData = rawList
.map((item) => {
const industryList = Array.isArray(item?.industryList)
? item.industryList
: Array.isArray(item?.areaList)
? item.areaList
: [];
return {
...item,
year: item?.year,
industryList
};
})
.filter((item) => Array.isArray(item.industryList) && item.industryList.length > 0);
// 提取年份 // 提取年份
const years = originalData.map(item => item.year); const years = originalData.map(item => item.year);
// 提取所有行业名称 // 提取所有行业名称
......
...@@ -150,7 +150,7 @@ ...@@ -150,7 +150,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="right"> <div class="right" v-loading="loading">
<div class="right-header"> <div class="right-header">
<div class="header-left"> <div class="header-left">
<div class="btn-left" :class="{ btnleftactive: activeProcessStatusId === item.id }" <div class="btn-left" :class="{ btnleftactive: activeProcessStatusId === item.id }"
...@@ -187,18 +187,17 @@ ...@@ -187,18 +187,17 @@
<div class="itemlist itemlist--clickable" v-for="(val, idx) in riskList" :key="val.rowKey" <div class="itemlist itemlist--clickable" v-for="(val, idx) in riskList" :key="val.rowKey"
@click="handleOpenRiskDetail(val)"> @click="handleOpenRiskDetail(val)">
<div class="box-title"> <div class="box-title">
<div class="risktitle">{{ val.title }}</div> <div class="risktitle" v-html="highlightRiskText(val.title)" />
<div class="risktype" :class="'risktype--' + getRiskListItemLevelKey(val.risktype)"> <div class="risktype" :class="'risktype--' + getRiskListItemLevelKey(val.risktype)">
<div class="icon" :class="'icon--' + getRiskListItemLevelKey(val.risktype)" /> <div class="icon" :class="'icon--' + getRiskListItemLevelKey(val.risktype)" />
<div class="text">{{ getRiskListItemLevelLabel(val.risktype) }}</div> <div class="text">{{ getRiskListItemLevelLabel(val.risktype) }}</div>
</div> </div>
</div> </div>
<div class="box-source"> <div class="box-source">
<img class="source-pic" :src="DefaultIcon2" alt="" /> <img class="source-pic" :src="val.pic || DefaultIcon2" alt="" />
<div class="source-text">{{ val.origin }}</div> <div class="source-text">{{ formatRiskSourceLine(val) }}</div>
<div class="source-text">{{ val.time }}</div>
</div> </div>
<div class="desc-box">{{ val.dsc }}</div> <div class="desc-box" v-html="highlightRiskText(val.dsc)" />
<div class="tag-box" v-if="val.tag.length"> <div class="tag-box" v-if="val.tag.length">
<AreaTag v-for="(tag, index) in val.tag" :key="index" :tagName="tag">{{ tag }}</AreaTag> <AreaTag v-for="(tag, index) in val.tag" :key="index" :tagName="tag">{{ tag }}</AreaTag>
</div> </div>
...@@ -206,7 +205,7 @@ ...@@ -206,7 +205,7 @@
</div> </div>
<div class="right-footer"> <div class="right-footer">
<div class="footer-left"> <div class="footer-left">
{{ `共 ${totalNum} 项调查` }} {{ `共 ${formatRiskStatCount(totalNum)}调查` }}
</div> </div>
<div class="footer-right"> <div class="footer-right">
<el-pagination @current-change="handleCurrentChange" :pageSize="pageSize" :current-page="currentPage" <el-pagination @current-change="handleCurrentChange" :pageSize="pageSize" :current-page="currentPage"
...@@ -241,7 +240,7 @@ ...@@ -241,7 +240,7 @@
<div class="risk-signal-detail-dialog_relation"> <div class="risk-signal-detail-dialog_relation">
<div class="relation"> <div class="relation">
<div class="logo"><img src="./assets/images/logo.png" alt="" /></div> <div class="logo"><img src="./assets/images/logo.png" alt="" /></div>
<div class="name-text">{{ "总统行政令——" }}</div> <div class="name-text">{{ "总统行政令" }}</div>
<div class="content-text">{{ "关于调整进口木材、锯材及其衍生产品进入美国的相关修正案" }}</div> <div class="content-text">{{ "关于调整进口木材、锯材及其衍生产品进入美国的相关修正案" }}</div>
</div> </div>
<div class="right-arrow"><img src="./assets/images/right-arrow.png" alt="" /></div> <div class="right-arrow"><img src="./assets/images/right-arrow.png" alt="" /></div>
...@@ -260,7 +259,7 @@ ...@@ -260,7 +259,7 @@
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue"; import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { OPEN_FIRST_RISK_DETAIL_QUERY_KEY } from "@/utils/riskSignalOverviewNavigate"; import { OPEN_FIRST_RISK_DETAIL_QUERY_KEY } from "@/utils/riskSignalOverviewNavigate";
import { getCountInfo, getDailyCount, getPageQuery } from "@/api/riskSignal/index"; import { getRiskSignalBaseInfo, 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";
...@@ -410,79 +409,7 @@ const handleClickBtn = item => { ...@@ -410,79 +409,7 @@ const handleClickBtn = item => {
handleGetPageQuery(); handleGetPageQuery();
}; };
const riskList = ref([ const riskList = ref([]);
{
rowKey: "risk-demo-1",
title: "扩大实体清单制裁范围,对中企子公司实施同等管制",
origin: "美国商务部",
fileType: "实体清单",
time: "2025年11月10日 16:14",
dsc: "任何被列入美国出口管制“实体清单”或“军事最终用户清单”的企业,如果其直接或间接持有另一家公司 ​50%或以上的股权,那么这家被控股的公司也将自动受到与清单上母公司同等的出口管制限制",
tag: ["生物科技", "人工智能"],
risktype: "特别重大风险",
pic: "src/views/riskSignal/assets/images/origin1.png"
},
{
rowKey: "risk-demo-2",
title: "大而美法案通过国会众议院投票,将提交至总统签署",
origin: "美国国会 · 科技法案",
time: "2025年11月10日 16:14",
dsc: "",
tag: ["能源", "人工智能"],
risktype: "重大风险",
pic: "src/views/riskSignal/assets/images/origin2.png",
textcolor: "rgba(255, 149, 77, 1)",
bgcolor: "rgba(255, 149, 77, 0.1)"
},
{
rowKey: "risk-demo-3",
title: "兰德公司发布智库报告《中美经济竞争:复杂经济和地缘政治关系中的收益和风险》",
origin: "兰德公司 · 科技智库",
time: "2025年11月10日 16:14",
dsc: "包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。",
tag: ["生物科技", "人工智能", "集成电路"],
risktype: "一般风险",
pic: "src/views/riskSignal/assets/images/origin3.png",
textcolor: "rgba(5, 95, 194, 1)",
bgcolor: "rgba(5, 95, 194, 0.1)"
},
{
rowKey: "risk-demo-4",
title: "美国白宫发布总统政令《关于进一步延长TikTok执法宽限期的行政令》",
origin: "美国白宫 · 总统政令",
time: "2025年11月10日 16:14",
dsc: "再次推迟(第四次)对TikTok禁令的执法,新的宽限期截止日为2025年12月16日​。在宽限期内及对于宽限期前的行为,司法部不得强制执行​《保护美国人免受外国对手控制应用程序法》或因此处罚相关实体​(如TikTok及其分发平台)。",
tag: ["人工智能"],
risktype: "一般风险",
pic: "src/views/riskSignal/assets/images/origin2.png",
textcolor: "rgba(5, 95, 194, 1)",
bgcolor: "rgba(5, 95, 194, 0.1)"
},
{
rowKey: "risk-demo-5",
title: "美国财政部更新《特别指定国民清单》",
origin: "美国财政部 · 特别指定国民清单",
time: "2025年11月10日 16:14",
dsc: "",
tag: ["生物科技"],
risktype: "特别重大风险",
pic: "src/views/riskSignal/assets/images/origin4.png",
textcolor: "rgba(206, 79, 81, 1)",
bgcolor: "rgba(206, 79, 81, 0.1)"
},
{
rowKey: "risk-demo-6",
title: "美国FDA针对两家中国第三方检测机构的数据完整性问题采取行动",
origin: "美国食品药品监督管理局 · 规则限制",
time: "2025年11月10日 16:14",
dsc: "FDA因发现数据伪造或无效问题,向两家中国第三方检测公司(天津中联科技检测有限公司和苏州大学卫生与环境技术研究所)正式发出“一般信函”。",
tag: ["生物科技", "人工智能"],
risktype: "特别重大风险",
pic: "src/views/riskSignal/assets/images/origin5.png",
textcolor: "rgba(206, 79, 81, 1)",
bgcolor: "rgba(206, 79, 81, 0.1)"
}
]);
const isRiskDetailVisible = ref(false); const isRiskDetailVisible = ref(false);
const riskDetailItem = reactive({ const riskDetailItem = reactive({
...@@ -572,32 +499,49 @@ const basicInfo = ref({ ...@@ -572,32 +499,49 @@ const basicInfo = ref({
pendingCount: 0, pendingCount: 0,
yearAdded: 0 yearAdded: 0
}); });
const handleGetCountInfo = async () => {
try { /** 风险统计:按数量级压缩展示(保留两位小数) */
const res = await getCountInfo(); const formatRiskStatCount = (raw) => {
console.log("基本统计信息", res); const n = Math.floor(Number(raw) || 0);
if (res.code === 200 && res.data) { if (n >= 1000000) {
basicInfo.value = res.data; return `${(n / 1000000).toFixed(2)} 百万项`;
}
if (n >= 100000) {
return `${(n / 100000).toFixed(2)} 十万项`;
}
if (n >= 10000) {
return `${(n / 10000).toFixed(2)} 万项`;
} }
} catch (error) { } return `${n} 项`;
}; };
// 每日统计信息 /** 合并 baseInfo:顶部统计 + 热力图 dateList(eventTime / total) */
const handleGetDailyCount = async () => { const handleGetRiskSignalBaseInfo = async () => {
try { try {
const res = await getDailyCount(); const res = await getRiskSignalBaseInfo();
console.log("每日统计信息", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
calendarData.value = res.data.map(item => { const d = res.data;
return [item.day, item.count]; basicInfo.value = {
yearAdded: Number(d.yearAdded) || 0,
monthAdded: Number(d.monthAdded) || 0,
dealCount: Number(d.dealCount) || 0,
pendingCount: Number(d.pendingCount) || 0
};
const list = Array.isArray(d.dateList) ? d.dateList : [];
calendarData.value = list.map((item) => {
const day = item.eventTime ?? item.day ?? "";
const count = item.total ?? item.count ?? 0;
return [day, Number(count) || 0];
}); });
} }
} catch (error) { } } catch (error) {
console.error("风险信号 baseInfo", error);
}
}; };
const handleCleandarChart = async () => { const handleCleandarChart = async () => {
await handleGetDailyCount(); await handleGetRiskSignalBaseInfo();
let chartCalendar = getCalendarHeatChart(calendarData.value); const chartCalendar = getCalendarHeatChart(calendarData.value);
setChart(chartCalendar, "chartCalendar"); setChart(chartCalendar, "chartCalendar");
}; };
...@@ -626,8 +570,40 @@ const handleSearch = async () => { ...@@ -626,8 +570,40 @@ const handleSearch = async () => {
handleGetPageQuery(); handleGetPageQuery();
}; };
const loading = ref(false);
const escapeHtml = (text) => {
return String(text ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
};
const escapeRegExp = (text) => {
return String(text ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
const highlightRiskText = (text) => {
const safeText = escapeHtml(text);
const keyword = (kewword.value || "").trim();
if (!keyword) return safeText;
const pattern = new RegExp(`(${escapeRegExp(keyword)})`, "gi");
return safeText.replace(pattern, `<span class="risk-keyword-highlight">$1</span>`);
};
const formatRiskSourceLine = (row) => {
const origin = String(row?.origin ?? "").trim();
const typeName = String(row?.typeName ?? "").trim();
const time = String(row?.time ?? "").trim();
const left = typeName ? `${origin}·${typeName}` : origin;
if (left && time) return `${left} ${time}`;
return left || time;
};
// 风险信号总数 // 风险信号总数
const totalNum = ref(6); const totalNum = ref(0);
const currentPage = ref(1); const currentPage = ref(1);
const pageSize = ref(10); const pageSize = ref(10);
// 处理页码改变事件 // 处理页码改变事件
...@@ -636,91 +612,142 @@ const handleCurrentChange = page => { ...@@ -636,91 +612,142 @@ const handleCurrentChange = page => {
handleGetPageQuery(); handleGetPageQuery();
}; };
// 按条件分页查询风险信号信息 /** PageLimit 列表项:领域标签(优先 domainNames,兼容 domainName / domains) */
const parseRiskSignalTagList = (item) => {
const names = item?.domainNames;
if (names != null && String(names).trim() !== "") {
return String(names)
.split(/[,,、;;|]+/)
.map((s) => s.trim())
.filter(Boolean);
}
const dn = item?.domainName;
if (dn != null && String(dn).trim() !== "") {
return [String(dn).trim()];
}
const raw = item?.domains;
if (Array.isArray(raw)) {
return raw.map((d) => (typeof d === "string" ? d : d?.name)).filter(Boolean);
}
if (typeof raw === "string" && raw.trim()) {
return raw.split(/[,,]/).map((s) => s.trim()).filter(Boolean);
}
return [];
};
/** 发布时间:YYYY-MM-DD HH:mm:ss →「2026年1月26日 00:00」;仅日期则「2026年1月26日」 */
const formatRiskPublishDisplay = (raw) => {
if (raw == null || raw === "") {
return "";
}
const s = String(raw).trim();
const dt = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})(?::(\d{2}))?/);
if (dt) {
const h = String(Number(dt[4])).padStart(2, "0");
const mi = String(Number(dt[5])).padStart(2, "0");
return `${Number(dt[1])}${Number(dt[2])}${Number(dt[3])}${h}:${mi}`;
}
const m = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})(?:$|[T\s])/);
if (m) {
return `${Number(m[1])}${Number(m[2])}${Number(m[3])}日`;
}
return s;
};
// 按条件分页查询风险信号信息(POST /api/riskSignal/PageLimit)
const handleGetPageQuery = async () => { const handleGetPageQuery = async () => {
const stripAll = (list, allLabel) => (Array.isArray(list) ? list.filter((x) => x !== allLabel) : []); const stripAll = (list, allLabel) => (Array.isArray(list) ? list.filter((x) => x !== allLabel) : []);
// 选中「全部xxx」时,传空数组表示不按该条件过滤(与之前未勾选时语义一致) // 选中「全部xxx」时,传空数组表示不按该条件过滤(与之前未勾选时语义一致)
const riskTypes = stripAll(selectedRiskTypeModel.value, RISK_FILTER_ALL_TYPE); const riskTypes = stripAll(selectedRiskTypeModel.value, RISK_FILTER_ALL_TYPE);
const srcCountryList = stripAll(selectedRiskSourceModel.value, RISK_FILTER_ALL_SOURCE); const srcCountryList = stripAll(selectedRiskSourceModel.value, RISK_FILTER_ALL_SOURCE);
const riskLevels = stripAll(selectedRiskDegreeModel.value, RISK_FILTER_ALL_LEVEL); const riskLevels = stripAll(selectedRiskDegreeModel.value, RISK_FILTER_ALL_LEVEL);
const techDomainIds = stripAll(selectedAreaModel.value, RISK_FILTER_ALL_AREA); stripAll(selectedAreaModel.value, RISK_FILTER_ALL_AREA);
const timeFilters = stripAll(selectedTimeModel.value, Time_FILTER_ALL_SOURCE); const timeFilters = stripAll(selectedTimeModel.value, Time_FILTER_ALL_SOURCE);
const pad2 = (n) => String(n).padStart(2, "0"); // 发布时间筛选统一传 day(后端:全部时间=900000,近一年=365,其它同理)
const formatYmd = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; const getDayFromTimeFilter = () => {
const buildStartDateFromTimeFilter = () => { // 选中「全部时间」或未选具体时间 → 走全量
if (!timeFilters.length) return ""; if (!timeFilters.length) return 900000;
// 单选时间窗:取第一个即可(normalizeExclusiveAllOption 已保证互斥) const getDaysById = (id) => {
const id = timeFilters[0]; if (id === TIME_FILTER_1M) return 30;
const now = new Date(); if (id === TIME_FILTER_3M) return 90;
const start = new Date(now); if (id === TIME_FILTER_6M) return 180;
if (id === TIME_FILTER_1M) start.setMonth(start.getMonth() - 1); if (id === TIME_FILTER_1Y) return 365;
else if (id === TIME_FILTER_3M) start.setMonth(start.getMonth() - 3); return null;
else if (id === TIME_FILTER_6M) start.setMonth(start.getMonth() - 6); };
else if (id === TIME_FILTER_1Y) start.setFullYear(start.getFullYear() - 1); // 多选:以最大时间窗为准(如 近一月 + 近一年 => 365)
else return ""; const days = (timeFilters || [])
return formatYmd(start); .map((id) => getDaysById(id))
.filter((x) => typeof x === "number" && !Number.isNaN(x));
return days.length ? Math.max(...days) : 900000;
}; };
const startDate = buildStartDateFromTimeFilter(); const day = getDayFromTimeFilter();
const endDate = timeFilters.length ? formatYmd(new Date()) : "";
let params; let params;
if (activeProcessStatusId.value === -1) { if (activeProcessStatusId.value === -1) {
params = { params = {
riskTypes, riskTypes,
srcCountryList, countryId: srcCountryList,
directionId: [],
riskLevels, riskLevels,
techDomainIds, day,
startDate,
endDate, keyWord: kewword.value,
keywords: kewword.value, pageNum: currentPage.value - 1,
pageNum: currentPage.value, pageSize: pageSize.value
pageSize: pageSize.value,
sortField: "time",
sortOrder: sortModel.value === true ? "asc" : "desc"
}; };
} else { } else {
params = { params = {
riskTypes, riskTypes,
srcCountryList, countryId: srcCountryList,
directionId: [],
riskLevels, riskLevels,
techDomainIds,
dealStatus: activeProcessStatusId.value, dealStatus: activeProcessStatusId.value,
startDate, day,
endDate,
keywords: kewword.value, keyWord: kewword.value,
pageNum: 1, pageNum: currentPage.value - 1,
pageSize: 10, pageSize: pageSize.value
sortField: "time",
sortOrder: sortModel.value === true ? "asc" : "desc"
}; };
} }
try { try {
loading.value = true;
const res = await getPageQuery(params); const res = await getPageQuery(params);
console.log("按条件查询", res); console.log("按条件查询", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
totalNum.value = res.data.totalElements; const d = res.data;
riskList.value = res.data.content.map((item, i) => { const content = Array.isArray(d.content) ? d.content : [];
totalNum.value = Number(d.totalElements ?? d.total ?? content.length) || 0;
riskList.value = content.map((item, i) => {
const stableId = item.id ?? item.riskId ?? item.riskSignalId; const stableId = item.id ?? item.riskId ?? item.riskSignalId;
const title = item.title ?? item.titleZh ?? "";
const publishRaw = item.publishDate ?? item.time ?? "";
return { return {
rowKey: rowKey:
stableId != null stableId != null
? String(stableId) ? String(stableId)
: `p${currentPage.value}-i${i}-${String(item.time ?? "")}-${String(item.titleZh ?? "")}`, : `p${currentPage.value}-i${i}-${String(publishRaw)}-${String(title)}`,
title: item.titleZh, title,
origin: item.srcOrgId, origin: item.orgName != null && String(item.orgName).trim() !== "" ? String(item.orgName).trim() : "",
fileType: "暂无数据", typeName: item.typeName ?? item.type ?? item.modle ?? item.module ?? "",
time: item.time, time: formatRiskPublishDisplay(publishRaw) || String(publishRaw || ""),
dsc: item.contentZh, dsc: item.contentZh ?? item.summary ?? item.description ?? "",
tag: [], tag: parseRiskSignalTagList(item),
risktype: item.level, risktype: item.riskLevel ?? item.level ?? "",
pic: "" pic: item.orgLogo || ""
}; };
}); });
} else {
riskList.value = [];
totalNum.value = 0;
} }
} catch (error) { } catch (error) {
console.error("按条件查询error", error); console.error("按条件查询error", error);
riskList.value = [];
totalNum.value = 0;
} finally {
loading.value = false;
} }
}; };
...@@ -736,12 +763,11 @@ watch( ...@@ -736,12 +763,11 @@ watch(
); );
onMounted(async () => { onMounted(async () => {
handleGetCountInfo();
handleCleandarChart(); handleCleandarChart();
await handleGetHylyList(); handleGetHylyList();
await handleGetPageQuery(); handleGetPageQuery();
await nextTick(); nextTick();
await consumeOpenFirstDetailFromQuery(); consumeOpenFirstDetailFromQuery();
}); });
</script> </script>
...@@ -764,7 +790,7 @@ onMounted(async () => { ...@@ -764,7 +790,7 @@ onMounted(async () => {
background-size: 100% 100%; background-size: 100% 100%;
.home-main-center { .home-main-center {
margin-top: 34px; margin-top: 16px;
.center-center { .center-center {
margin: 0 auto; margin: 0 auto;
...@@ -902,7 +928,7 @@ onMounted(async () => { ...@@ -902,7 +928,7 @@ onMounted(async () => {
.home-main-footer-main { .home-main-footer-main {
width: 1600px; width: 1600px;
margin: 30px auto; margin: 16px auto;
/* 意思:上30px / 左右auto / 下300px */ /* 意思:上30px / 左右auto / 下300px */
box-sizing: border-box; box-sizing: border-box;
// padding: 20px; // padding: 20px;
...@@ -910,7 +936,7 @@ onMounted(async () => { ...@@ -910,7 +936,7 @@ onMounted(async () => {
.left { .left {
width: 388px; width: 388px;
margin-bottom: 24px;
border-radius: 10px; border-radius: 10px;
padding-bottom: 24px; padding-bottom: 24px;
box-sizing: border-box; box-sizing: border-box;
...@@ -985,7 +1011,8 @@ onMounted(async () => { ...@@ -985,7 +1011,8 @@ onMounted(async () => {
margin-left: 16px; margin-left: 16px;
margin-bottom: 24px; margin-bottom: 24px;
width: 1196px; width: 1196px;
height: 1821px; position: relative;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2); box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
...@@ -1074,9 +1101,10 @@ onMounted(async () => { ...@@ -1074,9 +1101,10 @@ onMounted(async () => {
.right-main { .right-main {
width: 1196px; width: 1196px;
min-height: 1667px;
padding-left: 18px; padding-left: 18px;
padding-top: 6px; padding-top: 6px;
min-height: 520px;
.itemlist { .itemlist {
padding-left: 25px; padding-left: 25px;
...@@ -1094,10 +1122,20 @@ onMounted(async () => { ...@@ -1094,10 +1122,20 @@ onMounted(async () => {
justify-content: space-between; justify-content: space-between;
.risktitle { .risktitle {
max-width: 857px;
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
line-height: 24px; line-height: 24px;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 0 1 auto;
:deep(.risk-keyword-highlight) {
background: rgba(255, 204, 0, 0.35);
color: inherit;
}
} }
.risktype { .risktype {
...@@ -1111,6 +1149,7 @@ onMounted(async () => { ...@@ -1111,6 +1149,7 @@ onMounted(async () => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
flex: 0 0 auto;
.icon { .icon {
width: 4px; width: 4px;
...@@ -1193,6 +1232,11 @@ onMounted(async () => { ...@@ -1193,6 +1232,11 @@ onMounted(async () => {
line-height: 24px; line-height: 24px;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
text-align: justify; text-align: justify;
:deep(.risk-keyword-highlight) {
background: rgba(255, 204, 0, 0.35);
color: inherit;
}
} }
.tag-box { .tag-box {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论