提交 7c629762 authored 作者: hsx's avatar hsx

feat:新闻的高亮实体使用站内搜索

上级 7f04e169
<template>
<p class="p-regular-rereg">
<span class="text-regular" v-for="(segment, index) in processedText" :key="index">
<a v-if="segment.isEntity" :href="`https://cn.bing.com/search?q=${segment.entity?.text_span}`"
class="entity-link" target="_blank" rel="noopener noreferrer">
{{ segment.entity?.text_span }}
<img :src="SearchIcon" :width="10" :height="10" alt="search" />
</a>
<span v-else>
{{ segment.text }}
</span>
</span>
</p>
<p class="p-regular-rereg">
<span class="text-regular" v-for="(segment, index) in processedText" :key="index">
<span v-if="segment.isEntity" @click="$emit('onEntityClick', segment.entity)" class="entity-link">
{{ segment.entity?.text_span }}
<img :src="SearchIcon" :width="10" :height="10" alt="search" />
</span>
<span v-else>
{{ segment.text }}
</span>
</span>
</p>
</template>
<script lang="ts" setup>
import { TextEntity } from '@/api/intelligent';
import { ref, watch, onMounted } from 'vue';
import SearchIcon from './images/search.png'
import { TextEntity } from "@/api/intelligent";
import { ref, watch, onMounted } from "vue";
import SearchIcon from "./images/search.png";
export interface ProcessedTextSegment {
text: string
isEntity: boolean
entity?: TextEntity
text: string;
isEntity: boolean;
entity?: TextEntity;
}
const props = defineProps({
text: {
type: String,
default: ''
},
entities: {
type: Array<TextEntity>,
default: () => []
}
})
text: {
type: String,
default: ""
},
entities: {
type: Array<TextEntity>,
default: () => []
}
});
const emit = defineEmits(["onEntityClick"]);
// 处理后的文本段
const processedText = ref<ProcessedTextSegment[]>([])
const processedText = ref<ProcessedTextSegment[]>([]);
// 处理文本,识别并替换实体
const processText = () => {
console.log('props.entities.length', props.entities.length)
if (!props.text || !props.entities) {
// console.log('props.text', props.entities.length)
processedText.value = [{ text: '', isEntity: false }]
return
}
console.log("props.entities.length", props.entities.length);
if (!props.text || !props.entities) {
// console.log('props.text', props.entities.length)
processedText.value = [{ text: "", isEntity: false }];
return;
}
const result = []
let currentPosition = 0
const result = [];
let currentPosition = 0;
// 按实体文本长度排序,优先匹配长文本
const sortedEntities = [...props.entities].sort((a, b) =>
b.text_span.length - a.text_span.length
)
// 按实体文本长度排序,优先匹配长文本
const sortedEntities = [...props.entities].sort((a, b) => b.text_span.length - a.text_span.length);
while (currentPosition < props.text.length) {
let matched = false
while (currentPosition < props.text.length) {
let matched = false;
for (const entity of sortedEntities) {
const entityText = entity.text_span
const endPosition = currentPosition + entityText.length
for (const entity of sortedEntities) {
const entityText = entity.text_span;
const endPosition = currentPosition + entityText.length;
if (props.text.substring(currentPosition, endPosition) === entityText) {
// 如果当前位置是实体,添加到结果
result.push({
isEntity: true,
entity: { ...entity }
})
currentPosition = endPosition
matched = true
break
}
}
if (props.text.substring(currentPosition, endPosition) === entityText) {
// 如果当前位置是实体,添加到结果
result.push({
isEntity: true,
entity: { ...entity }
});
currentPosition = endPosition;
matched = true;
break;
}
}
if (!matched) {
// 如果不是实体,收集普通文本
let nextEntityStart = props.text.length
if (!matched) {
// 如果不是实体,收集普通文本
let nextEntityStart = props.text.length;
for (const entity of sortedEntities) {
const pos = props.text.indexOf(entity.text_span, currentPosition)
if (pos !== -1 && pos < nextEntityStart) {
nextEntityStart = pos
}
}
for (const entity of sortedEntities) {
const pos = props.text.indexOf(entity.text_span, currentPosition);
if (pos !== -1 && pos < nextEntityStart) {
nextEntityStart = pos;
}
}
if (nextEntityStart > currentPosition) {
const plainText = props.text.substring(currentPosition, nextEntityStart)
result.push({
text: plainText,
isEntity: false
})
currentPosition = nextEntityStart
} else {
// 没有更多实体,添加剩余文本
const remainingText = props.text.substring(currentPosition)
if (remainingText) {
result.push({
text: remainingText,
isEntity: false
})
}
currentPosition = props.text.length
}
}
}
if (nextEntityStart > currentPosition) {
const plainText = props.text.substring(currentPosition, nextEntityStart);
result.push({
text: plainText,
isEntity: false
});
currentPosition = nextEntityStart;
} else {
// 没有更多实体,添加剩余文本
const remainingText = props.text.substring(currentPosition);
if (remainingText) {
result.push({
text: remainingText,
isEntity: false
});
}
currentPosition = props.text.length;
}
}
}
processedText.value = result
}
processedText.value = result;
};
// 监听文本和实体变化
watch(() => props.text, processText)
watch(() => props.entities, processText, { deep: true })
watch(() => props.text, processText);
watch(() => props.entities, processText, { deep: true });
// 初始化处理
onMounted(processText)
onMounted(processText);
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
@use "@/styles/common.scss";
.entity-link {
color: var(--color-primary-100);
color: var(--color-primary-100);
&:hover {
cursor: pointer;
}
}
.p-regular-rereg {
text-indent: 2em;
margin: 4px 0;
text-indent: 2em;
margin: 4px 0;
}
</style>
\ No newline at end of file
</style>
<template>
<div class="full-width">
<div class="flex-display" style="align-items: center;">
<common-text class="text-title-3-bold" color="var(--text-primary-80-color)">{{ isOpenTranslation
? '中文' : '原文' }}</common-text>
<div class="flex-fill" style="margin: 0 10px;">
<el-divider></el-divider>
</div>
<el-button v-if="showMoreVisible" @click="() => { showMore = !showMore; updateText() }">
{{ showMore ? '收起' : '展开' }}
<el-icon>
<arrow-up v-if="showMore" />
<arrow-down v-else />
</el-icon>
</el-button>
</div>
<el-row :gutter="32">
<el-col :span="textColSpan" v-for="(item, index) in allTexts" :key="index">
<!-- <p class="p-news-content"> {{ item }}</p> -->
<intelligent-entity-text :text="item"
:entities="isHighlightEntity ? textEntities : []"></intelligent-entity-text>
</el-col>
</el-row>
</div>
<div class="full-width">
<div class="flex-display" style="align-items: center">
<common-text class="text-title-3-bold" color="var(--text-primary-80-color)">{{
isOpenTranslation ? "中文" : "原文"
}}</common-text>
<div class="flex-fill" style="margin: 0 10px">
<el-divider></el-divider>
</div>
<el-button
v-if="showMoreVisible"
@click="
() => {
showMore = !showMore;
updateText();
}
"
>
{{ showMore ? "收起" : "展开" }}
<el-icon>
<arrow-up v-if="showMore" />
<arrow-down v-else />
</el-icon>
</el-button>
</div>
<el-row :gutter="32">
<el-col :span="textColSpan" v-for="(item, index) in allTexts" :key="index">
<!-- <p class="p-news-content"> {{ item }}</p> -->
<intelligent-entity-text
:text="item"
@on-entity-click="e => $emit('onEntityClick', e)"
:entities="isHighlightEntity ? textEntities : []"
></intelligent-entity-text>
</el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import '@/styles/container.scss';
import '@/styles/common.scss';
import "@/styles/container.scss";
import "@/styles/common.scss";
import { ref, watch, onMounted } from 'vue';
import { TextEntity } from '@/api/intelligent';
import IntelligentEntityText from '@/components/base/texts/IntelligentEntityText.vue';
import { ElIcon, ElButton, ElDivider, ElRow, ElCol } from 'element-plus';
import CommonText from './CommonText.vue';
import { ref, watch, onMounted } from "vue";
import { TextEntity } from "@/api/intelligent";
import IntelligentEntityText from "@/components/base/texts/IntelligentEntityText.vue";
import { ElIcon, ElButton, ElDivider, ElRow, ElCol } from "element-plus";
import CommonText from "./CommonText.vue";
const allTexts = ref([]);
const textColSpan = ref(12);
......@@ -39,71 +51,73 @@ const hasTranslation = ref(false);
const showMore = ref(false);
const showMoreVisible = ref(false);
const props = defineProps({
//段落列表: 原始文本
textsRaw: {
type: Array<String>,
default: () => []
},
//段落列表: 翻译文本
textsTranslate: {
type: Array<String>,
default: () => []
},
//是否显示翻译
isOpenTranslation: {
type: Boolean,
default: true
},
//是否高亮实体
isHighlightEntity: {
type: Boolean,
default: true
},
//实体列表
textEntities: {
type: Array<TextEntity>,
default: () => []
}
})
//段落列表: 原始文本
textsRaw: {
type: Array<String>,
default: () => []
},
//段落列表: 翻译文本
textsTranslate: {
type: Array<String>,
default: () => []
},
//是否显示翻译
isOpenTranslation: {
type: Boolean,
default: true
},
//是否高亮实体
isHighlightEntity: {
type: Boolean,
default: true
},
//实体列表
textEntities: {
type: Array<TextEntity>,
default: () => []
}
});
const emit = defineEmits(["onEntityClick"]);
function updateText() {
const tempTexts = []
const tempRaws = props.textsRaw ?? []
const tempTranslates = props.textsTranslate ?? []
hasTranslation.value = tempTranslates.length > 0
const tempTexts = [];
const tempRaws = props.textsRaw ?? [];
const tempTranslates = props.textsTranslate ?? [];
hasTranslation.value = tempTranslates.length > 0;
if (hasTranslation.value && props.isOpenTranslation) {
// 遍历原始文本和翻译文本,将它们交替添加到 tempTexts 中,并保持原始文本和翻译文本的的数量一致
const maxCount = Math.max(tempRaws.length, tempTranslates.length)
for (let i = 0; i < maxCount; i++) {
if (i < tempTranslates.length) {
tempTexts.push(tempTranslates[i]);
} else {
tempTexts.push('');
}
if (i < tempRaws.length) {
tempTexts.push(tempRaws[i]);
} else {
tempTexts.push('');
}
}
console.log(tempTexts.length)
textColSpan.value = 12;
showMoreVisible.value = tempTexts.length > 6;
allTexts.value = showMore.value ? tempTexts : tempTexts.slice(0, 6);
} else {
textColSpan.value = 24;
showMoreVisible.value = tempRaws.length > 3;
allTexts.value = showMore.value ? tempRaws : tempRaws.slice(0, 3);
}
if (hasTranslation.value && props.isOpenTranslation) {
// 遍历原始文本和翻译文本,将它们交替添加到 tempTexts 中,并保持原始文本和翻译文本的的数量一致
const maxCount = Math.max(tempRaws.length, tempTranslates.length);
for (let i = 0; i < maxCount; i++) {
if (i < tempTranslates.length) {
tempTexts.push(tempTranslates[i]);
} else {
tempTexts.push("");
}
if (i < tempRaws.length) {
tempTexts.push(tempRaws[i]);
} else {
tempTexts.push("");
}
}
console.log(tempTexts.length);
textColSpan.value = 12;
showMoreVisible.value = tempTexts.length > 6;
allTexts.value = showMore.value ? tempTexts : tempTexts.slice(0, 6);
} else {
textColSpan.value = 24;
showMoreVisible.value = tempRaws.length > 3;
allTexts.value = showMore.value ? tempRaws : tempRaws.slice(0, 3);
}
}
watch(() => [props.textsRaw, props.textsTranslate, props.isOpenTranslation], () => {
updateText();
})
watch(
() => [props.textsRaw, props.textsTranslate, props.isOpenTranslation],
() => {
updateText();
}
);
onMounted(() => {
updateText();
})
</script>
\ No newline at end of file
updateText();
});
</script>
// 综合搜索
const ComprehensiveSearch = () => import('@/views/comprehensiveSearch/index.vue')
const SearchResults = () => import('@/views/comprehensiveSearch/searchResults/index.vue')
const Chat = () => import('@/views/comprehensiveSearch/chat/index.vue')
const ComprehensiveSearch = () => import("@/views/comprehensiveSearch/index.vue");
const SearchResults = () => import("@/views/comprehensiveSearch/searchResults/index.vue");
const Chat = () => import("@/views/comprehensiveSearch/chat/index.vue");
const comprehensiveSearchRoutes = [
// 综合搜索
{
path: "/comprehensiveSearch",
name: "comprehensiveSearch",
component: ComprehensiveSearch,
meta: {
title: "搜索-科技安全"
}
},
{
path: "/searchResults",
name: "searchResults",
component: SearchResults,
meta: {
title: "搜索结果",
dynamicTitle: true
}
},
{
path: "/chat",
name: "chat",
component: Chat,
meta: {
title: "智能问答"
}
},
]
// 综合搜索
{
path: "/comprehensiveSearch",
name: "comprehensiveSearch",
component: ComprehensiveSearch,
meta: {
title: "搜索-科技安全"
}
},
{
path: "/searchResults",
name: "searchResults",
component: SearchResults,
meta: {
title: "搜索结果",
dynamicTitle: true
}
},
{
path: "/chat",
name: "chat",
component: Chat,
meta: {
title: "智能问答"
}
}
];
import { useGotoPage } from "../common.js";
export function useGotoComprehensiveSearch() {
const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/comprehensiveSearch/", {}, isNewTabs)
const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/comprehensiveSearch/", {}, isNewTabs);
}
export function useGotoSearchResults() {
const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/searchResults/", {searchText, areaName}, isNewTabs)
const gotoPage = useGotoPage();
return (searchText, areaName, isNewTabs = true) => gotoPage("/searchResults/", { searchText, areaName }, isNewTabs);
}
export default comprehensiveSearchRoutes
\ No newline at end of file
export default comprehensiveSearchRoutes;
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论