提交 f0e97fcd authored 作者: 张伊明's avatar 张伊明

合并分支 'zz-dev' 到 'pre'

feat:跳转加密解密问题;风险信号样式调整 查看合并请求 !393
流水线 #638 已通过 于阶段
in 10 分 57 秒
......@@ -223,12 +223,33 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
},
// ========== 路由参数处理 ==========
/**
* 与 goToPage 中法案/政令等一致:query.data = btoa(encodeURIComponent(JSON.stringify({ topic, fileId, ... })))
*/
_decodeWrittingRouteDataParam(dataParam) {
if (dataParam == null || dataParam === '') return null
try {
const s = String(dataParam).trim()
if (!s) return null
const normalized = s.replace(/-/g, '+').replace(/_/g, '/')
const padded = normalized + '==='.slice((normalized.length + 3) % 4)
const decoded = atob(padded)
const jsonStr = decodeURIComponent(decoded)
const obj = JSON.parse(jsonStr)
return obj && typeof obj === 'object' && !Array.isArray(obj) ? obj : null
} catch (e) {
console.error('写作助手路由 query.data 解密失败', e)
return null
}
},
async setRouteParams(query) {
this.routeQuery = { ...query };
this._isDisableTemplate = Object.keys(query).length > 0;
const decoded = query?.data != null ? this._decodeWrittingRouteDataParam(query.data) : null
const effective = decoded ? { ...query, ...decoded } : { ...query }
this.routeQuery = { ...effective }
this._isDisableTemplate = Object.keys(effective).length > 0
if (Object.keys(query).length > 0) {
const { topic, fileId } = query;
if (Object.keys(effective).length > 0) {
const { topic, fileId } = effective
if (topic) {
this.curTempTitle = topic;
this.tempActiveIndex = this.tempList.findIndex(item => item.title === topic);
......
/**
* 与根目录 `goToPage.js` 中 getDecodedParams 及合作限制详情 query 解码行为一致,
* 供智库/合作限制从 `@/utils/goToPage` 迁出后使用(不修改 `goToPage` 目录与 `goToPage.js`)。
*/
import router from "@/router/index";
/** 新窗口打开合作限制详情:id 为 base64(encodeURIComponent(明文)),与概览列表跳转一致 */
export function goToCooperationRestrictionsDetail(plainId, sessionTabTitle) {
const s = String(plainId ?? "").trim();
if (!s) return;
if (sessionTabTitle != null && sessionTabTitle !== "") {
window.sessionStorage.setItem("cooperationRestrictionsTabName", String(sessionTabTitle));
}
let encoded;
try {
encoded = btoa(encodeURIComponent(s));
} catch (_) {
return;
}
if (!encoded) return;
const r = router.resolve({
name: "CooperationRestrictionsDetail",
query: { id: encoded }
});
window.open(r.href, "_blank");
}
/** 合作限制详情页 `route.query.id` 解码(兼容明文数字与 base64 包装) */
export function decodeCooperationRestrictionQueryId(raw) {
if (raw == null) return null;
const s0 = String(raw).trim();
if (!s0) return null;
if (/^\d+$/.test(s0)) return s0;
if (/^[A-Za-z0-9+/_-]+={0,2}$/.test(s0)) {
try {
const normalized = s0.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized + "===".slice((normalized.length + 3) % 4);
const decoded = atob(padded);
try {
return decodeURIComponent(decoded);
} catch (_) {
return decoded;
}
} catch (_) {}
}
return s0;
}
......@@ -78,6 +78,25 @@ export function getEmptyRiskDetailPayload() {
};
}
/**
* 风险类型(与列表/详情接口 `riskType` 一致):用于详情弹窗「风险类型」展示,优先于 direction 编码
* system / tech_control / external / status
*/
export const RISK_SIGNAL_RISK_TYPE_CODE_LABEL_MAP = {
system: "科技体系有效性",
tech_control: "核心技术自主可控性",
external: "外部打压危害性",
status: "安全状态失稳性"
};
export function getRiskSignalRiskTypeLabel(raw) {
if (raw == null || raw === "") {
return "";
}
const c = String(raw).trim().toLowerCase();
return RISK_SIGNAL_RISK_TYPE_CODE_LABEL_MAP[c] ?? "";
}
/** 风险信号详情接口 `direction` 字段 → 中文(与 `model` 的 0100/0101 含义不同,单独映射) */
export const RISK_SIGNAL_DIRECTION_LABEL_MAP = {
"0100": "政策法规打压类风险",
......@@ -273,6 +292,7 @@ export function buildListRowFallbackFromRawRow(row, fields) {
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 : "";
const riskType = String(row.riskType ?? "").trim();
return {
title,
time,
......@@ -281,7 +301,8 @@ export function buildListRowFallbackFromRawRow(row, fields) {
dsc,
tag: parseRiskSignalTagList(row),
category,
direction
direction,
riskType
};
}
......@@ -312,12 +333,18 @@ export function buildRiskDetailPayloadFromApi(data, listRow) {
const detailTags = parseRiskSignalTagList(data);
const apiCategory = String(category).trim();
const apiTime = formatRiskPublishDisplay(postRaw) || String(postRaw || "").trim();
// 风险动向类型:严格使用 direction(0100~0103),支持多值
// 风险类型:优先使用接口 `riskType`(system/tech_control/external/status),与风险信号管理筛选项一致
const dataRiskType = data.riskType != null ? String(data.riskType).trim() : "";
const listRiskType = String(list.riskType ?? "").trim();
const riskTypeCode = dataRiskType || listRiskType;
const riskTypeLabel = getRiskSignalRiskTypeLabel(riskTypeCode);
// 风险动向(direction 0100~0103):无有效 riskType 时沿用原逻辑
const directionCodes = [
...parseRiskSignalDirectionCodes(data.direction),
...parseRiskSignalDirectionCodes(list.direction)
];
const directionLabels = buildRiskSignalDirectionLabels(directionCodes);
const directionLabelsFromCodes = buildRiskSignalDirectionLabels(directionCodes);
const directionLabels = riskTypeLabel ? [riskTypeLabel] : directionLabelsFromCodes;
return {
title: data.title ?? list.title ?? "",
category: apiCategory || String(list.category ?? "").trim(),
......
......@@ -283,7 +283,7 @@ import cyyIcon from "@/assets/icons/cyy.png";
import ghdIcon from "@/assets/icons/ghd.png";
import mzdIcon from "@/assets/icons/mzd.png";
import { ElMessage } from "element-plus";
import { goToDataCountryBill } from "@/utils/goToPage";
import { goToDataCountryBill } from "@/utils/goToPage/methods/dataLibrary";
const router = useRouter();
......
......@@ -322,7 +322,7 @@ import iconILetter from "./assets/icons/icon-iLetter.png";
import { ElMessage } from "element-plus";
import { useGotoNewsDetail } from "@/router/modules/news";
import { goToBill } from "@/utils/goToPage";
import { goToBill } from "@/utils/goToPage/methods/bill";
// 跳转人物主页(MessageBubble 的 person-click 传入整条列表项,需取 personId)
const handleClickToCharacter = async item => {
......
......@@ -20,24 +20,51 @@ const route = useRoute();
const type = ref(route.query.type || 1);
const decodeMaybeBase64 = (raw) => {
if (raw == null) return null;
const s0 = String(raw).trim();
let s0 = String(raw).trim();
if (!s0) return null;
// 兼容 URL 里被 encodeURIComponent 的 base64(例如 OTg%3D)
if (s0.includes("%")) {
try {
const decodedOnce = decodeURIComponent(s0);
if (decodedOnce) s0 = String(decodedOnce).trim();
} catch (_) { }
}
// 明文 personId(如 Y000064)保持原样,避免误解码
if (/^[A-Za-z]\d{6,}$/.test(s0) || /^Y\d+/i.test(s0)) {
return s0;
}
if (s0.length >= 12 && /^[A-Za-z0-9+/_-]+={0,2}$/.test(s0)) {
// 只在“可逆校验通过”时才把它当作 base64(encodeURIComponent(x))
if (!/^[A-Za-z0-9+/_-]+={0,2}$/.test(s0)) return s0;
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
const normalizeBase64 = (s) => {
const normalized = String(s ?? "").replace(/-/g, "+").replace(/_/g, "/");
return normalized + "===".slice((normalized.length + 3) % 4);
};
try {
const normalized = s0.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized + "===".slice((normalized.length + 3) % 4);
const padded = normalizeBase64(s0);
const decoded = atob(padded);
const decodedStr = (() => {
try {
return decodeURIComponent(decoded);
} catch (_) {
return decoded;
}
} catch (_) { }
})();
const reEncoded = encodeBase64Param(decodedStr);
if (reEncoded && normalizeBase64(reEncoded) === padded) {
return decodedStr;
}
} catch (_) { }
return s0;
};
const personId = ref(decodeMaybeBase64(route.query.personId) || "Y000064");
......
......@@ -132,7 +132,7 @@ import { getPersonSummaryInfo } from "@/api/common/index";
import { ElMessage } from "element-plus";
import TimeSortSelectBox from '@/components/base/TimeSortSelectBox/index.vue'
import AreaTag from '@/components/base/AreaTag/index.vue'
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
const route = useRoute();
const container = ref(null)
......
......@@ -120,6 +120,7 @@ import RiskSignal from "@/components/base/riskSignal/index.vue";
import RiskSignalOverviewDetailDialog from "@/components/base/RiskSignalOverviewDetailDialog/index.vue";
import { ref, onMounted, computed } from "vue";
import router from "@/router";
import { goToCooperationRestrictionsDetail } from "@/utils/goToPageRouteHelpers.js";
import { navigateToViewRiskSignal } from "@/utils/riskSignalOverviewNavigate";
import { getCoopRestrictionTrends, getCoopRestrictionSignals } from "@/api/coopRestriction/coopRestriction.js";
import defaultImg from "./assets/usImg.png";
......@@ -206,36 +207,14 @@ const handleClickToDetail = item => {
const activeItem = item && item.ID ? item : mainTrend.value;
const id = activeItem?.ID || activeItem?.id || activeItem?.limitId;
if (!id) return;
window.sessionStorage.setItem("cooperationRestrictionsTabName", activeItem?.LIMITNAME || "");
const encodedId = (() => {
const s = String(id).trim();
if (!s) return "";
try { return btoa(encodeURIComponent(s)); } catch (_) { return s; }
})();
if (!encodedId) return;
const curRoute = router.resolve({
name: "CooperationRestrictionsDetail",
query: { id: encodedId }
});
window.open(curRoute.href, "_blank");
goToCooperationRestrictionsDetail(id, activeItem?.LIMITNAME || "");
};
// 点击风险信号详情
const handleToRiskDetail = (item) => {
const id = item?.cooperationId || item?.ID || item?.id || item?.limitId;
if (!id) return;
const encodedId = (() => {
const s = String(id).trim();
if (!s) return "";
try { return btoa(encodeURIComponent(s)); } catch (_) { return s; }
})();
if (!encodedId) return;
const curRoute = router.resolve({
name: "CooperationRestrictionsDetail",
query: { id: encodedId },
});
window.open(curRoute.href, "_blank");
goToCooperationRestrictionsDetail(id, undefined);
};
const isRiskOverviewDetailOpen = ref(false);
......
......@@ -73,8 +73,8 @@
<script setup>
import { ref, onMounted, watch, computed } from "vue";
import { useRouter } from "vue-router";
import { getCoopRestrictionList } from "@/api/coopRestriction/coopRestriction";
import { goToCooperationRestrictionsDetail } from "@/utils/goToPageRouteHelpers.js";
import TimeSortSelectBox from "@/components/base/TimeSortSelectBox/index.vue";
import defaultImg from "../../assets/images/default-icon2.png";
......@@ -136,33 +136,11 @@ const getMainDataList = async () => {
}
};
const router = useRouter();
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
const handleClick = item => {
const id = item?.id || item?.limitId || item?.ID;
if (!id) return;
window.sessionStorage.setItem(
"cooperationRestrictionsTabName",
item?.limitName || item?.title || item?.name || ""
);
const encodedId = encodeBase64Param(id);
if (!encodedId) return;
const routeData = router.resolve({
name: "CooperationRestrictionsDetail",
query: {
id: encodedId
}
});
window.open(routeData.href, "_blank");
const tabTitle = item?.limitName || item?.title || item?.name || "";
goToCooperationRestrictionsDetail(id, tabTitle);
};
const navList = ref([
......
......@@ -103,31 +103,11 @@
import { ref, watch, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { getCoopRestrictionIntroduction, getCoopRestrictionRelated, getCoopRestrictionBackground, getCoopRestrictionClause } from "@/api/coopRestriction/coopRestriction";
import { decodeCooperationRestrictionQueryId } from "@/utils/goToPageRouteHelpers.js";
const route = useRoute();
const router = useRouter();
const decodeQueryId = (raw) => {
if (raw == null) return null;
const s0 = String(raw).trim();
if (!s0) return null;
// 明文数字直接返回
if (/^\d+$/.test(s0)) return s0;
// base64(encodeURIComponent(id)) 解码(兼容短 base64:Mg==)
if (/^[A-Za-z0-9+/_-]+={0,2}$/.test(s0)) {
try {
const normalized = s0.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized + "===".slice((normalized.length + 3) % 4);
const decoded = atob(padded);
try {
return decodeURIComponent(decoded);
} catch (_) {
return decoded;
}
} catch (_) { }
}
return s0;
};
const limitId = computed(() => decodeQueryId(route.query?.id));
const limitId = computed(() => decodeCooperationRestrictionQueryId(route.query?.id));
import defaultImg from "./assets/image01.png"
import defaultCom from "../assets/images/default-icon2.png"
import Harvard from "./assets/哈佛.png";
......@@ -235,15 +215,35 @@ const handleClickOnEntity = (item) => {
}
})();
if (!encodedPersonId) return;
const url = `http://localhost:3000/characterPage?type=2&personId=${encodeURIComponent(encodedPersonId)}`;
window.open(url, "_blank");
const { href } = router.resolve({
path: "/characterPage",
query: {
type: 2,
personId: encodedPersonId
}
});
window.open(href, "_blank");
return;
}
// 默认按公司/机构跳转(含 ENTITYTYPE === 'O' 或字段缺失)
const companyId = item?.ENTITYID || item?.id;
if (!companyId) return;
const path = `/companyPages/${companyId}`;
const { href } = router.resolve({ path });
const encodedInstitutionId = (() => {
const s = String(companyId ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
})();
if (!encodedInstitutionId) return;
const { href } = router.resolve({
path: "/institution",
query: {
id: encodedInstitutionId
}
});
window.open(href, "_blank");
};
......
......@@ -173,7 +173,7 @@ import { getPostOrgList, getPostMemberList } from '@/api/bill/billHome'
import { search, getStatusList } from '@/api/comprehensiveSearch'
import { ElMessage } from 'element-plus'
import getDateRange from '@/utils/getDateRange'
import { getDecodedParams } from '@/utils/goToPage'
import { getDecodedParams } from '@/utils/goToPage/methods/getDecode'
import goToPage from '@/utils/goToPage/index'
const route = useRoute();
......
......@@ -186,7 +186,7 @@ import { ElMessage } from 'element-plus'
import getDateRange from '@/utils/getDateRange'
import { getDepartmentList } from "@/api/decree/home";
import { getDecodedParams } from '@/utils/goToPage'
import { getDecodedParams } from '@/utils/goToPage/methods/getDecode'
import goToPage from '@/utils/goToPage/index'
const route = useRoute();
......
......@@ -172,7 +172,7 @@ import InputBox from "../components/InputBox/index.vue";
import DataChartSwitchBox from '../components/dataChartSwitchBox/index.vue'
import { useRoute } from "vue-router";
import router from "@/router";
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
import { search, getThinkTankList } from "@/api/comprehensiveSearch";
import { ElMessage } from "element-plus";
......
......@@ -48,6 +48,52 @@ import { ElMessage } from "element-plus";
const route = useRoute();
const decodeMaybeBase64 = (raw) => {
if (raw == null) return "";
let s0 = String(raw).trim();
if (!s0) return "";
if (s0.includes("%")) {
try {
const decodedOnce = decodeURIComponent(s0);
if (decodedOnce) s0 = String(decodedOnce).trim();
} catch (_) { }
}
// 非 base64 直接返回(机构 id 含字母数字,明文也要支持)
if (!/^[A-Za-z0-9+/_-]+={0,2}$/.test(s0)) return s0;
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
const normalizeBase64 = (s) => {
const normalized = String(s ?? "").replace(/-/g, "+").replace(/_/g, "/");
return normalized + "===".slice((normalized.length + 3) % 4);
};
try {
const padded = normalizeBase64(s0);
const decoded = atob(padded);
const decodedStr = (() => {
try {
return decodeURIComponent(decoded);
} catch (_) {
return decoded;
}
})();
const reEncoded = encodeBase64Param(decodedStr);
if (reEncoded && normalizeBase64(reEncoded) === padded) {
return decodedStr;
}
} catch (_) { }
return s0;
};
const institutionId = computed(() => decodeMaybeBase64(route.query.id));
const institutionInfo = ref({
name: "",
enName: "",
......@@ -66,7 +112,7 @@ const showTagList = computed(() => {
const handleGetInfo = async () => {
const params = {
id: route.query.id
id: institutionId.value
};
try {
const res = await getGovOrgBasicInfo(params);
......
......@@ -320,11 +320,20 @@ const decodeRouteId = (raw) => {
const hearingId = computed(() => decodeRouteId(router.currentRoute.value?.params?.id));
const goToAllThinkTank = () => {
const thinkTankId = props?.thinkInfo?.thinkTankId || props?.thinkInfo?.id;
const route = router.resolve({
const s = thinkTankId != null && thinkTankId !== "" ? String(thinkTankId).trim() : "";
if (!s) return;
let encodedId;
try {
encodedId = btoa(encodeURIComponent(s));
} catch (_) {
encodedId = s;
}
if (!encodedId) return;
const r = router.resolve({
name: "MultiThinkTankViewAnalysis",
params: { id: thinkTankId }
params: { id: encodedId }
});
window.open(route.href, "_blank");
window.open(r.href, "_blank");
};
......@@ -466,29 +475,52 @@ const reportAuthors = computed(() => {
}
return [];
});
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
// 点击报告作者头像,跳转到人物主页
// 与核心研究人员逻辑一致:核心依赖 personId,本页面依赖作者的 id(作为 personId 传入)
const handleClickReportAuthor = async (author) => {
const personId = author?.personId;
const personId = author?.personId ?? author?.id;
if (!personId) return;
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
const params = { personId };
try {
const res = await getPersonSummaryInfo(params);
if (res.code === 200 && res.data) {
const arr = Array.isArray(personTypeList)
? personTypeList.filter((t) => {
const typeIdNum = Number(t.typeId);
const personTypeNum = Number(res.data.personType);
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
})
: [];
const params = { personId };
const res = await getPersonSummaryInfo(params);
if (res.code !== 200 || !res.data) return;
window.sessionStorage.setItem("curTabName", author?.name || "");
window.sessionStorage.setItem("curTabName", author?.name || "人物主页");
const encodedPersonId = encodeBase64Param(personId);
if (!encodedPersonId) return;
const route = router.resolve({
path: "/characterPage",
query: {
personId
type,
personId: encodedPersonId
}
});
window.open(route.href, "_blank");
} else {
ElMessage.warning("找不到当前人员的类型值!");
}
} catch (error) { }
};
const riskSignal = computed(() => {
const info = props.thinkInfo || {};
......
......@@ -314,6 +314,7 @@
</template>
<script setup>
import router from '@/router';
import { useRoute } from "vue-router";
import { onMounted, ref, computed, reactive, nextTick, watch } from "vue";
import AnalysisBox from "@/components/base/boxBackground/analysisBox.vue"
import AnalysisResultBox from "./boxBackground/analysisBox.vue"
......@@ -323,6 +324,73 @@ import {
getThinkTankReportViewpoint,
postReportDomainViewAnalysisStream
} from "@/api/thinkTank/overview";
const route = useRoute();
const decodeRouteId = (raw) => {
if (raw == null) return null;
let s0 = String(raw);
if (!s0) return null;
if (s0.includes("%")) {
try {
const decodedOnce = decodeURIComponent(s0);
if (decodedOnce) {
s0 = decodedOnce;
}
} catch (_) { }
}
if (/^\d+$/.test(s0) || /^Rand_/i.test(s0)) {
return s0;
}
const tryParseJson = (str) => {
if (typeof str !== "string") return null;
const s = str.trim();
if (!s) return null;
try {
return JSON.parse(s);
} catch (_) {
return null;
}
};
const tryAtob = (val) => {
if (val == null) return null;
const s = String(val).trim();
if (!s) return null;
const normalized = s.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized + "===".slice((normalized.length + 3) % 4);
try {
return atob(padded);
} catch (_) {
return null;
}
};
const atobStr = tryAtob(s0);
if (atobStr != null) {
try {
const decoded = decodeURIComponent(atobStr);
return tryParseJson(decoded) ?? decoded;
} catch (_) {
return tryParseJson(atobStr) ?? atobStr;
}
}
try {
const utf8 = decodeURIComponent(escape(s0));
if (utf8 && utf8 !== s0) {
if (/^\d+$/.test(utf8) || /^Rand_/i.test(utf8)) return utf8;
return tryParseJson(utf8) ?? utf8;
}
} catch (_) { }
return s0;
};
/** 与智库详情等一致:路由 id 为 base64(encodeURIComponent(明文)),请求接口前解密 */
const thinkTankIdFromRoute = computed(() => {
const id = decodeRouteId(route.params?.id);
return id != null && id !== "" ? String(id) : "";
});
const sort = ref("");
const searchPolicy = ref("");
const isBox2 = ref(true)
......@@ -951,7 +1019,7 @@ const handleGetHylyList = async () => {
};
//获取智库报告
const handleGetetThinkTankReport = async (page = currentPage.value) => {
const thinkTankId = router.currentRoute?.value?.params?.id || "";
const thinkTankId = thinkTankIdFromRoute.value;
const getDateYearsAgo = (years) => {
const d = new Date();
......
......@@ -229,12 +229,17 @@ const switchTab = name => {
const handleAnalysisClick = () => {
const id = reportId.value;
if (!id) return;
const param = { topic: "智库", fileId: id };
const jsonStr = JSON.stringify(param);
let data;
try {
data = btoa(encodeURIComponent(jsonStr));
} catch (_) {
return;
}
router.push({
path: "/writtingAsstaint",
query: {
topic: "智库",
fileId: id
}
query: { data }
});
};
onMounted(async () => {
......
......@@ -302,11 +302,20 @@ const decodeRouteId = (raw) => {
const reportId = computed(() => decodeRouteId(route.params?.id));
const goToAllThinkTank = () => {
const thinkTankId = props?.thinkInfo?.thinkTankId || props?.thinkInfo?.id;
const route = router.resolve({
const s = thinkTankId != null && thinkTankId !== "" ? String(thinkTankId).trim() : "";
if (!s) return;
let encodedId;
try {
encodedId = btoa(encodeURIComponent(s));
} catch (_) {
encodedId = s;
}
if (!encodedId) return;
const r = router.resolve({
name: "MultiThinkTankViewAnalysis",
params: { id: thinkTankId }
params: { id: encodedId }
});
window.open(route.href, "_blank");
window.open(r.href, "_blank");
};
......@@ -420,26 +429,52 @@ const reportAuthors = computed(() => {
return [];
});
// 点击报告作者头像,跳转到人物主页
// 与核心研究人员逻辑一致:核心依赖 personId,本页面依赖作者的 id(作为 personId 传入)
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
// 点击报告作者:与智库概览核心研究人员一致,personId 加密后传入,人物页解密
const handleClickReportAuthor = async (author) => {
const personId = author?.id;
const personId = author?.personId ?? author?.id;
if (!personId) return;
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
const params = { personId };
try {
const res = await getPersonSummaryInfo(params);
if (res.code === 200 && res.data) {
const arr = Array.isArray(personTypeList)
? personTypeList.filter((t) => {
const typeIdNum = Number(t.typeId);
const personTypeNum = Number(res.data.personType);
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
})
: [];
const params = { personId };
const res = await getPersonSummaryInfo(params);
if (res.code !== 200 || !res.data) return;
window.sessionStorage.setItem("curTabName", author?.name || "");
window.sessionStorage.setItem("curTabName", author?.name || "人物主页");
const encodedPersonId = encodeBase64Param(personId);
if (!encodedPersonId) return;
const route = router.resolve({
path: "/characterPage",
query: {
personId
type,
personId: encodedPersonId
}
});
window.open(route.href, "_blank");
} else {
ElMessage.warning("找不到当前人员的类型值!");
}
} catch (error) { }
};
const riskSignal = computed(() => {
const info = props.thinkInfo || {};
......@@ -1160,6 +1195,7 @@ onMounted(() => {
background-color: rgb(5, 95, 194);
border-radius: 6px;
display: flex;
cursor: pointer;
.btn-text {
color: rgb(255, 255, 255);
......
......@@ -316,40 +316,84 @@ const handleGetThinkTankReport = async () => {
}
};
/** 新标签页打开智库报告详情 /thinkTank/reportDetail/:id */
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
/** 新标签页打开报告详情:与智库概览「智库报告」一致,id 为 base64(encodeURIComponent(明文id)),报告详情页 decodeRouteId 解密 */
const handleClickProjectReport = (report) => {
const id = report?.id;
if (!id) {
ElMessage.warning("报告 id 为空,无法跳转");
return;
}
const reportTitle = String(report?.nameZh || report?.name || "").trim();
const name = String(report?.nameZh || report?.name || "").trim();
if (name) {
window.sessionStorage.setItem("curTabName", name);
}
const encodedId = encodeBase64Param(id);
if (!encodedId) return;
const route = router.resolve({
name: "ReportDetail",
params: { id: String(id) },
query: reportTitle ? { name: reportTitle } : {}
params: { id: encodedId }
});
window.open(route.href, "_blank");
};
// 点击项目团队成员:与智库概览-核心研究人员一致,personId 经 encodeURIComponent 后 base64,人物页会解密
const handleClickReportAuthor = async (author) => {
const personId = author?.personId;
if (!personId) return;
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
const params = { personId };
try {
const res = await getPersonSummaryInfo(params);
if (res.code !== 200 || !res.data) return;
window.sessionStorage.setItem("curTabName", author?.name || "");
if (res.code === 200 && res.data) {
const arr = Array.isArray(personTypeList)
? personTypeList.filter((t) => {
const typeIdNum = Number(t.typeId);
const personTypeNum = Number(res.data.personType);
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
})
: [];
if (!arr.length) {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
const personTypeName = arr[0]?.typeName || "";
if (personTypeName === "科技企业领袖") {
type = 1;
} else if (personTypeName === "国会议员") {
type = 2;
} else if (personTypeName === "智库研究人员") {
type = 3;
} else {
ElMessage.warning("找不到当前人员的类型值!");
return;
}
window.sessionStorage.setItem("curTabName", author?.name || "人物主页");
const encodedPersonId = encodeBase64Param(author?.personId);
if (!encodedPersonId) return;
const route = router.resolve({
path: "/characterPage",
query: {
personId
type,
personId: encodedPersonId
}
});
window.open(route.href, "_blank");
} else {
ElMessage.warning("找不到当前人员的类型值!");
}
} catch (error) { }
};
// 内容摘要
const box1Data =
......
......@@ -293,7 +293,7 @@ import defaultNewsIcon from "@/assets/icons/default-icon-news.png";
import AreaTag from "@/components/base/AreaTag/index.vue";
import { useRouter } from "vue-router";
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
const router = useRouter();
const thinkTankId = computed(() => getDecodedParams());
......
......@@ -64,9 +64,11 @@ import PolicyTracking from "./PolicyTracking/index.vue";
import ThinkInfo from "./thinkInfo/index.vue";
import { getThinkTankSummary } from "@/api/thinkTank/overview";
import { useRouter } from "vue-router";
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
const router = useRouter();
// 兼容 getDecodedParams 内部直接使用全局 router 的写法
window.router = router;
const tabActiveName = ref("智库动态");
const switchTab = name => {
tabActiveName.value = name;
......@@ -75,7 +77,9 @@ const switchTab = name => {
const thinkTank = ref({});
// 获取智库基本信息
const handleGetThinkTankSummary = async () => {
const id = getDecodedParams()
const decoded = getDecodedParams();
// getDecodedParams: 有时返回对象(来自 ?data=),有时返回字符串(来自 params)
const id = typeof decoded === "object" && decoded !== null ? decoded.id : decoded;
try {
const parmas = {
......
......@@ -78,7 +78,7 @@ import {
getResourceLibraryReportDateRangeFromTimeSelection
} from "../../utils/resourceLibraryFilters";
import { useRouter } from "vue-router";
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
import ThinkTankReport from "./ThinkTankReport/index.vue";
import CongressHearing from "./CongressHearing/index.vue";
const router = useRouter();
......
......@@ -259,7 +259,8 @@ import { ref, onMounted, nextTick, computed } from "vue";
import setChart from "@/utils/setChart";
import getPieChart from "./utils/piechart";
import getTreeMapChart from "./utils/treeMapChart";
import { getDecodedParams } from "@/utils/goToPage";
import { getDecodedParams } from "@/utils/goToPage/methods/getDecode";
import { ElMessage } from "element-plus";
import {
getThinkTankInfoBasic,
getThinkTankInfoBranch,
......@@ -705,6 +706,16 @@ const handlePersonNextPage = () => {
handleGetThinkPerson(personCurrentPage.value + 1);
};
const encodeBase64Param = (val) => {
const s = String(val ?? "").trim();
if (!s) return "";
try {
return btoa(encodeURIComponent(s));
} catch (_) {
return s;
}
};
// 点击人物头像,跳转到人物主页
const handleClickPerson = async item => {
......@@ -722,17 +733,26 @@ const handleClickPerson = async item => {
const res = await getPersonSummaryInfo(params);
console.log("人物全局信息", res);
if (res.code === 200 && res.data) {
const arr = personTypeList.filter(t => {
const arr = Array.isArray(personTypeList)
? personTypeList.filter(t => {
const typeIdNum = Number(t.typeId);
const personTypeNum = Number(res.data.personType);
return !Number.isNaN(typeIdNum) && !Number.isNaN(personTypeNum) && typeIdNum === personTypeNum;
});
})
: [];
console.log("arr", arr);
personTypeName = arr[0]?.typeName || "";
window.sessionStorage.setItem("curTabName", item.name || "人物主页");
const encodedPersonId = encodeBase64Param(item?.personId);
if (!encodedPersonId) return;
const route = router.resolve({
path: "/characterPage",
query: {
personId: item.personId
type,
personId: encodedPersonId
}
});
window.open(route.href, "_blank");
......
......@@ -67,11 +67,9 @@
{{ "No." + (index + 1) }}
</div> -->
<div class="rank">
<el-popover content="跳转至数据资源库" placement="top">
<template #reference>
<div class=" number" @click="handleToDataLibrary(item)">{{ item.reportNumber }} {{ "篇报告" }}</div>
</template>
</el-popover>
</div>
</div>
<div class="card-title">
......@@ -504,7 +502,8 @@ import { setCanvasCreator } from "echarts/core";
import { ElMessage } from "element-plus";
import { useRouter } from 'vue-router';
import { useGotoNewsDetail } from '@/router/modules/news';
import { goToThinkTank } from "@/utils/goToPage";
import { goToThinkTank } from "@/utils/goToPage/methods/thinkTank.js";
import { goToDataThinkTank } from "@/utils/goToPage/methods/dataLibrary.js";
const gotoNewsDetail = useGotoNewsDetail()
const containerRef = ref(null);
const isRiskDetailVisible = ref(false);
......@@ -2453,11 +2452,8 @@ const handleToDataLibrary = (item) => {
const selectParam = {
orgnizationName: item.name
}
const route = router.resolve({
path: "/dataLibrary/dataThinkTank",
query: selectParam
});
window.open(route.href, "_blank");
// 使用 goToPage/methods/dataLibrary 与数据资源库一致的 Base64(encodeURIComponent(JSON)) 传参
goToDataThinkTank(selectParam)
}
onMounted(async () => {
......
......@@ -173,24 +173,11 @@
<div class="search-btn" @click="handleSearch"></div>
</div>
<div class="select-box">
<el-select v-model="sortModel" class="resource-library-sort-select" placeholder="发布时间"
style="width: 120px" :teleported="true" placement="bottom-start"
:popper-options="resourceLibrarySortPopperOptions" @change="handleSortChange">
<template #prefix>
<img v-if="sortValue === 1"
src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png"
class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" />
<img v-else src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png"
class="resource-library-sort-prefix-img" alt="" @click.stop="toggleSortPrefix" />
</template>
<el-option :key="'risk-sort-asc'" label="正序" :value="0" />
<el-option :key="'risk-sort-desc'" label="倒序" :value="1" />
</el-select>
<TimeSortSelectBox :sort-demension="1" @handlePxChange="handleTimeSortChange" />
</div>
</div>
</div>
<template v-if="riskList && riskList.length">
<div class="right-main">
<div v-if="riskList && riskList.length" class="right-main">
<div class="itemlist itemlist--clickable" v-for="(val, idx) in riskList" :key="val.rowKey"
@click="handleOpenRiskDetail(val)">
<div class="box-title">
......@@ -211,6 +198,9 @@
</div>
</div>
</div>
<div v-else class="right-empty">
<el-empty class="right-el-empty" description="暂无数据" :image-size="100" />
</div>
<div class="right-footer">
<div class="footer-left">
{{ `共 ${formatThousands(totalNum)} 项调查` }}
......@@ -220,12 +210,6 @@
:total="totalNum" background layout="prev, pager, next" />
</div>
</div>
</template>
<template v-else>
<div class="right-empty">
<el-empty class="right-el-empty" description="暂无数据" :image-size="100" />
</div>
</template>
</div>
</div>
</div>
......@@ -311,12 +295,14 @@ import riskSourceUpArrow from "./assets/images/up-arrow.png";
import DefaultIcon2 from "@/assets/icons/default-icon2.png";
import { normalizeExclusiveAllOption } from "@/views/thinkTank/utils/resourceLibraryFilters";
import TimeSortSelectBox from "@/components/base/TimeSortSelectBox/index.vue";
const riskTypeOptions = ref([]);
const RISK_FILTER_ALL_TYPE = "全部类型";
const selectedRiskTypeModel = ref([RISK_FILTER_ALL_TYPE]);
const handleRiskTypeGroupChange = (val) => {
selectedRiskTypeModel.value = normalizeExclusiveAllOption(val, RISK_FILTER_ALL_TYPE);
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -336,6 +322,7 @@ const timeSource = ref([
const selectedTimeModel = ref([Time_FILTER_ALL_SOURCE]);
const handleTimeGroupChange = (val) => {
selectedTimeModel.value = normalizeExclusiveAllOption(val, Time_FILTER_ALL_SOURCE);
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -351,6 +338,7 @@ const RISK_FILTER_ALL_SOURCE = "全部国家";
const selectedRiskSourceModel = ref([RISK_FILTER_ALL_SOURCE]);
const handleRiskSourceGroupChange = (val) => {
selectedRiskSourceModel.value = normalizeExclusiveAllOption(val, RISK_FILTER_ALL_SOURCE);
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -384,6 +372,7 @@ const RISK_FILTER_ALL_LEVEL = "全部等级";
const selectedRiskDegreeModel = ref([RISK_FILTER_ALL_LEVEL]);
const handleRiskDegreeGroupChange = (val) => {
selectedRiskDegreeModel.value = normalizeExclusiveAllOption(val, RISK_FILTER_ALL_LEVEL);
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -410,6 +399,7 @@ const RISK_FILTER_ALL_AREA = "全部领域";
const selectedAreaModel = ref([RISK_FILTER_ALL_AREA]);
const handleAreaGroupChange = (val) => {
selectedAreaModel.value = normalizeExclusiveAllOption(val, RISK_FILTER_ALL_AREA);
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -710,26 +700,10 @@ const handleCleandarChart = async () => {
/** sort:0 正序;1 倒序;接口默认倒序=1 */
const sortValue = ref(1);
/** el-select 展示用:默认 null => 显示 placeholder「发布时间」 */
const sortModel = ref(null);
const resourceLibrarySortPopperOptions = {
modifiers: [
{ name: "preventOverflow", options: { mainAxis: false, altAxis: false } },
{ name: "flip", enabled: false }
]
};
const toggleSortPrefix = () => {
sortValue.value = sortValue.value === 1 ? 0 : 1;
sortModel.value = sortValue.value;
handleSortChange();
};
const handleSortChange = () => {
if (sortModel.value === 0 || sortModel.value === 1) {
sortValue.value = sortModel.value;
}
const handleTimeSortChange = (val) => {
// TimeSortSelectBox: 1=时间倒序,2=时间正序
sortValue.value = val === 2 ? 0 : 1;
currentPage.value = 1;
handleGetPageQuery();
};
......@@ -880,6 +854,7 @@ const handleGetPageQuery = async () => {
tag: parseRiskSignalTagList(item),
risktype: item.riskLevel ?? item.level ?? "",
direction: item.direction == null || item.direction === "" ? "" : item.direction,
riskType: item.riskType != null && String(item.riskType).trim() !== "" ? String(item.riskType).trim() : "",
pic: item.orgLogo || ""
};
});
......@@ -1308,9 +1283,7 @@ onMounted(async () => {
}
.select-box {
width: 120px;
height: 32px;
box-sizing: border-box;
.resource-library-sort-select {
:deep(.el-select__wrapper) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论