Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
58dd1d06
提交
58dd1d06
authored
3月 26, 2026
作者:
安云鹏
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
修改 底部 侧边栏 分析 报文
上级
581d2dec
全部展开
显示空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
135 行增加
和
26 行删除
+135
-26
2.md
src/assets/icons/aiBox/2.md
+0
-0
Steps步骤条_亮色_图标-待处理.png
src/assets/icons/aiBox/Steps步骤条_亮色_图标-待处理.png
+0
-0
index.vue
src/components/intelligenceLeftTabBar/index.vue
+4
-0
useMarkdownStream.js
src/hooks/useMarkdownStream.js
+7
-4
writtingAsstaintStore.js
src/stores/writtingAsstaintStore.js
+0
-0
WrittingBottom.vue
src/views/writtingAsstaint/components/WrittingBottom.vue
+20
-5
WrittingLeftBox.vue
src/views/writtingAsstaint/components/WrittingLeftBox.vue
+89
-7
WrittingMainBox.vue
src/views/writtingAsstaint/components/WrittingMainBox.vue
+3
-0
WrittingMind.vue
src/views/writtingAsstaint/components/WrittingMind.vue
+0
-0
WrittingNavr.vue
src/views/writtingAsstaint/components/WrittingNavr.vue
+0
-0
index.vue
src/views/writtingAsstaint/index.vue
+12
-10
没有找到文件。
src/assets/icons/aiBox/2.md
0 → 100644
浏览文件 @
58dd1d06
差异被折叠。
点击展开。
src/assets/icons/aiBox/Steps步骤条_亮色_图标-待处理.png
0 → 100644
浏览文件 @
58dd1d06
831 Bytes
src/components/intelligenceLeftTabBar/index.vue
浏览文件 @
58dd1d06
...
@@ -50,7 +50,11 @@ if(route.path){
...
@@ -50,7 +50,11 @@ if(route.path){
navPath
.
value
=
route
.
path
navPath
.
value
=
route
.
path
}
}
const
onNavListClick
=
(
path
)
=>
{
const
onNavListClick
=
(
path
)
=>
{
if
(
path
==
'/writtingAsstaint'
){
navPath
.
value
=
path
navPath
.
value
=
path
}
else
{
ElMessage
.
error
(
'正在开发中'
)
}
}
}
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
...
src/hooks/useMarkdownStream.js
浏览文件 @
58dd1d06
...
@@ -295,12 +295,15 @@ export function useMarkdownStream() {
...
@@ -295,12 +295,15 @@ export function useMarkdownStream() {
// 预处理内容
// 预处理内容
// const processedContent = preprocessMarkdown(rawContent.value)
// const processedContent = preprocessMarkdown(rawContent.value)
let
content
=
rawContent
.
value
||
''
let
content
=
rawContent
.
value
||
''
// 将 ==n== 转换为按钮样式的 HTML
// 将 ==n== 转换为按钮样式的 HTML
// 使用正向预读和反向预读确保只匹配被 == 包裹的数字
// 使用正向预读和反向预读确保只匹配被 == 包裹的数字
content
=
content
.
replace
(
/==
(\d
+
)
==/g
,
(
match
,
p1
)
=>
{
// content = content.replace(/==(\d+)、==/g, (match, p1) => {
return
`<button class="clause-ref-btn" data-clause="
${
p1
}
">
${
p1
}
</button>`
// return `<button class="clause-ref-btn" data-clause="${p1}">${p1}</button>`
})
// })
content
=
content
.
replace
(
/==
\s
*
(\d
+
)
、.*
?
==/g
,
(
match
,
p1
)
=>
{
return
`<button class="clause-ref-btn" data-clause="
${
match
.
replace
(
/==/g
,
''
)
}
">
${
p1
}
</button>`
;
});
return
md
.
render
(
content
)
return
md
.
render
(
content
)
})
})
...
...
src/stores/writtingAsstaintStore.js
浏览文件 @
58dd1d06
差异被折叠。
点击展开。
src/views/writtingAsstaint/components/WrittingBottom.vue
浏览文件 @
58dd1d06
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
<div
class=
"writtingBottom"
>
<div
class=
"writtingBottom"
>
<!-- 文档停止解析 -->
<!-- 文档停止解析 -->
<div
class=
"parsed"
v-if=
"store.bottomProgressNum>0&&store.bottomProgressNum
<10
0
"
>
<div
class=
"parsed"
v-if=
"store.bottomProgressNum>0&&store.bottomProgressNum
!=100&&store.writeProgressNum
<1
0
"
>
<div
class=
"analysis"
@
click=
"store.resetGenerateState"
>
<div
class=
"analysis"
@
click=
"store.resetGenerateState"
>
<div
class=
"icon"
></div>
<div
class=
"icon"
></div>
<span
class=
"text-tip-2-bold"
>
停止
</span>
<span
class=
"text-tip-2-bold"
>
停止
</span>
...
@@ -10,7 +10,9 @@
...
@@ -10,7 +10,9 @@
<div
class=
"progress"
>
<div
class=
"progress"
>
<div
class=
"login"
>
<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)"
/>
<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>
<div
class=
"text-tip-2"
>
<div
class=
"text-tip-2"
>
<div
ref=
"processContainerRef"
v-html=
"renderedProcess"
></div>
<div
ref=
"processContainerRef"
v-html=
"renderedProcess"
></div>
...
@@ -21,7 +23,7 @@
...
@@ -21,7 +23,7 @@
<!-- 开始写报 -->
<!-- 开始写报 -->
<!-- -->
<!-- -->
<div
class=
"parsed"
v-else-if=
"store.bottomProgressNum>=100"
>
<div
class=
"parsed"
v-else-if=
"store.bottomProgressNum>=100"
>
<div
class=
"analysis"
v-if=
"store.
isWriteStart&&store.writeProgressNum
<
100
"
@
click=
"store.writeGenerateState"
>
<div
class=
"analysis"
v-if=
"store.
writeProgressNum>0&&store.writeProgressNum!=
100"
@
click=
"store.writeGenerateState"
>
<div
class=
"icon"
></div>
<div
class=
"icon"
></div>
<span
class=
"text-tip-2-bold"
>
停止
</span>
<span
class=
"text-tip-2-bold"
>
停止
</span>
</div>
</div>
...
@@ -35,10 +37,21 @@
...
@@ -35,10 +37,21 @@
</div>
</div>
<div
class=
"progress"
>
<div
class=
"progress"
>
<div
class=
"login"
>
<!-- 如果store.writeProgressNum>=1 点击了写报 否则是思维导图已完成 -->
<div
class=
"login"
v-if=
"store.writeProgressNum>=1&&store.writeProgressNum
<100
"
>
<el-progress
type=
"circle"
:percentage=
"store.writeProgressNum"
:width=
"24"
:height=
"24"
style=
"margin-right: 15px;"
:show-text=
"false"
color=
"rgb(5, 95, 194)"
/>
<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>
<span
class=
"text-tip-2-bold"
>
智能写报中
</span>
</div>
<div
class=
"login"
v-else-if=
"store.writeProgressNum>=100"
>
<el-icon
style=
"width: 24px;height: 24px;margin-right: 4px;"
><CircleCheckFilled
style=
"width: 24px;height: 24px;"
class=
"var(--color-primary-35)"
/></el-icon>
<span
class=
"text-tip-2-bold"
>
文档解析完成
</span>
</div>
<div
class=
"login"
v-else-if=
"store.bottomProgressNum>=100"
>
<el-icon
style=
"width: 24px;height: 24px;margin-right: 4px;"
><CircleCheckFilled
style=
"width: 24px;height: 24px;"
class=
"var(--color-primary-35)"
/></el-icon>
<!--
<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>
<div
class=
"text-tip-2"
>
<div
class=
"text-tip-2"
>
<div
ref=
"processWriteLogRef"
v-html=
"renderedProcess"
>
</div>
<div
ref=
"processWriteLogRef"
v-html=
"renderedProcess"
>
</div>
</div>
</div>
...
@@ -76,6 +89,8 @@ const onAnalysisClick=()=>{
...
@@ -76,6 +89,8 @@ const onAnalysisClick=()=>{
}
}
const
onWriteClick
=
()
=>
{
const
onWriteClick
=
()
=>
{
store
.
isShowSteps
=
false
emit
(
"write"
);
emit
(
"write"
);
}
}
const
{
renderedProcess
,
updateProcess
,
clearContent
}
=
useStream
();
const
{
renderedProcess
,
updateProcess
,
clearContent
}
=
useStream
();
...
...
src/views/writtingAsstaint/components/WrittingLeftBox.vue
浏览文件 @
58dd1d06
...
@@ -159,7 +159,7 @@
...
@@ -159,7 +159,7 @@
<!-- </div>
<!-- </div>
</div> -->
</div> -->
<!-- 条款翻译侧边栏 srot -->
<!-- 条款翻译侧边栏 srot -->
<div
class=
"left-box translation-box"
:class=
"{ 'has-back-btn': store.isGenerating }"
v-if=
"store.isShow
ClauseTranslation
&&store.headerTabType=='message'"
>
<div
class=
"left-box translation-box"
:class=
"{ 'has-back-btn': store.isGenerating }"
v-if=
"store.isShow
Steps
&&store.headerTabType=='message'"
>
<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>
...
@@ -198,9 +198,9 @@
...
@@ -198,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>
...
@@ -245,16 +245,27 @@ watch(
...
@@ -245,16 +245,27 @@ watch(
async
(
newId
)
=>
{
async
(
newId
)
=>
{
if
(
!
newId
||
!
translationContentRef
.
value
)
return
;
if
(
!
newId
||
!
translationContentRef
.
value
)
return
;
await
nextTick
();
await
nextTick
();
const
container
=
translationContentRef
.
value
;
const
container
=
translationContentRef
.
value
;
const
item
=
container
.
querySelector
(
`.translation-item[data-clause-number="
${
newId
}
"]`
);
const
result
=
newId
.
replace
(
/^
\d
+、/
,
''
);
const
item
=
container
.
querySelector
(
`.translation-item[data-clause-number="
${
newId
.
match
(
/^
(\d
+
)
/
)[
1
]}
"]`
);
const
itemHtml
=
item
.
querySelector
(
`.translated-text`
);
if
(
!
item
)
return
;
if
(
!
item
)
return
;
// 你已经拿到的 外层大标签
const
element
=
itemHtml
// 你要找的文字
const
targetText
=
result
const
location
=
findTextInElement
(
element
,
targetText
);
const
containerRect
=
container
.
getBoundingClientRect
();
const
containerRect
=
container
.
getBoundingClientRect
();
const
itemRect
=
item
.
getBoundingClientRect
()
;
const
itemRect
=
location
;
const
delta
=
itemRect
.
top
-
containerRect
.
top
;
const
delta
=
itemRect
.
top
-
containerRect
.
top
;
const
targetTop
=
container
.
scrollTop
+
delta
;
const
targetTop
=
container
.
scrollTop
+
delta
;
console
.
log
(
delta
)
// 让高亮条款的“标题区域”贴到容器顶部
// 让高亮条款的“标题区域”贴到容器顶部
container
.
scrollTo
({
container
.
scrollTo
({
top
:
Math
.
max
(
0
,
targetTop
),
top
:
Math
.
max
(
0
,
targetTop
),
...
@@ -262,6 +273,77 @@ watch(
...
@@ -262,6 +273,77 @@ watch(
});
});
}
}
);
);
// 👇 核心:在 element 内部找文字位置
// =========================================
function
findTextInElement
(
element
,
targetText
)
{
// 遍历标签内的所有内容
const
nodes
=
element
.
childNodes
;
let
rect
=
''
for
(
let
i
=
0
;
i
<
nodes
.
length
;
i
++
)
{
const
node
=
nodes
[
i
];
// 只找纯文字
if
(
node
.
nodeType
===
3
)
{
const
originalText
=
node
.
textContent
;
function
cleanText
(
str
)
{
return
str
.
replace
(
/(/g
,
'('
)
.
replace
(
/)/g
,
')'
)
.
replace
(
/,/g
,
','
)
.
replace
(
/。/g
,
'.'
)
.
replace
(
/:/g
,
':'
)
.
replace
(
/;/g
,
';'
);
}
// 清理后的文字(无标点)
const
nodeClean
=
cleanText
(
originalText
);
const
targetClean
=
cleanText
(
targetText
);
console
.
log
(
nodeClean
)
console
.
log
(
targetClean
)
console
.
log
(
nodeClean
.
includes
(
targetClean
))
// 用干净文字对比
if
(
nodeClean
.
includes
(
targetClean
))
{
// 找到真实位置(用原始文本定位,不影响)
const
index
=
originalText
.
indexOf
(
originalText
.
includes
(
targetText
)
?
targetText
:
originalText
);
const
range
=
document
.
createRange
();
range
.
setStart
(
node
,
index
);
range
.
setEnd
(
node
,
index
+
targetText
.
length
);
// 拿到位置
rect
=
range
.
getBoundingClientRect
();
const
marks
=
element
.
querySelectorAll
(
'mark'
);
marks
.
forEach
(
mark
=>
{
// 把 mark 里的文字放回原位,删除标签
const
parent
=
mark
.
parentNode
;
while
(
mark
.
firstChild
)
{
parent
.
insertBefore
(
mark
.
firstChild
,
mark
);
}
parent
.
removeChild
(
mark
);
// 合并相邻文本节点(恢复页面原貌)
parent
.
normalize
();
});
const
mark
=
document
.
createElement
(
"mark"
);
mark
.
style
.
backgroundColor
=
'#055FC2'
;
mark
.
style
.
color
=
"#fff"
;
// 文字颜色
range
.
surroundContents
(
mark
);
break
;
}
}
}
return
rect
}
// 监听 store.processLog 变化,更新步骤内容并滚动
// 监听 store.processLog 变化,更新步骤内容并滚动
watch
(
watch
(
...
...
src/views/writtingAsstaint/components/WrittingMainBox.vue
浏览文件 @
58dd1d06
...
@@ -42,6 +42,8 @@ const handleGlobalClick = (e) => {
...
@@ -42,6 +42,8 @@ const handleGlobalClick = (e) => {
if
(
clauseId
)
{
if
(
clauseId
)
{
store
.
highlightClauseId
=
clauseId
;
store
.
highlightClauseId
=
clauseId
;
// 翻译栏一直显示,所以这里只需要确保它在视图内
// 翻译栏一直显示,所以这里只需要确保它在视图内
store
.
isShowSteps
=
true
;
console
.
log
(
store
.
highlightClauseId
)
}
}
}
}
};
};
...
@@ -88,6 +90,7 @@ watch(
...
@@ -88,6 +90,7 @@ watch(
},
},
{
immediate
:
true
}
{
immediate
:
true
}
);
);
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
...
...
src/views/writtingAsstaint/components/WrittingMind.vue
浏览文件 @
58dd1d06
差异被折叠。
点击展开。
src/views/writtingAsstaint/components/WrittingNavr.vue
deleted
100644 → 0
浏览文件 @
581d2dec
src/views/writtingAsstaint/index.vue
浏览文件 @
58dd1d06
...
@@ -33,24 +33,25 @@
...
@@ -33,24 +33,25 @@
<div
class=
"writting-main"
>
<div
class=
"writting-main"
>
<!-- 左侧子组件:绑定ref -->
<!-- 左侧子组件:绑定ref -->
<!--
<writtingleftBox
ref=
"leftBoxRef"
@
generate=
"handleGenerate"
/>
-->
<!--
<writtingleftBox
ref=
"leftBoxRef"
@
generate=
"handleGenerate"
/>
-->
<
!--
<writtingleftBox
ref=
"leftBoxRef"
/>
--
>
<
WrittingLeftBox
ref=
"leftBoxRef"
/
>
<!-- 翻译 -->
<!-- 翻译 -->
<
!--
<WrittingTranslate
v-if=
"store.isShowClauseTranslation&&store.headerTabType=='translate'"
></WrittingTranslate>
--
>
<
WrittingTranslate
v-if=
"store.isShowClauseTranslation&&store.headerTabType=='translate'"
></WrittingTranslate
>
<!-- 思维导图
v-else-if="store.isShowClauseTranslation&&store.headerTabType=='mind'"
-->
<!-- 思维导图
"
-->
<WrittingMind
></WrittingMind>
<WrittingMind
v-else-if=
"store.isShowClauseTranslation&&store.headerTabType=='mind' "
></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=
"无数据占位图"
/>
<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 -->
<!-- 右侧子组件:绑定ref -->
<!--
<writtingMainBox
v-show=
"!!store.reportContent"
ref=
"mainBoxRef"
:report-content=
"store.reportContent"
/>
-->
<!--
<writtingMainBox
v-show=
"!!store.reportContent"
ref=
"mainBoxRef"
:report-content=
"store.reportContent"
/>
-->
...
@@ -66,7 +67,7 @@ import { onMounted, onUnmounted, ref, nextTick } from "vue";
...
@@ -66,7 +67,7 @@ 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"
;
import
writtingl
eftBox
from
"./components/WrittingLeftBox.vue"
;
import
WrittingL
eftBox
from
"./components/WrittingLeftBox.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"
;
//底部
...
@@ -105,11 +106,12 @@ const handleGenerate = async () => {
...
@@ -105,11 +106,12 @@ const handleGenerate = async () => {
};
};
const
handleWrite
=
async
()
=>
{
const
handleWrite
=
async
()
=>
{
try
{
try
{
console
.
log
(
1
)
// // 等待DOM更新(确保子组件DOM已挂载)
// // 等待DOM更新(确保子组件DOM已挂载)
store
.
tabList
[
2
].
active
=
true
//写报生成之后放开写报按钮
store
.
headerTabType
=
'translate'
await
nextTick
();
await
nextTick
();
await
store
.
generateWrite
()
await
store
.
generateWrite
()
console
.
log
(
2
)
}
catch
(
error
)
{
}
catch
(
error
)
{
ElMessage
.
error
(
error
.
message
);
ElMessage
.
error
(
error
.
message
);
console
.
error
(
"生成写报失败:"
,
error
);
console
.
error
(
"生成写报失败:"
,
error
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论