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

feat:所有概览页风险信号点击item逻辑开发,以及风险信号管理功能样式开发

上级 eaea9d5b
流水线 #498 已通过 于阶段
in 2 分 3 秒
......@@ -24,3 +24,27 @@ export function getRiskSignalInfoById(id) {
url: `/api/riskSignal/getRiskSignalInfoById/${id}`
});
}
/** 确定风险:更新处理状态(GET /api/riskSignal/UpdateStatu/{id}) */
export function updateRiskSignalStatus(id) {
return request({
method: "GET",
url: `/api/riskSignal/UpdateStatu/${id}`
});
}
/** 风险类型字典(GET /api/riskSignal/getRiskTypes) */
export function getRiskTypes() {
return request({
method: "GET",
url: `/api/riskSignal/getRiskTypes`
});
}
/** 风险来源国家字典(GET /api/riskSignal/listRiskCountry) */
export function listRiskCountry() {
return request({
method: "GET",
url: `/api/riskSignal/listRiskCountry`
});
}
......@@ -26,13 +26,12 @@ export function getNewReport() {
})
}
// 风险信号
export function getThinkTankRiskSignal() {
// 风险信号(统一走通用接口 /api/commonFeature/riskSignal/{moduleId})
export function getThinkTankRiskSignal(moduleId = "0102") {
return request({
method: 'GET',
url: `/api/thinkTankOverview/riskSignal`,
})
method: "GET",
url: `/api/commonFeature/riskSignal/${moduleId}`,
});
}
/**
......
......@@ -17,21 +17,29 @@
class="risk-signal-detail-dialog__level"
:class="listLevelModifierClass"
>{{ listLevelText }}</span>
<div v-if="bodyFromApi" class="risk-signal-detail-dialog__read-indicator">
<el-icon v-if="riskDetailStatus === false" class="risk-signal-detail-dialog__header-badge-close">
<Close />
</el-icon>
<img
v-else-if="riskDetailStatus === true"
class="risk-signal-detail-dialog__header-badge-read"
:src="greenRightImg"
alt=""
/>
<span v-if="riskDetailStatus != null" class="read">{{ riskDetailStatus ? "已读" : "未读" }}</span>
</div>
</template>
<div class="risk-signal-detail-dialog__main" v-loading="detailLoading">
<div v-if="bodyFromApi">
<div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body">
<span class="risk-signal-detail-dialog__title">{{ riskDetailItem.title }}</span>
<div
v-if="riskDetailItem.directionLabels.length || riskDetailItem.category"
class="risk-signal-detail-dialog__directions"
>
<div v-if="riskDetailItem.directionLabels.length" class="risk-signal-detail-dialog__directions">
<div
v-for="(dirLabel, dirIndex) in riskDetailItem.directionLabels"
:key="'overview-risk-detail-direction-' + dirIndex + '-' + dirLabel"
class="risk-signal-detail-dialog__origin"
>{{ dirLabel }}</div>
<div v-if="riskDetailItem.category" class="risk-signal-detail-dialog__origin">{{ riskDetailItem.category }}</div>
</div>
<div class="risk-signal-detail-dialog__meta">
<span>{{ metaLine }}</span>
......@@ -64,8 +72,13 @@
</div>
</div>
<template #footer>
<el-button type="primary" class="risk-signal-detail-dialog__action-btn" @click="visible = false">
确定风险
<el-button
type="primary"
class="risk-signal-detail-dialog__action-btn"
:loading="confirmLoading"
@click="handleConfirm"
>
确定
</el-button>
</template>
</el-dialog>
......@@ -75,7 +88,10 @@
import { computed, nextTick, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import AreaTag from "@/components/base/AreaTag/index.vue";
import { getRiskSignalInfoById } from "@/api/riskSignal/index.js";
import { ElMessage } from "element-plus";
import { Close } from "@element-plus/icons-vue";
import greenRightImg from "@/views/viewRiskSignal/assets/images/green-right.png";
import { getRiskSignalInfoById, updateRiskSignalStatus } from "@/api/riskSignal/index.js";
import {
buildListRowFallbackFromRawRow,
buildRiskDetailPayloadFromApi,
......@@ -114,7 +130,7 @@ const props = defineProps({
}
});
const emit = defineEmits(["closed"]);
const emit = defineEmits(["closed", "confirmed"]);
const visible = defineModel({ type: Boolean, default: false });
......@@ -123,6 +139,9 @@ const router = useRouter();
const listLevelText = ref("");
const bodyFromApi = ref(false);
const detailLoading = ref(false);
const confirmLoading = ref(false);
const riskDetailCurrentId = ref(null);
const riskDetailStatus = ref(null);
const riskDetailItem = reactive(getEmptyRiskDetailPayload());
......@@ -171,6 +190,9 @@ const resetState = () => {
listLevelText.value = "";
bodyFromApi.value = false;
detailLoading.value = false;
confirmLoading.value = false;
riskDetailCurrentId.value = null;
riskDetailStatus.value = null;
assignRiskDetail(getEmptyRiskDetailPayload());
};
......@@ -181,6 +203,8 @@ const loadDetail = async () => {
}
const listFallback = buildListRowFallbackFromRawRow(props.row, fieldMap.value);
listLevelText.value = normalizeRiskSignalListLevelText(listFallback.risktype);
riskDetailCurrentId.value = null;
riskDetailStatus.value = null;
assignRiskDetail(getEmptyRiskDetailPayload());
bodyFromApi.value = false;
detailLoading.value = false;
......@@ -192,6 +216,8 @@ const loadDetail = async () => {
try {
const res = await getRiskSignalInfoById(id);
if (res.code === 200 && res.data) {
riskDetailCurrentId.value = id;
riskDetailStatus.value = Boolean(res.data.status);
assignRiskDetail(buildRiskDetailPayloadFromApi(res.data, listFallback));
bodyFromApi.value = true;
}
......@@ -226,6 +252,30 @@ const handleRelationClick = () => {
window.open(href, "_blank");
};
const handleConfirm = async () => {
const id = riskDetailCurrentId.value;
if (id == null || id === "") {
visible.value = false;
return;
}
confirmLoading.value = true;
try {
const res = await updateRiskSignalStatus(String(id));
if (res.code === 200) {
ElMessage.success(typeof res.msg === "string" && res.msg.trim() ? res.msg : "操作成功");
visible.value = false;
emit("confirmed", { id: String(id) });
} else {
ElMessage.warning(typeof res.msg === "string" && res.msg.trim() ? res.msg : "操作失败");
}
} catch (error) {
console.error("确定风险 UpdateStatu error", error);
ElMessage.error("操作失败");
} finally {
confirmLoading.value = false;
}
};
function handleClosed() {
resetState();
emit("closed");
......
......@@ -232,6 +232,52 @@
margin-right: 6px;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__read-indicator {
position: absolute;
right: 115px;
top: 50%;
transform: translateY(-50%);
display: inline-flex;
align-items: center;
gap: 8px;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__header-badge-close {
width: 16px;
height: 16px;
border-radius: 8px;
background-color: rgba(206, 79, 81, 1);
color: rgba(255, 255, 255, 1);
display: inline-flex;
align-items: center;
justify-content: center;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__header-badge-close svg {
width: 12px;
height: 12px;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__header-badge-close svg path {
fill: rgba(255, 255, 255, 1) !important;
stroke: rgba(255, 255, 255, 1) !important;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__header-badge-read {
width: 16px;
height: 16px;
display: block;
}
.risk-signal-detail-dialog .read {
font-family: "Source Han Sans CN", sans-serif;
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0;
color: rgb(95, 101, 108);
}
.risk-signal-detail-dialog .header-icon img {
width: 100%;
height: 100%;
......@@ -319,7 +365,13 @@
margin-top: 24px;
padding-left: 12px;
padding-right: 12px;
height: 310px;
height: 360px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 14;
line-clamp: 14;
overflow: hidden;
text-overflow: ellipsis;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__desc-p {
......
......@@ -5,7 +5,10 @@ export function resolveRiskSignalRowId(row) {
if (!row || typeof row !== "object") {
return "";
}
const keys = ["id", "riskId", "riskSignalId", "orderId", "signalId"];
// 概览页通用接口 `/api/commonFeature/riskSignal/{moduleId}` 返回结构里,
// 详情接口 getRiskSignalInfoById 需要的是 signalId(而非 orderId/reportId 等业务 id)。
// 因此优先取 signalId;其次再取已映射的 id/riskId 等。
const keys = ["signalId", "id", "riskId", "riskSignalId", "orderId"];
for (const k of keys) {
const v = row[k];
if (v != null && String(v).trim() !== "") {
......@@ -296,6 +299,7 @@ export function buildRiskDetailPayloadFromApi(data, listRow) {
const category =
data.typeName ?? data.module ?? data.modle ?? data.riskCategory ?? "";
const org = data.orgName != null && String(data.orgName).trim() !== "" ? String(data.orgName).trim() : "";
// 相关模块类型:严格使用 data.model(0100/0101/0103...),与风险动向类型 data.direction(0100~0103) 语义不同
const modelCode = data.model != null ? String(data.model).trim() : "";
const referedRaw = data.referedId ?? data.referredId;
const relationReferedId = referedRaw != null ? String(referedRaw).trim() : "";
......@@ -308,6 +312,7 @@ export function buildRiskDetailPayloadFromApi(data, listRow) {
const detailTags = parseRiskSignalTagList(data);
const apiCategory = String(category).trim();
const apiTime = formatRiskPublishDisplay(postRaw) || String(postRaw || "").trim();
// 风险动向类型:严格使用 direction(0100~0103),支持多值
const directionCodes = [
...parseRiskSignalDirectionCodes(data.direction),
...parseRiskSignalDirectionCodes(list.direction)
......
......@@ -49,7 +49,7 @@
:key="item.signalId != null ? String(item.signalId) : 'risk-' + index"
@mouseenter="onMouseEnter(item, index)"
@mouseleave="onMouseLeave"
@click.stop="handleRiskSignalRowToManage(item)"
@click.stop
:class="['risk-signals-item', { 'risk-signals-item-hightLight': riskSignalActiveIndex === index }]"
>
<div class="item-left" :class="{
......@@ -125,13 +125,6 @@
</div>
</div>
</div>
<RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div>
</template>
......@@ -142,7 +135,6 @@ import WaveBall from "./WaveBall.vue";
import { getLatestRiskUpdates, getLatestRisks } from "@/api/zmOverview/risk/index.js";
import router from "@/router/index";
import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import RiskSignalOverviewDetailDialog from "@/components/base/RiskSignalOverviewDetailDialog/index.vue";
import icon1 from "./icon/title-1.svg";
import icon2 from "./icon/title-2.svg";
import icon3 from "./icon/title-3.svg";
......@@ -660,13 +652,7 @@ const filteredHotNewsList = computed(() => {
return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value);
});
const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalRowToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true;
};
// 首页风险信号不弹详情弹窗
const handleToRiskManage = () => {
navigateToViewRiskSignal(router);
......
......@@ -1388,7 +1388,7 @@ const strengthLabels = {
// 获取风险信号数据
const fetchRiskSignals = async () => {
try {
const data = await getRiskSignal();
const data = await getRiskSignal("0109");
if (data && Array.isArray(data)) {
console.log(data);
warningList.value = data.map(item => ({
......@@ -1408,7 +1408,7 @@ const fetchRiskSignals = async () => {
// 添加获取社交媒体信息的方法
const fetchSocialMediaInfo = async () => {
try {
const data = await getSocialMediaInfo();
const data = await getSocialMediaInfo("0109");
if (data && Array.isArray(data)) {
// console.log(data);
socialMediaList.value = data.map(item => ({
......@@ -1429,7 +1429,7 @@ const fetchSocialMediaInfo = async () => {
// 添加获取新闻资讯的方法
const fetchNewsInfo = async () => {
try {
const data = await getNewsInfo();
const data = await getNewsInfo("0109");
if (data && Array.isArray(data)) {
newsList.value = data.map(item => ({
...item,
......
......@@ -807,15 +807,15 @@ const warningList = ref([
// 获取智库风险信号
const handleGetThinkTankRiskSignal = async () => {
try {
const res = await getThinkTankRiskSignal();
const res = await getThinkTankRiskSignal("0102");
console.log("智库风险信号", res);
if (res.code === 200) {
warningList.value = res.data.map(item => {
return {
title: item.name,
time: item.times,
id: item.reportId,
status: item.riskLevel || "暂无数据"
title: item.signalTitle,
time: item.signalTime,
id: item.signalId,
status: item.signalLevel || "暂无数据"
};
});
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论