提交 8c356cbc authored 作者: 张烨's avatar 张烨

feat:政令原文增加搜索功能

上级 4b63bc6c
<template>
<div class="decree-overview-wrap">
<div class="left">
<div
class="sider-btn"
:class="{ siderBtnActive: siderBtnActive === item.name }"
@click="handleClickLeftSiderBtn(item)"
v-for="(item, index) in siderList"
:key="index"
>
<div class="btn-text">{{ item.name }}</div>
<div class="btn-icon">
<el-icon v-if="siderBtnActive === item.name" color="#fff"><CaretRight /></el-icon>
</div>
</div>
<SiderTabs :siderList="siderList" @clickSiderItem="handleClickLeftSiderBtn" />
</div>
<div class="main">
<router-view />
......@@ -23,6 +12,7 @@
import { ref, onMounted } from "vue";
import router from "@/router";
import { useRoute } from "vue-router";
import SiderTabs from "@/components/base/SiderTabs/index.vue";
const route = useRoute();
const decreeId = ref(route.query.id);
......@@ -30,24 +20,34 @@ const decreeId = ref(route.query.id);
const siderList = ref([
{
name: "政令简介",
active: true,
path: "/decreeLayout/overview/introduction"
},
{
name: "政令背景",
active: false,
path: "/decreeLayout/overview/background"
},
{
name: "政令举措",
active: false,
path: "/decreeLayout/overview/measures"
},
]);
const siderBtnActive = ref("政令简介");
const handleClickLeftSiderBtn = item => {
siderBtnActive.value = item.name;
const handleClickLeftSiderBtn = node => {
siderBtnActive.value = node.name;
siderList.value.forEach(item => {
if (item.name === node.name) {
item.active = true;
} else {
item.active = false;
}
});
router.push({
path: item.path,
path: node.path,
query: {
id: decreeId.value
}
......@@ -76,44 +76,17 @@ onMounted(() => {
width: 100%;
overflow: hidden;
background: rgba(247, 248, 249, 1);
display: flex;
justify-content: center;
position: relative;
.left {
position: absolute;
left: 0px;
top: 0px;
width: 160px;
padding-top: 8px;
.sider-btn {
margin-top: 20px;
margin-left: 20px;
width: 120px;
height: 32px;
display: flex;
border-radius: 16px;
cursor: pointer;
.btn-text {
margin-left: 28px;
width: 68px;
height: 32px;
line-height: 32px;
text-align: left;
box-sizing: border-box;
font-size: 16px;
font-family: Microsoft YaHei;
color: rgba(95, 101, 108, 1);
}
.btn-icon {
width: 22px;
padding-top: 9px;
}
}
.siderBtnActive {
background: var(--color-main-active);
.btn-text {
color: #fff !important;
}
}
padding: 0px 16px;
}
.main {
width: 1760px;
width: 1600px;
margin: 0 auto;
}
}
</style>
\ No newline at end of file
<template>
<div class="page-box">
<div class="risk-notice" v-if="riskInfo?.riskLevel">
<div class="risk-notice-header">
<div class="risk-notice-icon">
<img :src="warning1" alt="">
</div>
<div class="risk-notice-title">{{riskInfo.riskLevel}}</div>
<div class="risk-notice-icon">
<img :src="warning2" alt="">
</div>
</div>
<div class="risk-notice-content">{{ riskInfo.content }}</div>
<div v-if="riskInfo.riskLevel" style="margin-bottom: 16px;">
<WarnningPane :warnningLevel="riskInfo.riskLevel" :warnningContent="riskInfo.content" />
</div>
<div class="introduction-wrap">
<div class="left">
......@@ -162,8 +153,7 @@
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import warning1 from "./assets/icon/warning-icon1.png";
import warning2 from "./assets/icon/warning-icon2.png";
import WarnningPane from '@/components/base/WarningPane/index.vue'
import {
getDecreeBasicInfo,
getDecreeRiskSignal,
......@@ -190,10 +180,10 @@ const onRiskSignalData = async () => {
if (res.code === 200 && res.data) {
riskInfo.value = res.data;
} else {
riskInfo.value = null;
riskInfo.value = { riskLevel: "", content: "" };
}
} catch (error) {
riskInfo.value = null;
riskInfo.value = { riskLevel: "", content: "" };
console.error("获取风险警告数据失败:", error);
}
};
......@@ -314,69 +304,21 @@ onMounted(() => {
<style lang="scss" scoped>
.page-box {
height: 901px;
width: 1600px;
display: flex;
flex-direction: column;
padding: 16px 0;
gap: 16px;
}
.risk-notice {
cursor: pointer;
width: 100%;
border: 1px solid rgba(255, 163, 158, 1);
border-radius: 10px;
background-color: rgba(255, 241, 240, 1);
padding: 12px 14px;
.risk-notice-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
color: #d0021b;
font-weight: 600;
.risk-notice-icon {
font-size: 0px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.risk-notice-title {
font-size: 16px;
flex: auto;
width: 20px;
}
}
.risk-notice-content {
font-size: 14px;
line-height: 20px;
color: rgb(206, 79, 81);
padding-left: 24px;
letter-spacing: 1px;
}
}
.introduction-wrap {
width: 100%;
height: 20px;
flex: auto;
display: flex;
gap: 16px;
.left {
height: 100%;
width: 20px;
flex: auto;
display: flex;
flex-direction: column;
flex: auto;
margin-right: 16px;
.box1 {
height: 414px;
......@@ -479,8 +421,7 @@ onMounted(() => {
.box2 {
margin-top: 16px;
height: 20px;
flex: auto;
height: 420px;
.box2-main {
margin-top: 3px;
......@@ -581,7 +522,6 @@ onMounted(() => {
.right {
width: 520px;
height: 100%;
.box3 {
width: 520px;
......
......@@ -36,10 +36,25 @@
<el-icon><Document /></el-icon>
<div class="text">下载</div>
</div>
<div class="btn" @click="handleFindWord">
<div class="btn" @click="handleFindWord('open')">
<el-icon><Search /></el-icon>
<div class="text">查找</div>
</div>
<div class="find-word-box" v-if="findWordBox">
<div class="find-word-input">
<el-input v-model="findWordTxt" placeholder="查找原文内容" @input="handleUpdateWord()" />
</div>
<div class="find-word-limit">{{ findWordNum }}/{{ findWordMax }}</div>
<div class="find-word-icon" @click="handleFindWord('last')">
<el-icon><ArrowUp /></el-icon>
</div>
<div class="find-word-icon" @click="handleFindWord('next')">
<el-icon><ArrowDown /></el-icon>
</div>
<div class="find-word-icon" @click="handleFindWord('close')">
<el-icon><Close /></el-icon>
</div>
</div>
</div>
<div class="report-main">
<div v-if="!reportData.length" class="noContent">{{ "暂无数据" }}</div>
......@@ -58,21 +73,22 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, nextTick } from "vue";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { debounce } from "lodash";
import { getDecreeSummary } from "@/api/decree/introduction";
import { getDecreeReport } from "@/api/decree/introduction";
const route = useRoute();
// 修改为数组存储分段数据
const reportData = ref([]);
const summaryInfo = ref({});
// 政令原文操作
const isHighlight = ref(true);
const isHighlight = ref(false);
const isTranslate = ref(true);
const findWordTxt = ref("")
const findWordBox = ref(false);
const findWordNum = ref(0);
const findWordMax = ref(0);
const handleDownload = async () => {
if (summaryInfo.value?.url) {
try {
......@@ -112,11 +128,76 @@ const handleDownload = async () => {
ElMessage.warning("暂无下载链接!");
}
}
const handleFindWord = () => {
const handleHighlight = () => {
let spans = document.querySelectorAll(`span.highlight`);
spans.forEach((span, index) => {
if (index+1 === findWordNum.value) {
// 平滑滚动 behavior: 'smooth'
span.scrollIntoView({})
span.style.backgroundColor = "#ff9632";
} else {
span.style.backgroundColor = "#ffff00";
}
})
}
const handleFindWord = (event) => {
switch (event) {
case "open":
findWordBox.value = true;
break;
case "last":
if (findWordMax.value > 1) {
findWordNum.value = findWordNum.value==1 ? findWordMax.value : findWordNum.value-1;
handleHighlight()
}
break;
case "next":
if (findWordMax.value > 1) {
findWordNum.value = findWordNum.value==findWordMax.value ? 1 : findWordNum.value+1;
handleHighlight()
}
break;
case "close":
findWordBox.value = false;
findWordTxt.value = "";
handleUpdateWord()
break;
}
}
const handleUpdateWord = debounce(() => {
console.log("更新查找词", findWordTxt.value);
findWordNum.value = 0;
findWordMax.value = 0;
if (findWordTxt.value) {
originData.forEach((item, index) => {
if (item.content) {
reportData.value[index].content = highlightText(item.content, findWordTxt.value);
}
if (isTranslate.value && item.contentEn) {
reportData.value[index].contentEn = highlightText(item.contentEn, findWordTxt.value);
}
});
if (findWordMax.value > 0) {
nextTick(() => { handleFindWord('next') })
}
} else {
originData.forEach((item, index) => {
reportData.value[index].content = item.content;
reportData.value[index].contentEn = item.contentEn;
});
}
}, 300)
const highlightText = (text, searchTerm) => {
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return text.replace(new RegExp(escapedTerm, 'g'), (match) => {
findWordMax.value++;
return `<span class="highlight">${match}</span>`;
});
}
// 获取全局信息
const summaryInfo = ref({});
const handleGetSummary = async () => {
const params = {
id: route.query.id
......@@ -131,18 +212,14 @@ const handleGetSummary = async () => {
};
// 获取报告原文 - 修改为获取分段数组
const reportData = ref([]);
let originData = [];
const handleGetReport = async () => {
try {
const res = await getDecreeReport({id: route.query.id});
console.log("报告原文", res);
if (res.code === 200 && res.data) {
reportData.value = [
// {
// content: "设立第二紧急委员会(以下简称“委员会”)。自2026年1月16日美国东部<span>标准时间</span>凌晨12:01起,成立一个由一名主席和两名成员组成的<span>委员会</span>,所有成员均由总统任命,负责调查并报告这些争议。任何成员均不得在任何铁路员工组织或任何铁路承运人中拥有经济或其他利益关系。委员会的运作取决于资金的可用性。",
// contentEn: "Establishment of a Second <span>Emergency</span> Board (Board). There is established, effective 12:01 a.m. eastern standard time on <span>January 16</span>, 2026, a Board composed of a chair and two other members, all of whom shall be appointed by the President to investigate and report on these disputes. No member shall be pecuniarily or otherwise interested in any organization of railroad employees or any carrier. The Board shall perform its functions subject to the availability of funds.",
// num: 0,
// }
];
originData = [];
let num = Math.max(res.data.content.length, res.data.contentEn.length)
for (let i = 0; i < num; i++) {
let obj = {
......@@ -150,8 +227,9 @@ const handleGetReport = async () => {
contentEn: res.data.contentEn[i] || "",
num: i + 1,
}
reportData.value.push(obj);
originData.push(obj);
}
reportData.value = JSON.parse(JSON.stringify(originData));
}
} catch (error) { }
};
......@@ -164,8 +242,9 @@ onMounted(() => {
<style lang="scss" scoped>
.high-light {
:deep(span) {
color: #055FC2;
:deep(span.highlight) {
// color: #055FC2;
background-color: #ffff00;
}
}
.layout-container {
......@@ -287,6 +366,33 @@ onMounted(() => {
align-items: center;
border-bottom: solid 1px rgba(234, 236, 238, 1);
margin: 0 20px 10px;
position: relative;
.find-word-box {
position: absolute;
top: -50px;
right: 0px;
width: 430px;
height: 60px;
border: 1px solid rgba(230, 231, 232, 1);
background-color: white;
border-radius: 6px;
display: flex;
align-items: center;
.find-word-input {
width: 20px;
flex: auto;
}
.find-word-limit {
border-right: solid 1px rgba(230, 231, 232, 1);
color: #5F656C;
padding-right: 16px;
}
.find-word-icon {
padding: 10px 12px;
margin: 0 2px;
cursor: pointer;
}
}
.report-title {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论