提交 7157f6a4 authored 作者: 安云鹏's avatar 安云鹏

tj

上级 5ea53c1a
...@@ -51,6 +51,29 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -51,6 +51,29 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
containerRef:null containerRef:null
}, },
// ====头
//list
tabList:[
{
type:'translate',
name:'翻译',
active: true
},
{
type:'mind',
name:'思维导图',
active: false
},
{
type:'message',
name:'写报',
active: false
}
],
headerTabType:'translate',
// 底部
bottomProgressNum:0, //文档解析 假进度
}), }),
getters: { getters: {
...@@ -60,7 +83,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -60,7 +83,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
const now = new Date() const now = new Date()
const pad = n => n.toString().padStart(2, '0') const pad = n => n.toString().padStart(2, '0')
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}` return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
} },
}, },
actions: { actions: {
...@@ -128,6 +152,20 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -128,6 +152,20 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
this._keepStepsViewOnError(); this._keepStepsViewOnError();
await this._showErrorDialog(message); await this._showErrorDialog(message);
}, },
//header tab切换
async handleHeaderTab(type){
this.headerTabType=type
},
async generateWrite(){
console.log(3)
await this.fetchReportData({
query: this.writtingTitle,
desc: this.descText,
topic: this.curTempTitle,
result: this.clauseTranslationMessages[this.clauseTranslationMessages.length-1]
});
},
// ========== 路由参数处理 ========== // ========== 路由参数处理 ==========
async setRouteParams(query) { async setRouteParams(query) {
...@@ -279,6 +317,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -279,6 +317,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
this.abortController = new AbortController(); this.abortController = new AbortController();
this.processLog = ''; this.processLog = '';
// 进度初始化0
this.bottomProgressNum=0
try { try {
const formData = new FormData(); const formData = new FormData();
formData.append('pdf', selectedFile); formData.append('pdf', selectedFile);
...@@ -309,18 +349,22 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -309,18 +349,22 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
console.warn('SSE消息JSON解析失败', parseError, event.data); console.warn('SSE消息JSON解析失败', parseError, event.data);
return; return;
} }
switch (event.event) { switch (event.event) {
case 'progress': case 'progress':
// 仅更新执行步骤 // 仅更新执行步骤
if (jsonData.message) { if (jsonData.message) {
this.processLog += `${this.formattedTime}:${jsonData.message}\r\n`; this.processLog = `${this.formattedTime}:${jsonData.message}\r\n`;
} }
this.bottomProgressNum+=1
break; break;
case 'metadata': case 'metadata':
if (jsonData && jsonData.payload) { if (jsonData && jsonData.payload) {
this.pdfMetadata = jsonData.payload; this.pdfMetadata = jsonData.payload;
} }
if(this.bottomProgressNum<90){
this.bottomProgressNum+=5
}
break; break;
case 'clause_translation': case 'clause_translation':
// 保存条款翻译消息并显示侧边栏 // 保存条款翻译消息并显示侧边栏
...@@ -328,16 +372,32 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -328,16 +372,32 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
this.clauseTranslationMessages.push(jsonData); this.clauseTranslationMessages.push(jsonData);
this.isShowClauseTranslation = true; this.isShowClauseTranslation = true;
this.isShowSteps = true; // 翻译出现时,步骤侧边栏也显示 this.isShowSteps = true; // 翻译出现时,步骤侧边栏也显示
// 假进度
if(this.bottomProgressNum<90){
this.bottomProgressNum+=5
}
} }
break; break;
case 'result': case 'result':
if (jsonData && Object.keys(jsonData).length) { if (jsonData && Object.keys(jsonData).length) {
await this.fetchReportData({ console.log(
query: this.writtingTitle, {
desc: this.descText, query: this.writtingTitle,
topic: this.curTempTitle, desc: this.descText,
result: jsonData topic: this.curTempTitle,
}); result: jsonData
}
)
// 假进度完成
this.bottomProgressNum=100
this.tabList[2].active=true //有结果之后放开写报按钮
// await this.fetchReportData({
// query: this.writtingTitle,
// desc: this.descText,
// topic: this.curTempTitle,
// result: jsonData
// });
} }
break; break;
case 'error': case 'error':
...@@ -374,6 +434,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -374,6 +434,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
// ========== AI 生成报文 SSE(更新报文内容 + 执行步骤) ========== // ========== AI 生成报文 SSE(更新报文内容 + 执行步骤) ==========
async fetchReportData(params) { async fetchReportData(params) {
console.log(7)
if (this.abortController) this.abortController.abort(); if (this.abortController) this.abortController.abort();
this.abortController = new AbortController(); this.abortController = new AbortController();
this.processLog = ''; this.processLog = '';
...@@ -425,7 +487,6 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -425,7 +487,6 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
lastFlushedIndex = Math.max(lastFlushedIndex, lineIdx + 1); lastFlushedIndex = Math.max(lastFlushedIndex, lineIdx + 1);
} }
}; };
try { try {
const { fetchEventSource } = await import('@microsoft/fetch-event-source'); const { fetchEventSource } = await import('@microsoft/fetch-event-source');
await fetchEventSource('/sseWrite/api/v1/workflow/invoke', { await fetchEventSource('/sseWrite/api/v1/workflow/invoke', {
...@@ -502,8 +563,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', { ...@@ -502,8 +563,8 @@ export const useWrittingAsstaintStore = defineStore('writtingAsstaint', {
// 路由参数优先 // 路由参数优先
this.isGenerating = true; this.isGenerating = true;
this.isShowProcess = true; this.isShowProcess = true;
if (Object.keys(this.routeQuery).length !== 0) { if (Object.keys(this.routeQuery).length !== 0) {
const { fileId } = this.routeQuery; const { fileId } = this.routeQuery;
// 外部跳转:根据 topic 决定调用哪种数据获取接口,再触发生成 // 外部跳转:根据 topic 决定调用哪种数据获取接口,再触发生成
......
<template> <template>
<div class="writtingBottom"> <div class="writtingBottom">
<div class="notParsed" v-if="isfile==0" > <div class="parsed" v-if="store.isGenerating">
<div class="analysis" @click="analysisClick(1)"> <div class="analysis" @click="store.resetGenerateState">
<img src="@/assets/icons/aiBox/ai-logo.png" alt="">
<span class="text-tip-1">文档解析</span>
</div>
<div class="progress">
<img src="../assets/images/tips-icon.png" alt="">
<span class="text-tip-2">内容由AI生成,无法确保真实准确,仅供参考</span>
</div>
</div>
<div class="parsed" v-else-if="isfile==1">
<div class="analysis" @click="analysisClick(0)">
<div class="icon"></div> <div class="icon"></div>
<span class="text-tip-2-bold">停止</span> <span class="text-tip-2-bold">停止</span>
</div> </div>
<div class="progress"> <div class="progress">
<div class="login"> <div class="login">
<el-progress type="circle" :percentage="25" width="24" height="24" style="margin-right: 15px;" :show-text="false" color="rgb(5, 95, 194)"/> <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> <span class="text-tip-2-bold">文档翻译中</span>
</div> </div>
<span class="text-tip-2"> <div class="text-tip-2" >
内容由AI生成,无法确保真实准确,仅供参考 <div ref="processContainerRef" v-html="renderedProcess"></div>
</span> </div>
</div>
</div>
<div class="notParsed" v-else >
<div class="parsedItem" v-if="store.bottomProgressNum<100">
<div class="analysis" @click="onAnalysisClick()">
<img src="@/assets/icons/aiBox/ai-logo.png" alt="">
<span class="text-tip-1">文档解析</span>
</div>
<div class="progress">
<img src="../assets/images/tips-icon.png" alt="">
<span class="text-tip-2">内容由AI生成,无法确保真实准确,仅供参考</span>
</div>
</div>
<div class="parsedItem" v-else-if="store.bottomProgressNum>=100">
<div class="analysis" @click="onWriteClick()">
<img src="@/assets/icons/aiBox/ai-logo.png" alt="">
<span class="text-tip-1">智能写报</span>
</div>
<div class="progress">
<div class="login">
<el-progress type="circle" :percentage="50" :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>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, onUnmounted, ref, nextTick } from "vue"; import { onMounted, onUnmounted, ref, nextTick ,watch} from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
const isfile=ref(1) //0未解析 1已解析 import { useStream } from "@/hooks/useStream";
const analysisClick=(type)=>{ // 子组件直接获取Pinia Store(核心优化)
if(type==0){ const store = useWrittingAsstaintStore();
console.log('停止')
isfile.value=type const emit = defineEmits(["generate","write"]);
return
}else{
console.log('开始解析')
isfile.value=type
}
const onAnalysisClick=()=>{
store.isShowSteps = !store.isShowSteps
emit("generate");
}
const onWriteClick=()=>{
emit("write");
} }
const { renderedProcess, updateProcess, clearContent } = useStream();
const processContainerRef = ref(null);
// 监听 store.processLog 变化,更新步骤内容并滚动
watch(
() => store.processLog,
async (newLog) => {
if (newLog !== undefined && newLog !== null) {
await updateProcess(newLog, processContainerRef.value);
}
},
{ immediate: true }
);
defineExpose({
processContainerRef
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.writtingBottom{ .writtingBottom{
...@@ -58,6 +96,11 @@ const analysisClick=(type)=>{ ...@@ -58,6 +96,11 @@ const analysisClick=(type)=>{
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 64px; height: 64px;
.parsedItem{
display: flex;
justify-content: space-between;
width: 100%;
}
.analysis{ .analysis{
margin-left: 22px; margin-left: 22px;
display: flex; display: flex;
...@@ -94,6 +137,7 @@ const analysisClick=(type)=>{ ...@@ -94,6 +137,7 @@ const analysisClick=(type)=>{
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 64px; height: 64px;
.analysis{ .analysis{
border: 1px solid var(--color-primary-100); border: 1px solid var(--color-primary-100);
background-color: #fff; background-color: #fff;
......
<template> <template>
<div class="headerBox"> <div class="headerBox">
<div class="logo" v-if="isLogo==0"> <div class="tabBox" v-if="store.isGenerating||store.isShowSteps">
<img src="@/assets/icons/tool-item-icon1.png" alt="">
<span class="text-title-3-bold">智能写库</span>
</div>
<div class="tabBox" v-else-if="isLogo==1">
<div class="fileName"> <div class="fileName">
<img src="@/assets/icons/pdf-icon.png" alt=" "> <img src="@/assets/icons/pdf-icon.png" alt=" ">
<span class="text-tip-1-bold">Promoting the Export of the American AI Technology Stack.pdf</span> <span class="text-tip-1-bold">{{ store.uploadFileList[0]?.name||'文件错误' }}</span>
</div> </div>
<div class="tab"> <div class="tab">
<div class="tabList text-tip-1-bold" v-for="(item,index) in tabList" :key="index" :class="{'on':tabType==item.type}" @click="onTabListClick(item.type)">{{ item.name }}</div> <div class="tabList text-tip-1-bold" v-for="(item,index) in store.tabList" :key="index" :class="{'on':store.headerTabType==item.type}"
:style="!item.active?'color:#bfbfbf;cursor: no-drop;':''"
@click="onTabListClick(item.type,item.active)">{{ item.name }}</div>
</div> </div>
<div class="switch"> <div class="switch">
<el-switch v-model="cmSwitch"/> <el-switch v-model="store.isShowOriginal"/>
<div class="iconBOx"> <div class="iconBOx">
<img src="@/assets/icons/translate-icon.png" alt=""> <img src="@/assets/icons/translate-icon.png" alt="">
<span class="text-tip-1">显示原文</span> <span class="text-tip-1">显示原文</span>
</div> </div>
<el-button :icon="Search" @click="onSearchClick">查找</el-button> <el-button @click="onSearchClick">查找</el-button>
</div> </div>
</div> </div>
<div class="logo" v-else>
<img src="@/assets/icons/tool-item-icon1.png" alt="">
<span class="text-title-3-bold">智能写库</span>
</div>
</div> </div>
</template> </template>
...@@ -29,37 +33,16 @@ import { onMounted, onUnmounted, ref, nextTick } from "vue"; ...@@ -29,37 +33,16 @@ import { onMounted, onUnmounted, ref, nextTick } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
/** // 子组件直接获取Pinia Store(核心优化)
* 判断展示logo 0展示logo 1展示tab const store = useWrittingAsstaintStore();
*/
const isLogo=ref(1)
/** const onTabListClick= async (type,active)=>{
* tab切换 if(!active) return
*/ store.handleHeaderTab(type)
const tabList=ref([
{
type:'translate',
name:'翻译'
},
{
type:'mind',
name:'思维导图'
},
{
type:'message',
name:'写报'
}
])
const tabType=ref('translate')
const onTabListClick=(type)=>{
tabType.value=type
} }
// //
const onSearchClick=()=>{} const onSearchClick=()=>{}
const cmSwitch=ref()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.headerBox{ .headerBox{
......
<template> <template>
<div class="left-box-wrapper"> <div class="left-box-wrapper">
<div class="back" @click="store.resetGenerateState" v-if="store.isGenerating">&lt; 返回</div> <!-- <div class="back" @click="store.resetGenerateState" v-if="store.isGenerating">&lt; 返回</div> -->
<div class="left-box" :class="{ 'has-back-btn': store.isGenerating }" <div class="left-box" :class="{ 'has-back-btn': store.isGenerating }"
v-if="!store.isShowClauseTranslation && !store.isShowSteps"> v-if="!store.isShowClauseTranslation && !store.isShowSteps">
<div class="left-box-input"> <div class="left-box-input">
...@@ -11,15 +11,15 @@ ...@@ -11,15 +11,15 @@
<div class="header">报文主题</div> <div class="header">报文主题</div>
<div class="title-box"> <div class="title-box">
<div class="title">主题名称</div> <div class="title">主题名称</div>
<el-input :disabled="store.isDisableTemplate" style="width: 476px; height: 32px" <el-input :disabled="store.isDisableTemplate" style="width: 476px; height: 32px ;background: #f7f8f9;"
class="title-input" placeholder="输入主题名称,如:大而美法案" v-model="store.writtingTitle" /> class="title-input" placeholder="输入主题名称,如:大而美法案" v-model="store.writtingTitle" />
</div> </div>
<div class="description-box"> <!-- <div class="description-box">
<div class="title">主题描述</div> <div class="title">主题描述</div>
<el-input :disabled="store.isDisableTemplate" class="description-input" type="textarea" <el-input :disabled="store.isDisableTemplate" class="description-input" type="textarea"
style="width: 476px" :rows="8" placeholder="输入报文主题描述,如:从科技领域方面分析大而美法案通过后对中国可能产生的影响" style="width: 476px" :rows="8" placeholder="输入报文主题描述,如:从科技领域方面分析大而美法案通过后对中国可能产生的影响"
v-model="store.descText" /> v-model="store.descText" />
</div> </div> -->
</div> </div>
<!-- 报文模板 --> <!-- 报文模板 -->
...@@ -88,24 +88,24 @@ ...@@ -88,24 +88,24 @@
</div> </div>
<!-- 提交区域 --> <!-- 提交区域 -->
<div class="submit-area"> <!-- <div class="submit-area">
<div class="tips"> <div class="tips">
<div class="tips-icon"> <div class="tips-icon">
<img src="../assets/images/tips-icon.png" alt="" /> <img src="../assets/images/tips-icon.png" alt="" />
</div> </div>
<div class="tips-text">内容由AI生成,无法确保真实准确,仅供参考</div> <div class="tips-text">内容由AI生成,无法确保真实准确,仅供参考</div>
</div> </div> -->
<!-- 生成按钮 --> <!-- 生成按钮 -->
<div class="submit-btn" @click="triggerGenerate" v-if="!store.isGenerating"> <!-- <div class="submit-btn" @click="triggerGenerate" v-if="!store.isGenerating">
<div class="submit-icon"> <div class="submit-icon">
<img src="../assets/images/ai.png" alt="" /> <img src="../assets/images/ai.png" alt="" />
</div> </div>
<div class="submit-text">生成报文</div> <div class="submit-text">生成报文</div>
</div> </div> -->
<!-- 生成中状态 --> <!-- 生成中状态 -->
<div class="process-footer-box" v-else> <!-- <div class="process-footer-box" v-else>
<div class="footer-left"> <div class="footer-left">
{{ store.isGenerating ? "报文生成中..." : "报文已生成" }} {{ store.isGenerating ? "报文生成中..." : "报文已生成" }}
</div> </div>
...@@ -114,11 +114,11 @@ ...@@ -114,11 +114,11 @@
<div class="text">停止</div> <div class="text">停止</div>
</div> </div>
</div> </div>
</div> </div> end -->
</div> </div>
<!-- 步骤侧边栏(拆分出来) --> <!-- 步骤侧边栏(拆分出来) -->
<div class="left-box process" :class="{ 'has-back-btn': store.isGenerating }" v-if="store.isShowSteps"> <!-- <div class="left-box process" :class="{ 'has-back-btn': store.isGenerating }" v-if="store.isShowSteps">
<div class="left-box-input"> <div class="left-box-input">
<div class="process-box"> <div class="process-box">
<div class="process-main-box"> <div class="process-main-box">
...@@ -137,17 +137,17 @@ ...@@ -137,17 +137,17 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div> -->
<div class="submit-area"> <!-- <div class="submit-area">
<div class="tips"> <div class="tips">
<div class="tips-icon"> <div class="tips-icon">
<img src="../assets/images/tips-icon.png" alt="" /> <img src="../assets/images/tips-icon.png" alt="" />
</div> </div>
<div class="tips-text">内容由AI生成,无法确保真实准确,仅供参考</div> <div class="tips-text">内容由AI生成,无法确保真实准确,仅供参考</div>
</div> </div> -->
<!-- 生成中状态 --> <!-- 生成中状态 -->
<div class="process-footer-box"> <!-- <div class="process-footer-box">
<div class="footer-left"> <div class="footer-left">
{{ store.isGenerating ? "报文生成中..." : "报文已生成" }} {{ store.isGenerating ? "报文生成中..." : "报文已生成" }}
</div> </div>
...@@ -155,25 +155,23 @@ ...@@ -155,25 +155,23 @@
<div class="icon"></div> <div class="icon"></div>
<div class="text">停止</div> <div class="text">停止</div>
</div> </div>
</div> </div> -->
</div> <!-- </div>
</div> </div> -->
<!-- 条款翻译侧边栏 srot -->
<!-- 条款翻译侧边栏 --> <div class="left-box translation-box" :class="{ 'has-back-btn': store.isGenerating }" v-if="store.isShowClauseTranslation&&store.headerTabType=='message'">
<div class="left-box translation-box" :class="{ 'has-back-btn': store.isGenerating }"
v-if="store.isShowClauseTranslation">
<div class="translation-main-box"> <div class="translation-main-box">
<div class="translation-actions" v-if="!store.isGenerating"> <!-- <div class="translation-actions" v-if="!store.isGenerating">
<div class="back-input-btn" @click="store.backToInputAndClear">返回输入栏</div> <div class="back-input-btn" @click="store.backToInputAndClear">返回输入栏</div>
</div> </div> -->
<!-- 政令标题卡片 --> <!-- 政令标题卡片 -->
<div class="metadata-card" v-if="store.pdfMetadata"> <!-- <div class="metadata-card" v-if="store.pdfMetadata">
<div class="card-header"> <div class="card-header">
<div class="chinese-name">{{ store.pdfMetadata.name }}</div> <div class="chinese-name">{{ store.pdfMetadata.name }}</div>
<div class="type-tag">{{ store.pdfMetadata.signing_date }}</div> <div class="type-tag">{{ store.pdfMetadata.signing_date }}</div>
</div> </div>
<div class="english-name">{{ store.pdfMetadata.order_title }}</div> <div class="english-name">{{ store.pdfMetadata.order_title }}</div>
</div> </div> -->
<div class="translation-header-new"> <div class="translation-header-new">
<div class="header-left">共{{ store.clauseTranslationMessages.length }}章节</div> <div class="header-left">共{{ store.clauseTranslationMessages.length }}章节</div>
<div class="header-right"> <div class="header-right">
...@@ -200,9 +198,9 @@ ...@@ -200,9 +198,9 @@
</div> </div>
</div> </div>
<!-- 步骤侧边栏显隐按钮 --> <!-- 步骤侧边栏显隐按钮 -->
<div class="toggle-steps-btn" @click="store.isShowSteps = !store.isShowSteps"> <!-- <div class="toggle-steps-btn" @click="store.isShowSteps = !store.isShowSteps">
<div class="arrow" :class="{ 'is-active': store.isShowSteps }"></div> <div class="arrow" :class="{ 'is-active': store.isShowSteps }"></div>
</div> </div> -->
</div> </div>
</div> </div>
</template> </template>
...@@ -213,7 +211,7 @@ import { ElButton, ElIcon, ElInput, ElUpload, ElSwitch } from "element-plus"; ...@@ -213,7 +211,7 @@ import { ElButton, ElIcon, ElInput, ElUpload, ElSwitch } from "element-plus";
import { Upload } from "@element-plus/icons-vue"; import { Upload } from "@element-plus/icons-vue";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
import { useStream } from "@/hooks/useStream"; import { useStream } from "@/hooks/useStream";
console.log('测试一下git')
// 子组件直接获取Pinia Store(核心优化) // 子组件直接获取Pinia Store(核心优化)
const store = useWrittingAsstaintStore(); const store = useWrittingAsstaintStore();
// 组件内部引用 // 组件内部引用
...@@ -308,7 +306,7 @@ defineExpose({ ...@@ -308,7 +306,7 @@ defineExpose({
width: 521px; width: 521px;
height: 100%; height: 100%;
padding-top: 22px; padding-top: 22px;
padding-bottom: 29px; padding-bottom: 10px;
box-sizing: border-box; box-sizing: border-box;
border-right: 1px solid rgba(234, 236, 238, 1); border-right: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1); border-top: 1px solid rgba(234, 236, 238, 1);
......
<template>
<div >
<!-- 右侧子组件:绑定ref -->
<writtingMainBox ref="mainBoxRef" :report-content="store.reportContent" />
</div>
</template>
<script setup>
import { ref, watch, onMounted, onUnmounted } from "vue";
const mainBoxRef = ref(null); // 右侧子组件ref
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div >
我是思维导图
</div>
</template>
<script setup>
import { ref, watch, onMounted, onUnmounted } from "vue";
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<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="item-body">
<div class="original-text" v-if="store.isShowOriginal">
<span class="index-badge">{{item.payload?.clause_section }}</span>
{{ item.payload?.clause_content }}
</div>
<div class="translated-text">
<span class="clause-title">{{ getChineseNumber(item.payload?.clause_number) }}</span>
{{ item.payload?.clause_content_zh }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref, nextTick ,watch} from "vue";
import { useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
import { useStream } from "@/hooks/useStream";
// 子组件直接获取Pinia Store(核心优化)
const store = useWrittingAsstaintStore();
const translationContentRef = ref(null);
// 数字转中文序号
const getChineseNumber = (num) => {
const zh = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
const n = parseInt(num);
if (n <= 10) return zh[n];
if (n < 20) return '十' + zh[n % 10];
if (n < 100) {
return zh[Math.floor(n / 10)] + '十' + (n % 10 === 0 ? '' : zh[n % 10]);
}
return num;
};
</script>
<style lang="scss" scoped>
.translation-content{
overflow-y: auto;
margin-top: 30px;
height: calc(100% - 50px);
.translation-item{
margin-bottom: 24px;
.item-body{
display: flex;
justify-content: space-between;
width: 85%;
margin: 0 auto;
.original-text{
min-width: 48%;
line-height: 30px;
margin-right: 62px;
flex: 2;
}
.translated-text{
width: 100%;
line-height: 30px;
}
}
// .item-header{
// width: 48%;
// }
// .item-body{
// width: 48%;
// }
}
}
</style>
\ No newline at end of file
...@@ -31,19 +31,32 @@ ...@@ -31,19 +31,32 @@
<WrittingHeader></WrittingHeader> <WrittingHeader></WrittingHeader>
<div class="writting-main"> <div class="writting-main">
<!-- 左侧子组件:绑定ref --> <!-- 左侧子组件:绑定ref -->
<writtingleftBox ref="leftBoxRef" @generate="handleGenerate" /> <!-- <writtingleftBox ref="leftBoxRef" @generate="handleGenerate" /> -->
<!-- 右侧子组件:绑定ref --> <writtingleftBox ref="leftBoxRef" />
<writtingMainBox v-show="!!store.reportContent" ref="mainBoxRef" :report-content="store.reportContent" />
<!-- 翻译 -->
<WrittingTranslate v-if="store.isShowClauseTranslation&&store.headerTabType=='translate'"></WrittingTranslate>
<!-- 思维导图 -->
<WrittingMind v-else-if="store.isShowClauseTranslation&&store.headerTabType=='mind'"></WrittingMind>
<!-- 写报 -->
<WrittingMessage v-else-if="store.isShowClauseTranslation&&store.headerTabType=='message'"></WrittingMessage>
<!-- 无数据时显示占位图 --> <!-- 无数据时显示占位图 -->
<div v-show="!store.reportContent" class="main-placeholder"> <div v-else class="main-placeholder">
<img src="./assets/images/container-image.png" alt="无数据占位图" /> <img src="./assets/images/container-image.png" alt="无数据占位图" />
<div class="placeholder-text"> <div class="placeholder-text">
<div v-if="store.isGenerating">智能体写报任务执行中...</div> <div v-if="store.isGenerating">智能体写报任务执行中...</div>
<div v-else>上传文件后点击“生成报文”开始写报...</div> <div v-else>上传文件后点击“生成报文”开始写报...</div>
</div> </div>
</div> </div>
<!-- 右侧子组件:绑定ref -->
<writtingMainBox v-show="!!store.reportContent" ref="mainBoxRef" :report-content="store.reportContent" />
</div> </div>
<WrittingBottom></WrittingBottom> <WrittingBottom @generate="handleGenerate" @write="handleWrite"></WrittingBottom>
</div> </div>
</div> </div>
</template> </template>
...@@ -57,13 +70,19 @@ import writtingleftBox from "./components/WrittingLeftBox.vue"; ...@@ -57,13 +70,19 @@ import writtingleftBox from "./components/WrittingLeftBox.vue";
import writtingMainBox from "./components/WrittingMainBox.vue"; import writtingMainBox from "./components/WrittingMainBox.vue";
import WrittingHeader from "./components/WrittingHeader.vue"; //头 import WrittingHeader from "./components/WrittingHeader.vue"; //头
import WrittingBottom from "./components/WrittingBottom.vue"; //底部 import WrittingBottom from "./components/WrittingBottom.vue"; //底部
import WrittingTranslate from "./components/WrittingTranslate.vue"; //翻译
import WrittingMind from "./components/WrittingTranslate.vue"; //思维导图
import WrittingMessage from "./components/WrittingMessage.vue"; //写报
// 获取路由实例(组件内读取) // 获取路由实例(组件内读取)
const route = useRoute(); const route = useRoute();
// 获取Pinia Store实例 // 获取Pinia Store实例
const leftBoxRef = ref(null); // 左侧子组件ref const leftBoxRef = ref(null); // 左侧子组件ref
const mainBoxRef = ref(null); // 右侧子组件ref
const store = useWrittingAsstaintStore(); const store = useWrittingAsstaintStore();
// 2. 核心:触发生成流程 // 2. 核心:触发生成流程
...@@ -84,6 +103,18 @@ const handleGenerate = async () => { ...@@ -84,6 +103,18 @@ const handleGenerate = async () => {
console.error("生成报文失败:", error); console.error("生成报文失败:", error);
} }
}; };
const handleWrite=async ()=>{
try {
console.log(1)
// // 等待DOM更新(确保子组件DOM已挂载)
await nextTick();
await store.generateWrite()
console.log(2)
} catch (error) {
ElMessage.error(error.message);
console.error("生成写报失败:", error);
}
}
// 生命周期 // 生命周期
onMounted(async () => { onMounted(async () => {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论