提交 581d2dec authored 作者: 安云鹏's avatar 安云鹏

思维导图,写报

上级 d3d19811
......@@ -7,7 +7,7 @@
<router-view />
</div>
</div>
<div class="right-btn" @click="handleClickToolBox">
<div class="right-btn" @click="handleClickToolBox" v-if="route.path!='/writtingAsstaint'">
<div class="item">
<div class="icon">
<img src="@/assets/icons/overview/domain.png" alt="" />
......@@ -22,7 +22,7 @@
</div>
</div>
<div class="tool-box">
<div class="tool-box" v-if="route.path!='/writtingAsstaint'">
<!-- <div class="tool-item">
<img src="@/assets/icons/tool-item-icon1.png" alt="" />
</div>
......@@ -96,7 +96,6 @@ import { ElMessage } from "element-plus";
const router = useRouter();
const route = useRoute();
const isShowAiBox = ref(false);
const closeAiBox = () => {
......@@ -804,4 +803,7 @@ body {
cursor: not-allowed;
pointer-events: none;
}
</style>
<template>
<div class="writtingBottom">
<div class="parsed" v-if="store.isGenerating">
<!-- 文档停止解析 -->
<div class="parsed" v-if="store.bottomProgressNum>0&&store.bottomProgressNum<100">
<div class="analysis" @click="store.resetGenerateState">
<div class="icon"></div>
<span class="text-tip-2-bold">停止</span>
......@@ -15,12 +17,18 @@
</div>
</div>
</div>
<div class="parsed" v-else-if="store.isShowSteps&&store.bottomProgressNum>=100">
<div class="analysis" v-if="store.isWriteStart" @click="store.resetGenerateState">
<!-- 开始写报 -->
<!-- -->
<div class="parsed" v-else-if="store.bottomProgressNum>=100">
<div class="analysis" v-if="store.isWriteStart&&store.writeProgressNum<100" @click="store.writeGenerateState">
<div class="icon"></div>
<span class="text-tip-2-bold">停止</span>
</div>
<div class="analysis" v-else-if="store.writeProgressNum>=100" @click="store.resetGenerateState">
<img src="@/assets/icons/aiBox/ai-logo-color.png" alt="">
<span class="text-tip-1">重新上传</span>
</div>
<div class="notAnalysis" v-else @click="onWriteClick()" >
<img src="@/assets/icons/aiBox/ai-logo.png" alt="">
<span class="text-tip-1">智能写报</span>
......@@ -28,14 +36,15 @@
<div class="progress">
<div class="login">
<el-progress type="circle" :percentage="store.bottomProgressNum" :width="24" :height="24" style="margin-right: 15px;" :show-text="false" color="rgb(5, 95, 194)"/>
<span class="text-tip-2-bold">文档翻译</span>
<el-progress type="circle" :percentage="store.writeProgressNum" :width="24" :height="24" style="margin-right: 15px;" :show-text="false" color="rgb(5, 95, 194)"/>
<span class="text-tip-2-bold">智能写报</span>
</div>
<div class="text-tip-2" >
<div ref="processContainerRef" v-html="renderedProcess"></div>
<div ref="processWriteLogRef" v-html="renderedProcess"> </div>
</div>
</div>
</div>
<!-- 开始解析文档 -->
<div class="parsed" v-else >
<div class="notAnalysis" @click="onAnalysisClick()">
<img src="@/assets/icons/aiBox/ai-logo.png" alt="">
......@@ -75,15 +84,30 @@ const processContainerRef = ref(null);
watch(
() => store.processLog,
async (newLog) => {
console.log(newLog)
if (newLog !== undefined && newLog !== null) {
await updateProcess(newLog, processContainerRef.value);
}
},
{ immediate: true }
);
defineExpose({
processContainerRef
});
const processWriteLogRef=ref(null)
watch(
() => store.processWriteLog,
async (newLog) => {
if (newLog !== undefined && newLog !== null) {
const lines = newLog.split('\n').filter(newLog => newLog.trim());
const lastLine = lines[lines.length - 1];
await updateProcess(lastLine?lastLine:'', processWriteLogRef.value);
}
},
{ immediate: true }
);
</script>
<style lang="scss" scoped>
.writtingBottom{
......@@ -129,7 +153,7 @@ defineExpose({
.analysis{
border: 1px solid var(--color-primary-100);
background-color: #fff;
background-color: rgb(246, 250, 255);
color: var(--color-primary-100);
width: 437px;
height: 36px;
......@@ -145,6 +169,11 @@ defineExpose({
background-color: var(--color-primary-100);
margin-right: 12px;
}
img{
width: 21px;
height: 16px;
margin-right: 12px;
}
}
.progress{
display: flex;
......
<template>
<div class="headerBox">
<div class="tabBox" v-if="store.isGenerating||store.isShowSteps">
<div class="tabBox" v-if="store.bottomProgressNum>0">
<div class="fileName">
<img src="@/assets/icons/pdf-icon.png" alt=" ">
<span class="text-tip-1-bold">{{ store.uploadFileList[0]?.name||'文件错误' }}</span>
......@@ -10,13 +10,17 @@
:style="!item.active?'color:#bfbfbf;cursor: no-drop;':''"
@click="onTabListClick(item.type,item.active)">{{ item.name }}</div>
</div>
<div class="switch">
<div class="switch" v-if="store.headerTabType=='translate'">
<el-switch v-model="store.isShowOriginal"/>
<div class="iconBOx">
<img src="@/assets/icons/translate-icon.png" alt="">
<span class="text-tip-1">显示原文</span>
</div>
<el-button @click="onSearchClick">查找</el-button>
<el-button @click="store.handleIsSsearchFor"><img style="width: 16px;" src="@/assets/icons/aiBox/search.png" alt=""> 查找</el-button>
</div>
<div v-else>
<el-button @click="store.exportContent">导出</el-button>
</div>
</div>
......@@ -24,12 +28,11 @@
<img src="@/assets/icons/tool-item-icon1.png" alt="">
<span class="text-title-3-bold">智能写库</span>
</div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref, nextTick } from "vue";
import { onMounted, onUnmounted, ref, nextTick ,computed,watch} from "vue";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
......@@ -42,8 +45,7 @@ const onTabListClick= (type,active)=>{
console.log(1)
store.handleHeaderTab(type)
}
// 查找
const onSearchClick=()=>{}
</script>
<style lang="scss" scoped>
......@@ -118,4 +120,8 @@ const onSearchClick=()=>{}
}
}
</style>
\ No newline at end of file
......@@ -103,7 +103,7 @@ watch(
}
.content-box {
width: 1069px;
width: 100%;
height: 100%;
overflow-y: auto;
padding: 20px 80px;
......@@ -111,8 +111,8 @@ watch(
font-size: 16px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
// border: 1px solid rgba(234, 236, 238, 1);
// border-radius: 10px;
background: rgba(255, 255, 255, 1);
margin: 17px auto 0 auto;
......
<template>
<div class="translation-content" ref="translationContentRef">
<div class="translation-content" ref="translationContentRef">
<!-- :class="{ active: store.highlightClauseId === item.payload?.clause_number }"
:data-clause-number="item.payload?.clause_number" -->
<div class="translation-item" v-for="(item, index) in store.clauseTranslationMessages" :key="index">
<!-- 查找 -->
<div class="searchFor" v-if="store.isSsearchFor">
<el-input v-model="keyword" style="width: 260px;" placeholder="查找原文内容" />
<div class="searchTextNum">
<span v-if="total==0">0</span>
<span v-else> {{ current + 1 }}</span>/{{ total }}
</div>
<div class="prev" @click="prev"><el-icon><ArrowUp /></el-icon></div>
<div class="next" @click="next"><el-icon><ArrowDown /></el-icon></div>
<div class="close" @click="closeClick"><el-icon><CloseBold /></el-icon></div>
</div>
<div class="content-box" ref="contentBox">
<div class="translation-item" v-for="(item, index) in renderList" :key="index">
<div class="item-body">
<div class="original-text" v-if="store.isShowOriginal">
<span class="index-badge">{{item.payload?.clause_section }}</span>
{{ item.payload?.clause_content }}
<template v-for="(t, i) in item.fragments" :key="i">
<span :class="{ high: t.hit, current: t.hit && currentGlobalIndex === t.globalIndex}">
{{ t.text }}
</span>
</template>
</div>
<div class="translated-text">
<span class="clause-title">{{ getChineseNumber(item.payload?.clause_number) }}</span>
{{ item.payload?.clause_content_zh }}
<span class="clause-title">第{{ getChineseNumber(item.payload?.clause_number) }}节</span>{{ item.payload?.clause_content_zh }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref, nextTick ,watch} from "vue";
import { onMounted, onUnmounted, ref, nextTick ,watch,computed} from "vue";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
......@@ -42,13 +55,122 @@ const getChineseNumber = (num) => {
}
return num;
};
const closeClick=()=>{
store.handleIsSsearchFor()
current.value=0
total.value=0
keyword.value=''
}
const keyword = ref('')
const contentBox = ref(null)
const current = ref(0)
const total = ref(0)
// 渲染列表(不修改原数据,自动更新)
const renderList = computed(() => {
const key = keyword.value.trim()
return (store.clauseTranslationMessages || []).map(item => {
const section = item.payload?.clause_section || ''
const content = item.payload?.clause_content || ''
const fullText = section + ' ' + content
if (!key) {
return {
...item,
fragments: [{ text: fullText, hit: false, globalIndex: -1 }]
}
}
const parts = fullText.split(new RegExp(`(${key})`, 'g'))
const fragments = []
parts.forEach(t => {
fragments.push({
text: t,
hit: t === key,
globalIndex: -1
})
})
return { ...item, fragments }
})
})
// 全局匹配列表(只计算一次,修复蓝色全部选中BUG)
const globalMatchList = computed(() => {
const arr = []
renderList.value.forEach(item => {
item.fragments.forEach(f => {
if (f.hit) arr.push(f)
})
})
return arr
})
// 当前高亮的全局索引
const currentGlobalIndex = computed(() => {
if (!globalMatchList.value.length) return -1
return globalMatchList.value[current.value]?.globalIndex ?? -2
})
// 总数
watch(globalMatchList, (val) => {
total.value = val.length
current.value = 0
}, { immediate: true })
// 给每个命中项分配唯一 index
watch([renderList, globalMatchList], () => {
let idx = 0
const map = new Map()
globalMatchList.value.forEach(item => {
map.set(item, idx++)
})
renderList.value.forEach(item => {
item.fragments.forEach(f => {
if (f.hit) f.globalIndex = map.get(f)
})
})
})
function doSearch() {
current.value = 0
scrollTo(0)
}
function prev() {
if (!total.value) return
current.value = (current.value - 1 + total.value) % total.value
console.log(current.value )
scrollTo(current.value)
}
function next() {
if (!total.value) return
current.value = (current.value + 1) % total.value
scrollTo(current.value)
}
function scrollTo(idx) {
setTimeout(() => {
const all = contentBox.value?.querySelectorAll('.high')
if (all?.[idx]) {
contentBox.value.scrollTo({
top: all[idx].offsetTop - 100,
behavior: 'smooth'
})
}
}, 0)
}
</script>
<style lang="scss" scoped>
.translation-content{
.content-box{
overflow-y: auto;
margin-top: 30px;
height: calc(100% - 50px);
height: calc(100vh - 250px);
.translation-item{
margin-bottom: 24px;
.item-body{
......@@ -67,13 +189,48 @@ const getChineseNumber = (num) => {
line-height: 30px;
}
}
// .item-header{
// width: 48%;
// }
// .item-body{
// width: 48%;
// }
}
}
.searchFor{
display: flex;
align-items: center;
width: 430px;
height: 60px;
padding: 12px 0;
border-radius: 10px;
background-color: #fff;
border: 1px solid rgb(234, 236, 238);
position: fixed;
top: 120px;
right: 20px;
:deep(.el-input__wrapper){
background-color: #fff;
}
.searchTextNum{
width: 70px;
height: 100%;
border-right: 1px solid rgb(234, 236, 238);
line-height: 40px;
margin-right: 16px;
text-align: center;
}
.prev{
margin-right: 12px;
}
.next{
margin-right: 12px;
}
// position: absolute;
}
.high {
background: #ffeb3b;
}
.current {
background: #409eff !important;
color: #fff !important;
}
</style>
\ No newline at end of file
......@@ -25,6 +25,7 @@
</div>
</div>
</div> -->
<IntelligenceLeftTabBar></IntelligenceLeftTabBar>
<!-- 主体区域:子组件 -->
<div style="width: 100%;">
......@@ -32,25 +33,24 @@
<div class="writting-main">
<!-- 左侧子组件:绑定ref -->
<!-- <writtingleftBox ref="leftBoxRef" @generate="handleGenerate" /> -->
<writtingleftBox ref="leftBoxRef" />
<!-- <writtingleftBox ref="leftBoxRef" /> -->
<!-- 翻译 -->
<WrittingTranslate v-if="store.isShowClauseTranslation&&store.headerTabType=='translate'"></WrittingTranslate>
<!-- 思维导图 -->
<WrittingMind v-else-if="store.isShowClauseTranslation&&store.headerTabType=='mind'"></WrittingMind>
<!-- <WrittingTranslate v-if="store.isShowClauseTranslation&&store.headerTabType=='translate'"></WrittingTranslate> -->
<!-- 思维导图 v-else-if="store.isShowClauseTranslation&&store.headerTabType=='mind'"-->
<WrittingMind ></WrittingMind>
<!-- 写报 -->
<WrittingMessage v-else-if="store.isShowClauseTranslation&&store.headerTabType=='message'"></WrittingMessage>
<!-- <WrittingMessage v-else-if="store.isShowClauseTranslation&&store.headerTabType=='message'"></WrittingMessage> -->
<!-- 无数据时显示占位图 -->
<div v-else class="main-placeholder">
<!-- <div v-else class="main-placeholder">
<img src="./assets/images/container-image.png" alt="无数据占位图" />
<div class="placeholder-text">
<div v-if="store.isGenerating">智能体写报任务执行中...</div>
<div v-else>上传文件后点击“生成报文”开始写报...</div>
</div>
</div>
</div> -->
<!-- 右侧子组件:绑定ref -->
<!-- <writtingMainBox v-show="!!store.reportContent" ref="mainBoxRef" :report-content="store.reportContent" /> -->
......@@ -71,7 +71,7 @@ import writtingleftBox from "./components/WrittingLeftBox.vue";
import WrittingHeader from "./components/WrittingHeader.vue"; //头
import WrittingBottom from "./components/WrittingBottom.vue"; //底部
import WrittingTranslate from "./components/WrittingTranslate.vue"; //翻译
import WrittingMind from "./components/WrittingTranslate.vue"; //思维导图
import WrittingMind from "./components/WrittingMind.vue"; //思维导图
import WrittingMessage from "./components/WrittingMessage.vue"; //写报
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论