Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
581d2dec
提交
581d2dec
authored
3月 24, 2026
作者:
安云鹏
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
思维导图,写报
上级
d3d19811
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
255 行增加
和
59 行删除
+255
-59
ai-logo-color.png
src/assets/icons/aiBox/ai-logo-color.png
+0
-0
search.png
src/assets/icons/aiBox/search.png
+0
-0
writtingAsstaintStore.js
src/stores/writtingAsstaintStore.js
+0
-0
index.vue
src/views/home/index.vue
+5
-3
WrittingBottom.vue
src/views/writtingAsstaint/components/WrittingBottom.vue
+37
-8
WrittingHeader.vue
src/views/writtingAsstaint/components/WrittingHeader.vue
+15
-8
WrittingMainBox.vue
src/views/writtingAsstaint/components/WrittingMainBox.vue
+3
-3
WrittingMessage.vue
src/views/writtingAsstaint/components/WrittingMessage.vue
+1
-1
WrittingMind.vue
src/views/writtingAsstaint/components/WrittingMind.vue
+0
-0
WrittingTranslate.vue
src/views/writtingAsstaint/components/WrittingTranslate.vue
+185
-27
index.vue
src/views/writtingAsstaint/index.vue
+9
-9
没有找到文件。
src/assets/icons/aiBox/ai-logo-color.png
0 → 100644
浏览文件 @
581d2dec
816 Bytes
src/assets/icons/aiBox/search.png
0 → 100644
浏览文件 @
581d2dec
399 Bytes
src/stores/writtingAsstaintStore.js
浏览文件 @
581d2dec
差异被折叠。
点击展开。
src/views/home/index.vue
浏览文件 @
581d2dec
...
...
@@ -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
>
src/views/writtingAsstaint/components/WrittingBottom.vue
浏览文件 @
581d2dec
<
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.
bottom
ProgressNum"
: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.
write
ProgressNum"
: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=
"process
ContainerRef"
v-html=
"renderedProcess"
>
</div>
<div
ref=
"process
WriteLogRef"
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
;
...
...
src/views/writtingAsstaint/components/WrittingHeader.vue
浏览文件 @
581d2dec
<
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>
</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
src/views/writtingAsstaint/components/WrittingMainBox.vue
浏览文件 @
581d2dec
...
...
@@ -103,7 +103,7 @@ watch(
}
.content-box
{
width
:
10
69px
;
width
:
10
0%
;
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
;
...
...
src/views/writtingAsstaint/components/WrittingMessage.vue
浏览文件 @
581d2dec
<
template
>
<div
>
<!-- 右侧子组件:绑定ref -->
<writtingMainBox
v-show=
"!!store.reportContent"
:report-content=
"store.reportContent"
/>
<writtingMainBox
v-show=
"!!store.reportContent"
:report-content=
"store.reportContent"
/>
</div>
</
template
>
...
...
src/views/writtingAsstaint/components/WrittingMind.vue
浏览文件 @
581d2dec
差异被折叠。
点击展开。
src/views/writtingAsstaint/components/WrittingTranslate.vue
浏览文件 @
581d2dec
<
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
class=
"translation-content"
ref=
"translationContentRef"
>
<!-- :class="
{ active: store.highlightClauseId === item.payload?.clause_number }"
:data-clause-number="item.payload?.clause_number" -->
<!-- 查找 -->
<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"
>
<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 }}
</div>
</div>
</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
(
100
vh
-
2
50px
);
.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
src/views/writtingAsstaint/index.vue
浏览文件 @
581d2dec
...
...
@@ -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/Writting
Translate
.vue"
;
//思维导图
import
WrittingMind
from
"./components/Writting
Mind
.vue"
;
//思维导图
import
WrittingMessage
from
"./components/WrittingMessage.vue"
;
//写报
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论