提交 e0f148d0 authored 作者: 闫鹏's avatar 闫鹏

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

Yp dev 查看合并请求 !320
流水线 #377 已通过 于阶段
in 6 分 2 秒
...@@ -33,7 +33,7 @@ const financeRoutes = [ ...@@ -33,7 +33,7 @@ const financeRoutes = [
}, },
// V2.0单条制裁详情-实体清单原文 // V2.0单条制裁详情-实体清单原文
{ {
path: "/exportControl/origin", path: "/finance/origin",
name: "financeEntityListOrigin", name: "financeEntityListOrigin",
component: () => import("@/views/finance/singleSanction/originPage/index.vue") component: () => import("@/views/finance/singleSanction/originPage/index.vue")
// meta: { // meta: {
......
...@@ -476,12 +476,6 @@ ...@@ -476,12 +476,6 @@
header-row-class-name="table-header" header-row-class-name="table-header"
row-class-name="table-row" row-class-name="table-row"
> >
<!-- <el-table-column prop="index" label="序号" width="80" align="center">
<template #default="scope">
{{ scope.$index + 1 + (currentPage - 1) * pageSize }}
</template>
</el-table-column> -->
<el-table-column prop="name" label="实体名称" min-width="200"> <el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope"> <template #default="scope">
<div class="tableName" @click="handleCompClick(scope.row)"> <div class="tableName" @click="handleCompClick(scope.row)">
...@@ -970,7 +964,7 @@ onMounted(async () => { ...@@ -970,7 +964,7 @@ onMounted(async () => {
label: item.nameZh, label: item.nameZh,
value: tabMap[item.id], value: tabMap[item.id],
id: [item.id], id: [item.id],
disabled: item.id == "13" // 商业管制清单不展示 disabled: false
})); }));
resourceTabs.value.unshift({ label: "全部制裁", value: "all", id: "", disabled: false }); resourceTabs.value.unshift({ label: "全部制裁", value: "all", id: "", disabled: false });
console.log("返回的数据结构 infoList =》", infoList.value); console.log("返回的数据结构 infoList =》", infoList.value);
...@@ -1108,7 +1102,7 @@ const processYearDomainCountData = yearDomainCountData => { ...@@ -1108,7 +1102,7 @@ const processYearDomainCountData = yearDomainCountData => {
const handleEntityClick = item => { const handleEntityClick = item => {
console.log("item", item); console.log("item", item);
window.sessionStorage.setItem("curTabName", item.name || item.entityNameZh); window.sessionStorage.setItem("curTabName", item.name || item.entityNameZh);
gotoCompanyPages(item.entityId); gotoCompanyPages(item.id);
// const route = router.resolve({ // const route = router.resolve({
// name: "companyPages", // name: "companyPages",
// params: { // params: {
...@@ -1494,7 +1488,7 @@ watch( ...@@ -1494,7 +1488,7 @@ watch(
const fetchEntitiesList = async (page = 1, size = 10) => { const fetchEntitiesList = async (page = 1, size = 10) => {
try { try {
console.log("activeResourceTabItem.value.id", activeResourceTabItem.value.id); console.log("activeResourceTabItem.value.id", activeResourceTabItem.value.id);
const res = await getEntitiesList(activeResourceTabItem.value.id, page, size); const res = await getEntitiesList(activeResourceTabItem.value.id.join(","), page, size);
if (res) { if (res) {
entitiesList.value = res.content.map(item => ({ entitiesList.value = res.content.map(item => ({
...item, ...item,
...@@ -1630,6 +1624,7 @@ const fetchSocialMediaInfo = async () => { ...@@ -1630,6 +1624,7 @@ const fetchSocialMediaInfo = async () => {
if (data && Array.isArray(data)) { if (data && Array.isArray(data)) {
// console.log(data); // console.log(data);
socialMediaList.value = data.map(item => ({ socialMediaList.value = data.map(item => ({
...item,
avatar: item.personImage, avatar: item.personImage,
name: item.personName, name: item.personName,
time: formatTime(item.time), time: formatTime(item.time),
...@@ -1662,7 +1657,7 @@ const fetchNewsInfo = async () => { ...@@ -1662,7 +1657,7 @@ const fetchNewsInfo = async () => {
}; };
const handlePerClick = item => { const handlePerClick = item => {
// console.log("点击了社交媒体消息:", item); console.log("点击了社交媒体消息:", item);
window.sessionStorage.setItem("curTabName", item.name); window.sessionStorage.setItem("curTabName", item.name);
const route = router.resolve({ const route = router.resolve({
path: "/characterPage", path: "/characterPage",
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
</template> </template>
<div class="right-main"> <div class="right-main">
<div class="right-main-content"> <div class="right-main-content">
<div class="hintWrap"> <!-- <div class="hintWrap">
<div class="icon1"></div> <div class="icon1"></div>
<div class="title"> <div class="title">
2025年实体清单制裁范围扩大至芯片制造环节,为中国的芯片制造能力划定“技术天花板”,阻止其向更先进水平发展。制裁范围向上游设备和材料、下游先进封装以及关键工具(如EDA软件)延伸,意图瓦解中国构建自主可控产业链的努力。 2025年实体清单制裁范围扩大至芯片制造环节,为中国的芯片制造能力划定“技术天花板”,阻止其向更先进水平发展。制裁范围向上游设备和材料、下游先进封装以及关键工具(如EDA软件)延伸,意图瓦解中国构建自主可控产业链的努力。
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
<div class="icon2Wrap"> <div class="icon2Wrap">
<div class="icon2"></div> <div class="icon2"></div>
</div> </div>
</div> </div> -->
<div class="right-main-content-main"> <div class="right-main-content-main">
<div class="fishbone-wrapper"> <div class="fishbone-wrapper">
<div class="fishbone-scroll-container" ref="scrollContainerRef"> <div class="fishbone-scroll-container" ref="scrollContainerRef">
......
...@@ -433,11 +433,6 @@ onMounted(() => { ...@@ -433,11 +433,6 @@ onMounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.introduction-page { .introduction-page {
width: 1601px; width: 1601px;
margin: 0 auto; margin: 0 auto;
......
...@@ -155,15 +155,8 @@ const getTagStyle = tag => { ...@@ -155,15 +155,8 @@ const getTagStyle = tag => {
// 跳转公司详情页 // 跳转公司详情页
const handleCompClick = item => { const handleCompClick = item => {
console.log("item", item); console.log("item", item);
window.sessionStorage.setItem("curTabName", item.entityNameZh || item.entityName); window.sessionStorage.setItem("curTabName", item.name || item.orgName);
gotoCompanyPages(item.id); gotoCompanyPages(item.id);
// const route = router.resolve({
// name: "companyPages",
// params: {
// id: item.id
// }
// });
// window.open(route.href, "_blank");
}; };
</script> </script>
......
...@@ -540,10 +540,13 @@ watch(customDateRange, () => { ...@@ -540,10 +540,13 @@ watch(customDateRange, () => {
border: 1px solid rgba(170, 173, 177, 1); border: 1px solid rgba(170, 173, 177, 1);
background-color: #fff; background-color: #fff;
border-radius: 3px; border-radius: 3px;
border: 1px solid #ddd;
border-radius: 4px;
height: 32px;
} }
:deep(.el-input__inner) { :deep(.el-input__inner) {
font-size: 16px; font-size: 14px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
...@@ -552,8 +555,8 @@ watch(customDateRange, () => { ...@@ -552,8 +555,8 @@ watch(customDateRange, () => {
} }
.filters { .filters {
display: flex; // display: flex;
align-items: center; // align-items: center;
.el-checkbox { .el-checkbox {
margin-right: 20px; margin-right: 20px;
...@@ -565,8 +568,9 @@ watch(customDateRange, () => { ...@@ -565,8 +568,9 @@ watch(customDateRange, () => {
height: 32px; height: 32px;
} }
:deep(.el-checkbox__label) { :deep(.el-checkbox__label) {
font-size: 16px; font-size: 14px;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
margin-top: 3px;
} }
} }
} }
...@@ -596,9 +600,10 @@ watch(customDateRange, () => { ...@@ -596,9 +600,10 @@ watch(customDateRange, () => {
padding-left: 20px; padding-left: 20px;
.el-checkbox { .el-checkbox {
width: 50%; // width: 50%;
margin-right: 0; // margin-right: 0;
margin-bottom: 4px; // margin-bottom: 4px;
height: 24px;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
......
...@@ -537,26 +537,30 @@ const domainChartOption = ref({ ...@@ -537,26 +537,30 @@ const domainChartOption = ref({
width: 1.1 width: 1.1
} }
}, },
// labelLayout: function (params) {
// const isLeft = params.labelRect.x < params.viewWidth / 2;
// const points = params.labelLinePoints;
// // Update the end point.
// points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
// return {
// labelLinePoints: points
// };
// },
labelLayout: function (params) { labelLayout: function (params) {
const isLeft = params.labelRect.x < params.viewWidth / 2; // hideOverlap: true
const points = params.labelLinePoints; const points = params.labelLinePoints;
// Update the end point. const isLeft = params.labelRect.x < params.rect.x + params.rect.width / 2;
// 调整指示线终点到 label 垂直中心
const labelCenterY = params.labelRect.y + params.labelRect.height / 2;
points[2][1] = labelCenterY;
// 调整指示线终点到 label 水平边缘
points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width; points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
// if (isLeft) {
// // 左侧:终点对齐到标签左边界
// points[2][0] = 0;
// } else {
// // 右侧:终点对齐到标签右边界
// points[2][0] = params.labelRect.x + params.labelRect.width;
// }
// return {
// labelLinePoints: points
// };
}, },
itemStyle: { itemStyle: {
borderWidth: 0 borderWidth: 0
...@@ -714,11 +718,27 @@ const typeChartOption = ref({ ...@@ -714,11 +718,27 @@ const typeChartOption = ref({
width: 1 width: 1
} }
}, },
// labelLayout: function (params) {
// const isLeft = params.labelRect.x < params.viewWidth / 2;
// const points = params.labelLinePoints;
// // Update the end point.
// points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
// return {
// labelLinePoints: points
// };
// },
labelLayout: function (params) { labelLayout: function (params) {
const isLeft = params.labelRect.x < params.viewWidth / 2; // hideOverlap: true
const points = params.labelLinePoints; const points = params.labelLinePoints;
// Update the end point. const isLeft = params.labelRect.x < params.rect.x + params.rect.width / 2;
// 调整指示线终点到 label 垂直中心
const labelCenterY = params.labelRect.y + params.labelRect.height / 2;
points[2][1] = labelCenterY;
// 调整指示线终点到 label 水平边缘
points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width; points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
......
...@@ -540,11 +540,6 @@ onMounted(async () => { ...@@ -540,11 +540,6 @@ onMounted(async () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.deepMiningChartmode { .deepMiningChartmode {
height: calc(100vh - 220px) !important; height: calc(100vh - 220px) !important;
// overflow: hidden; // overflow: hidden;
...@@ -628,6 +623,9 @@ onMounted(async () => { ...@@ -628,6 +623,9 @@ onMounted(async () => {
.search-input { .search-input {
flex: 1; flex: 1;
border: 1px solid #ddd;
border-radius: 4px;
height: 32px;
} }
} }
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<div class="left-main"> <div class="left-main">
<div class="top-bar"> <div class="top-bar">
<el-select v-model="searchDomain" placeholder="全部领域" class="domain-select"> <el-select v-model="searchDomain" placeholder="全部领域" class="domain-select">
<el-option label="全部领域" value="" /> <el-option label="全部领域" value="0" />
<el-option label="人工智能" value="1" /> <el-option label="人工智能" value="1" />
<el-option label="生物科技" value="2" /> <el-option label="生物科技" value="2" />
<el-option label="新一代信息技术" value="3" /> <el-option label="新一代信息技术" value="3" />
...@@ -515,7 +515,7 @@ const getNetProfitData = async () => { ...@@ -515,7 +515,7 @@ const getNetProfitData = async () => {
// 单次制裁-影响分析-制裁企业列表 // 单次制裁-影响分析-制裁企业列表
const entityList = ref([]); const entityList = ref([]);
const searchDomain = ref(""); const searchDomain = ref("0");
const searchKeyword = ref(""); const searchKeyword = ref("");
const selectedCompanyId = ref(null); const selectedCompanyId = ref(null);
const sanRecordId = ref(""); const sanRecordId = ref("");
...@@ -584,7 +584,7 @@ const getEntityList = async () => { ...@@ -584,7 +584,7 @@ const getEntityList = async () => {
const res = await getSingleSanctionEntityList({ const res = await getSingleSanctionEntityList({
sanRecordId: sanRecordId.value, sanRecordId: sanRecordId.value,
isOnlyCn: false, isOnlyCn: false,
domainId: searchDomain.value || undefined, domainId: searchDomain.value == 0 ? undefined : searchDomain.value || undefined,
searchText: searchKeyword.value || undefined searchText: searchKeyword.value || undefined
}); });
if (res.code === 200) { if (res.code === 200) {
...@@ -990,11 +990,6 @@ onMounted(async () => { ...@@ -990,11 +990,6 @@ onMounted(async () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.industrial-impact { .industrial-impact {
width: 100%; width: 100%;
padding-top: 16px; padding-top: 16px;
...@@ -1056,7 +1051,8 @@ onMounted(async () => { ...@@ -1056,7 +1051,8 @@ onMounted(async () => {
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
height: 32px; height: 32px;
border-radius: 4px; border: 1px solid rgba(170, 173, 177, 0.4);
border-radius: 5px;
} }
} }
} }
......
...@@ -1118,11 +1118,6 @@ onBeforeUnmount(() => { ...@@ -1118,11 +1118,6 @@ onBeforeUnmount(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.industrial-impact { .industrial-impact {
width: 100%; width: 100%;
padding-top: 16px; padding-top: 16px;
......
...@@ -28,10 +28,6 @@ const activeIndex = ref(0); ...@@ -28,10 +28,6 @@ const activeIndex = ref(0);
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.impact-analysis { .impact-analysis {
width: 1601px; width: 1601px;
margin: 0 auto; margin: 0 auto;
......
...@@ -100,10 +100,10 @@ ...@@ -100,10 +100,10 @@
<el-checkbox v-model="onlyChina" label="只看中国实体" /> <el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select <el-select
v-model="filterField" v-model="filterField"
placeholder="全部领域" placeholder="选择领域"
style="width: 150px; margin: 0 12px 0 16px" style="width: 150px; margin: 0 12px 0 16px"
> >
<el-option label="全部领域" value="" /> <!-- <el-option label="全部领域" value="" /> -->
<el-option <el-option
v-for="item in domainOptions" v-for="item in domainOptions"
:key="item.value" :key="item.value"
...@@ -243,6 +243,24 @@ import { useGotoCompanyPages } from "@/router/modules/company"; ...@@ -243,6 +243,24 @@ import { useGotoCompanyPages } from "@/router/modules/company";
const gotoCompanyPages = useGotoCompanyPages(); const gotoCompanyPages = useGotoCompanyPages();
const route = useRoute(); const route = useRoute();
const domainOptions = ref([
{ label: "全部领域", value: "0" },
{ label: "人工智能", value: "1" },
{ label: "生物科技", value: "2" },
{ label: "新一代信息技术", value: "3" },
{ label: "量子科技", value: "4" },
{ label: "新能源", value: "5" },
{ label: "集成电路", value: "6" },
{ label: "海洋", value: "7" },
{ label: "先进制造", value: "8" },
{ label: "新材料", value: "9" },
{ label: "航空航天", value: "10" },
{ label: "深海", value: "11" },
{ label: "极地", value: "12" },
{ label: "太空", value: "13" },
{ label: "核", value: "14" }
]);
// 跳转公司详情页 // 跳转公司详情页
const handleCompClick = item => { const handleCompClick = item => {
if (!item.entityId) { if (!item.entityId) {
...@@ -297,7 +315,7 @@ const getSanctionOverviewList = async () => { ...@@ -297,7 +315,7 @@ const getSanctionOverviewList = async () => {
const res = await getSingleSanctionOverviewList({ const res = await getSingleSanctionOverviewList({
sanRecordId: sanRecordId.value, sanRecordId: sanRecordId.value,
isOnlyCn: onlyChina.value, isOnlyCn: onlyChina.value,
domainId: filterField.value || undefined, domainId: filterField.value == 0 ? undefined : filterField.value || undefined,
searchText: searchKeyword.value || undefined, searchText: searchKeyword.value || undefined,
searchType: searchType.value, searchType: searchType.value,
entityTypeId: filterEntity.value || undefined entityTypeId: filterEntity.value || undefined
...@@ -465,7 +483,7 @@ const formattedData = computed(() => { ...@@ -465,7 +483,7 @@ const formattedData = computed(() => {
const filterEntity = ref("2"); const filterEntity = ref("2");
const onlyChina = ref(false); const onlyChina = ref(false);
const filterField = ref(""); const filterField = ref(domainOptions.value[0].value);
const searchKeyword = ref(""); const searchKeyword = ref("");
const activeTab = ref("add"); const activeTab = ref("add");
const searchType = computed(() => activeTab.value); const searchType = computed(() => activeTab.value);
...@@ -484,23 +502,6 @@ watch(searchKeyword, () => { ...@@ -484,23 +502,6 @@ watch(searchKeyword, () => {
debouncedSearch(); debouncedSearch();
}); });
const domainOptions = ref([
{ label: "人工智能", value: "1" },
{ label: "生物科技", value: "2" },
{ label: "新一代信息技术", value: "3" },
{ label: "量子科技", value: "4" },
{ label: "新能源", value: "5" },
{ label: "集成电路", value: "6" },
{ label: "海洋", value: "7" },
{ label: "先进制造", value: "8" },
{ label: "新材料", value: "9" },
{ label: "航空航天", value: "10" },
{ label: "深海", value: "11" },
{ label: "极地", value: "12" },
{ label: "太空", value: "13" },
{ label: "核", value: "14" }
]);
const tagColors = [ const tagColors = [
{ bg: "rgb(242, 235, 255)", border: "rgb(211, 190, 255)", text: "rgb(114, 46, 209)" }, // Purple { bg: "rgb(242, 235, 255)", border: "rgb(211, 190, 255)", text: "rgb(114, 46, 209)" }, // Purple
{ bg: "rgb(225, 250, 248)", border: "rgb(178, 242, 238)", text: "rgb(16, 178, 166)" }, // Cyan { bg: "rgb(225, 250, 248)", border: "rgb(178, 242, 238)", text: "rgb(16, 178, 166)" }, // Cyan
...@@ -570,11 +571,6 @@ onMounted(() => { ...@@ -570,11 +571,6 @@ onMounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// * {
// margin: 0;
// padding: 0;
// }
.sanctions-overview { .sanctions-overview {
width: 1601px; width: 1601px;
margin: 0 auto; margin: 0 auto;
...@@ -888,19 +884,19 @@ onMounted(() => { ...@@ -888,19 +884,19 @@ onMounted(() => {
margin-bottom: 20px; margin-bottom: 20px;
:deep(.el-input__inner) { :deep(.el-input__inner) {
font-size: 16px; font-size: 14px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
color: rgb(95, 101, 108); color: #606266;
&::placeholder { &::placeholder {
color: rgb(95, 101, 108); color: rgb(95, 101, 108, 0.8);
} }
} }
:deep(.el-checkbox__label) { :deep(.el-checkbox__label) {
font-size: 16px; font-size: 14px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
......
...@@ -60,7 +60,9 @@ ...@@ -60,7 +60,9 @@
:class="{ active: selectedSanctionId === item.id }" :class="{ active: selectedSanctionId === item.id }"
@click="selectSanction(item)" @click="selectSanction(item)"
> >
<div class="sanction-name">
{{ item.name }} {{ item.name }}
</div>
<div class="sanction-type">{{ item.postDate }}</div> <div class="sanction-type">{{ item.postDate }}</div>
</div> </div>
</div> </div>
...@@ -108,6 +110,7 @@ import router from "@/router"; ...@@ -108,6 +110,7 @@ import router from "@/router";
// 处理点击实体清单原文按钮 // 处理点击实体清单原文按钮
const handleClickOriginalText = () => { const handleClickOriginalText = () => {
// 打开新标签页 // 打开新标签页
window.open(`/exportControl/origin?id=${sanRecordId.value}`, "_blank"); window.open(`/exportControl/origin?id=${sanRecordId.value}`, "_blank");
}; };
...@@ -206,13 +209,14 @@ const totalElements = ref(0); ...@@ -206,13 +209,14 @@ const totalElements = ref(0);
const openSanctionModal = async () => { const openSanctionModal = async () => {
sanctionModalVisible.value = true; sanctionModalVisible.value = true;
console.log("制裁事件列表11:", sanctionList.value); console.log("制裁事件列表11:", sanctionList.value);
loading.value = true;
await fetchSanctionData(); await fetchSanctionData();
}; };
// ========== 获取制裁数据 ========== // ========== 获取制裁数据 ==========
const fetchSanctionData = async () => { const fetchSanctionData = async () => {
try { try {
loading.value = true;
const res = await getSanctionProcess([1], currentPage.value, 10); const res = await getSanctionProcess([1], currentPage.value, 10);
loading.value = false; loading.value = false;
if (res && !!res.content) { if (res && !!res.content) {
...@@ -254,6 +258,7 @@ const selectSanction = async item => { ...@@ -254,6 +258,7 @@ const selectSanction = async item => {
sanctionModalVisible.value = false; sanctionModalVisible.value = false;
console.log("跳转URL:", window.location.href); console.log("跳转URL:", window.location.href);
// 根据最新URL参数刷新当前页面 // 根据最新URL参数刷新当前页面
window.sessionStorage.setItem("curTabName", item.postDate + " 《实体清单新增条目》");
window.open(`${window.location.pathname}?id=${item.id}&sanTypeId=${item.sanTypeId}`, "_self"); window.open(`${window.location.pathname}?id=${item.id}&sanTypeId=${item.sanTypeId}`, "_self");
}; };
...@@ -274,15 +279,11 @@ onMounted(() => { ...@@ -274,15 +279,11 @@ onMounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.entity-list { .entity-list {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
padding-bottom: 50px; padding-bottom: 0px;
.header { .header {
width: 100%; width: 100%;
height: 148px; height: 148px;
...@@ -464,8 +465,15 @@ onMounted(() => { ...@@ -464,8 +465,15 @@ onMounted(() => {
.sanction-list { .sanction-list {
max-height: 400px; max-height: 400px;
min-height: 400px;
overflow-y: auto; overflow-y: auto;
padding: 0 10px; padding: 0 10px;
.sanction-name {
max-width: 85%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
} }
.sanction-item { .sanction-item {
......
...@@ -15,26 +15,21 @@ ...@@ -15,26 +15,21 @@
<div class="header-right"> <div class="header-right">
<!-- 中英文切换开关 --> <!-- 中英文切换开关 -->
<div class="toggle-group"> <div class="toggle-group">
<span :class="{ active: !showChinese }">英文</span> <!-- <span :class="{ active: !showChinese }">英文</span> -->
<el-switch <el-switch v-model="showChinese" @change="handleToggleChange" />
v-model="showChinese" <img :src="transIcon" alt="" />
active-text="中" <span :class="{ active: showChinese }">显示原文</span>
inactive-text="英"
:inline-prompt="true"
@change="handleToggleChange"
/>
<span :class="{ active: showChinese }">中文</span>
</div> </div>
<!-- 下载按钮 --> <!-- 下载按钮 -->
<el-button type="primary" :icon="Download" @click="handleDownload"> 下载 </el-button> <el-button plain :icon="Download" @click="handleDownload"> 下载 </el-button>
</div> </div>
</div> </div>
<!-- 外层滚动容器,统一控制两侧滚动 --> <!-- 外层滚动容器,统一控制两侧滚动 -->
<div class="report-box" ref="reportBoxRef"> <div class="report-box" ref="reportBoxRef">
<div class="pdf-pane-wrap" :class="{ 'center-mode': !showChinese }"> <div class="pdf-pane-wrap" v-if="showChinese" :class="{ 'center-mode': !showChinese }">
<pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" /> <pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" />
</div> </div>
<div class="pdf-pane-wrap" v-if="showChinese"> <div class="pdf-pane-wrap">
<pdf ref="rightPdfRef" :pdfUrl="headerTitle.transUrl" class="pdf-pane-inner" /> <pdf ref="rightPdfRef" :pdfUrl="headerTitle.transUrl" class="pdf-pane-inner" />
</div> </div>
</div> </div>
...@@ -47,6 +42,7 @@ import { ref, onMounted, watch, computed } from "vue"; ...@@ -47,6 +42,7 @@ import { ref, onMounted, watch, computed } from "vue";
import { Download } from "@element-plus/icons-vue"; import { Download } from "@element-plus/icons-vue";
import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js"; import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js";
import title from "../assets/title.png"; import title from "../assets/title.png";
import transIcon from "../assets/icon-translation.png";
import pdf from "./pdf.vue"; import pdf from "./pdf.vue";
const leftPdfRef = ref(null); const leftPdfRef = ref(null);
...@@ -299,15 +295,15 @@ onMounted(() => { ...@@ -299,15 +295,15 @@ onMounted(() => {
} }
} }
:deep(.el-button) { // :deep(.el-button) {
--el-button-bg-color: #055fc2; // --el-button-bg-color: #055fc2;
--el-button-border-color: #055fc2; // --el-button-border-color: #055fc2;
--el-button-hover-bg-color: #044c9b; // --el-button-hover-bg-color: #044c9b;
--el-button-hover-border-color: #044c9b; // --el-button-hover-border-color: #044c9b;
font-size: 14px; // font-size: 14px;
padding: 10px 20px; // padding: 10px 20px;
} // }
} }
} }
...@@ -318,6 +314,8 @@ onMounted(() => { ...@@ -318,6 +314,8 @@ onMounted(() => {
display: flex; display: flex;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
// ✅ 添加居中对齐
justify-content: center;
} }
.pdf-pane-wrap { .pdf-pane-wrap {
...@@ -330,6 +328,8 @@ onMounted(() => { ...@@ -330,6 +328,8 @@ onMounted(() => {
&.center-mode { &.center-mode {
flex: 0 0 100%; flex: 0 0 100%;
max-width: 100%; max-width: 100%;
// ✅ 添加居中样式
width: 728px; // 约一半宽度,保持单栏时美观
margin: 0 auto; margin: 0 auto;
} }
} }
......
<template> <template>
<div class="entity-list"> <div class="wrap">
<div class="header"> <div class="header">
<div class="header-title"> <div class="header-top">
<div class="header-top-left">
<img :src="headerTitle.img" alt="" /> <img :src="headerTitle.img" alt="" />
<div> <div>
<div class="title">{{ headerTitle.title }}</div> <div class="title">{{ headerTitle.title }}</div>
<div class="department">{{ headerTitle.department }}</div> <div class="en-title">
{{ headerTitle.sanTitle }}
</div>
</div>
</div>
<div class="header-top-right">
<div class="image-name-box">
<div class="image"><img :src="headerTitle.postOrgLogoUrl" alt="" /></div>
<div class="name">{{ headerTitle.postOrgName }}</div>
</div>
<div class="time">{{ headerTitle.postDate }}</div>
</div> </div>
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div class="main-header"> <div class="main-header">
<div class="header-left">实体清单制裁文件</div> <div>实体清单制裁文件</div>
<div class="header-right"> <div class="btn-box">
<!-- 中英文切换开关 --> <div class="translate">
<div class="toggle-group"> <div class="search-input-wrap" v-if="showSearchInput">
<!-- <span :class="{ active: !showChinese }">英文</span> --> <input
<el-switch v-model="showChinese" @change="handleToggleChange" /> v-model="searchKeywordText"
<img :src="transIcon" alt="" /> class="search-input"
<span :class="{ active: showChinese }">显示原文</span> placeholder="回车查询"
@keyup.enter="handleSearchInPdf"
/>
<div class="search-match-count">{{ matchInfo.current }}/{{ matchInfo.total }}</div>
<button
class="search-nav-btn"
type="button"
@click="handlePrevMatch"
:disabled="matchInfo.total === 0 || matchInfo.current <= 1"
>
上一个
</button>
<button
class="search-nav-btn"
type="button"
@click="handleNextMatch"
:disabled="matchInfo.total === 0 || matchInfo.current >= matchInfo.total"
>
下一个
</button>
</div>
<div class="switch">
<el-switch v-model="valueSwitch" />
</div>
<div class="translate-image">
<img
class="translate-icon"
src="../assets/icon-translation.png"
alt=""
style="
width: 16px;
height: 16px;
max-width: 16px;
max-height: 16px;
display: block;
object-fit: contain;
"
/>
</div>
<div class="translate-text">{{ "显示原文" }}</div>
</div> </div>
<!-- 下载按钮 --> <div class="btn" @click="handleDownload">
<el-button plain :icon="Download" @click="handleDownload"> 下载 </el-button> <div class="icon">
<img src="../assets/image-pdf.png" alt="" />
</div> </div>
<div class="text">{{ "下载" }}</div>
</div> </div>
<!-- 外层滚动容器,统一控制两侧滚动 -->
<div class="report-box" ref="reportBoxRef">
<div class="pdf-pane-wrap" v-if="showChinese" :class="{ 'center-mode': !showChinese }">
<pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" />
</div> </div>
<div class="pdf-pane-wrap"> </div>
<pdf ref="rightPdfRef" :pdfUrl="headerTitle.transUrl" class="pdf-pane-inner" /> <div class="report-box">
<div class="pdf-pane-wrap" v-if="valueSwitch && reportUrlEnWithPage">
<pdf ref="leftPdfRef" :pdfUrl="reportUrlEnWithPage" class="pdf-pane-inner" />
</div>
<div class="pdf-pane-wrap" :class="{ 'is-full': !valueSwitch }" v-if="reportUrlWithPage">
<pdf
:key="`right-pdf-${valueSwitch ? 'split' : 'full'}`"
ref="rightPdfRef"
:pdfUrl="reportUrlWithPage"
class="pdf-pane-inner"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -38,16 +96,23 @@ ...@@ -38,16 +96,23 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch, computed } from "vue"; import { computed, ref, onMounted, watch } from "vue";
import { Download } from "@element-plus/icons-vue"; import pdf from "./pdf.vue";
import { getThinkTankReportSummary, getThinkTankReportcontentUrl } from "@/api/thinkTank/overview";
import { useRoute, useRouter } from "vue-router";
import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js"; import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js";
import title from "../assets/title.png"; import title from "../assets/title.png";
import transIcon from "../assets/icon-translation.png";
import pdf from "./pdf.vue";
const leftPdfRef = ref(null); const router = useRouter();
const rightPdfRef = ref(null); const route = useRoute();
const reportBoxRef = ref(null); const reportUrl = ref("");
const reportUrlEn = ref("");
const thinkInfo = ref({});
const defaultPdfPage = ref(1);
const sourceCurrentPage = ref(Number(route.query.currentPage) || 1);
const sourcePageSize = ref(Number(route.query.pageSize) || 12);
const opinionId = ref(route.query.opinionId || "");
const opinionContent = ref(route.query.opinionContent || "");
const headerTitle = ref({ const headerTitle = ref({
img: title, img: title,
...@@ -56,47 +121,188 @@ const headerTitle = ref({ ...@@ -56,47 +121,188 @@ const headerTitle = ref({
srcUrl: "", srcUrl: "",
transUrl: "" transUrl: ""
}); });
const sanRecordId = ref(""); const sanRecordId = ref("");
const isSyncing = ref(false); const buildPdfPageUrl = url => {
if (!url) return "";
// ✅ 控制中文 PDF 显示 return `${url}#page=${defaultPdfPage.value}`;
const showChinese = ref(true); };
// ✅ 计算当前显示模式 const reportUrlWithPage = computed(() => buildPdfPageUrl(headerTitle.value.srcUrl));
const showMode = computed(() => { const reportUrlEnWithPage = computed(() => buildPdfPageUrl(headerTitle.value.transUrl));
return showChinese.value ? "both" : "en"; const valueSwitch = ref(true);
}); const showSearchInput = ref(true);
const searchKeywordText = ref("");
const leftPdfRef = ref(null);
const rightPdfRef = ref(null);
const matchInfo = ref({ current: 0, total: 0 });
const activePdfRef = ref(null);
const clearPdfSearchState = () => {
activePdfRef.value = null;
matchInfo.value = { current: 0, total: 0 };
const leftPdf = leftPdfRef.value;
const rightPdf = rightPdfRef.value;
if (leftPdf && typeof leftPdf.clearSearch === "function") {
leftPdf.clearSearch();
}
if (rightPdf && typeof rightPdf.clearSearch === "function") {
rightPdf.clearSearch();
}
};
// ✅ 切换中英文显示 const updateMatchInfo = () => {
const handleToggleChange = value => { const pdf = activePdfRef.value;
console.log("切换中英文显示:", value ? "中英双栏" : "仅英文"); if (pdf && typeof pdf.getMatchInfo === "function") {
showChinese.value = value; matchInfo.value = pdf.getMatchInfo();
return;
}
matchInfo.value = { current: 0, total: 0 };
}; };
// ✅ 下载功能 watch(
const handleDownload = async () => { () => searchKeywordText.value,
const files = [ val => {
{ url: headerTitle.value.srcUrl, name: "英文原版.pdf" }, const keyword = String(val ?? "").trim();
{ url: headerTitle.value.transUrl, name: "中文翻译.pdf" } if (!keyword) {
]; clearPdfSearchState();
}
}
);
for (const file of files) { watch(
if (file.url) { () => valueSwitch.value,
() => {
// 切换「显示原文」会导致 PDF 重新挂载/布局变化:清空搜索与计数,回到初始状态
searchKeywordText.value = "";
clearPdfSearchState();
}
);
const handleSearchInPdf = async () => {
const keyword = searchKeywordText.value?.trim();
if (!keyword) return;
activePdfRef.value = null;
matchInfo.value = { current: 0, total: 0 };
const leftPdf = leftPdfRef.value;
const rightPdf = rightPdfRef.value;
let page = 0;
let targetRef = null;
if (leftPdf && typeof leftPdf.searchKeyword === "function") {
page = await leftPdf.searchKeyword(keyword);
if (page) targetRef = leftPdf;
}
if (!page && rightPdf && typeof rightPdf.searchKeyword === "function") {
page = await rightPdf.searchKeyword(keyword);
if (page) targetRef = rightPdf;
}
if (page && targetRef && typeof targetRef.goToPage === "function") {
targetRef.goToPage(page);
activePdfRef.value = targetRef;
updateMatchInfo();
} else {
try { try {
const response = await fetch(file.url); const { ElMessage } = await import("element-plus");
ElMessage.warning("未找到包含该关键词的页面");
} catch (_) {}
}
};
const handlePrevMatch = () => {
const pdf = activePdfRef.value;
if (!pdf || typeof pdf.prevMatch !== "function") return;
pdf.prevMatch();
updateMatchInfo();
};
const handleNextMatch = () => {
const pdf = activePdfRef.value;
if (!pdf || typeof pdf.nextMatch !== "function") return;
pdf.nextMatch();
updateMatchInfo();
};
// 下载:中英文都下载,与政令原文页相同的 fetch → blob → a 标签触发下载
const downloadOnePdf = async (url, filename) => {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/pdf" }
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const blob = await response.blob(); const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a"); const link = document.createElement("a");
link.href = URL.createObjectURL(blob); link.href = blobUrl;
link.download = file.name; link.download = filename;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
URL.revokeObjectURL(link.href); window.URL.revokeObjectURL(blobUrl);
};
const handleDownload = async () => {
const urlZh = headerTitle.value.srcUrl ? String(headerTitle.value.srcUrl) : "";
const urlEn = headerTitle.value.transUrl ? String(headerTitle.value.transUrl) : "";
if (!urlZh && !urlEn) {
try {
const { ElMessage } = await import("element-plus");
ElMessage.warning("暂无下载链接");
} catch (_) {}
return;
}
const baseName = (thinkInfo.value?.name || "报告原文").replace(/[/\\?%*:|"<>]/g, "-");
const { ElMessage } = await import("element-plus");
try {
if (urlZh) {
await downloadOnePdf(urlZh, `${baseName}_中文.pdf`);
}
if (urlEn) {
if (urlZh) await new Promise(r => setTimeout(r, 300));
await downloadOnePdf(urlEn, `${baseName}_英文.pdf`);
}
if (urlZh || urlEn) {
ElMessage.success(urlZh && urlEn ? "已下载中文、英文两份 PDF" : "下载成功");
}
} catch (error) { } catch (error) {
console.error(`下载${file.name}失败:`, error); console.error("下载失败:", error);
ElMessage.error("PDF 下载失败,请稍后重试");
} }
};
/** 接口可能返回对象或数组;字段名兼容 snake/camel */
const normalizeReportSummaryRow = row => {
if (!row || typeof row !== "object") return {};
return {
...row,
name: row.name || "",
imageUrl: row.imageUrl || row.image || "",
ename: row.ename || row.nameEn || row.englishName || "",
times: row.times || row.postDate || "",
thinkTankName: row.thinkTankName || row.thinktankName || "",
thinkTankLogoUrl: row.thinkTankLogoUrl || row.thinktankLogo || ""
};
};
const applyReportOriginalDocumentTitle = title => {
const text = String(title || "").trim();
if (!text) return;
window.sessionStorage.setItem("reportOriginalTabName", text);
document.title = text;
};
// 获取报告全局信息
const handleGetThinkTankReportSummary = async () => {
try {
const res = await getThinkTankReportSummary(router.currentRoute._value.params.id);
console.log("报告全局信息", res);
if (res.code === 200 && res.data) {
const raw = res.data;
const first = Array.isArray(raw) ? raw[0] : raw;
const normalized = normalizeReportSummaryRow(first);
thinkInfo.value = normalized;
applyReportOriginalDocumentTitle(normalized.name);
} }
} catch (error) {
console.error("获取报告全局信息error", error);
} }
}; };
...@@ -104,7 +310,6 @@ const getUrlParams = () => { ...@@ -104,7 +310,6 @@ const getUrlParams = () => {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
sanRecordId.value = urlParams.get("id") || ""; sanRecordId.value = urlParams.get("id") || "";
}; };
const getSingleSanctionOverviewData = async () => { const getSingleSanctionOverviewData = async () => {
if (!sanRecordId.value) return; if (!sanRecordId.value) return;
try { try {
...@@ -127,195 +332,430 @@ const getSingleSanctionOverviewData = async () => { ...@@ -127,195 +332,430 @@ const getSingleSanctionOverviewData = async () => {
// 更新头部信息 // 更新头部信息
headerTitle.value = { headerTitle.value = {
...singleSanctionOverview,
...headerTitle.value, ...headerTitle.value,
title: `${dateStr}${singleSanctionOverview.sanTitleZh || singleSanctionOverview.sanTitle}》`, title: `${dateStr}${singleSanctionOverview.sanTitleZh || singleSanctionOverview.sanTitle}》`,
department: singleSanctionOverview.fileCode || "", department: singleSanctionOverview.fileCode || "",
srcUrl: singleSanctionOverview.srcUrl || "", srcUrl: singleSanctionOverview.srcUrl || "",
transUrl: singleSanctionOverview.transUrl || "" transUrl: singleSanctionOverview.transUrl || ""
}; };
applyReportOriginalDocumentTitle(headerTitle.value.title || "");
} }
} catch (error) { } catch (error) {
console.error("获取制裁概况失败:", error); console.error("获取制裁概况失败:", error);
} }
}; };
//获取原文
// 同步滚动处理 const handleGetThinkTankReportcontentUrl = async () => {
const handleSyncScroll = () => { try {
if (isSyncing.value) return; const res = await getThinkTankReportcontentUrl(router.currentRoute._value.params.id);
isSyncing.value = true; console.log("获取原文", res);
if (res.code === 200 && res.data) {
requestAnimationFrame(() => { reportUrl.value = res.data.content;
isSyncing.value = false; reportUrlEn.value = res.data.contentEn;
}); }
} catch (error) {
console.error("获取原文error", error);
}
}; };
// 监听滚动事件 const tabActiveName = ref("报告分析");
const setupScrollSync = () => {
const reportBox = reportBoxRef.value;
if (!reportBox) return;
reportBox.addEventListener("scroll", handleSyncScroll, { passive: true }); const switchTab = name => {
tabActiveName.value = name;
}; };
onMounted(async () => {
// 监听 PDF 加载完成 window.sessionStorage.setItem("curTabName", "实体清单原文");
watch( handleGetThinkTankReportSummary();
() => [headerTitle.value.srcUrl, headerTitle.value.transUrl], handleGetThinkTankReportcontentUrl();
() => { console.log("原文展示");
setTimeout(() => {
setupScrollSync();
}, 1000);
},
{ deep: true }
);
onMounted(() => {
getUrlParams(); getUrlParams();
getSingleSanctionOverviewData(); getSingleSanctionOverviewData();
setTimeout(() => {
setupScrollSync();
}, 500);
}); });
</script> </script>
<style scoped lang="scss">
// * {
// margin: 0;
// padding: 0;
// }
.entity-list { <style lang="scss" scoped>
width: 100%; .wrap {
height: 100%; overflow-y: hidden;
overflow-y: auto; height: 100vh;
display: flex;
flex-direction: column;
.header { .header {
width: 100%; width: 100%;
height: 148px; height: 88px;
background-color: #fff;
padding-top: 16px;
box-sizing: border-box; box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1); border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
position: sticky;
top: 0;
z-index: 99999;
overflow: hidden;
.header-title { .header-top {
width: 1601px;
height: 72px;
background-color: rgba(246, 250, 255, 1);
margin: 0 auto; margin: 0 auto;
border-radius: 10px; margin-top: 20px;
border: 2px solid rgba(174, 214, 255, 1); width: 1600px;
display: flex;
justify-content: space-between;
.header-top-left {
display: flex; display: flex;
align-items: center;
margin-bottom: 12px;
position: relative;
img { img {
width: 54px; width: 44px;
height: 54px; height: 54px;
margin-left: 15px;
margin-right: 11px;
} }
.title { .title {
margin-left: 20px;
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px; line-height: 26px;
color: rgb(59, 65, 75); letter-spacing: 0px;
text-align: left;
} }
.department { .en-title {
margin-top: 4px;
margin-left: 20px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
color: rgb(95, 101, 108); letter-spacing: 0px;
text-align: left;
/* 👇 下面是 两行文本超出省略 核心代码 */
display: -webkit-box;
-webkit-line-clamp: 1;
/* 限制显示 2 行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
} }
.tag-box {
margin-top: 11px;
display: flex;
gap: 8px;
margin-left: 20px;
} }
} }
.main { .header-top-right {
display: flex;
flex-direction: column;
text-align: right;
align-items: flex-end;
.image-name-box {
width: 200px;
height: 24px;
gap: 6px;
text-align: right;
display: flex;
justify-content: flex-end;
.name {
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: right;
}
.image {
width: 16px;
height: 16px;
margin-top: 5px;
img {
width: 100%;
height: 100%;
}
}
}
.time {
height: 24px;
margin-top: 5px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: right;
}
}
}
.header-bottom {
margin: 0 auto; margin: 0 auto;
margin-top: 30px;
width: 1600px;
height: 48px;
display: flex;
justify-content: space-between;
.tab-box {
width: 224px;
height: 48px;
display: flex;
gap: 24px;
.tab {
width: 94px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
border-bottom: 2px solid transparent;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 400;
line-height: 24px;
}
.textActive {
color: rgba(5, 95, 194, 1);
font-weight: 700;
}
}
.tabActive {
border-bottom: 2px solid rgba(5, 95, 194, 1);
}
}
}
}
.main {
margin: 0px auto;
margin-top: 20px;
background: rgb(255, 255, 255); background: rgb(255, 255, 255);
width: 1601px; width: 1600px;
height: calc(100vh - 148px); // height: 900px;
margin-bottom: 20px; margin-bottom: 20px;
border: 1px solid rgb(234, 236, 238); padding-bottom: 15px;
border: 1px, solid, rgb(234, 236, 238);
box-shadow: 0 0 20px 0 rgba(25, 69, 130, 0.1); box-shadow: 0 0 20px 0 rgba(25, 69, 130, 0.1);
.main-header { .main-header {
height: 64px; height: 64px;
/* box-sizing: border-box; */
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
background: rgb(255, 255, 255); background: rgb(255, 255, 255);
margin: 0 70px; margin: 0 70px;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
font-family: "Source Han Sans CN"; font-family: "Source Han Sans CN";
font-style: Bold;
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
line-height: 26px; line-height: 26px;
letter-spacing: 0px;
width: 1456px; width: 1456px;
text-align: left;
display: flex; display: flex;
align-items: center;
justify-content: space-between; justify-content: space-between;
align-items: center;
overflow: visible;
.header-right { .btn-box {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 24px; gap: 8px;
flex-shrink: 0;
.toggle-group { .translate {
display: flex; display: flex;
flex-wrap: nowrap;
align-items: center; align-items: center;
gap: 10px; height: 24px;
margin-right: 16px;
flex-shrink: 0;
span { :deep(.el-switch) {
font-size: 14px; width: 22px !important;
color: rgb(150, 150, 150); height: 14px !important;
transition: color 0.3s; margin-bottom: 5px;
margin-right: 8px;
}
&.active { :deep(.el-switch__core) {
color: rgb(5, 95, 194); width: 22px !important;
font-weight: 600; height: 14px !important;
min-width: 22px !important;
} }
:deep(.el-switch__button),
:deep(.el-switch__action) {
width: 10px !important;
height: 10px !important;
} }
:deep(.el-switch) { /* 打开时圆球从左边移到最右边:轨道 22px - 圆球 10px = 12px */
--el-switch-on-color: #055fc2; :deep(.el-switch.is-checked .el-switch__button),
--el-switch-off-color: #e6e7e8; :deep(.el-switch.is-checked .el-switch__action) {
transform: translateX(6px) !important;
}
.el-switch__label { .translate-image {
color: #fff; display: flex;
font-size: 12px; width: 16px;
font-weight: 600; height: 16px;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.translate-text {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
.btn {
width: 88px;
height: 32px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 6px;
background: rgba(255, 255, 255, 1);
display: flex;
gap: 8px;
cursor: pointer;
.icon {
width: 16px;
height: 16px;
display: inline-flex;
margin-top: 8px;
margin-left: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-top: 4px;
width: 32px;
height: 24px;
color: rgb(59, 65, 75);
font-family: "Source Han Sans CN";
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
&.is-active { .search-btn {
color: #fff; cursor: pointer;
} }
.search-input-wrap {
display: inline-flex;
align-items: center;
gap: 8px;
margin-left: 4px;
flex-shrink: 0;
} }
.search-input {
width: 160px;
height: 24px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
background: rgba(246, 250, 255, 1);
border-radius: 4px;
padding: 0 10px;
font-family: "Source Han Sans CN";
font-size: 14px;
line-height: 22px;
outline: none;
} }
.search-match-count {
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 14px;
line-height: 22px;
min-width: 48px;
text-align: center;
flex-shrink: 0;
} }
// :deep(.el-button) { .search-nav-btn {
// --el-button-bg-color: #055fc2; width: 68px;
// --el-button-border-color: #055fc2; height: 24px;
// --el-button-hover-bg-color: #044c9b; box-sizing: border-box;
// --el-button-hover-border-color: #044c9b; border: 1px solid rgba(231, 243, 255, 1);
background: rgba(246, 250, 255, 1);
border-radius: 4px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 14px;
line-height: 22px;
// font-size: 14px; cursor: pointer;
// padding: 10px 20px; padding: 0;
// } flex-shrink: 0;
white-space: nowrap;
}
.search-nav-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
} }
} }
.report-box { .report-box {
margin-left: 70px; margin-left: 70px;
width: 1456px; width: 1456px;
height: calc(100% - 64px); height: 881px;
display: flex; display: flex;
overflow-y: auto; overflow-y: auto;
/* 右侧统一滚动条,控制两侧原文+译文一起滚动 */
overflow-x: hidden; overflow-x: hidden;
// ✅ 添加居中对齐
justify-content: center;
} }
.pdf-pane-wrap { .pdf-pane-wrap {
...@@ -323,15 +763,11 @@ onMounted(() => { ...@@ -323,15 +763,11 @@ onMounted(() => {
max-width: 50%; max-width: 50%;
height: 100%; height: 100%;
min-width: 0; min-width: 0;
transition: all 0.3s; }
&.center-mode { .pdf-pane-wrap.is-full {
flex: 0 0 100%; flex: 0 0 100%;
max-width: 100%; max-width: 100%;
// ✅ 添加居中样式
width: 728px; // 约一半宽度,保持单栏时美观
margin: 0 auto;
}
} }
.pdf-pane-inner { .pdf-pane-inner {
......
<template>
<div class="pdf-viewer">
<!-- PDF 页面:canvas + textLayer 必须在同一容器内渲染 -->
<div class="page-wrap" v-for="page in pageCount" :key="page">
<canvas :ref="el => setCanvasRef(page, el)"></canvas>
<div :ref="el => setOverlayRef(page, el)" class="textLayer"></div>
</div>
<div v-if="loading" class="loading">加载中...</div>
</div>
</template>
<script>
import { ref, shallowRef, nextTick, watch } from "vue";
import * as pdfjsLib from "pdfjs-dist/legacy/build/pdf";
import PdfWorker from "pdfjs-dist/legacy/build/pdf.worker.min?worker";
// 使用 Vite 的 ?worker 直接注入 Worker,避免线上 mjs MIME 类型问题
pdfjsLib.GlobalWorkerOptions.workerPort = new PdfWorker();
export default {
name: "PdfViewer",
props: {
pdfUrl: {
type: String,
required: true
}
},
setup(props) {
const canvasMap = {};
const overlayMap = {};
const pageCount = ref(0);
const loading = ref(true);
const renderedPageCount = ref(0);
let resolveRenderAll = null;
const waitAllPagesRendered = () => {
if (pageCount.value > 0 && renderedPageCount.value >= pageCount.value) {
return Promise.resolve();
}
return new Promise(resolve => {
resolveRenderAll = resolve;
});
};
// pdfjs 的 document 对象内部使用 #private 字段,用 shallowRef 保持为原始对象引用
const pdfDocRef = shallowRef(null);
const searchKey = ref("");
const matchList = ref([]);
const matchIdx = ref(0);
const pdfjsApiRef = shallowRef(pdfjsLib);
// 保存 canvas
const setCanvasRef = (page, el) => {
if (!el) return;
canvasMap[page] = el;
};
// 保存 textLayer 容器(用于搜索高亮)
const setOverlayRef = (page, el) => {
if (!el) return;
overlayMap[page] = el;
};
// 清理 URL
const parsePdfUrl = pdfUrl => {
if (!pdfUrl || typeof pdfUrl !== "string") return "";
const [urlPart] = pdfUrl.split("#");
return urlPart;
};
// 清空所有高亮
const clearHighlights = () => {
Object.values(overlayMap).forEach(layer => {
if (!layer) return;
const rects = layer.querySelectorAll(".highlight-rect");
rects.forEach(n => n.remove());
});
};
// 重置搜索状态
const clearSearch = () => {
searchKey.value = "";
matchList.value = [];
matchIdx.value = 0;
clearHighlights();
};
// 渲染单页 PDF
const renderPage = async (pdf, pageNum) => {
const pdfPage = await pdf.getPage(pageNum);
const canvas = canvasMap[pageNum];
const textLayer = overlayMap[pageNum];
if (!canvas || !textLayer) return;
const baseViewport = pdfPage.getViewport({ scale: 1 });
const desiredWidth = canvas.clientWidth || 726;
const scale = desiredWidth / baseViewport.width;
const viewport = pdfPage.getViewport({ scale });
const context = canvas.getContext("2d");
canvas.width = viewport.width;
canvas.height = viewport.height;
canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`;
textLayer.style.width = canvas.width + "px";
textLayer.style.height = canvas.height + "px";
textLayer.innerHTML = "";
textLayer.style.setProperty("--scale-factor", String(viewport.scale || 1));
await pdfPage.render({ canvasContext: context, viewport }).promise;
try {
const textContent = await pdfPage.getTextContent();
let api = pdfjsApiRef.value || pdfjsLib;
let rt = api?.renderTextLayer;
if (typeof rt !== "function") {
try {
const legacy = await import("pdfjs-dist/legacy/build/pdf");
pdfjsApiRef.value = legacy;
api = legacy;
rt = legacy?.renderTextLayer;
} catch (_) {}
}
if (typeof rt === "function") {
await rt({
textContent,
container: textLayer,
viewport,
textDivs: [],
enhanceTextSelection: false
}).promise;
}
} catch (e) {
console.warn("textLayer 渲染失败", e);
}
renderedPageCount.value += 1;
if (pageCount.value > 0 && renderedPageCount.value >= pageCount.value) {
if (typeof resolveRenderAll === "function") {
const fn = resolveRenderAll;
resolveRenderAll = null;
fn();
}
}
};
// 渲染 PDF
const renderPdf = async pdfUrl => {
const url = parsePdfUrl(pdfUrl);
if (!url) return;
loading.value = true;
pdfDocRef.value = null;
clearHighlights();
matchList.value = [];
searchKey.value = "";
renderedPageCount.value = 0;
resolveRenderAll = null;
try {
const pdf = await pdfjsLib.getDocument(url).promise;
pdfDocRef.value = pdf;
pageCount.value = pdf.numPages;
await nextTick();
for (let p = 1; p <= pdf.numPages; p++) {
await renderPage(pdf, p);
}
} catch (err) {
console.error("PDF 加载失败", err);
} finally {
loading.value = false;
}
};
// 搜索关键词 + 高亮
const doSearch = async () => {
const doc = pdfDocRef.value;
const key = searchKey.value.trim();
clearHighlights();
matchList.value = [];
matchIdx.value = 0;
if (!doc || !key) return;
await waitAllPagesRendered();
for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {
const layer = overlayMap[pageNum];
if (!layer) continue;
const nodes = Array.from(layer.querySelectorAll("span"));
for (const el of nodes) {
const t = el.textContent || "";
if (!t) continue;
let start = 0;
while (true) {
const idx = t.indexOf(key, start);
if (idx === -1) break;
matchList.value.push({ pageNum, el, startIdx: idx, endIdx: idx + key.length });
start = idx + Math.max(1, key.length);
}
}
}
if (matchList.value.length > 0) jumpTo(0);
};
// 跳转到第 N 个匹配项
const jumpTo = idx => {
if (idx < 0 || idx >= matchList.value.length) return;
matchIdx.value = idx;
const m = matchList.value[idx];
const el = m?.el;
if (!el) return;
clearHighlights();
const textNode = el.firstChild;
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
try {
const range = document.createRange();
range.setStart(textNode, Math.max(0, m.startIdx ?? 0));
range.setEnd(textNode, Math.max(0, m.endIdx ?? 0));
const rectList = Array.from(range.getClientRects());
const pageWrap = el.closest(".page-wrap");
const layer = overlayMap[m.pageNum];
if (pageWrap && layer && rectList.length) {
const pageRect = pageWrap.getBoundingClientRect();
rectList.forEach(r => {
const mark = document.createElement("div");
mark.className = "highlight-rect";
mark.style.left = r.left - pageRect.left + "px";
mark.style.top = r.top - pageRect.top + "px";
mark.style.width = r.width + "px";
mark.style.height = r.height + "px";
layer.appendChild(mark);
});
}
range.detach?.();
} catch (e) {
// ignore
}
}
// 滚动到匹配位置
const container = el.closest(".report-box");
if (container) {
const TOP_OFFSET = 72;
const containerRect = container.getBoundingClientRect();
const elRect = el.getBoundingClientRect();
const targetTop = elRect.top - containerRect.top + container.scrollTop - TOP_OFFSET;
container.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" });
} else {
el.scrollIntoView({ behavior: "smooth", block: "center" });
}
};
const prevMatch = () => jumpTo(matchIdx.value - 1);
const nextMatch = () => jumpTo(matchIdx.value + 1);
const getMatchInfo = () => {
const total = matchList.value.length;
const current = total ? matchIdx.value + 1 : 0;
return { current, total };
};
// 外部调用方法
const searchKeyword = async keyword => {
searchKey.value = keyword;
await doSearch();
return matchList.value.length > 0 ? matchList.value[0].pageNum : 0;
};
const goToPage = pageNum => {
const canvasEl = canvasMap[pageNum];
if (!canvasEl) return;
const container = canvasEl.closest(".report-box");
if (container) {
const containerRect = container.getBoundingClientRect();
const canvasRect = canvasEl.getBoundingClientRect();
const targetTop = canvasRect.top - containerRect.top + container.scrollTop;
container.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" });
} else {
canvasEl.scrollIntoView({ behavior: "smooth", block: "start" });
}
};
// 获取容器元素(用于同步滚动)
const getContainer = () => {
const firstCanvas = canvasMap[1];
return firstCanvas ? firstCanvas.closest(".report-box") : null;
};
watch(
() => props.pdfUrl,
newVal => {
if (newVal) renderPdf(newVal);
},
{ immediate: true }
);
return {
pageCount,
setCanvasRef,
setOverlayRef,
loading,
searchKey,
doSearch,
prevMatch,
nextMatch,
getMatchInfo,
matchList,
matchIdx,
searchKeyword,
clearSearch,
goToPage,
getContainer
};
}
};
</script>
<style scoped>
.pdf-viewer {
position: relative;
width: 100%;
}
.page-wrap {
position: relative;
margin-bottom: 16px;
width: 100%;
}
canvas {
width: 100%;
height: auto;
display: block;
}
.textLayer {
position: absolute;
left: 0;
top: 0;
inset: 0;
overflow: hidden;
pointer-events: none;
z-index: 2;
line-height: 1;
}
.textLayer :deep(span) {
position: absolute;
transform-origin: 0% 0%;
white-space: pre;
line-height: 1;
font-size: calc(var(--font-height, 0px) * var(--scale-factor, 1));
transform: scaleX(var(--scale-x, 1));
color: transparent;
}
.textLayer :deep(.highlight-text) {
background: #ff0;
opacity: 0.6;
padding: 0 1px;
border-radius: 2px;
}
.textLayer :deep(.highlight-rect) {
position: absolute;
background: #ff0;
opacity: 0.6;
border-radius: 2px;
pointer-events: none;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 18px;
color: #333;
}
</style>
...@@ -17,7 +17,6 @@ import PdfWorker from "pdfjs-dist/legacy/build/pdf.worker.min?worker"; ...@@ -17,7 +17,6 @@ import PdfWorker from "pdfjs-dist/legacy/build/pdf.worker.min?worker";
// 使用 Vite 的 ?worker 直接注入 Worker,避免线上 mjs MIME 类型问题 // 使用 Vite 的 ?worker 直接注入 Worker,避免线上 mjs MIME 类型问题
pdfjsLib.GlobalWorkerOptions.workerPort = new PdfWorker(); pdfjsLib.GlobalWorkerOptions.workerPort = new PdfWorker();
export default { export default {
name: "PdfViewer", name: "PdfViewer",
props: { props: {
...@@ -33,7 +32,6 @@ export default { ...@@ -33,7 +32,6 @@ export default {
const loading = ref(true); const loading = ref(true);
const renderedPageCount = ref(0); const renderedPageCount = ref(0);
let resolveRenderAll = null; let resolveRenderAll = null;
const waitAllPagesRendered = () => { const waitAllPagesRendered = () => {
if (pageCount.value > 0 && renderedPageCount.value >= pageCount.value) { if (pageCount.value > 0 && renderedPageCount.value >= pageCount.value) {
return Promise.resolve(); return Promise.resolve();
...@@ -42,12 +40,14 @@ export default { ...@@ -42,12 +40,14 @@ export default {
resolveRenderAll = resolve; resolveRenderAll = resolve;
}); });
}; };
// pdfjs 的 document 对象内部使用 #private 字段,
// pdfjs 的 document 对象内部使用 #private 字段,用 shallowRef 保持为原始对象引用 // 若被 Vue 响应式深度代理会触发 "Cannot read from private field"。
// 因此用 shallowRef 保持为原始对象引用。
const pdfDocRef = shallowRef(null); const pdfDocRef = shallowRef(null);
const searchKey = ref(""); const searchKey = ref("");
const matchList = ref([]); const matchList = ref([]);
const matchIdx = ref(0); const matchIdx = ref(0);
// pdfjs 3.x 的 renderTextLayer 在不同入口下导出不一致,这里做一次缓存 + 兜底加载
const pdfjsApiRef = shallowRef(pdfjsLib); const pdfjsApiRef = shallowRef(pdfjsLib);
// 保存 canvas // 保存 canvas
...@@ -69,7 +69,7 @@ export default { ...@@ -69,7 +69,7 @@ export default {
return urlPart; return urlPart;
}; };
// 清空所有高亮 // 清空所有高亮(不销毁 textLayer)
const clearHighlights = () => { const clearHighlights = () => {
Object.values(overlayMap).forEach(layer => { Object.values(overlayMap).forEach(layer => {
if (!layer) return; if (!layer) return;
...@@ -78,7 +78,7 @@ export default { ...@@ -78,7 +78,7 @@ export default {
}); });
}; };
// 重置搜索状态 // 重置搜索状态:清空关键词、匹配列表与高亮
const clearSearch = () => { const clearSearch = () => {
searchKey.value = ""; searchKey.value = "";
matchList.value = []; matchList.value = [];
...@@ -86,7 +86,7 @@ export default { ...@@ -86,7 +86,7 @@ export default {
clearHighlights(); clearHighlights();
}; };
// 渲染单页 PDF // 渲染单页 PDF(canvas + textLayer)
const renderPage = async (pdf, pageNum) => { const renderPage = async (pdf, pageNum) => {
const pdfPage = await pdf.getPage(pageNum); const pdfPage = await pdf.getPage(pageNum);
const canvas = canvasMap[pageNum]; const canvas = canvasMap[pageNum];
...@@ -94,6 +94,7 @@ export default { ...@@ -94,6 +94,7 @@ export default {
if (!canvas || !textLayer) return; if (!canvas || !textLayer) return;
// 以画布的可视宽度为基准自适应缩放,避免 CSS 强行拉伸导致 textLayer/高亮错位
const baseViewport = pdfPage.getViewport({ scale: 1 }); const baseViewport = pdfPage.getViewport({ scale: 1 });
const desiredWidth = canvas.clientWidth || 726; const desiredWidth = canvas.clientWidth || 726;
const scale = desiredWidth / baseViewport.width; const scale = desiredWidth / baseViewport.width;
...@@ -102,19 +103,23 @@ export default { ...@@ -102,19 +103,23 @@ export default {
const context = canvas.getContext("2d"); const context = canvas.getContext("2d");
canvas.width = viewport.width; canvas.width = viewport.width;
canvas.height = viewport.height; canvas.height = viewport.height;
// 保证 canvas 不再被 CSS 拉伸,和 textLayer 共享同一坐标系
canvas.style.width = `${viewport.width}px`; canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`; canvas.style.height = `${viewport.height}px`;
textLayer.style.width = canvas.width + "px"; textLayer.style.width = canvas.width + "px";
textLayer.style.height = canvas.height + "px"; textLayer.style.height = canvas.height + "px";
textLayer.innerHTML = ""; textLayer.innerHTML = "";
// pdf.js v5 text layer 依赖 scale-factor 参与定位计算
textLayer.style.setProperty("--scale-factor", String(viewport.scale || 1)); textLayer.style.setProperty("--scale-factor", String(viewport.scale || 1));
await pdfPage.render({ canvasContext: context, viewport }).promise; await pdfPage.render({ canvasContext: context, viewport }).promise;
// 渲染 textLayer(pdfjs-dist 3.x):使用 renderTextLayer(不要用 TextLayer 构造器)
try { try {
const textContent = await pdfPage.getTextContent(); const textContent = await pdfPage.getTextContent();
let api = pdfjsApiRef.value || pdfjsLib; let api = pdfjsApiRef.value || pdfjsLib;
let rt = api?.renderTextLayer; let rt = api?.renderTextLayer;
// 兜底:某些入口下 renderTextLayer 不在 pdfjsLib 上,尝试 legacy 入口
if (typeof rt !== "function") { if (typeof rt !== "function") {
try { try {
const legacy = await import("pdfjs-dist/legacy/build/pdf"); const legacy = await import("pdfjs-dist/legacy/build/pdf");
...@@ -128,6 +133,7 @@ export default { ...@@ -128,6 +133,7 @@ export default {
textContent, textContent,
container: textLayer, container: textLayer,
viewport, viewport,
// pdfjs 3.x 需要传入 textDivs 数组
textDivs: [], textDivs: [],
enhanceTextSelection: false enhanceTextSelection: false
}).promise; }).promise;
...@@ -175,7 +181,7 @@ export default { ...@@ -175,7 +181,7 @@ export default {
} }
}; };
// 搜索关键词 + 高亮 // 搜索关键词 + 高亮(记录每个命中的子串范围)
const doSearch = async () => { const doSearch = async () => {
const doc = pdfDocRef.value; const doc = pdfDocRef.value;
const key = searchKey.value.trim(); const key = searchKey.value.trim();
...@@ -184,6 +190,7 @@ export default { ...@@ -184,6 +190,7 @@ export default {
matchIdx.value = 0; matchIdx.value = 0;
if (!doc || !key) return; if (!doc || !key) return;
// 首次搜索时确保所有页的 textLayer 已渲染完成,避免“越搜越多”
await waitAllPagesRendered(); await waitAllPagesRendered();
for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) { for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {
...@@ -214,7 +221,7 @@ export default { ...@@ -214,7 +221,7 @@ export default {
const el = m?.el; const el = m?.el;
if (!el) return; if (!el) return;
clearHighlights(); clearHighlights();
// 用 Range 精确计算“子串”在页面上的矩形位置,再画黄色块,避免把整段 span 都标黄
const textNode = el.firstChild; const textNode = el.firstChild;
if (textNode && textNode.nodeType === Node.TEXT_NODE) { if (textNode && textNode.nodeType === Node.TEXT_NODE) {
try { try {
...@@ -242,7 +249,7 @@ export default { ...@@ -242,7 +249,7 @@ export default {
} }
} }
// 滚动到匹配位置 // 优先只滚动右侧 report-box,避免触发整页滚动导致 header 遮挡
const container = el.closest(".report-box"); const container = el.closest(".report-box");
if (container) { if (container) {
const TOP_OFFSET = 72; const TOP_OFFSET = 72;
...@@ -285,12 +292,6 @@ export default { ...@@ -285,12 +292,6 @@ export default {
} }
}; };
// 获取容器元素(用于同步滚动)
const getContainer = () => {
const firstCanvas = canvasMap[1];
return firstCanvas ? firstCanvas.closest(".report-box") : null;
};
watch( watch(
() => props.pdfUrl, () => props.pdfUrl,
newVal => { newVal => {
...@@ -313,8 +314,7 @@ export default { ...@@ -313,8 +314,7 @@ export default {
matchIdx, matchIdx,
searchKeyword, searchKeyword,
clearSearch, clearSearch,
goToPage, goToPage
getContainer
}; };
} }
}; };
...@@ -349,11 +349,13 @@ canvas { ...@@ -349,11 +349,13 @@ canvas {
line-height: 1; line-height: 1;
} }
/* 不展示整页“文字层”,只在命中时显示黄色背景 */
.textLayer :deep(span) { .textLayer :deep(span) {
position: absolute; position: absolute;
transform-origin: 0% 0%; transform-origin: 0% 0%;
white-space: pre; white-space: pre;
line-height: 1; line-height: 1;
/* pdf.js v5 TextLayer:用变量计算真实字形盒子尺寸,否则背景宽高会不准 */
font-size: calc(var(--font-height, 0px) * var(--scale-factor, 1)); font-size: calc(var(--font-height, 0px) * var(--scale-factor, 1));
transform: scaleX(var(--scale-x, 1)); transform: scaleX(var(--scale-x, 1));
color: transparent; color: transparent;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<SearchContainer <SearchContainer
style="margin-bottom: 0; margin-top: 48px; height: fit-content" style="margin-bottom: 0; margin-top: 48px; height: fit-content"
v-if="homeMainRef" v-if="homeMainRef"
placeholder="搜索出口管制" placeholder="搜索投融资限制"
:containerRef="homeMainRef" :containerRef="homeMainRef"
areaName="实体清单" areaName="实体清单"
/> />
......
...@@ -628,6 +628,9 @@ onMounted(async () => { ...@@ -628,6 +628,9 @@ onMounted(async () => {
.search-input { .search-input {
flex: 1; flex: 1;
border: 1px solid #ddd;
border-radius: 4px;
height: 32px;
} }
} }
......
...@@ -1053,6 +1053,9 @@ onMounted(async () => { ...@@ -1053,6 +1053,9 @@ onMounted(async () => {
.search-input { .search-input {
width: 288px; width: 288px;
border: 1px solid #ddd;
border-radius: 4px;
height: 32px;
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
height: 32px; height: 32px;
......
...@@ -613,11 +613,6 @@ onMounted(() => { ...@@ -613,11 +613,6 @@ onMounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// * {
// margin: 0;
// padding: 0;
// }
.sanctions-overview { .sanctions-overview {
width: 1601px; width: 1601px;
margin: 0 auto; margin: 0 auto;
...@@ -960,7 +955,7 @@ onMounted(() => { ...@@ -960,7 +955,7 @@ onMounted(() => {
margin-bottom: 20px; margin-bottom: 20px;
:deep(.el-input__inner) { :deep(.el-input__inner) {
font-size: 16px; font-size: 14px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论