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

feat:风险信号接口与样式修改

上级 743cbb31
流水线 #476 已通过 于阶段
in 1 分 41 秒
...@@ -16,3 +16,11 @@ export function getPageQuery(data) { ...@@ -16,3 +16,11 @@ export function getPageQuery(data) {
data: data data: data
}) })
} }
/** 风险信号详情(GET /api/riskSignal/getRiskSignalInfoById/{id}) */
export function getRiskSignalInfoById(id) {
return request({
method: "GET",
url: `/api/riskSignal/getRiskSignalInfoById/${id}`
});
}
...@@ -12,39 +12,57 @@ ...@@ -12,39 +12,57 @@
> >
<template #header> <template #header>
<img class="header-icon" src="@/views/viewRiskSignal/assets/images/risk-icon.png" alt="" /> <img class="header-icon" src="@/views/viewRiskSignal/assets/images/risk-icon.png" alt="" />
<span class="risk-signal-detail-dialog__level risk-signal-detail-dialog__level--lv1">特别重大风险</span> <span
v-if="listLevelText"
class="risk-signal-detail-dialog__level"
:class="listLevelModifierClass"
>{{ listLevelText }}</span>
</template> </template>
<div class="risk-signal-detail-dialog__body"> <div class="risk-signal-detail-dialog__main" v-loading="detailLoading">
<span class="risk-signal-detail-dialog__title">扩大实体清单制裁范围,对中企子公司实施同等管制</span> <div v-if="bodyFromApi">
<div class="risk-signal-detail-dialog__origin">政策法规打压类风险</div> <div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body">
<div class="risk-signal-detail-dialog__meta"> <span class="risk-signal-detail-dialog__title">{{ riskDetailItem.title }}</span>
<span>2025年11月10日 16:14·美国商务部</span> <div
<div class="risk-signal-detail-dialog__tags"> v-if="riskDetailItem.directionLabels.length || riskDetailItem.category"
<AreaTag :key="'overview-risk-dialog-tag-bio'" tagName="生物科技">生物科技</AreaTag> class="risk-signal-detail-dialog__directions"
<AreaTag :key="'overview-risk-dialog-tag-ai'" tagName="人工智能">人工智能</AreaTag> >
<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>
<div v-if="riskDetailItem.tag.length" class="risk-signal-detail-dialog__tags">
<AreaTag
v-for="(tag, index) in riskDetailItem.tag"
:key="'overview-risk-detail-tag-' + index + '-' + tag"
:tag-name="tag"
>{{ tag }}</AreaTag>
</div>
</div>
</div> </div>
</div> <div
</div> v-if="showRelationBar"
<div class="risk-signal-detail-dialog_relation"> class="risk-signal-detail-dialog_relation"
<div class="relation"> @click="handleRelationClick"
<div class="logo"> >
<img src="@/views/viewRiskSignal/assets/images/logo.png" alt="" /> <div class="relation">
<div class="logo">
<img src="@/views/viewRiskSignal/assets/images/logo.png" alt="" />
</div>
<div class="name-text">{{ riskDetailItem.relationType }}</div>
<div class="content-text">{{ riskDetailItem.relationTitle }}</div>
</div>
<div class="right-arrow">
<img src="@/views/viewRiskSignal/assets/images/right-arrow.png" alt="" />
</div>
</div> </div>
<div class="name-text">总统行政令——</div> <div class="risk-signal-detail-dialog__desc">{{ riskDetailItem.dsc }}</div>
<div class="content-text">关于调整进口木材、锯材及其衍生产品进入美国的相关修正案</div>
</div>
<div class="right-arrow">
<img src="@/views/viewRiskSignal/assets/images/right-arrow.png" alt="" />
</div> </div>
</div> </div>
<div class="risk-signal-detail-dialog__desc">
<p class="risk-signal-detail-dialog__desc-p">
任何被列入美国出口管制“实体清单”或“军事最终用户清单”的企业,如果其直接或间接持有另一家公司50%或以上的股权,那么这家被控股的公司也将自动受到与清单上母公司同等的出口管制限制
</p>
<p class="risk-signal-detail-dialog__desc-p">
任何被列入美国出口管制“实体清单”或“军事最终用户清单”的企业,如果其直接或间接持有另一家公司50%或以上的股权,那么这家被控股的公司也将自动受到与清单上母公司同等的出口管制限制
</p>
</div>
<template #footer> <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" @click="visible = false">
确定风险 确定风险
...@@ -54,9 +72,41 @@ ...@@ -54,9 +72,41 @@
</template> </template>
<script setup> <script setup>
import { computed, nextTick, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import AreaTag from "@/components/base/AreaTag/index.vue"; import AreaTag from "@/components/base/AreaTag/index.vue";
import { getRiskSignalInfoById } from "@/api/riskSignal/index.js";
import {
buildListRowFallbackFromRawRow,
buildRiskDetailPayloadFromApi,
getEmptyRiskDetailPayload,
getRiskDetailLevelModifier,
normalizeRiskSignalListLevelText,
resolveRiskSignalRelationRoute,
resolveRiskSignalRowId
} from "@/utils/riskSignalOverviewDetailHelpers.js";
defineProps({ const props = defineProps({
/** 当前点击的列表行(建议先赋值再打开 v-model) */
row: {
type: Object,
default: null
},
/** 与 RiskSignal 组件 name 一致 */
nameField: {
type: String,
default: "name"
},
/** 与 RiskSignal 组件 postDate 一致 */
postDateField: {
type: String,
default: "postDate"
},
/** 与 RiskSignal 组件 riskLevel 一致 */
riskLevelField: {
type: String,
default: "riskLevel"
},
/** 与遮罩、弹窗层级一致,避免被大屏/轮播盖住 */ /** 与遮罩、弹窗层级一致,避免被大屏/轮播盖住 */
zIndex: { zIndex: {
type: Number, type: Number,
...@@ -68,7 +118,116 @@ const emit = defineEmits(["closed"]); ...@@ -68,7 +118,116 @@ const emit = defineEmits(["closed"]);
const visible = defineModel({ type: Boolean, default: false }); const visible = defineModel({ type: Boolean, default: false });
const router = useRouter();
const listLevelText = ref("");
const bodyFromApi = ref(false);
const detailLoading = ref(false);
const riskDetailItem = reactive(getEmptyRiskDetailPayload());
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 ?? "";
riskDetailItem.category = val.category ?? "";
riskDetailItem.directionLabels = Array.isArray(val.directionLabels) ? [...val.directionLabels] : [];
riskDetailItem.relationType = val.relationType ?? "";
riskDetailItem.relationTitle = val.relationTitle ?? "";
riskDetailItem.relationModelCode = val.relationModelCode ?? "";
riskDetailItem.relationReferedId = val.relationReferedId ?? "";
};
const metaLine = computed(() => {
const t = String(riskDetailItem.time ?? "").trim();
const o = String(riskDetailItem.origin ?? "").trim();
if (t && o) {
return `${t}·${o}`;
}
return t || o;
});
const showRelationBar = computed(() => {
const ti = String(riskDetailItem.relationTitle ?? "").trim();
const ty = String(riskDetailItem.relationType ?? "").trim();
const id = String(riskDetailItem.relationReferedId ?? "").trim();
return Boolean(ti || ty || id);
});
const listLevelModifierClass = computed(
() => `risk-signal-detail-dialog__level--${getRiskDetailLevelModifier(listLevelText.value)}`
);
const fieldMap = computed(() => ({
nameField: props.nameField,
postDateField: props.postDateField,
riskLevelField: props.riskLevelField
}));
const resetState = () => {
listLevelText.value = "";
bodyFromApi.value = false;
detailLoading.value = false;
assignRiskDetail(getEmptyRiskDetailPayload());
};
/** 拉取详情:标题区等级来自列表行;正文区仅接口成功后展示 */
const loadDetail = async () => {
if (!visible.value || !props.row) {
return;
}
const listFallback = buildListRowFallbackFromRawRow(props.row, fieldMap.value);
listLevelText.value = normalizeRiskSignalListLevelText(listFallback.risktype);
assignRiskDetail(getEmptyRiskDetailPayload());
bodyFromApi.value = false;
detailLoading.value = false;
const id = resolveRiskSignalRowId(props.row);
if (!id) {
return;
}
detailLoading.value = true;
try {
const res = await getRiskSignalInfoById(id);
if (res.code === 200 && res.data) {
assignRiskDetail(buildRiskDetailPayloadFromApi(res.data, listFallback));
bodyFromApi.value = true;
}
} catch (error) {
console.error("获取风险信号详情 error", error);
} finally {
detailLoading.value = false;
}
};
watch(
() => [visible.value, props.row],
async () => {
if (!visible.value) {
resetState();
return;
}
await nextTick();
await loadDetail();
}
);
const handleRelationClick = () => {
const loc = resolveRiskSignalRelationRoute(
riskDetailItem.relationModelCode,
riskDetailItem.relationReferedId
);
if (!loc) {
return;
}
const href = router.resolve(loc).href;
window.open(href, "_blank");
};
function handleClosed() { function handleClosed() {
resetState();
emit("closed"); emit("closed");
} }
</script> </script>
......
...@@ -196,6 +196,14 @@ ...@@ -196,6 +196,14 @@
border-radius: 20px; border-radius: 20px;
} }
.risk-signal-detail-dialog .risk-signal-detail-dialog__directions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__body { .risk-signal-detail-dialog .risk-signal-detail-dialog__body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -327,3 +335,11 @@ ...@@ -327,3 +335,11 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
} }
/* 详情请求中 v-loading 遮罩区域(与列表一致有足够高度以便居中) */
.risk-signal-detail-dialog .risk-signal-detail-dialog__main {
position: relative;
width: 100%;
min-height: 480px;
box-sizing: border-box;
}
/** 风险信号概览详情弹窗:与 viewRiskSignal 详情逻辑对齐的纯函数 */
/** 从概览/列表行解析详情接口 id */
export function resolveRiskSignalRowId(row) {
if (!row || typeof row !== "object") {
return "";
}
const keys = ["id", "riskId", "riskSignalId", "orderId", "signalId"];
for (const k of keys) {
const v = row[k];
if (v != null && String(v).trim() !== "") {
return String(v).trim();
}
}
return "";
}
/** PageLimit 列表项:领域标签(优先 domainNames,兼容 domainName / domains) */
export function 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日」 */
export function 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;
}
export function getEmptyRiskDetailPayload() {
return {
title: "",
origin: "",
time: "",
dsc: "",
tag: [],
risktype: "",
category: "",
/** 风险方向(接口 direction,可多值)对应中文文案列表 */
directionLabels: [],
relationType: "",
relationTitle: "",
relationModelCode: "",
relationReferedId: ""
};
}
/** 风险信号详情接口 `direction` 字段 → 中文(与 `model` 的 0100/0101 含义不同,单独映射) */
export const RISK_SIGNAL_DIRECTION_LABEL_MAP = {
"0100": "政策法规打压类风险",
"0101": "核心自主可控性风险",
"0102": "科技要素风险",
"0103": "其他风险动向"
};
export function getRiskSignalDirectionLabel(code) {
const c = String(code ?? "").trim();
return RISK_SIGNAL_DIRECTION_LABEL_MAP[c] ?? "";
}
/** 解析接口 `direction`:支持数组、逗号/分号分隔字符串 */
export function parseRiskSignalDirectionCodes(raw) {
if (raw == null) {
return [];
}
if (Array.isArray(raw)) {
return raw.map((x) => String(x ?? "").trim()).filter(Boolean);
}
const s = String(raw).trim();
if (!s) {
return [];
}
return s.split(/[,,、;;|]+/).map((x) => x.trim()).filter(Boolean);
}
/** 方向编码列表 → 去重后的中文标签(顺序:先 data 后 list 合并去重) */
export function buildRiskSignalDirectionLabels(codes) {
if (!Array.isArray(codes) || !codes.length) {
return [];
}
const seen = new Set();
const labels = [];
for (const code of codes) {
const c = String(code ?? "").trim();
if (!c || seen.has(c)) {
continue;
}
seen.add(c);
const lbl = getRiskSignalDirectionLabel(c);
if (lbl) {
labels.push(lbl);
}
}
return labels;
}
/** 风险信号详情:model 字典编码 → 中文类型 */
export const RISK_SIGNAL_MODEL_LABEL_MAP = {
"0100": "法案",
"0101": "政令",
"0102": "智库报告",
"0103": "出口管制",
"0104": "市场准入",
"0105": "创新主体",
"0106": "合作限制",
"0107": "科研资助体系",
"0108": "规则限制",
"0109": "投融资限制",
"01031": "实体清单",
"01032": "商业管制清单",
"01091": "特别指定国民清单",
"01092": "涉军企业清单",
"01041": "337调查",
"01042": "232调查",
"01043": "301调查"
};
export function getRiskSignalModelLabel(code) {
const c = String(code ?? "").trim();
return RISK_SIGNAL_MODEL_LABEL_MAP[c] ?? "";
}
/** 根据 model + referedId 解析新开页路由 */
export function resolveRiskSignalRelationRoute(modelCode, referedId) {
const code = String(modelCode ?? "").trim();
const id = String(referedId ?? "").trim();
if (!id) {
return null;
}
switch (code) {
case "0100":
return { path: "/billLayout/bill/introduction", query: { billId: id } };
case "0101":
return { path: "/decreeLayout/overview/introduction", query: { id } };
case "0102":
return { name: "ReportDetail", params: { id } };
case "0103":
return { path: "/exportControl", query: { id } };
case "0104":
case "01041":
case "01042":
case "01043":
return { path: "/marketSingleCaseLayout/overview", query: { id } };
case "0105": {
const segs = id
.split("/")
.map((s) => s.trim())
.filter(Boolean);
if (segs.length >= 2) {
return { name: "InnovativeInstitutions", params: { id: segs[0], type: segs[1] } };
}
return { name: "InnovativeInstitutions", params: { id, type: "0" } };
}
case "0106":
return { path: "/cooperationRestrictions/detail", query: { id } };
case "0107":
return { path: "/exportControl/researchfunding", query: { id } };
case "0108":
return { path: "/exportControl/ruledetail", query: { id } };
case "0109":
return { path: "/finance", query: { id } };
case "01031":
return { path: "/exportControl/entityList", query: { sanTypeId: id } };
case "01032":
return { path: "/exportControl/commercialControlList", query: { sanTypeId: id } };
case "01091":
return { path: "/finance/singleSanction", query: { id } };
case "01092":
return { path: "/finance/cmccontrolList", query: { sanTypeId: id } };
default:
return null;
}
}
/** 与详情弹窗标题区等级色块一致 */
export function getRiskDetailLevelModifier(level) {
const t = String(level ?? "").trim();
if (t === "status" || t.length <= 1) {
return "lv4";
}
if (t.includes("特别重大")) {
return "lv1";
}
if (t === "重大风险" || (t.includes("重大") && !t.includes("较大") && !t.includes("特别"))) {
return "lv2";
}
if (t === "较大风险" || t.includes("较大")) {
return "lv3";
}
if (t === "低风险") {
return "lv5";
}
if (t === "一般风险" || !t || t === "暂无数据") {
return "lv4";
}
return "lv4";
}
/** 与 `@/components/base/riskSignal` 左侧等级一致:空 / null 等展示「暂无数据」 */
export const RISK_SIGNAL_LIST_LEVEL_EMPTY_TEXT = "暂无数据";
/**
* 列表行风险等级 → 弹窗标题区文案(null、空串与「暂无数值」等与列表「暂无数据」对齐)
* @param {unknown} raw
*/
export function normalizeRiskSignalListLevelText(raw) {
if (raw == null) {
return RISK_SIGNAL_LIST_LEVEL_EMPTY_TEXT;
}
const s = String(raw).trim();
if (s === "" || s === "null" || s === "undefined" || s === "暂无数值") {
return RISK_SIGNAL_LIST_LEVEL_EMPTY_TEXT;
}
return s;
}
/**
* 将概览页原始行转为详情接口合并用的兜底对象(优先已映射字段 title/time/risktype,再用字段别名)
* @param {Record<string, unknown>} row
* @param {{ nameField?: string; postDateField?: string; riskLevelField?: string }} fields
*/
export function buildListRowFallbackFromRawRow(row, fields) {
if (!row || typeof row !== "object") {
return {};
}
const nameField = fields?.nameField ?? "name";
const postDateField = fields?.postDateField ?? "postDate";
const riskLevelField = fields?.riskLevelField ?? "riskLevel";
let title = "";
if (row.title != null && String(row.title).trim() !== "") {
title = String(row.title);
} else if (row.titleZh != null && String(row.titleZh).trim() !== "") {
title = String(row.titleZh);
} else if (row[nameField] != null) {
title = String(row[nameField]);
}
const time = String(row.time ?? row[postDateField] ?? row.postDate ?? "").trim();
const risktype = String(row.risktype ?? row[riskLevelField] ?? row.riskLevel ?? "").trim();
const origin = String(row.orgName ?? row.origin ?? "").trim();
const dsc = String(row.summary ?? row.contentZh ?? row.description ?? row.dsc ?? row.content ?? "").trim();
const category = String(row.typeName ?? row.module ?? row.category ?? "").trim();
const direction = row.direction != null && row.direction !== "" ? row.direction : "";
return {
title,
time,
risktype,
origin,
dsc,
tag: parseRiskSignalTagList(row),
category,
direction
};
}
/**
* 将 getRiskSignalInfoById 返回 data 转为详情 payload(listRow 为列表行兜底,形状同 buildListRowFallbackFromRawRow)
* @param {Record<string, unknown>} data
* @param {Record<string, unknown>} listRow
*/
export function buildRiskDetailPayloadFromApi(data, listRow) {
if (!data || typeof data !== "object") {
return getEmptyRiskDetailPayload();
}
const list = listRow && typeof listRow === "object" ? listRow : {};
const postRaw = data.postDate ?? data.publishDate ?? data.time ?? "";
const category =
data.typeName ?? data.module ?? data.modle ?? data.riskCategory ?? "";
const org = data.orgName != null && String(data.orgName).trim() !== "" ? String(data.orgName).trim() : "";
const modelCode = data.model != null ? String(data.model).trim() : "";
const referedRaw = data.referedId ?? data.referredId;
const relationReferedId = referedRaw != null ? String(referedRaw).trim() : "";
const modelLabel = getRiskSignalModelLabel(modelCode);
const relationType = modelLabel ? `${modelLabel}-` : modelCode ? `${modelCode}-` : "";
const relationTitle =
data.modelTitle != null && String(data.modelTitle).trim() !== ""
? String(data.modelTitle).trim()
: "";
const detailTags = parseRiskSignalTagList(data);
const apiCategory = String(category).trim();
const apiTime = formatRiskPublishDisplay(postRaw) || String(postRaw || "").trim();
const directionCodes = [
...parseRiskSignalDirectionCodes(data.direction),
...parseRiskSignalDirectionCodes(list.direction)
];
const directionLabels = buildRiskSignalDirectionLabels(directionCodes);
return {
title: data.title ?? list.title ?? "",
category: apiCategory || String(list.category ?? "").trim(),
directionLabels,
time: apiTime || String(list.time ?? "").trim(),
origin: org || list.origin || "",
dsc: data.description ?? data.contentZh ?? data.summary ?? list.dsc ?? "",
tag: detailTags.length ? detailTags : Array.isArray(list.tag) ? [...list.tag] : [],
risktype: String(list.risktype ?? "").trim(),
relationType,
relationTitle,
relationModelCode: modelCode,
relationReferedId: relationReferedId
};
}
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
class="risk-signals-item" class="risk-signals-item"
v-for="(item, index) in warningList" v-for="(item, index) in warningList"
:key="item.signalId || item.billId || index" :key="item.signalId || item.billId || index"
@click="handleRiskSignalItemToManage" @click="handleRiskSignalItemToManage(item)"
:class="{ highlighted: item.eventType === highlightedEventType }" :class="{ highlighted: item.eventType === highlightedEventType }"
> >
<div <div
...@@ -115,7 +115,13 @@ ...@@ -115,7 +115,13 @@
</div> </div>
</div> </div>
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</template> </template>
...@@ -292,8 +298,10 @@ const handleSwithCurNews = name => { ...@@ -292,8 +298,10 @@ const handleSwithCurNews = name => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
:key="item.signalId != null ? String(item.signalId) : 'risk-' + index" :key="item.signalId != null ? String(item.signalId) : 'risk-' + index"
@mouseenter="onMouseEnter(item, index)" @mouseenter="onMouseEnter(item, index)"
@mouseleave="onMouseLeave" @mouseleave="onMouseLeave"
@click.stop="handleRiskSignalRowToManage" @click.stop="handleRiskSignalRowToManage(item)"
:class="['risk-signals-item', { 'risk-signals-item-hightLight': riskSignalActiveIndex === index }]" :class="['risk-signals-item', { 'risk-signals-item-hightLight': riskSignalActiveIndex === index }]"
> >
<div class="item-left" :class="{ <div class="item-left" :class="{
...@@ -125,7 +125,13 @@ ...@@ -125,7 +125,13 @@
</div> </div>
</div> </div>
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</template> </template>
...@@ -655,8 +661,10 @@ const filteredHotNewsList = computed(() => { ...@@ -655,8 +661,10 @@ const filteredHotNewsList = computed(() => {
return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value); return hotNewsList.value.filter(newsItem => newsItem.signalId === currentHoveredSignalId.value);
}); });
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalRowToManage = () => { const handleRiskSignalRowToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -238,7 +238,13 @@ ...@@ -238,7 +238,13 @@
<DivideHeader id="position4" class="divide4" :titleText="'资源库'"></DivideHeader> <DivideHeader id="position4" class="divide4" :titleText="'资源库'"></DivideHeader>
<ResourceLibrarySection :on-click-to-detail="handleClickToDetailO" :on-after-page-change="handlePageChange" /> <ResourceLibrarySection :on-click-to-detail="handleClickToDetailO" :on-after-page-change="handlePageChange" />
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -469,8 +475,10 @@ const handleClickToDetailO = item => { ...@@ -469,8 +475,10 @@ const handleClickToDetailO = item => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -113,7 +113,13 @@ ...@@ -113,7 +113,13 @@
<RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content" <RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content"
riskLevel="title" @item-click="handleRiskSignalItemToManage" /> riskLevel="title" @item-click="handleRiskSignalItemToManage" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="content"
post-date-field="time"
risk-level-field="title"
/>
</div> </div>
</template> </template>
...@@ -214,8 +220,10 @@ const handleToRiskDetail = (item) => { ...@@ -214,8 +220,10 @@ const handleToRiskDetail = (item) => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -403,7 +403,13 @@ ...@@ -403,7 +403,13 @@
</div> </div>
</div> </div>
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</template> </template>
...@@ -495,7 +501,10 @@ const onNavigateTo = () => { ...@@ -495,7 +501,10 @@ const onNavigateTo = () => {
// 查看更多风险信号 // 查看更多风险信号
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const handleRiskSignalItemToManage = () => { const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -692,7 +692,13 @@ ...@@ -692,7 +692,13 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</template> </template>
<script setup> <script setup>
...@@ -801,9 +807,10 @@ const handleToPosi = id => { ...@@ -801,9 +807,10 @@ const handleToPosi = id => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
// 风险信号:概览写死详情弹窗 const handleToRiskSignalDetail = (item) => {
const handleToRiskSignalDetail = () => { riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -544,7 +544,13 @@ ...@@ -544,7 +544,13 @@
</custom-container> </custom-container>
</el-col> </el-col>
</el-row> </el-row>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="title"
post-date-field="time"
risk-level-field="status"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -635,8 +641,10 @@ const messageList = ref([ ...@@ -635,8 +641,10 @@ const messageList = ref([
} }
]); ]);
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleToRiskSignalDetail = () => { const handleToRiskSignalDetail = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -549,7 +549,13 @@ ...@@ -549,7 +549,13 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</template> </template>
<script setup> <script setup>
...@@ -668,9 +674,10 @@ const handleToPosi = id => { ...@@ -668,9 +674,10 @@ const handleToPosi = id => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
// 风险信号:概览写死详情弹窗 const handleToRiskSignalDetail = (item) => {
const handleToRiskSignalDetail = () => { riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -75,7 +75,12 @@ ...@@ -75,7 +75,12 @@
</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" @click="handleRiskSignalItemClick"> <div
v-for="(item, index) in warningList"
:key="item.title + '-' + item.time + '-' + index"
class="waring-item"
@click="handleRiskSignalItemClick(item)"
>
<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',
...@@ -114,7 +119,13 @@ ...@@ -114,7 +119,13 @@
<AdvantagesAnalysis /> <AdvantagesAnalysis />
<div style="width: 100%;height: 100px;"></div> <div style="width: 100%;height: 100px;"></div>
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="title"
post-date-field="time"
risk-level-field="riskLevel"
/>
</div> </div>
</template> </template>
...@@ -224,8 +235,15 @@ const warningList = ref([ ...@@ -224,8 +235,15 @@ const warningList = ref([
]); ]);
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemClick = () => { const mapRiskOverviewRowFromStatus = (item) => ({
...item,
riskLevel: item.status === 0 ? "特别重大" : item.status === 1 ? "重大风险" : "一般风险"
});
const handleRiskSignalItemClick = (item) => {
riskOverviewDetailRow.value = mapRiskOverviewRowFromStatus(item);
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -147,7 +147,13 @@ ...@@ -147,7 +147,13 @@
</div> --> </div> -->
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
riskLevel="signalLevel" postDate="signalTime" name="signalTitle" /> riskLevel="signalLevel" postDate="signalTime" name="signalTitle" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
<DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader>
<div class="center-center"> <div class="center-center">
...@@ -518,8 +524,10 @@ const handleClickToDetail = university => { ...@@ -518,8 +524,10 @@ const handleClickToDetail = university => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -59,7 +59,13 @@ ...@@ -59,7 +59,13 @@
</overviewMainBox> </overviewMainBox>
</div> </div>
<RiskSignal :list="box2Data" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" postDate="signalTime" name="signalTitle" riskLevel="signalLevel" /> <RiskSignal :list="box2Data" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
<DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide-header" :titleText="'资讯要闻'"></DivideHeader>
...@@ -933,7 +939,10 @@ const handleFetchSurveyList = async () => { ...@@ -933,7 +939,10 @@ const handleFetchSurveyList = async () => {
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const handleRiskSignalItemToManage = () => { const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -112,7 +112,12 @@ ...@@ -112,7 +112,12 @@
</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" @click="handleRiskSignalItemClick"> <div
class="waring-item"
v-for="(item, index) in warningList"
:key="item.title + '-' + item.time + '-' + index"
@click="handleRiskSignalItemClick(item)"
>
<div style="display: flex; height: 47px"> <div style="display: flex; height: 47px">
<div <div
class="waring-status" class="waring-status"
...@@ -195,7 +200,13 @@ ...@@ -195,7 +200,13 @@
<strengthComparison /> <strengthComparison />
</div> </div>
</div> </div>
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="title"
post-date-field="time"
risk-level-field="riskLevel"
/>
</div> </div>
</template> </template>
...@@ -227,8 +238,15 @@ const handleToSearch = () => { ...@@ -227,8 +238,15 @@ const handleToSearch = () => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemClick = () => { const mapRiskOverviewRowFromStatus = (item) => ({
...item,
riskLevel: item.status === 0 ? "特别重大" : item.status === 1 ? "重大风险" : "一般风险"
});
const handleRiskSignalItemClick = (item) => {
riskOverviewDetailRow.value = mapRiskOverviewRowFromStatus(item);
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -71,7 +71,13 @@ ...@@ -71,7 +71,13 @@
</div> --> </div> -->
<RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" <RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
riskLevel="signalLevel" postDate="signalTime" name="signalTitle" /> riskLevel="signalLevel" postDate="signalTime" name="signalTitle" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</template> </template>
...@@ -198,8 +204,10 @@ const handleToRiskDetail = (item) => { ...@@ -198,8 +204,10 @@ const handleToRiskDetail = (item) => {
// }; // };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -92,7 +92,13 @@ ...@@ -92,7 +92,13 @@
</div> --> </div> -->
<RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" <RiskSignal :list="list" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
postDate="signalTime" name="signalTitle" riskLevel="signalLevel" /> postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
</template> </template>
...@@ -128,8 +134,10 @@ const formatDate = (dateStr) => { ...@@ -128,8 +134,10 @@ const formatDate = (dateStr) => {
return `${y}${m}${d}日`; return `${y}${m}${d}日`;
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -157,7 +157,13 @@ ...@@ -157,7 +157,13 @@
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" @item-click="handleRiskSignalItemToManage"
postDate="signalTime" name="signalTitle" riskLevel="signalLevel" /> postDate="signalTime" name="signalTitle" riskLevel="signalLevel" />
<RiskSignalOverviewDetailDialog v-model="isRiskOverviewDetailOpen" /> <RiskSignalOverviewDetailDialog
v-model="isRiskOverviewDetailOpen"
:row="riskOverviewDetailRow"
name-field="signalTitle"
post-date-field="signalTime"
risk-level-field="signalLevel"
/>
</div> </div>
<DivideHeader id="position2" class="divide-header" :titleText="'言论动态'" v-if="false"></DivideHeader> <DivideHeader id="position2" class="divide-header" :titleText="'言论动态'" v-if="false"></DivideHeader>
<div class="center-center"> <div class="center-center">
...@@ -828,8 +834,10 @@ const handleClickCate = cate => { ...@@ -828,8 +834,10 @@ const handleClickCate = cate => {
}; };
const isRiskOverviewDetailOpen = ref(false); const isRiskOverviewDetailOpen = ref(false);
const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => { const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskOverviewDetailOpen.value = true; isRiskOverviewDetailOpen.value = true;
}; };
......
...@@ -211,7 +211,12 @@ ...@@ -211,7 +211,12 @@
<RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" postDate="time" name="title" <RiskSignal :list="warningList" @more-click="handleToMoreRiskSignal" postDate="time" name="title"
@item-click="handleRiskSignalItemToManage" /> @item-click="handleRiskSignalItemToManage" />
<RiskSignalOverviewDetailDialog v-model="isRiskDetailVisible" /> <RiskSignalOverviewDetailDialog
v-model="isRiskDetailVisible"
:row="riskOverviewDetailRow"
name-field="title"
post-date-field="time"
/>
</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">
...@@ -2139,8 +2144,10 @@ const handleClick = tank => { ...@@ -2139,8 +2144,10 @@ 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:当前页弹窗(内容与样式与风险信号管理页 dialog 一致,写死展示) const riskOverviewDetailRow = ref(null);
const handleRiskSignalItemToManage = () => {
const handleRiskSignalItemToManage = (item) => {
riskOverviewDetailRow.value = item ?? null;
isRiskDetailVisible.value = true; isRiskDetailVisible.value = true;
}; };
......
...@@ -171,14 +171,14 @@ ...@@ -171,14 +171,14 @@
style="width: 120px" :teleported="true" placement="bottom-start" style="width: 120px" :teleported="true" placement="bottom-start"
:popper-options="resourceLibrarySortPopperOptions" @change="handleSortChange"> :popper-options="resourceLibrarySortPopperOptions" @change="handleSortChange">
<template #prefix> <template #prefix>
<img v-if="sortModel !== true" <img v-if="sortValue === 1"
src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png" src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png"
class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" /> class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" />
<img v-else src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png" <img v-else src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png"
class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" /> class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" />
</template> </template>
<el-option :key="'risk-sort-asc'" label="正序" :value="true" /> <el-option :key="'risk-sort-asc'" label="正序" :value="0" />
<el-option :key="'risk-sort-desc'" label="倒序" :value="false" /> <el-option :key="'risk-sort-desc'" label="倒序" :value="1" />
</el-select> </el-select>
</div> </div>
</div> </div>
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
</div> </div>
<div class="right-footer"> <div class="right-footer">
<div class="footer-left"> <div class="footer-left">
{{ `共 ${formatRiskStatCount(totalNum)}调查` }} {{ `共 ${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"
...@@ -220,32 +220,45 @@ ...@@ -220,32 +220,45 @@
width="1280px" align-center :show-close="true" destroy-on-close @closed="handleCloseRiskDetail"> width="1280px" align-center :show-close="true" destroy-on-close @closed="handleCloseRiskDetail">
<template #header> <template #header>
<img class="header-icon" src="./assets/images/risk-icon.png" alt="" /> <img class="header-icon" src="./assets/images/risk-icon.png" alt="" />
<span class="risk-signal-detail-dialog__level" :class="riskDetailLevelModifierClass">{{ riskDetailItem.risktype <span v-if="riskDetailListLevelText" class="risk-signal-detail-dialog__level"
}}</span> :class="riskDetailListLevelModifierClass">{{ riskDetailListLevelText }}</span>
</template> </template>
<div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body"> <div class="risk-signal-detail-dialog__main" v-loading="riskDetailDetailLoading">
<span class="risk-signal-detail-dialog__title">{{ riskDetailItem.title }}</span> <div v-if="riskDetailBodyFromApi">
<div class="risk-signal-detail-dialog__origin">{{ "政策法规打压类风险" }}</div> <div v-if="riskDetailItem.title" class="risk-signal-detail-dialog__body">
<div class="risk-signal-detail-dialog__meta"> <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-for="(dirLabel, dirIndex) in riskDetailItem.directionLabels"
:key="'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> {{ riskDetailItem.time }}·{{ riskDetailItem.origin }}</span> <span>{{ riskDetailMetaLine }}</span>
<div v-if="riskDetailItem.tag.length" class="risk-signal-detail-dialog__tags"> <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" <AreaTag v-for="(tag, index) in riskDetailItem.tag" :key="'risk-detail-tag-' + index + '-' + tag"
:tagName="tag"> :tagName="tag">
{{ tag }}</AreaTag> {{ tag }}</AreaTag>
</div>
</div>
</div> </div>
<div v-if="riskDetailShowRelationBar" class="risk-signal-detail-dialog_relation"
@click="handleRiskDetailRelationClick">
<div class="relation">
<div class="logo"><img src="./assets/images/logo.png" alt="" /></div>
<div class="name-text">{{ riskDetailItem.relationType }}</div>
<div class="content-text">{{ riskDetailItem.relationTitle }}</div>
</div>
<div class="right-arrow"><img src="./assets/images/right-arrow.png" alt="" /></div>
</div>
<div class="risk-signal-detail-dialog__desc">{{ riskDetailItem.dsc }}</div>
</div> </div>
</div> </div>
<div class="risk-signal-detail-dialog_relation">
<div class="relation">
<div class="logo"><img src="./assets/images/logo.png" alt="" /></div>
<div class="name-text">{{ "总统行政令" }}</div>
<div class="content-text">{{ "关于调整进口木材、锯材及其衍生产品进入美国的相关修正案" }}</div>
</div>
<div class="right-arrow"><img src="./assets/images/right-arrow.png" alt="" /></div>
</div>
<div class="risk-signal-detail-dialog__desc">{{ riskDetailItem.dsc }}</div>
<template #footer> <template #footer>
<el-button type="primary" class="risk-signal-detail-dialog__action-btn" @click="isRiskDetailVisible = false"> <el-button type="primary" class="risk-signal-detail-dialog__action-btn" @click="isRiskDetailVisible = false">
确定风险 确定风险
...@@ -259,7 +272,15 @@ ...@@ -259,7 +272,15 @@
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 { getRiskSignalBaseInfo, getPageQuery } from "@/api/riskSignal/index"; import {
buildRiskDetailPayloadFromApi,
formatRiskPublishDisplay,
getRiskDetailLevelModifier,
normalizeRiskSignalListLevelText,
parseRiskSignalTagList,
resolveRiskSignalRelationRoute
} from "@/utils/riskSignalOverviewDetailHelpers.js";
import { getRiskSignalBaseInfo, getPageQuery, getRiskSignalInfoById } 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";
...@@ -412,13 +433,41 @@ const handleClickBtn = item => { ...@@ -412,13 +433,41 @@ const handleClickBtn = item => {
const riskList = ref([]); const riskList = ref([]);
const isRiskDetailVisible = ref(false); const isRiskDetailVisible = ref(false);
/** 弹窗标题区:始终展示当前列表项的风险等级文案 */
const riskDetailListLevelText = ref("");
/** 标题/分类/时间/标签/正文/关联条等:仅接口返回后再展示 */
const riskDetailBodyFromApi = ref(false);
/** 详情接口请求中(与列表 v-loading 一致,遮罩内垂直水平居中) */
const riskDetailDetailLoading = ref(false);
const getEmptyRiskDetailPayload = () => ({
title: "",
origin: "",
time: "",
dsc: "",
tag: [],
risktype: "",
category: "",
directionLabels: [],
relationType: "",
relationTitle: "",
relationModelCode: "",
relationReferedId: ""
});
const riskDetailItem = reactive({ const riskDetailItem = reactive({
title: "", title: "",
origin: "", origin: "",
time: "", time: "",
dsc: "", dsc: "",
tag: [], tag: [],
risktype: "" risktype: "",
category: "",
directionLabels: [],
relationType: "",
relationTitle: "",
relationModelCode: "",
relationReferedId: ""
}); });
const assignRiskDetail = (val) => { const assignRiskDetail = (val) => {
...@@ -428,30 +477,82 @@ const assignRiskDetail = (val) => { ...@@ -428,30 +477,82 @@ const assignRiskDetail = (val) => {
riskDetailItem.dsc = val.dsc ?? ""; riskDetailItem.dsc = val.dsc ?? "";
riskDetailItem.tag = Array.isArray(val.tag) ? [...val.tag] : []; riskDetailItem.tag = Array.isArray(val.tag) ? [...val.tag] : [];
riskDetailItem.risktype = val.risktype ?? ""; riskDetailItem.risktype = val.risktype ?? "";
riskDetailItem.category = val.category ?? "";
riskDetailItem.directionLabels = Array.isArray(val.directionLabels) ? [...val.directionLabels] : [];
riskDetailItem.relationType = val.relationType ?? "";
riskDetailItem.relationTitle = val.relationTitle ?? "";
riskDetailItem.relationModelCode = val.relationModelCode ?? "";
riskDetailItem.relationReferedId = val.relationReferedId ?? "";
}; };
const handleOpenRiskDetail = (val) => { /** 详情弹窗:时间与来源一行(避免模板里拼接) */
assignRiskDetail(val); const riskDetailMetaLine = computed(() => {
isRiskDetailVisible.value = true; const t = String(riskDetailItem.time ?? "").trim();
const o = String(riskDetailItem.origin ?? "").trim();
if (t && o) {
return `${t}·${o}`;
}
return t || o;
});
/** 详情关联条:有文案或有关联 id 时展示 */
const riskDetailShowRelationBar = computed(() => {
const t = String(riskDetailItem.relationTitle ?? "").trim();
const p = String(riskDetailItem.relationType ?? "").trim();
const id = String(riskDetailItem.relationReferedId ?? "").trim();
return Boolean(t || p || id);
});
const handleRiskDetailRelationClick = () => {
const loc = resolveRiskSignalRelationRoute(
riskDetailItem.relationModelCode,
riskDetailItem.relationReferedId
);
if (!loc) {
return;
}
const href = router.resolve(loc).href;
window.open(href, "_blank");
}; };
const handleCloseRiskDetail = () => { /** 将 getRiskSignalInfoById 返回 data 写入详情(listRow 为列表行兜底) */
assignRiskDetail({ tag: [] }); const assignRiskDetailFromApi = (data, listRow) => {
assignRiskDetail(buildRiskDetailPayloadFromApi(data, listRow));
}; };
/** 与通用风险信号组件 `riskSignal` 中 itemLeftStatus1~5 色值一致 */ const handleOpenRiskDetail = async (val) => {
const getRiskDetailLevelModifier = (level) => { riskDetailListLevelText.value = normalizeRiskSignalListLevelText(val?.risktype);
const t = String(level ?? "").trim(); assignRiskDetail(getEmptyRiskDetailPayload());
if (t.includes("特别重大")) return "lv1"; riskDetailBodyFromApi.value = false;
if (t === "重大风险" || (t.includes("重大") && !t.includes("较大") && !t.includes("特别"))) return "lv2"; riskDetailDetailLoading.value = false;
if (t === "较大风险" || t.includes("较大")) return "lv3"; isRiskDetailVisible.value = true;
if (t === "低风险") return "lv5"; const id = val?.id;
if (t === "一般风险" || !t || t === "暂无数据") return "lv4"; if (id == null || id === "") {
return "lv4"; return;
}
riskDetailDetailLoading.value = true;
try {
const res = await getRiskSignalInfoById(String(id));
if (res.code === 200 && res.data) {
assignRiskDetailFromApi(res.data, val);
riskDetailBodyFromApi.value = true;
}
} catch (error) {
console.error("获取风险信号详情 error", error);
} finally {
riskDetailDetailLoading.value = false;
}
}; };
const riskDetailLevelModifierClass = computed( const handleCloseRiskDetail = () => {
() => `risk-signal-detail-dialog__level--${getRiskDetailLevelModifier(riskDetailItem.risktype)}` riskDetailListLevelText.value = "";
riskDetailBodyFromApi.value = false;
riskDetailDetailLoading.value = false;
assignRiskDetail(getEmptyRiskDetailPayload());
};
const riskDetailListLevelModifierClass = computed(
() => `risk-signal-detail-dialog__level--${getRiskDetailLevelModifier(riskDetailListLevelText.value)}`
); );
/** 列表项风险等级样式键:与 `@/components/base/riskSignal` itemLeftStatus1~5 一致 */ /** 列表项风险等级样式键:与 `@/components/base/riskSignal` itemLeftStatus1~5 一致 */
...@@ -545,7 +646,9 @@ const handleCleandarChart = async () => { ...@@ -545,7 +646,9 @@ const handleCleandarChart = async () => {
setChart(chartCalendar, "chartCalendar"); setChart(chartCalendar, "chartCalendar");
}; };
/** null:占位「发布时间」且默认倒序;true 正序;false 倒序(显式选中),与智库概览资源库一致 */ /** sort:0 正序;1 倒序;接口默认倒序=1 */
const sortValue = ref(1);
/** el-select 展示用:默认 null => 显示 placeholder「发布时间」 */
const sortModel = ref(null); const sortModel = ref(null);
const resourceLibrarySortPopperOptions = { const resourceLibrarySortPopperOptions = {
...@@ -556,17 +659,22 @@ const resourceLibrarySortPopperOptions = { ...@@ -556,17 +659,22 @@ const resourceLibrarySortPopperOptions = {
}; };
const toggleSortPrefix = () => { const toggleSortPrefix = () => {
sortModel.value = sortModel.value === true ? false : true; sortValue.value = sortValue.value === 1 ? 0 : 1;
sortModel.value = sortValue.value;
handleSortChange(); handleSortChange();
}; };
const handleSortChange = () => { const handleSortChange = () => {
if (sortModel.value === 0 || sortModel.value === 1) {
sortValue.value = sortModel.value;
}
handleGetPageQuery(); handleGetPageQuery();
}; };
// 搜索 // 搜索
const kewword = ref(""); const kewword = ref("");
const handleSearch = async () => { const handleSearch = async () => {
currentPage.value = 1;
handleGetPageQuery(); handleGetPageQuery();
}; };
...@@ -612,55 +720,13 @@ const handleCurrentChange = page => { ...@@ -612,55 +720,13 @@ 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) // 按条件分页查询风险信号信息(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 riskLevel = stripAll(selectedRiskDegreeModel.value, RISK_FILTER_ALL_LEVEL);
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);
...@@ -689,8 +755,9 @@ const handleGetPageQuery = async () => { ...@@ -689,8 +755,9 @@ const handleGetPageQuery = async () => {
riskTypes, riskTypes,
countryId: srcCountryList, countryId: srcCountryList,
directionId: [], directionId: [],
riskLevels, riskLevel,
day, day,
sort: sortValue.value,
keyWord: kewword.value, keyWord: kewword.value,
pageNum: currentPage.value - 1, pageNum: currentPage.value - 1,
...@@ -701,9 +768,10 @@ const handleGetPageQuery = async () => { ...@@ -701,9 +768,10 @@ const handleGetPageQuery = async () => {
riskTypes, riskTypes,
countryId: srcCountryList, countryId: srcCountryList,
directionId: [], directionId: [],
riskLevels, riskLevel,
dealStatus: activeProcessStatusId.value, dealStatus: activeProcessStatusId.value,
day, day,
sort: sortValue.value,
keyWord: kewword.value, keyWord: kewword.value,
pageNum: currentPage.value - 1, pageNum: currentPage.value - 1,
...@@ -723,18 +791,22 @@ const handleGetPageQuery = async () => { ...@@ -723,18 +791,22 @@ const handleGetPageQuery = async () => {
const stableId = item.id ?? item.riskId ?? item.riskSignalId; const stableId = item.id ?? item.riskId ?? item.riskSignalId;
const title = item.title ?? item.titleZh ?? ""; const title = item.title ?? item.titleZh ?? "";
const publishRaw = item.publishDate ?? item.time ?? ""; const publishRaw = item.publishDate ?? item.time ?? "";
const typeName = item.typeName ?? item.type ?? item.modle ?? item.module ?? "";
return { return {
id: stableId != null ? stableId : null,
rowKey: rowKey:
stableId != null stableId != null
? String(stableId) ? String(stableId)
: `p${currentPage.value}-i${i}-${String(publishRaw)}-${String(title)}`, : `p${currentPage.value}-i${i}-${String(publishRaw)}-${String(title)}`,
title, title,
origin: item.orgName != null && String(item.orgName).trim() !== "" ? String(item.orgName).trim() : "", origin: item.orgName != null && String(item.orgName).trim() !== "" ? String(item.orgName).trim() : "",
typeName: item.typeName ?? item.type ?? item.modle ?? item.module ?? "", typeName,
category: String(typeName).trim(),
time: formatRiskPublishDisplay(publishRaw) || String(publishRaw || ""), time: formatRiskPublishDisplay(publishRaw) || String(publishRaw || ""),
dsc: item.contentZh ?? item.summary ?? item.description ?? "", dsc: item.contentZh ?? item.summary ?? item.description ?? "",
tag: parseRiskSignalTagList(item), tag: parseRiskSignalTagList(item),
risktype: item.riskLevel ?? item.level ?? "", risktype: item.riskLevel ?? item.level ?? "",
direction: item.direction == null || item.direction === "" ? "" : item.direction,
pic: item.orgLogo || "" pic: item.orgLogo || ""
}; };
}); });
...@@ -1306,6 +1378,14 @@ onMounted(async () => { ...@@ -1306,6 +1378,14 @@ onMounted(async () => {
border-radius: 20px; border-radius: 20px;
} }
.risk-signal-detail-dialog__directions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.risk-signal-detail-dialog__body { .risk-signal-detail-dialog__body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -1411,7 +1491,7 @@ onMounted(async () => { ...@@ -1411,7 +1491,7 @@ onMounted(async () => {
} }
.risk-signal-detail-dialog__desc { .risk-signal-detail-dialog__desc {
height: 360px;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
font-family: 'Source Han Sans CN'; font-family: 'Source Han Sans CN';
font-weight: 400; font-weight: 400;
...@@ -1422,7 +1502,12 @@ onMounted(async () => { ...@@ -1422,7 +1502,12 @@ onMounted(async () => {
margin-top: 24px; margin-top: 24px;
padding-left: 12px; padding-left: 12px;
padding-right: 12px; padding-right: 12px;
height: 310px; display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 12;
line-clamp: 12;
overflow: hidden;
text-overflow: ellipsis;
} }
.risk-signal-detail-dialog__tags { .risk-signal-detail-dialog__tags {
...@@ -1604,16 +1689,44 @@ onMounted(async () => { ...@@ -1604,16 +1689,44 @@ onMounted(async () => {
padding-top: 34px; padding-top: 34px;
} }
/* 详情请求中 v-loading 遮罩区域(与列表一致有足够高度以便居中) */
.risk-signal-detail-dialog .risk-signal-detail-dialog__main {
position: relative;
width: 100%;
min-height: 480px;
box-sizing: border-box;
}
.risk-signal-detail-dialog .risk-signal-detail-dialog__directions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
/* 避免内容撑出横向滚动;长文换行(不出现滚动条) */ /* 避免内容撑出横向滚动;长文换行(不出现滚动条) */
.risk-signal-detail-dialog .risk-signal-detail-dialog__title, .risk-signal-detail-dialog .risk-signal-detail-dialog__title,
.risk-signal-detail-dialog .risk-signal-detail-dialog__desc,
.risk-signal-detail-dialog .risk-signal-detail-dialog__meta, .risk-signal-detail-dialog .risk-signal-detail-dialog__meta,
.risk-signal-detail-dialog .risk-signal-detail-dialog__body { .risk-signal-detail-dialog .risk-signal-detail-dialog__body,
.risk-signal-detail-dialog .risk-signal-detail-dialog__directions {
max-width: 100%; max-width: 100%;
overflow-wrap: anywhere; overflow-wrap: anywhere;
word-break: break-word; word-break: break-word;
} }
.risk-signal-detail-dialog .risk-signal-detail-dialog__desc {
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-word;
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_relation { .risk-signal-detail-dialog .risk-signal-detail-dialog_relation {
max-width: 100%; max-width: 100%;
width: 100% !important; width: 100% !important;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论