提交 d08db7c1 authored 作者: hsx's avatar hsx

feat:翻译面板组件

上级 da965171
<template>
<el-space alignment="flex-start" :size="10">
<img :width="64" :height="52" :src="news ?? DefaultIconNews" alt="" />
<el-space direction="vertical" alignment="flex-start" :size="0">
<common-text :line-limit="1" class="text-regular text-hover" color="var(--text-primary-80-color)">
<img :width="97" :height="72" :src="news ?? DefaultIconNews" alt="" />
<el-space direction="vertical" alignment="flex-start" :size="0" fill>
<div class="full-width flex-display">
<common-text :line-limit="1" class="text-title-3-bold text-hover flex-fill"
color="var(--text-primary-80-color)">
{{ title }}
</common-text>
<common-text :line-limit="1" class="text-tip-1" color="var(--text-primary-65-color)">
<common-text class="text-tip-2" color="var(--text-primary-65-color)">
{{ from }}
</common-text>
</div>
<common-text :line-limit="contentLineLimit" class="text-compact" color="var(--text-primary-65-color)">
{{ content }}
</common-text>
</el-space>
</el-space>
</template>
......@@ -34,14 +40,13 @@ const props = defineProps({
type: String,
default: ""
},
contentLineLimit: {
type: Number,
default: 2
}
});
const emit = defineEmits(['item-click', 'more-click']);
const handleToNewsAnalysis = (item, index) => {
emit('item-click', item, index)
};
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
@use '@/styles/container.scss';
</style>
\ No newline at end of file
<template>
<el-space alignment="flex-start" :size="10">
<img :width="64" :height="52" :src="news ?? DefaultIconNews" alt="" />
<el-space direction="vertical" alignment="flex-start" :size="0">
<common-text :line-limit="1" class="text-regular text-hover" color="var(--text-primary-80-color)">
{{ title }}
</common-text>
<common-text :line-limit="1" class="text-tip-1" color="var(--text-primary-65-color)">
{{ from }}
</common-text>
</el-space>
</el-space>
</template>
<script setup>
import DefaultIconNews from "@/assets/icons/default-icon-news.png";
import { ElSpace } from "element-plus";
import CommonText from "../texts/CommonText.vue";
const props = defineProps({
img: {
type: String,
default: ''
},
title: {
type: String,
default: ""
},
from: {
type: String,
default: ""
}
});
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
</style>
\ No newline at end of file
<template>
<div class="news-item">
<el-space direction="vertical" class="flex-fill" alignment='flex-start'>
<common-text :lineLimit="1" class="text-bold" color="var(--text-primary-80-color)">{{
<common-text :lineLimit="1" class="text-bold text-hover" color="var(--text-primary-80-color)">{{
props.title
}}</common-text>
<common-text class="text-tip-2" color="var(--text-primary-65-color)">
......@@ -12,7 +12,6 @@
</el-space>
</el-space>
<img style="width: 122px; height: 82px" :src="props.img">
<!-- <img v-else style="width: 122px; height: 82px" :src="DefaultIconNews"> -->
</div>
</template>
......
......@@ -26,8 +26,8 @@ const props = defineProps({
text: {
type: String,
default: ''
}
, entities: {
},
entities: {
type: Array<TextEntity>,
default: () => []
}
......@@ -37,8 +37,9 @@ const props = defineProps({
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)
// console.log('props.text', props.entities.length)
processedText.value = [{ text: '', isEntity: false }]
return
}
......
<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-down v-if="showMore" />
<arrow-up 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>
</template>
<script lang="ts" setup>
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';
const allTexts = ref([]);
const textColSpan = ref(12);
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: () => []
}
})
function updateText() {
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);
}
}
watch(() => [props.textsRaw, props.textsTranslate, props.isOpenTranslation], () => {
updateText();
})
onMounted(() => {
updateText();
})
</script>
\ No newline at end of file
......@@ -75,6 +75,7 @@
&:hover {
color: rgb(5, 95, 194) !important;
font-weight: 700;
cursor: pointer;
}
}
......
......@@ -38,6 +38,14 @@ const span = 12
</pre>
<div class="mouse-hover">鼠标悬停</div>
</el-col>
<el-col :span="span">
<pre>{{ `import '@/styles/common.scss';\n<template>
<div class="text-hover"></div>
</template>
`}}
</pre>
<div class="text-hover">文字悬停</div>
</el-col>
</el-row>
</template>
......
<template>
<el-space direction="vertical" class="background-as-card flex-fill common-padding" fill alignment="flex-start">
<el-switch v-model="isHightLightEntity" active-text="高亮实体" @change="console.log(isHightLightEntity)" />
<el-switch v-model="isOpenTranslation" active-text="译文" />
<text-translate-pane :texts-raw="textEns" :texts-translate="textZns" :text-entities="textEntities"
:is-open-translation="isOpenTranslation" :is-highlight-entity="isHightLightEntity">
</text-translate-pane>
</el-space>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import TextTranslatePane from '@/components/base/texts/TextTranslatePane.vue'
import { ElSwitch, ElButton, ElSpace } from 'element-plus'
const isOpenTranslation = ref(true);
const isHightLightEntity = ref(true);
const textEntities = ref([{
text_span: '美国',
type: '国家'
},
{
text_span: 'U.S.',
type: '国家'
}]);
const textZns = ref(['华盛顿当地时间2024年7月2日,美国商务部产业与安全局(BIS)',
'发布一项最终规则,宣布修订《出口管理条例》(EAR) ,',
'以违反美国国家安全或外交政策利益为由在实体清单中增列来自四个国家的6个实体,其中包括2家中国企业。[1]',
'多余段落-测试']);
const textEns = ref(['Washington local time on July 2, 2024, the U.S. Department of Commerce, Industry and Security (BIS) released a final rule, announcing the revision of the Export Administration Regulations (EAR) ,',
'Violating the U.S. national security or diplomatic policy interests, BIS increased the list of 6 entities from four countries, including 2 Chinese companies, on the entity list. [1]',
'Your company has a large amount of money in the U.S.',
]);
</script>
<style scoped>
:deep(.el-col) {
border: 0px !important;
}
</style>
......@@ -4,6 +4,9 @@ import '@/styles/tabs.scss'
import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue';
import AiTipPane from '@/components/base/panes/AiTipPane.vue';
import CommonText from '@/components/base/texts/CommonText.vue';
import IntelligentEntityText from '@/components/base/texts/IntelligentEntityText.vue';
import TranslateExample from './TranslateExample.vue';
const span = 12
</script>
......@@ -50,6 +53,40 @@ const span = 12
</pre>
<ai-tip-pane>huidadadadadasda</ai-tip-pane>
</el-col>
<el-col :span="span">
<pre>{{ `import IntelligentEntityText from '@/components/base/texts/IntelligentEntityText.vue';\n<template>
<intelligent-entity-text
text="华盛顿当地时间2024年7月2日,美国商务部产业与安全局(BIS) 发布一项最终规则,宣布修订《出口管理条例》(EAR) ,以违反美国国家安全或外交政策利益为由在实体清单 中增列来自四个国家的6个实体,其中包括2家中国企业。[1]"
:entities="[{
text_span: '华盛顿',
type: 'location'
}, {
text_span: '美国商务部产业与安全局',
type: 'organization'
}]">
</intelligent-entity-text>
</template>
`}}
</pre>
<intelligent-entity-text
text="华盛顿当地时间2024年7月2日,美国商务部产业与安全局(BIS) 发布一项最终规则,宣布修订《出口管理条例》(EAR) ,以违反美国国家安全或外交政策利益为由在实体清单 中增列来自四个国家的6个实体,其中包括2家中国企业。[1]"
:entities="[{
text_span: '华盛顿',
type: 'location'
}, {
text_span: '美国商务部产业与安全局',
type: 'organization'
}]">
</intelligent-entity-text>
</el-col>
<el-col :span="span">
<pre>{{ `参考src/styles/components/TextPage/TranslateExample.vue` }}
</pre>
<translate-example>
</translate-example>
</el-col>
</el-row>
</template>
......
.layout-grid-line {
.el-col {
.el-col:not(disinheritance) {
border: 1px double var(--bg-black-5);
}
......
......@@ -35,7 +35,7 @@
<script setup>
// 导入组件
import AnalysisBox from '@/components/base/BoxBackground/AnalysisBox.vue';
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
import { ref, watch } from 'vue';
import { getEnterpriseBranch, getEnterpriseKeyPerson } from '@/api/companyPages';
import PersonAvatar from '@/components/base/people/PersonAvatar.vue';
......
......@@ -5,8 +5,9 @@
<el-divider></el-divider>
<!-- 新闻列表 -->
<el-space :size="0" direction="vertical">
<news-item class="list-item" v-for="(t, i) in newsPage.content" :key="i" :news="t"></news-item>
<el-space class="full-width" :size="0" direction="vertical" fill alignment="flex-start">
<news-item class="list-item" v-for="(t, i) in newsPage.content" :key="i.id" :title="t.title" :from="t.from"
:content="t.content" @click="gotoNewsDetail(t.id)"></news-item>
</el-space>
<!-- 底部分隔线 -->
......@@ -20,14 +21,17 @@
</template>
<script setup>
import { ref, watch } from 'vue';
// 导入API
import { getEnterpriseNewDynamicPage } from '@/api/companyPages';
// 导入组件
import AnalysisBox from '@/components/base/BoxBackground/AnalysisBox.vue';
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
import NewsItem from '@/components/base/newsList/NewsItem.vue';
import { useGotoNewsDetail } from '@/router/modules/news';
import { ElDivider, ElSpace, ElPagination } from 'element-plus';
const gotoNewsDetail = useGotoNewsDetail()
// 导入Vue组合式API
import { ref, watch } from 'vue';
// 响应式数据
const newsPage = ref({}) // 新闻分页数据
......@@ -78,7 +82,11 @@ const onCurrentChange = e => {
<style lang="css" scoped>
/* 新闻列表项样式 */
.list-item {
margin-bottom: 20px;
width: 100%;
padding-bottom: 25px;
margin-bottom: 25px;
margin-left: 25px;
margin-right: 25px;
/* 底部外边距 */
border-bottom: 1px solid rgba(240, 242, 244, 1);
/* 底部边框 */
......
......@@ -3,7 +3,7 @@ import { ref, onMounted, onUnmounted, watch } from 'vue';
import { ElRadioGroup, ElRadioButton, ElSpace } from 'element-plus';
import * as echarts from 'echarts';
import { getSanctionList } from '@/api/companyPages';
import AnalysisBox from '@/components/base/BoxBackground/AnalysisBox.vue';
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
import AiTipPane from '@/components/base/panes/AiTipPane.vue'
export interface LineDataItem {
......
......@@ -34,37 +34,17 @@
</color-prefix-title>
<div class="flex-fill"></div>
<el-switch v-model="isHightLightEntity" active-text="高亮实体" @change="handleHighlightEntity" />
<el-button v-if="hasTranslation" :type="isOpenTranslation ? 'primary' : ''" plain
<el-button v-if="textZns.length > 0" :type="isOpenTranslation ? 'primary' : ''" plain
@click="handleTranslation">
<color-svg :svg-url="TranslationSvg" color="var(--color-primary-100)" :size="18"
style="margin-right:10px"></color-svg>
译文
</el-button>
</div>
<div class="common-padding">
<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="zhEnTexts.length > 6" @click="() => showMore = !showMore">
{{ showMore ? '收起' : '展开' }}
<el-icon>
<arrow-down v-if="showMore" />
<arrow-up v-else />
</el-icon>
</el-button>
</div>
<el-row :gutter="32">
<el-col :span="znEnColSpan"
v-for="(item, index) in showMore ? zhEnTexts : zhEnTexts.slice(0, 6)" :key="index">
<!-- <p class="p-news-content"> {{ item }}</p> -->
<intelligent-entity-text :text="item"
:entities="isHightLightEntity ? textEntities : []"></intelligent-entity-text>
</el-col>
</el-row>
</div>
<text-translate-pane class="common-padding" :texts-raw="textEns" :texts-translate="textZns"
:text-entities="textEntities" :is-open-translation="isOpenTranslation"
:is-highlight-entity="isHightLightEntity">
</text-translate-pane>
</el-space>
<el-space direction="vertical" class="background-as-card relation-news-box" alignment="flex-start">
<el-space style="margin-top: 10px;">
......@@ -73,7 +53,7 @@
</color-prefix-title>
</el-space>
<el-space direction="vertical" fill class="common-padding">
<news-item v-for="item in relationNews" :key="item.newsId" :news="item" :img="item.newsImage"
<news-item-mini v-for="item in relationNews" :key="item.newsId" :news="item" :img="item.newsImage"
:title="item.newsTitle" :from="`${item.newsDate} · ${item.newsOrg}`"
@click="gotoNewsDetail(item.newsId)" />
</el-space>
......@@ -87,28 +67,26 @@ import '@/styles/container.scss';
import '@/styles/common.scss';
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { ElSpace, ElImage, ElButton, ElIcon, ElScrollbar, ElRow, ElCol, ElDivider, ElSwitch } from "element-plus";
import { ElSpace, ElButton, ElScrollbar, ElSwitch } from "element-plus";
import CommonText from "@/components/base/texts/CommonText.vue";
import AreaTag from "@/components/base/AreaTag/index.vue";
import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue';
import { getRelationNews } from "@/api/news/newsDetail";
import NewsItem from "@/components/base/newsList/NewsItem.vue";
import NewsItemMini from "@/components/base/newsList/NewsItemMini.vue";
import ColorSvg from "@/components/base/images/ColorSvg.vue";
import TranslationSvg from './assets/images/翻译 1.svg';
import NewsLogo from './assets/images/组合 293.svg';
import { extractTextEntity } from "@/api/intelligent";
import { useGotoNewsDetail } from "@/router/modules/news";
import IntelligentEntityText from "@/components/base/texts/IntelligentEntityText.vue";
import TextTranslatePane from "@/components/base/texts/TextTranslatePane.vue";
const newsDetail = ref({});
const relationNews = ref([]);
const zhEnTexts = ref([]);
const znEnColSpan = ref(12);
const hasTranslation = ref(false);
const isOpenTranslation = ref(true);
const isHightLightEntity = ref(true);
const textEntities = ref([]);
const showMore = ref(false);
const textZns = ref([]);
const textEns = ref([]);
const route = useRoute();
const gotoNewsDetail = useGotoNewsDetail();
......@@ -118,50 +96,26 @@ onMounted(async () => {
}
const { data: newsDetailData } = await getNewsDetail(params);
newsDetail.value = newsDetailData ?? {};
textZns.value = newsDetail.value?.contentZh?.split('\n') ?? [];
textEns.value = newsDetail.value?.content?.split('\n') ?? [];
const { data: relationNewsData } = await getRelationNews(params);
relationNews.value = relationNewsData ?? [];
console.log(relationNews.value)
updateText();
await handleHighlightEntity();
});
async function handleHighlightEntity() {
if (textEntities.value.length > 0) return
const { result: entityDataZh } = await extractTextEntity(newsDetail.value.contentZh);
const { result: entityDataZh } = await extractTextEntity(newsDetail.value?.contentZh ?? '');
textEntities.value = [...entityDataZh ?? []]
if (newsDetail.value.contentZh !== newsDetail.value.content) {
const { result: entityData } = await extractTextEntity(newsDetail.value.content);
const { result: entityData } = await extractTextEntity(newsDetail.value?.content ?? '');
textEntities.value = [...textEntities.value, ...entityData ?? []]
}
console.log(isHightLightEntity.value)
}
function handleTranslation() {
isOpenTranslation.value = !isOpenTranslation.value;
updateText();
}
function updateText() {
const enTexts = newsDetail.value.content?.split('\n')
const zhTexts = newsDetail.value.contentZh?.split('\n')
console.log(enTexts.length, zhTexts.length)
const tempZhEnTexts = []
hasTranslation.value = enTexts.length === zhTexts.length
if (hasTranslation.value && isOpenTranslation.value) {
for (let i = 0; i < enTexts.length; i++) {
tempZhEnTexts.push(zhTexts[i]);
tempZhEnTexts.push(enTexts[i]);
}
znEnColSpan.value = 12;
} else {
for (let i = 0; i < enTexts.length; i++) {
tempZhEnTexts.push(enTexts[i]);
}
znEnColSpan.value = 24;
}
zhEnTexts.value = tempZhEnTexts;
}
</script>
......
<template>
<el-space direction="vertical" fill class="full-width mouse-hover" v-for="(item, index) in props.news" :key="index">
<news-item2 :img="item.newsImage" :title="item.newsTitle" :from="`新闻来源:${item.newsOrg} 发表时间:${item.newsDate}`"
:aeraTags="item.industryList?.map(t => t.industryName)" @click="gotoNewsDetail(item.newsId)" />
<news-item-with-tag :img="item.newsImage" :title="item.newsTitle"
:from="`新闻来源:${item.newsOrg} 发表时间:${item.newsDate}`" :aeraTags="item.industryList?.map(t => t.industryName)"
@click="gotoNewsDetail(item.newsId)" />
<div class="divider-news-list"></div>
</el-space>
</template>
<script setup>
import NewsItem2 from '@/components/base/newsList/NewsItem2.vue';
import NewsItemWithTag from '@/components/base/newsList/NewsItemWithTag.vue';
import { useGotoNewsDetail } from '@/router/modules/news';
import { ElSpace } from 'element-plus';
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论