Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
9435564d
提交
9435564d
authored
4月 15, 2026
作者:
张伊明
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
wip: save immature local changes before syncing pre
Made-with: Cursor
上级
12c1ff12
显示空白字符变更
内嵌
并排
正在显示
20 个修改的文件
包含
1968 行增加
和
52 行删除
+1968
-52
index.vue
src/components/base/moduleHeader/index.vue
+15
-7
index.vue
src/components/intelligenceLeftTabBar/index.vue
+86
-45
intelligentTranslation.js
src/router/modules/intelligentTranslation.js
+37
-0
icon-iLetter.png
src/views/bill/billHome/assets/icons/icon-iLetter.png
+0
-0
PLAN-任务拆分.md
src/views/intelligentTranslation/PLAN-任务拆分.md
+58
-0
PLAN.md
src/views/intelligentTranslation/PLAN.md
+120
-0
documentCompare.vue
src/views/intelligentTranslation/documentCompare.vue
+270
-0
Line_Right.svg
src/views/intelligentTranslation/icons/Line_Right.svg
+8
-0
Line_Upload.svg
src/views/intelligentTranslation/icons/Line_Upload.svg
+7
-0
back-layer.png
src/views/intelligentTranslation/icons/back-layer.png
+0
-0
container-1885-placeholder.png
...telligentTranslation/icons/container-1885-placeholder.png
+0
-0
file-generic.svg
src/views/intelligentTranslation/icons/file-generic.svg
+6
-0
file-type-icon.png
src/views/intelligentTranslation/icons/file-type-icon.png
+0
-0
refresh-2.svg
src/views/intelligentTranslation/icons/refresh-2.svg
+7
-0
status-complete.png
src/views/intelligentTranslation/icons/status-complete.png
+0
-0
trash.svg
src/views/intelligentTranslation/icons/trash.svg
+6
-0
vector-1967.svg
src/views/intelligentTranslation/icons/vector-1967.svg
+7
-0
index.vue
src/views/intelligentTranslation/index.vue
+698
-0
mock.js
src/views/intelligentTranslation/mock.js
+89
-0
uploadRecords.vue
src/views/intelligentTranslation/uploadRecords.vue
+554
-0
没有找到文件。
src/components/base/moduleHeader/index.vue
浏览文件 @
9435564d
...
...
@@ -77,6 +77,7 @@ import Menu11 from "@/assets/icons/overview/menu11.png";
import
Menu12
from
"@/assets/icons/overview/menu12.png"
;
import
Tool1
from
'./tool1.svg'
import
Tool2
from
'./tool2.svg'
import
Tool3
from
'@/assets/icons/tool-item-icon2.png'
import
{
ElMessage
}
from
"element-plus"
;
import
{
useWrittingAsstaintStore
}
from
"@/stores/writtingAsstaintStore"
;
const
store
=
useWrittingAsstaintStore
();
...
...
@@ -323,6 +324,11 @@ const toolList = ref([
icon
:
Tool2
,
path
:
"/writtingAsstaint"
},
{
title
:
"智能翻译"
,
icon
:
Tool3
,
path
:
"/intelligent-translation"
},
])
const
handleToModule
=
(
item
,
index
)
=>
{
...
...
@@ -590,7 +596,8 @@ onMounted(() => {
position
:
absolute
;
z-index
:
999999
;
width
:
273px
;
height
:
171px
;
height
:
auto
;
min-height
:
171px
;
top
:
52px
;
left
:
300px
;
box-sizing
:
border-box
;
...
...
@@ -599,16 +606,17 @@ onMounted(() => {
backdrop-filter
:
blur
(
30px
);
box-shadow
:
0px
0px
20px
0px
rgba
(
25
,
69
,
130
,
0
.1
);
background
:
rgba
(
255
,
255
,
255
,
0
.8
);
padding
:
8px
0
12px
32px
;
.menu-content
{
width
:
562px
;
height
:
348px
;
margin-top
:
8px
;
margin-left
:
72px
;
width
:
100%
;
height
:
auto
;
margin-top
:
0
;
margin-left
:
0
;
.menu-item
{
margin-top
:
36
px
;
width
:
2
8
0px
;
margin-top
:
20
px
;
width
:
2
2
0px
;
height
:
24px
;
display
:
flex
;
cursor
:
pointer
;
...
...
src/components/intelligenceLeftTabBar/index.vue
浏览文件 @
9435564d
<
template
>
<div
class=
"intelligenceLeftTabBar"
>
<div
class=
"navBox"
:class=
"
{navBoxShow:isNavMenuShow}">
<div
class=
"navList"
v-for=
"(item,index) in navList "
:key=
"index"
:class=
"
{on:navPath==item.path}" @click="onNavListClick(item.path)">
<div
class=
"icon"
:style=
"
{background:`url(${item.img})no-repeat`,backgroundSize:'24px 24px',backgroundPosition:'17px 17px'}">
</div>
<span
class=
"text-tip-1"
style=
"white-space: nowrap; "
>
{{
item
.
name
}}
</span>
<div
class=
"intelligence-left-tab-bar"
>
<div
class=
"nav-box"
:class=
"
{ 'nav-box-show': isNavMenuShow }">
<div
v-for=
"item in navList"
:key=
"item.path"
class=
"nav-list"
:class=
"
{ 'on': navPath === item.path }"
@click="onNavListClick(item.path)"
>
<div
class=
"icon"
:style=
"getIconStyle(item.img)"
></div>
<span
class=
"text-tip-1 nav-text"
>
{{
item
.
name
}}
</span>
</div>
</div>
<img
class=
"show"
src=
"@/assets/icons/muenShow.png"
:style=
"isNavMenuShow?'transform: scaleX(1)':''"
alt=
""
@
click=
"()=>
{isNavMenuShow=!isNavMenuShow}">
<img
class=
"show"
:class=
"
{ 'show-expanded': isNavMenuShow }"
src="@/assets/icons/muenShow.png"
alt=""
@click="onToggleNavMenu"
/>
</div>
</
template
>
<
script
setup
>
import
{
ref
}
from
'vue'
import
{
useRoute
,
useRouter
}
from
'vue-router'
import
{
ElMessage
}
from
'element-plus'
import
muen1
from
'@/assets/icons/tool-item-icon1.png'
import
muen2
from
'@/assets/icons/tool-item-icon2.png'
import
muen3
from
'@/assets/icons/tool-item-icon3.png'
import
muen4
from
'@/assets/icons/tool-item-icon4.png'
import
{
onMounted
,
onUnmounted
,
ref
,
nextTick
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
useWrittingAsstaintStore
}
from
"@/stores/writtingAsstaintStore"
;
const
isNavMenuShow
=
ref
(
false
)
const
navList
=
ref
([
const
isNavMenuShow
=
ref
(
false
)
const
navPath
=
ref
(
''
)
const
navList
=
ref
([
{
img
:
muen1
,
path
:
'/writtingAsstaint'
,
name
:
'智能写报'
img
:
muen1
,
path
:
'/writtingAsstaint'
,
name
:
'智能写报'
},
{
img
:
muen2
,
path
:
'/writtingAsstaint1
'
,
name
:
'智能翻译'
img
:
muen2
,
path
:
'/intelligent-translation
'
,
name
:
'智能翻译'
},
{
img
:
muen3
,
path
:
'/writtingAsstaint2'
,
name
:
'智能查询'
img
:
muen3
,
path
:
'/writtingAsstaint2'
,
name
:
'智能查询'
},
{
img
:
muen4
,
path
:
'/writtingAsstaint3'
,
name
:
'智能对话'
},
img
:
muen4
,
path
:
'/writtingAsstaint3'
,
name
:
'智能对话'
}
])
const
navPath
=
ref
()
const
route
=
useRoute
()
if
(
route
.
path
){
navPath
.
value
=
route
.
path
const
route
=
useRoute
()
const
router
=
useRouter
()
if
(
route
.
path
)
{
navPath
.
value
=
route
.
path
}
const
onNavListClick
=
(
path
)
=>
{
if
(
path
==
'/writtingAsstaint'
){
navPath
.
value
=
path
}
else
{
ElMessage
.
error
(
'正在开发中'
)
const
getIconStyle
=
(
img
)
=>
({
background
:
`url(
${
img
}
) no-repeat`
,
backgroundSize
:
'24px 24px'
,
backgroundPosition
:
'17px 17px'
})
const
onToggleNavMenu
=
()
=>
{
isNavMenuShow
.
value
=
!
isNavMenuShow
.
value
}
const
onNavListClick
=
(
path
)
=>
{
if
(
path
===
'/writtingAsstaint'
||
path
===
'/intelligent-translation'
)
{
navPath
.
value
=
path
router
.
push
(
path
)
return
}
ElMessage
.
error
(
'正在开发中'
)
}
</
script
>
<
style
lang=
"scss"
scoped
>
.intelligence
LeftTabBar
{
.intelligence
-left-tab-bar
{
padding
:
5px
0
;
border-right
:
1px
solid
rgb
(
234
,
236
,
238
);
position
:
relative
;
.navBox
{
.nav-box
{
height
:
100%
;
width
:
65px
;
transition
:
all
0
.3s
;
padding
:
0
3px
;
.navList
{
.nav-list
{
display
:
flex
;
align-items
:
center
;
cursor
:
pointer
;
border-radius
:
8px
;
overflow
:
hidden
;
height
:
60px
;
.icon
{
.icon
{
width
:
60px
;
height
:
60px
;
flex-shrink
:
0
;
...
...
@@ -82,26 +114,35 @@ const onNavListClick=(path)=>{
margin-right
:
15px
;
}
}
.on
{
.on
{
background-color
:
var
(
--
color-primary-10
);
color
:
var
(
--
color-primary-100
);
font-weight
:
Bold
;
font-weight
:
700
;
}
}
.navBoxShow
{
.nav-box-show
{
width
:
200px
;
transition
:
all
0
.3s
;
}
.show
{
.nav-text
{
white-space
:
nowrap
;
}
.show
{
position
:
absolute
;
width
:
24px
;
height
:
24px
;
right
:
21px
;
bottom
:
21px
;
cursor
:
pointer
;
transform
:
scaleX
(
-1
)
transform
:
scaleX
(
-1
);
}
}
.show-expanded
{
transform
:
scaleX
(
1
);
}
}
</
style
>
src/router/modules/intelligentTranslation.js
0 → 100644
浏览文件 @
9435564d
// 智能翻译
const
IntelligentTranslation
=
()
=>
import
(
'@/views/intelligentTranslation/index.vue'
)
const
IntelligentTranslationDocument
=
()
=>
import
(
'@/views/intelligentTranslation/documentCompare.vue'
)
const
IntelligentTranslationUploadRecords
=
()
=>
import
(
'@/views/intelligentTranslation/uploadRecords.vue'
)
const
intelligentTranslationRoutes
=
[
{
path
:
"/intelligent-translation"
,
name
:
"intelligentTranslation"
,
component
:
IntelligentTranslation
,
meta
:
{
title
:
"智能翻译"
,
isShowHeader
:
true
}
},
{
path
:
"/intelligent-translation/document"
,
name
:
"intelligentTranslationDocument"
,
component
:
IntelligentTranslationDocument
,
meta
:
{
title
:
"智能翻译-文档对照"
,
isShowHeader
:
true
}
},
{
path
:
"/intelligent-translation/upload-records"
,
name
:
"intelligentTranslationUploadRecords"
,
component
:
IntelligentTranslationUploadRecords
,
meta
:
{
title
:
"智能翻译-文档上传记录"
,
isShowHeader
:
true
}
}
]
export
default
intelligentTranslationRoutes
src/views/bill/billHome/assets/icons/icon-iLetter.png
0 → 100644
浏览文件 @
9435564d
652 Bytes
src/views/intelligentTranslation/PLAN-任务拆分.md
0 → 100644
浏览文件 @
9435564d
# 智能翻译开发任务拆分(Checklist)
> 目标:按最小可交付顺序完成“智能翻译”功能,并保持结构清晰、便于后续接接口与调整样式。
## 阶段 1:入口与路由
-
[
x
]
在
`moduleHeader`
增加“智能翻译”入口(图标
`tool-item-icon2.png`
)
-
[
x
]
配置主路由
`intelligentTranslation`
-
[
x
]
配置文档对照页路由(建议
`intelligentTranslationDocument`
)
-
[
x
]
本地验证:点击入口可进入主页面
## 阶段 2:页面骨架搭建
-
[
x
]
创建目录
`src/views/intelligentTranslation/`
-
[
x
]
创建
`index.vue`
(主页面)
-
[
x
]
创建
`documentCompare.vue`
(文档对照页)
-
[
x
]
引入左侧通用组件
`src/components/intelligenceLeftTabBar/index.vue`
-
[
x
]
搭建基础布局结构(左侧导航 + 右侧内容区)
## 阶段 3:主页面核心交互(需求 a)
-
[
x
]
实现“文本模式”默认视图
-
[
x
]
原文输入框
-
[
x
]
上传文档按钮
-
[
x
]
翻译按钮(位于右上)
-
[
x
]
右侧翻译内容区
-
[
x
]
实现文本翻译占位逻辑(点击翻译后展示 mock 结果)
-
[
x
]
实现“文件模式”切换逻辑
-
[
x
]
上传后输入区替换为上传文件列表
-
[
x
]
右侧翻译内容区隐藏
-
[
x
]
实现文件模式“翻译”按钮跳转到文档对照页
-
[
x
]
传递上传文件名到对照页
## 阶段 4:上传记录区(需求 a 下半区)
-
[
x
]
在主页面底部实现上传记录区域
-
[
x
]
以占位数据展示:文档名、时间
-
[
x
]
保持结构和类名清晰,便于后续替换为接口
## 阶段 5:文档对照页实现(需求 b)
-
[
x
]
顶部 header 显示文档名称(来自上传文件名)
-
[
x
]
左侧实现文章标题树(mock)
-
[
x
]
右侧实现段落对照(左原文右译文)
-
[
x
]
实现点击树节点后右侧过滤/定位对应段落
## 阶段 6:占位数据与类型管理
-
[
x
]
新建
`mock.js`
统一管理页面 mock 数据
-
[
x
]
(可选)不使用
`types.ts`
,采用 JS 方案
-
[
x
]
在关键位置加
`TODO: replace with API`
## 阶段 7:样式与可维护性
-
[
x
]
输出简洁样式(不做复杂视觉)
-
[
x
]
统一语义化 class 命名
-
[
x
]
检查结构是否便于后续改样式
## 阶段 8:联调与验收
-
[
x
]
从入口进入主页面流程完整可跑通
-
[
x
]
文本翻译流程可用(占位)
-
[
x
]
文档上传 -> 文件模式 -> 跳转对照页流程可用
-
[
x
]
对照页标题、树、段落对照展示正确
-
[
x
]
检查并修复本次改动引入的 lint 问题
-
[
x
]
左侧导航“智能翻译”支持直接跳转并高亮
src/views/intelligentTranslation/PLAN.md
0 → 100644
浏览文件 @
9435564d
# 智能翻译页面实现计划
## 1. 目标概述
-
新增“智能翻译”页面,并接入现有入口与路由。
-
页面包含两个视图:
-
a)主页面:文本翻译 / 文档上传翻译 + 上传记录
-
b)文档对照页面:文档标题 + 文章导航树 + 原文/译文段落对照
-
当前阶段全部使用前端占位数据,不接真实接口。
---
## 2. 入口与路由接入
### 2.1 moduleHeader 入口
-
文件:
`src/components/base/moduleHeader/index.vue`
-
在指定区域(315-326 附近)新增“智能翻译”入口
-
文案:
`智能翻译`
-
图标:
`@/assets/icons/tool-item-icon2.png`
-
点击跳转到路由名:
`intelligentTranslation`
### 2.2 路由
-
新增主路由:
-
`name: intelligentTranslation`
-
`path: /intelligent-translation`
(可按项目现有习惯微调)
-
`component: src/views/intelligentTranslation/index.vue`
-
新增文档对照页路由:
-
建议
`name: intelligentTranslationDocument`
-
建议
`path: /intelligent-translation/document`
-
`component: src/views/intelligentTranslation/documentCompare.vue`
---
## 3. 目录与文件规划
在
`src/views/intelligentTranslation/`
下创建:
1.
`index.vue`
:主页面(需求 a)
2.
`documentCompare.vue`
:文档对照页面(需求 b)
3.
`mock.ts`
(可选):占位数据统一管理
4.
`types.ts`
(可选):类型定义,便于后续接接口
---
## 4. 页面 a(主页面)实现方案
## 4.1 布局结构
-
页面最左侧使用通用组件:
`src/components/intelligenceLeftTabBar/index.vue`
-
右侧为主工作区:
-
上方约 2/3:翻译操作区
-
下方约 1/3:文档翻译上传记录
## 4.2 上方翻译区状态
定义两种状态:
### 状态 1:文本模式(默认)
-
左侧:原文输入区(文本输入框)
-
左侧底部:上传文档按钮
-
左侧右上:翻译按钮
-
右侧:翻译内容展示区
-
交互:输入原文并点击翻译,在右侧展示占位翻译结果
### 状态 2:文件模式(上传文件后)
-
上传后左侧输入区切换为“上传文件列表”
-
右侧翻译内容区隐藏
-
左侧右上继续保留翻译按钮
-
点击翻译按钮后跳转到页面 b
## 4.3 上传记录区
-
展示“文档名 + 时间”两列(占位数据)
-
数据来源先用 mock,后续替换为接口
---
## 5. 页面 b(文档对照页)实现方案
## 5.1 页面结构
-
顶部 Header:显示文档名称(来自上传文件名)
-
下方左右两栏:
-
左侧:文章导航树(占位数据模拟接口)
-
右侧:原文/译文按段落一一对照
## 5.2 数据与交互
-
树数据:模拟章节结构(可含 children)
-
段落数据:
`[{ sectionId, sourceText, translatedText }]`
-
交互:点击左侧章节,右侧过滤或定位对应段落
---
## 6. 占位数据策略
-
所有“接口返回数据”先统一在
`mock.ts`
管理,避免散落到组件内。
-
页面仅依赖数据结构,后续接真实接口时只替换数据来源。
-
在关键数据位置添加
`TODO: replace with API`
便于后续联调。
---
## 7. 样式与结构约束
-
样式先做简洁版,保证可读与可用。
-
重点保证 HTML 结构和 class 命名清晰,便于后续二次改样式。
-
建议类名示例:
-
`intelligent-translation-page`
-
`translation-main`
-
`source-upload-panel`
-
`translation-result-panel`
-
`upload-record-list`
-
`document-compare-page`
-
`article-nav-tree`
-
`paragraph-compare-row`
---
## 8. 验收清单
1.
moduleHeader 中新增“智能翻译”入口并可跳转
2.
主页面默认文本模式可输入并生成占位翻译结果
3.
上传文档后切换到文件模式(输入区变文件列表,右侧翻译区隐藏)
4.
文件模式点击翻译可进入文档对照页
5.
文档对照页 header 正确显示上传文件名
6.
文档对照页左侧展示文章树,右侧按段落对照展示原文/译文
7.
上传记录区展示占位“文档名+时间”
8.
代码结构清晰,便于后续样式和接口替换
src/views/intelligentTranslation/documentCompare.vue
0 → 100644
浏览文件 @
9435564d
<
template
>
<div
class=
"document-compare-page"
>
<intelligence-left-tab-bar
/>
<div
class=
"compare-main"
>
<div
v-if=
"isCompareLoading"
class=
"compare-loading-placeholder"
>
<img
class=
"compare-loading-placeholder-img"
:src=
"comparePlaceholderUrl"
alt=
""
/>
</div>
<template
v-else
>
<div
class=
"compare-header"
>
<div
class=
"header-title"
>
文档对照
</div>
<div
class=
"header-file-name"
>
{{
displayFileName
}}
</div>
</div>
<div
class=
"compare-content"
>
<div
class=
"article-nav-tree"
>
<div
class=
"tree-title"
>
文章标题
</div>
<div
class=
"tree-list"
>
<button
v-for=
"item in articleTree"
:key=
"item.id"
type=
"button"
class=
"tree-item"
:class=
"
{ active: activeSectionId === item.id }"
@click="onSelectSection(item.id)"
>
{{
item
.
title
}}
</button>
</div>
</div>
<div
class=
"paragraph-compare-panel"
>
<div
class=
"panel-title"
>
段落对照
</div>
<div
class=
"paragraph-list"
>
<div
v-for=
"paragraph in displayParagraphs"
:key=
"paragraph.id"
class=
"paragraph-compare-row"
>
<div
class=
"paragraph-cell source-cell"
>
<div
class=
"cell-label"
>
原文
</div>
<div
class=
"cell-content"
>
{{
paragraph
.
sourceText
}}
</div>
</div>
<div
class=
"paragraph-cell translated-cell"
>
<div
class=
"cell-label"
>
译文
</div>
<div
class=
"cell-content"
>
{{
paragraph
.
translatedText
}}
</div>
</div>
</div>
<div
v-if=
"displayParagraphs.length === 0"
class=
"empty-tip"
>
当前章节暂无段落占位数据
</div>
</div>
</div>
</div>
</
template
>
</div>
</div>
</template>
<
script
setup
>
import
{
computed
,
onMounted
,
ref
}
from
'vue'
import
{
useRoute
}
from
'vue-router'
import
IntelligenceLeftTabBar
from
'@/components/intelligenceLeftTabBar/index.vue'
import
comparePlaceholderUrl
from
'./icons/container-1885-placeholder.png'
import
{
fetchDocumentCompareMock
}
from
'./mock'
const
route
=
useRoute
()
const
isCompareLoading
=
ref
(
true
)
const
displayFileName
=
computed
(()
=>
{
const
fileName
=
route
.
query
.
fileName
return
typeof
fileName
===
'string'
&&
fileName
?
fileName
:
'未命名文档(占位)'
})
const
articleTree
=
ref
([])
const
paragraphRows
=
ref
([])
const
activeSectionId
=
ref
(
'all'
)
const
displayParagraphs
=
computed
(()
=>
{
if
(
activeSectionId
.
value
===
'all'
)
{
return
paragraphRows
.
value
}
return
paragraphRows
.
value
.
filter
(
(
item
)
=>
item
.
sectionId
===
activeSectionId
.
value
)
})
const
onSelectSection
=
(
sectionId
)
=>
{
activeSectionId
.
value
=
sectionId
}
onMounted
(
async
()
=>
{
const
fileName
=
typeof
route
.
query
.
fileName
===
'string'
?
route
.
query
.
fileName
:
''
try
{
const
data
=
await
fetchDocumentCompareMock
(
fileName
)
articleTree
.
value
=
data
.
articleTree
paragraphRows
.
value
=
data
.
paragraphRows
}
finally
{
isCompareLoading
.
value
=
false
}
})
</
script
>
<
style
scoped
lang=
"scss"
>
.document-compare-page
{
display
:
flex
;
min-height
:
calc
(
100vh
-
64px
);
background
:
#fff
;
}
.compare-main
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
padding
:
20px
;
gap
:
12px
;
overflow
:
auto
;
}
.compare-loading-placeholder
{
flex
:
1
;
min-height
:
560px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
16px
;
box-sizing
:
border-box
;
background
:
var
(
--
bg-white-100
);
border-radius
:
8px
;
}
.compare-loading-placeholder-img
{
max-width
:
100%
;
max-height
:
min
(
720px
,
calc
(
100vh
-
160px
));
width
:
auto
;
height
:
auto
;
object-fit
:
contain
;
display
:
block
;
}
.compare-header
{
min-height
:
56px
;
border
:
1px
solid
#e8ecf2
;
border-radius
:
8px
;
padding
:
10px
16px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
12px
;
}
.header-title
{
color
:
#344054
;
font-weight
:
600
;
}
.header-file-name
{
color
:
#475467
;
font-size
:
14px
;
}
.compare-content
{
flex
:
1
;
min-height
:
560px
;
display
:
flex
;
gap
:
12px
;
}
.article-nav-tree
{
width
:
300px
;
flex-shrink
:
0
;
border
:
1px
solid
#e8ecf2
;
border-radius
:
8px
;
background
:
#fff
;
padding
:
12px
;
display
:
flex
;
flex-direction
:
column
;
}
.tree-title
,
.panel-title
{
color
:
#344054
;
font-size
:
14px
;
font-weight
:
600
;
margin-bottom
:
12px
;
}
.tree-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.tree-item
{
height
:
36px
;
border
:
1px
solid
#d0d5dd
;
border-radius
:
6px
;
background
:
#fff
;
color
:
#344054
;
cursor
:
pointer
;
text-align
:
left
;
padding
:
0
10px
;
}
.tree-item.active
{
border-color
:
#2563eb
;
color
:
#2563eb
;
background
:
#eff4ff
;
}
.paragraph-compare-panel
{
flex
:
1
;
border
:
1px
solid
#e8ecf2
;
border-radius
:
8px
;
background
:
#fff
;
padding
:
12px
;
display
:
flex
;
flex-direction
:
column
;
}
.paragraph-list
{
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
}
.paragraph-compare-row
{
display
:
grid
;
grid-template-columns
:
1fr
1fr
;
gap
:
12px
;
}
.paragraph-cell
{
border
:
1px
solid
#e4e7ec
;
border-radius
:
6px
;
padding
:
10px
;
min-height
:
120px
;
}
.cell-label
{
font-size
:
12px
;
color
:
#667085
;
margin-bottom
:
8px
;
}
.cell-content
{
font-size
:
14px
;
line-height
:
22px
;
color
:
#344054
;
white-space
:
pre-wrap
;
}
.empty-tip
{
color
:
#98a2b3
;
font-size
:
14px
;
}
</
style
>
src/views/intelligentTranslation/icons/Line_Right.svg
0 → 100644
浏览文件 @
9435564d
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
width=
"16"
height=
"16"
fill=
"none"
>
<path
d=
"M11.7051 5.35352L6.35366 0L5.64642 0.706971L9.79488 4.85703L0 4.85703L0 5.85703L9.78715 5.85703L5.64618 9.99716L6.35322 10.7043L11.7051 5.35352Z"
fill=
"rgb(59,65,75)"
fill-rule=
"evenodd"
transform=
"matrix(-1,0,0,1,14,2.64062)"
/>
</svg>
src/views/intelligentTranslation/icons/Line_Upload.svg
0 → 100644
浏览文件 @
9435564d
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
aria-hidden=
"true"
>
<path
d=
"M6.25 4.96523L7.40469 4.96523L7.40469 10.2512C7.40469 10.3199 7.46094 10.3762 7.52969 10.3762L8.46719 10.3762C8.53594 10.3762 8.59219 10.3199 8.59219 10.2512L8.59219 4.96523L9.75 4.96523C9.85469 4.96523 9.9125 4.84492 9.84844 4.76367L8.09844 2.54805C8.04844 2.48398 7.95156 2.48398 7.90156 2.54805L6.15156 4.76211C6.0875 4.84492 6.14531 4.96523 6.25 4.96523ZM12.7812 9.78242L13.7188 9.78242C13.7875 9.78242 13.8438 9.83867 13.8438 9.90742L13.8438 13.0012C13.8438 13.2777 13.6203 13.5012 13.3438 13.5012L2.65625 13.5012C2.37969 13.5012 2.15625 13.2777 2.15625 13.0012L2.15625 9.90742C2.15625 9.83867 2.2125 9.78242 2.28125 9.78242L3.21875 9.78242C3.2875 9.78242 3.34375 9.83867 3.34375 9.90742L3.34375 12.3137L12.6562 12.3137L12.6562 9.90742C12.6562 9.83867 12.7125 9.78242 12.7812 9.78242Z"
fill=
"currentColor"
fill-rule=
"evenodd"
/>
</svg>
src/views/intelligentTranslation/icons/back-layer.png
0 → 100644
浏览文件 @
9435564d
438 Bytes
src/views/intelligentTranslation/icons/container-1885-placeholder.png
0 → 100644
浏览文件 @
9435564d
25.5 KB
src/views/intelligentTranslation/icons/file-generic.svg
0 → 100644
浏览文件 @
9435564d
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 32 40"
fill=
"none"
aria-hidden=
"true"
>
<path
fill=
"#98A2B3"
d=
"M4 2h14.5L28 11.5V38a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Z"
/>
<path
fill=
"#7B8794"
d=
"M18.5 2 28 11.5h-7.5a2 2 0 0 1-2-2V2Z"
/>
<rect
x=
"8"
y=
"18"
width=
"16"
height=
"2"
rx=
"1"
fill=
"#fff"
opacity=
".9"
/>
<rect
x=
"8"
y=
"23"
width=
"12"
height=
"2"
rx=
"1"
fill=
"#fff"
opacity=
".7"
/>
</svg>
src/views/intelligentTranslation/icons/file-type-icon.png
0 → 100644
浏览文件 @
9435564d
2.2 KB
src/views/intelligentTranslation/icons/refresh-2.svg
0 → 100644
浏览文件 @
9435564d
<svg
viewBox=
"0 0 24 24"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
aria-hidden=
"true"
>
<path
d=
"M17.3393 3.55622C17.2726 3.18762 16.9241 2.94363 16.5608 3.01126C16.1975 3.07889 15.957 3.43254 16.0237 3.80114L16.3603 5.66297C15.105 4.83723 13.6075 4.35727 12 4.35727C7.79287 4.35727 4.34185 7.64195 4.0021 11.8217C3.97174 12.1952 4.27407 12.5 4.64343 12.5C5.01279 12.5 5.3087 12.1954 5.34514 11.8224C5.68025 8.39297 8.53183 5.7144 12 5.7144C13.3857 5.7144 14.6726 6.14157 15.7404 6.87394L13.9332 7.07558C13.5661 7.11654 13.3012 7.45173 13.3416 7.82425C13.3819 8.19676 13.7123 8.46554 14.0794 8.42457L17.4233 8.05146C17.6079 8.03087 17.7758 7.93331 17.8867 7.78215C17.9976 7.63099 18.0415 7.43985 18.008 7.25451L17.3393 3.55622ZM19.9979 13.1784C19.6582 17.3581 16.2071 20.6428 12 20.6428C10.4549 20.6428 9.01114 20.1993 7.78651 19.4315L7.98332 21.2472C8.02369 21.6197 7.7588 21.9549 7.39165 21.9959C7.02451 22.0368 6.69415 21.7681 6.65378 21.3956L6.28603 18.0027C6.24867 17.6581 6.47344 17.3403 6.80723 17.2658L9.8501 16.5872C10.2108 16.5068 10.5676 16.7383 10.6469 17.1043C10.7261 17.4703 10.498 17.8323 10.1372 17.9127L8.49331 18.2793C9.51288 18.9177 10.7137 19.2857 12 19.2857C15.4682 19.2857 18.3198 16.6071 18.6549 13.1777C18.6913 12.8047 18.9872 12.5 19.3566 12.5C19.7259 12.5 20.0283 12.8049 19.9979 13.1784Z"
fill=
"currentColor"
fill-rule=
"evenodd"
/>
</svg>
src/views/intelligentTranslation/icons/status-complete.png
0 → 100644
浏览文件 @
9435564d
553 Bytes
src/views/intelligentTranslation/icons/trash.svg
0 → 100644
浏览文件 @
9435564d
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
aria-hidden=
"true"
>
<path
d=
"M11.0487 13.5C11.5816 13.5 12.0209 13.0821 12.0475 12.5499L12.5 3.5L3.5 3.5L3.9525 12.5499C3.97911 13.0821 4.41838 13.5 4.95125 13.5L11.0487 13.5Z"
stroke=
"currentColor"
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"1"
/>
<path
d=
"M6.5 10.5L6.5 6.5M9.5 6.5L9.5 10.5"
stroke=
"currentColor"
stroke-linecap=
"round"
stroke-width=
"1"
/>
<path
d=
"M2.5 3.5L13.5 3.5"
stroke=
"currentColor"
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"1"
/>
<path
d=
"M5.5 2.5C5.5 1.94772 5.94772 1.5 6.5 1.5L9.5 1.5C10.0523 1.5 10.5 1.94772 10.5 2.5L10.5 3.5L5.5 3.5L5.5 2.5Z"
stroke=
"currentColor"
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"1"
/>
</svg>
src/views/intelligentTranslation/icons/vector-1967.svg
0 → 100644
浏览文件 @
9435564d
<svg
viewBox=
"0 0 16 16"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
aria-hidden=
"true"
>
<path
d=
"M7.56352 0C7.99862 7.73245e-05 8.42472 0.122418 8.7922 0.352766C9.15967 0.583115 9.45338 0.911984 9.63909 1.30105C9.82481 1.69012 9.89488 2.12336 9.84114 2.5503C9.7874 2.97723 9.61207 3.38028 9.33558 3.71246C9.05909 4.04465 8.69282 4.29229 8.27946 4.42655L8.27744 5.20905L10.198 5.20905C12.2623 5.20905 14.1095 6.48173 14.8234 8.39771C14.8756 8.53766 14.8817 8.69037 14.8408 8.83395C14.7999 8.97753 14.714 9.10461 14.5955 9.19698C14.4771 9.28935 14.3321 9.34227 14.1814 9.34815C14.0306 9.35402 13.8819 9.31256 13.7564 9.22971C13.631 9.14685 13.5353 9.02685 13.483 8.8869C13.2351 8.2226 12.7871 7.64942 12.1996 7.24461C11.612 6.8398 10.9131 6.62283 10.197 6.62292L4.92906 6.62292C4.3147 6.62292 3.71115 6.78277 3.17903 7.08642C2.64692 7.39007 2.20497 7.82682 1.89758 8.35282C1.5902 8.87881 1.42819 9.47552 1.42784 10.083L1.42784 11.126C1.42784 11.7338 1.58962 12.3308 1.89691 12.8571C2.20421 13.3834 2.6462 13.8204 3.17845 14.1243C3.7107 14.4281 4.31447 14.5881 4.92906 14.5881L10.198 14.5881C10.3488 14.5881 10.4956 14.6353 10.6176 14.7229C10.7396 14.8106 10.8304 14.9341 10.877 15.0759C10.9236 15.2177 10.9236 15.3704 10.877 15.5122C10.8304 15.654 10.7396 15.7776 10.6176 15.8652C10.4956 15.9528 10.3488 16 10.198 16L4.92906 16C4.06383 16 3.21384 15.7748 2.46453 15.347C1.71522 14.9192 1.09298 14.304 0.660369 13.563C0.227754 12.8221 -3.18398e-08 11.9816 0 11.126L0 10.082C0.000352498 9.22669 0.228333 8.38651 0.661039 7.64588C1.09374 6.90526 1.71594 6.29027 2.46511 5.8627C3.21428 5.43513 4.06406 5.21004 4.92906 5.21004L6.84961 5.21004L6.84961 4.42655C6.43643 4.29236 6.07031 4.04488 5.79386 3.71292C5.51741 3.38096 5.342 2.97819 5.28806 2.55148C5.23413 2.12477 5.30387 1.6917 5.48919 1.30267C5.6745 0.913637 5.96776 0.584662 6.33482 0.354041C6.70188 0.12342 7.12763 0.000641265 7.56252 0L7.56352 0ZM13.4046 10.603C13.5293 10.6229 13.6258 10.7194 13.6459 10.8417C13.7232 11.3276 13.9435 11.7802 14.2793 12.143C14.6152 12.5059 15.0516 12.7629 15.5343 12.8819L15.7545 12.9257C15.809 12.9341 15.86 12.9578 15.9014 12.9939C15.9428 13.0299 15.973 13.0769 15.9884 13.1293C16.0039 13.1817 16.0039 13.2374 15.9884 13.2898C15.973 13.3422 15.9428 13.3892 15.9014 13.4252C15.86 13.4613 15.809 13.4849 15.7545 13.4934C14.749 13.6495 13.9365 14.3853 13.6902 15.3607L13.6459 15.5784C13.6374 15.6323 13.6134 15.6827 13.577 15.7237C13.5405 15.7647 13.493 15.7945 13.44 15.8098C13.387 15.825 13.3307 15.825 13.2777 15.8098C13.2247 15.7945 13.1772 15.7647 13.1407 15.7237C13.1042 15.6827 13.0803 15.6323 13.0718 15.5784C12.9947 15.0923 12.7745 14.6395 12.4386 14.2765C12.1028 13.9134 11.6662 13.6563 11.1834 13.5372L10.9652 13.4934C10.9107 13.4849 10.8597 13.4613 10.8183 13.4252C10.7769 13.3892 10.7467 13.3422 10.7313 13.2898C10.7158 13.2374 10.7158 13.1817 10.7313 13.1293C10.7467 13.0769 10.7769 13.0299 10.8183 12.9939C10.8597 12.9578 10.9107 12.9341 10.9652 12.9257C11.4564 12.8491 11.9139 12.6312 12.2807 12.2991C12.6475 11.967 12.9072 11.5355 13.0275 11.0584L13.0718 10.8427C13.0798 10.7928 13.1009 10.7459 13.1331 10.7068C13.1652 10.6676 13.2072 10.6374 13.2549 10.6194C13.3026 10.6014 13.3542 10.5961 13.4046 10.604L13.4046 10.603ZM4.92906 9.04201C5.1516 9.03466 5.37078 9.09716 5.55518 9.22056C5.73959 9.34396 5.87975 9.52191 5.95559 9.72892C6.03142 9.93593 6.03904 10.1614 5.97734 10.3729C5.91564 10.5845 5.78779 10.7713 5.61213 10.9066C5.43646 11.0419 5.22199 11.1187 4.99945 11.126C4.77691 11.1334 4.55773 11.0709 4.37333 10.9475C4.18892 10.8241 4.04876 10.6461 3.97292 10.4391C3.89708 10.2321 3.88947 10.0067 3.95117 9.79512C4.01287 9.58356 4.14072 9.39676 4.31638 9.26147C4.49205 9.12618 4.70652 9.04936 4.92906 9.04201ZM10.198 9.04201C10.4205 9.03477 10.6397 9.09737 10.824 9.22085C11.0084 9.34433 11.1484 9.52235 11.2242 9.7294C11.2999 9.93644 11.3074 10.1619 11.2456 10.3734C11.1838 10.5849 11.0559 10.7717 10.8801 10.9069C10.7044 11.0421 10.4899 11.1188 10.2674 11.126C10.0448 11.1334 9.82565 11.0709 9.64125 10.9475C9.45684 10.8241 9.31668 10.6461 9.24084 10.4391C9.165 10.2321 9.15739 10.0067 9.21909 9.79512C9.28079 9.58356 9.40864 9.39676 9.5843 9.26147C9.75997 9.12618 9.97445 9.04936 10.197 9.04201L10.198 9.04201ZM7.56353 1.41288C7.41134 1.41282 7.26182 1.45239 7.13001 1.5276C6.9982 1.60281 6.88873 1.71101 6.81262 1.84132C6.73651 1.97163 6.69645 2.11946 6.69645 2.26995C6.69645 2.42043 6.73651 2.56826 6.81262 2.69858C6.88873 2.82889 6.9982 2.93709 7.13001 3.0123C7.26183 3.08751 7.41134 3.12708 7.56353 3.12702C7.74657 3.12702 7.92491 3.06972 8.07299 2.96333C8.22108 2.85695 8.3313 2.70693 8.38786 2.5348C8.44442 2.36266 8.44442 2.17724 8.38786 2.0051C8.3313 1.83296 8.22108 1.68295 8.07299 1.57656C7.92491 1.47018 7.74657 1.41288 7.56353 1.41288L7.56353 1.41288Z"
fill=
"currentColor"
fill-rule=
"nonzero"
/>
</svg>
src/views/intelligentTranslation/index.vue
0 → 100644
浏览文件 @
9435564d
<
template
>
<div
class=
"intelligent-translation-page"
>
<intelligence-left-tab-bar
/>
<div
class=
"translation-main"
>
<div
class=
"translation-workspace"
>
<div
class=
"workspace-left workspace-panel"
:class=
"
{ 'workspace-left--full': !isTextMode }">
<div
class=
"workspace-toolbar"
>
<div
class=
"workspace-toolbar-title"
>
<img
class=
"workspace-toolbar-icon"
src=
"@/assets/icons/tool-item-icon2.png"
alt=
""
/>
<span
class=
"workspace-toolbar-heading"
>
智能翻译
</span>
</div>
<button
class=
"translate-btn"
type=
"button"
@
click=
"onTranslate"
>
AI翻译
</button>
</div>
<div
class=
"workspace-source-block"
>
<div
v-if=
"isTextMode"
class=
"source-input-panel"
>
<textarea
v-model=
"sourceText"
class=
"source-textarea"
placeholder=
"请输入您要翻译的内容"
/>
</div>
<div
v-else
class=
"upload-file-panel"
>
<ul
class=
"upload-file-list"
>
<li
v-for=
"(file, index) in selectedFiles"
:key=
"`$
{file.name}-${index}`" class="upload-file-item">
<img
class=
"upload-file-type-icon"
:src=
"fileTypeIconUrl"
alt=
""
/>
<span
class=
"upload-file-name"
>
{{
file
.
name
}}
</span>
<button
type=
"button"
class=
"upload-file-item-delete"
aria-label=
"删除该文件"
@
click=
"removeFileAt(index)"
>
<span
class=
"upload-file-delete-icon"
v-html=
"trashIconSvg"
/>
</button>
</li>
</ul>
</div>
<div
class=
"upload-action-row"
>
<label
class=
"upload-btn"
for=
"translation-upload-input"
>
<span
class=
"upload-btn-inner"
>
<span
class=
"upload-btn-main"
>
<span
class=
"upload-btn-icon"
v-html=
"lineUploadIconSvg"
/>
<span
class=
"upload-btn-text"
>
上传文档
</span>
</span>
<span
class=
"upload-format-hint"
>
上传的文件只支持 pdf 格式
</span>
</span>
</label>
<input
id=
"translation-upload-input"
ref=
"fileInputRef"
class=
"upload-input"
type=
"file"
multiple
accept=
".pdf,application/pdf"
@
change=
"onFileChange"
/>
</div>
</div>
</div>
<div
v-if=
"isTextMode"
class=
"workspace-right workspace-panel"
>
<div
class=
"workspace-result-toolbar"
>
<div
class=
"workspace-result-toolbar-title"
>
<span
class=
"workspace-result-toolbar-icon"
v-html=
"vector1967Svg"
/>
<span
class=
"workspace-result-toolbar-heading"
>
智能体·AI翻译
</span>
</div>
<div
class=
"workspace-result-toolbar-actions"
>
<button
type=
"button"
class=
"workspace-result-tool-btn"
aria-label=
"功能"
@
click=
"onResultToolbarAgent"
>
<span
class=
"workspace-result-tool-btn-icon"
v-html=
"vector1967Svg"
/>
</button>
<button
type=
"button"
class=
"workspace-result-tool-btn"
aria-label=
"刷新"
@
click=
"onResultToolbarRefresh"
>
<span
class=
"workspace-result-tool-btn-icon"
v-html=
"refresh2Svg"
/>
</button>
</div>
</div>
<div
class=
"workspace-result-body"
>
<div
class=
"result-content"
>
{{
translatedText
}}
</div>
</div>
</div>
</div>
<div
class=
"upload-record-list"
>
<div
class=
"record-header"
>
<span
class=
"record-title"
>
文档翻译上传记录
</span>
<button
type=
"button"
class=
"record-view-all"
@
click=
"onViewAllUploadRecords"
>
查看全部
>
</button>
</div>
<div
class=
"record-cards"
>
<div
v-for=
"record in uploadRecords"
:key=
"`$
{record.name}-${record.time}`" class="record-card">
<img
class=
"record-card-icon"
:src=
"fileTypeIconUrl"
alt=
""
/>
<span
class=
"record-card-name"
>
{{
record
.
name
}}
</span>
<span
class=
"record-card-time"
>
{{
formatRecordDateOnly
(
record
.
time
)
}}
</span>
</div>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
>
import
{
computed
,
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
IntelligenceLeftTabBar
from
'@/components/intelligenceLeftTabBar/index.vue'
import
{
uploadRecordMock
}
from
'./mock'
import
fileTypeIconUrl
from
'./icons/file-type-icon.png'
import
lineUploadIconSvg
from
'./icons/Line_Upload.svg?raw'
import
refresh2Svg
from
'./icons/refresh-2.svg?raw'
import
trashIconSvg
from
'./icons/trash.svg?raw'
import
vector1967Svg
from
'./icons/vector-1967.svg?raw'
const
router
=
useRouter
()
const
fileInputRef
=
ref
(
null
)
const
sourceText
=
ref
(
''
)
const
translatedText
=
ref
(
'点击“翻译”后展示占位结果'
)
const
selectedFiles
=
ref
([])
const
uploadRecords
=
ref
(
uploadRecordMock
)
const
isTextMode
=
computed
(()
=>
selectedFiles
.
value
.
length
===
0
)
const
onTranslate
=
()
=>
{
if
(
isTextMode
.
value
)
{
const
text
=
sourceText
.
value
.
trim
()
translatedText
.
value
=
text
?
`【Mock译文】
${
text
}
`
:
'【Mock译文】当前没有输入文本,请先填写原文内容。'
return
}
router
.
push
({
name
:
'intelligentTranslationDocument'
,
query
:
{
fileName
:
selectedFiles
.
value
[
0
]?.
name
||
''
}
})
}
const
onFileChange
=
(
event
)
=>
{
const
{
files
}
=
event
.
target
if
(
!
files
?.
length
)
{
selectedFiles
.
value
=
[]
return
}
const
pdfOnly
=
Array
.
from
(
files
).
filter
(
(
f
)
=>
/
\.
pdf$/i
.
test
(
f
.
name
)
||
f
.
type
===
'application/pdf'
)
selectedFiles
.
value
=
pdfOnly
if
(
fileInputRef
.
value
)
{
const
dt
=
new
DataTransfer
()
pdfOnly
.
forEach
((
f
)
=>
dt
.
items
.
add
(
f
))
fileInputRef
.
value
.
files
=
dt
.
files
if
(
pdfOnly
.
length
===
0
)
{
fileInputRef
.
value
.
value
=
''
}
}
}
const
removeFileAt
=
(
index
)
=>
{
const
next
=
selectedFiles
.
value
.
filter
((
_
,
i
)
=>
i
!==
index
)
selectedFiles
.
value
=
next
if
(
fileInputRef
.
value
)
{
const
dt
=
new
DataTransfer
()
next
.
forEach
((
f
)
=>
dt
.
items
.
add
(
f
))
fileInputRef
.
value
.
files
=
dt
.
files
if
(
next
.
length
===
0
)
{
fileInputRef
.
value
.
value
=
''
}
}
}
const
onResultToolbarAgent
=
()
=>
{
}
const
onResultToolbarRefresh
=
()
=>
{
}
const
onViewAllUploadRecords
=
()
=>
{
router
.
push
({
name
:
'intelligentTranslationUploadRecords'
})
}
/** 上传记录日期仅展示到年月日 */
function
formatRecordDateOnly
(
timeStr
)
{
if
(
timeStr
==
null
||
timeStr
===
''
)
{
return
''
}
const
s
=
String
(
timeStr
).
trim
()
const
head
=
s
.
match
(
/^
(\d{4}
-
\d{2}
-
\d{2})
/
)
if
(
head
)
{
return
head
[
1
]
}
const
t
=
Date
.
parse
(
s
)
if
(
!
Number
.
isNaN
(
t
))
{
const
d
=
new
Date
(
t
)
const
y
=
d
.
getFullYear
()
const
m
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
)
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
'0'
)
return
`
${
y
}
-
${
m
}
-
${
day
}
`
}
return
s
}
</
script
>
<
style
scoped
lang=
"scss"
>
.intelligent-translation-page
{
display
:
flex
;
min-height
:
calc
(
100vh
-
65px
);
background
:
#fff
;
}
.translation-main
{
flex
:
1
;
padding
:
16px
;
overflow
:
auto
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
background-color
:
var
(
--
color-primary-2
);
}
.translation-workspace
{
min-height
:
560px
;
display
:
flex
;
gap
:
16px
;
}
.workspace-panel
{
border
:
none
;
border-radius
:
10px
;
box-shadow
:
0
0
20px
0
rgba
(
25
,
69
,
130
,
0
.1
);
}
.workspace-left
{
width
:
50%
;
min-width
:
420px
;
flex
:
0
0
auto
;
display
:
flex
;
flex-direction
:
column
;
background
:
#fff
;
}
.workspace-left--full
{
width
:
100%
;
min-width
:
0
;
flex
:
1
1
auto
;
max-width
:
100%
;
}
.workspace-toolbar
{
box-sizing
:
border-box
;
height
:
68px
;
padding
:
16px
24px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
box-shadow
:
0
1px
0
0
#e8ecf2
;
}
.workspace-toolbar-title
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
min-width
:
0
;
}
.workspace-toolbar-icon
{
width
:
24px
;
height
:
24px
;
flex-shrink
:
0
;
display
:
block
;
}
/* 业务系统 / 1级标题-综艺:YouSheBiaoTiHei,黑色主色 80% */
.workspace-toolbar-heading
{
font-family
:
'YouSheBiaoTiHei'
,
sans-serif
;
font-size
:
30px
;
font-weight
:
400
;
line-height
:
32px
;
letter-spacing
:
0
;
color
:
var
(
--
text-primary-80-color
);
}
.translate-btn
{
box-sizing
:
border-box
;
width
:
120px
;
height
:
36px
;
padding
:
0
;
border
:
none
;
border-radius
:
50px
;
background
:
linear-gradient
(
90deg
,
rgba
(
0
,
91
,
244
,
1
)
0%
,
rgba
(
101
,
0
,
253
,
1
)
100%
);
color
:
#fff
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
center
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
}
.workspace-source-block
{
box-sizing
:
border-box
;
flex
:
1
;
min-height
:
0
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
padding
:
16px
24px
23px
24px
;
}
.source-input-panel
{
flex
:
1
;
min-height
:
0
;
display
:
flex
;
flex-direction
:
column
;
}
.upload-file-panel
{
box-sizing
:
border-box
;
width
:
100%
;
flex
:
0
1
auto
;
min-height
:
0
;
max-height
:
100%
;
overflow
:
auto
;
padding
:
0
;
}
.upload-file-item-delete
{
flex-shrink
:
0
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
margin-left
:
auto
;
padding
:
4px
;
border
:
none
;
background
:
transparent
;
cursor
:
pointer
;
color
:
var
(
--
color-red-100
);
line-height
:
0
;
}
.upload-file-delete-icon
{
display
:
inline-flex
;
width
:
16px
;
height
:
16px
;
}
.upload-file-delete-icon
:deep
(
svg
)
{
display
:
block
;
width
:
16px
;
height
:
16px
;
}
.source-textarea
{
box-sizing
:
border-box
;
width
:
100%
;
flex
:
1
;
min-height
:
200px
;
border
:
none
;
border-radius
:
6px
;
resize
:
vertical
;
padding
:
10px
;
outline
:
none
;
background
:
transparent
;
}
.upload-file-list
{
margin
:
0
;
padding
:
0
;
list-style
:
none
;
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.upload-file-item
{
box-sizing
:
border-box
;
height
:
48px
;
padding
:
12px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-start
;
gap
:
8px
;
text-align
:
left
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
700
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
color
:
#475467
;
background
:
var
(
--
bg-black-2
);
border
:
1px
solid
var
(
--
bg-black-5
);
border-radius
:
6px
;
}
.upload-file-type-icon
{
flex-shrink
:
0
;
width
:
24px
;
height
:
24px
;
object-fit
:
contain
;
display
:
block
;
}
.upload-file-name
{
flex
:
1
;
min-width
:
0
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.upload-action-row
{
flex-shrink
:
0
;
width
:
100%
;
margin-top
:
auto
;
}
.upload-btn
{
box-sizing
:
border-box
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
100%
;
height
:
100px
;
margin
:
0
;
border-radius
:
6px
;
border
:
1px
dashed
var
(
--
color-primary-35
);
background
:
var
(
--
color-primary-2
);
color
:
#344054
;
cursor
:
pointer
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
center
;
}
.upload-btn-inner
{
box-sizing
:
border-box
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
gap
:
8px
;
min-width
:
0
;
padding
:
0
12px
;
}
.upload-btn-main
{
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
justify-content
:
center
;
gap
:
8px
;
}
.upload-btn-icon
{
flex-shrink
:
0
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
color
:
inherit
;
line-height
:
0
;
}
.upload-btn-icon
:deep
(
svg
)
{
display
:
block
;
width
:
16px
;
height
:
16px
;
}
.upload-btn-text
{
flex-shrink
:
0
;
text-align
:
center
;
}
.upload-format-hint
{
align-self
:
stretch
;
box-sizing
:
border-box
;
width
:
100%
;
margin
:
0
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
14px
;
line-height
:
22px
;
letter-spacing
:
0
;
text-align
:
left
;
color
:
var
(
--
text-primary-50-color
);
}
.upload-input
{
display
:
none
;
}
.workspace-right
{
background
:
var
(
--
bg-white-100
);
flex
:
1
;
min-width
:
0
;
padding
:
0
;
display
:
flex
;
flex-direction
:
column
;
}
.workspace-result-toolbar
{
box-sizing
:
border-box
;
height
:
68px
;
padding
:
16px
24px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
box-shadow
:
0
1px
0
0
#e8ecf2
;
flex-shrink
:
0
;
}
.workspace-result-toolbar-title
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
min-width
:
0
;
}
.workspace-result-toolbar-icon
{
flex-shrink
:
0
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
color
:
var
(
--
text-primary-80-color
,
#344054
);
line-height
:
0
;
}
.workspace-result-toolbar-icon
:deep
(
svg
)
{
display
:
block
;
width
:
24px
;
height
:
24px
;
}
.workspace-result-toolbar-heading
{
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
left
;
color
:
var
(
--
text-primary-80-color
);
}
.workspace-result-toolbar-actions
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
flex-shrink
:
0
;
}
.workspace-result-tool-btn
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
margin
:
0
;
padding
:
4px
;
border
:
none
;
border-radius
:
6px
;
background
:
transparent
;
cursor
:
pointer
;
color
:
#5f656c
;
line-height
:
0
;
}
.workspace-result-tool-btn
:hover
{
background
:
var
(
--
bg-black-2
,
#f2f4f7
);
}
.workspace-result-tool-btn-icon
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
}
.workspace-result-tool-btn-icon
:deep
(
svg
)
{
display
:
block
;
width
:
24px
;
height
:
24px
;
}
.workspace-result-body
{
box-sizing
:
border-box
;
flex
:
1
;
min-height
:
0
;
display
:
flex
;
flex-direction
:
column
;
padding
:
16px
24px
23px
24px
;
}
.result-content
{
flex
:
1
;
min-height
:
0
;
color
:
#475467
;
white-space
:
pre-wrap
;
}
.upload-record-list
{
box-sizing
:
border-box
;
background
:
transparent
;
}
.record-header
{
padding-inline
:
18px
;
box-sizing
:
border-box
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
12px
;
margin-bottom
:
12px
;
}
.record-title
{
flex
:
1
;
min-width
:
0
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
700
;
font-size
:
18px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
left
;
color
:
var
(
--
text-primary-80-color
,
#344054
);
}
.record-view-all
{
flex-shrink
:
0
;
margin
:
0
;
padding
:
0
;
border
:
none
;
background
:
transparent
;
cursor
:
pointer
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
right
;
color
:
var
(
--
color-primary-100
);
}
.record-view-all
:hover
{
opacity
:
0
.85
;
}
.record-cards
{
display
:
flex
;
flex-direction
:
row
;
flex-wrap
:
wrap
;
gap
:
16px
;
align-items
:
stretch
;
}
.record-card
{
box-sizing
:
border-box
;
width
:
246px
;
height
:
288px
;
flex
:
0
0
auto
;
padding
:
16px
;
border-radius
:
10px
;
background
:
var
(
--
bg-white-100
);
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
gap
:
0
;
}
.record-card-icon
{
flex-shrink
:
0
;
display
:
block
;
width
:
62px
;
height
:
68px
;
object-fit
:
contain
;
margin-bottom
:
30px
;
}
.record-card-name
{
box-sizing
:
border-box
;
max-width
:
184px
;
width
:
100%
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
center
;
word-break
:
break-word
;
overflow-wrap
:
anywhere
;
color
:
var
(
--
text-primary-80-color
);
}
.record-card-time
{
margin-top
:
12px
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
center
;
color
:
var
(
--
text-primary-50-color
);
}
</
style
>
src/views/intelligentTranslation/mock.js
0 → 100644
浏览文件 @
9435564d
// TODO: replace with API
export
const
uploadRecordMock
=
[
{
name
:
'季度风险监测报告.docx'
,
time
:
'2026-04-11 09:20'
},
{
name
:
'海外舆情摘编-第12期.pdf'
,
time
:
'2026-04-10 16:43'
},
{
name
:
'政策快报-智能风控.txt'
,
time
:
'2026-04-09 11:08'
}
]
/** 文档上传记录列表(含表格字段) */
export
const
uploadRecordListMock
=
[
{
id
:
'r-1'
,
name
:
'季度风险监测报告.pdf'
,
uploadTime
:
'2026-04-11 09:20'
,
sizeBytes
:
2457600
,
status
:
'已完成'
},
{
id
:
'r-2'
,
name
:
'海外舆情摘编-第12期.pdf'
,
uploadTime
:
'2026-04-10 16:43'
,
sizeBytes
:
892416
,
status
:
'翻译中'
},
{
id
:
'r-3'
,
name
:
'政策快报-智能风控.pdf'
,
uploadTime
:
'2026-04-09 11:08'
,
sizeBytes
:
524288
,
status
:
'已完成'
},
{
id
:
'r-4'
,
name
:
'合规审查备忘录.pdf'
,
uploadTime
:
'2026-04-08 14:22'
,
sizeBytes
:
1310720
,
status
:
'失败'
}
]
// TODO: replace with API
export
const
articleTreeMock
=
[
{
id
:
'all'
,
title
:
'全部章节'
},
{
id
:
'sec-1'
,
title
:
'一、风险概览'
},
{
id
:
'sec-2'
,
title
:
'二、重点行业观察'
},
{
id
:
'sec-3'
,
title
:
'三、处置建议'
}
]
// TODO: replace with API
export
const
paragraphRowsMock
=
[
{
id
:
'p-1'
,
sectionId
:
'sec-1'
,
sourceText
:
'Global liquidity tightened in Q1, increasing refinancing pressure in emerging markets.'
,
translatedText
:
'第一季度全球流动性收紧,增加了新兴市场的再融资压力。'
},
{
id
:
'p-2'
,
sectionId
:
'sec-1'
,
sourceText
:
'Cross-border capital flow volatility remained high across major regions.'
,
translatedText
:
'主要地区跨境资本流动波动仍处于高位。'
},
{
id
:
'p-3'
,
sectionId
:
'sec-2'
,
sourceText
:
'Technology supply chains showed mixed recovery signals with persistent inventory risk.'
,
translatedText
:
'科技供应链出现分化修复信号,但库存风险仍然存在。'
},
{
id
:
'p-4'
,
sectionId
:
'sec-3'
,
sourceText
:
'It is recommended to improve early-warning thresholds and optimize contingency drills.'
,
translatedText
:
'建议完善预警阈值设置并优化应急演练机制。'
}
]
/** 文档对照数据(TODO: 替换为真实接口) */
export
function
fetchDocumentCompareMock
(
_fileName
,
delayMs
=
2500
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
resolve
({
articleTree
:
articleTreeMock
,
paragraphRows
:
paragraphRowsMock
})
},
delayMs
)
})
}
src/views/intelligentTranslation/uploadRecords.vue
0 → 100644
浏览文件 @
9435564d
<
template
>
<div
class=
"upload-records-page"
>
<intelligence-left-tab-bar
/>
<div
class=
"records-main"
>
<header
class=
"page-header"
>
<div
class=
"page-header-inner"
>
<img
class=
"workspace-toolbar-icon"
src=
"@/assets/icons/tool-item-icon2.png"
alt=
""
/>
<span
class=
"workspace-toolbar-heading"
>
智能翻译
</span>
</div>
</header>
<div
class=
"records-toolbar"
>
<div
class=
"records-toolbar-left"
>
<button
type=
"button"
class=
"back-btn"
aria-label=
"返回"
@
click=
"onBack"
>
<img
class=
"back-icon"
:src=
"backIconUrl"
alt=
""
/>
</button>
<span
class=
"records-toolbar-title"
>
文档上传记录
</span>
</div>
<div
class=
"records-toolbar-right"
>
<el-input
v-model=
"searchKeyword"
class=
"records-search-input"
clearable
placeholder=
"搜索文档"
/>
<label
class=
"records-upload-btn"
for=
"records-upload-input"
>
<span
class=
"records-upload-btn-inner"
>
<span
class=
"records-upload-btn-main"
>
<span
class=
"records-upload-icon"
v-html=
"lineUploadIconSvg"
/>
<span
class=
"records-upload-text"
>
上传文档
</span>
</span>
</span>
</label>
<input
id=
"records-upload-input"
ref=
"fileInputRef"
class=
"records-upload-input"
type=
"file"
multiple
accept=
".pdf,application/pdf"
@
change=
"onFileChange"
/>
</div>
</div>
<div
class=
"records-table-section"
>
<el-table
:data=
"paginatedRows"
class=
"records-table"
style=
"width: 100%"
>
<el-table-column
prop=
"name"
label=
"文档名称"
min-width=
"220"
show-overflow-tooltip
/>
<el-table-column
prop=
"uploadTime"
label=
"上传时间"
width=
"180"
/>
<el-table-column
label=
"文档大小"
width=
"120"
>
<template
#
default=
"
{ row }">
{{
formatFileSize
(
row
.
sizeBytes
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"状态"
width=
"140"
>
<
template
#
default=
"{ row }"
>
<span
v-if=
"isCompletedStatus(row.status)"
class=
"status-cell"
>
<img
class=
"status-cell-icon"
:src=
"statusCompleteIconUrl"
alt=
""
/>
<span
class=
"status-cell-text"
>
{{
row
.
status
}}
</span>
</span>
<span
v-else
class=
"status-cell status-cell--plain"
>
{{
row
.
status
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"200"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<button
type=
"button"
class=
"cell-action cell-action--delete"
@
click=
"onDelete(row)"
>
删除
</button>
<button
type=
"button"
class=
"cell-action"
@
click=
"onExport(row)"
>
导出
</button>
<button
type=
"button"
class=
"cell-action"
@
click=
"onEdit(row)"
>
编辑
</button>
</
template
>
</el-table-column>
</el-table>
<div
class=
"records-pagination-row"
>
<el-pagination
v-model:current-page=
"currentPage"
:page-size=
"pageSize"
:total=
"filteredRows.length"
layout=
"prev, pager, next"
background
/>
</div>
</div>
</div>
</div>
</template>
<
script
setup
>
import
{
computed
,
ref
,
watch
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
{
ElMessage
}
from
'element-plus'
import
IntelligenceLeftTabBar
from
'@/components/intelligenceLeftTabBar/index.vue'
import
{
uploadRecordListMock
}
from
'./mock'
import
backIconUrl
from
'./icons/back-layer.png'
import
lineUploadIconSvg
from
'./icons/Line_Upload.svg?raw'
import
statusCompleteIconUrl
from
'./icons/status-complete.png'
function
isCompletedStatus
(
status
)
{
return
status
===
'已完成'
}
const
router
=
useRouter
()
const
fileInputRef
=
ref
(
null
)
const
searchKeyword
=
ref
(
''
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
10
)
const
records
=
ref
(
uploadRecordListMock
.
map
((
item
)
=>
({
...
item
}))
)
const
filteredRows
=
computed
(()
=>
{
const
q
=
searchKeyword
.
value
.
trim
().
toLowerCase
()
if
(
!
q
)
{
return
records
.
value
}
return
records
.
value
.
filter
((
row
)
=>
row
.
name
.
toLowerCase
().
includes
(
q
))
})
const
paginatedRows
=
computed
(()
=>
{
const
start
=
(
currentPage
.
value
-
1
)
*
pageSize
.
value
return
filteredRows
.
value
.
slice
(
start
,
start
+
pageSize
.
value
)
})
watch
(
searchKeyword
,
()
=>
{
currentPage
.
value
=
1
})
watch
(
filteredRows
,
(
rows
)
=>
{
const
maxPage
=
Math
.
max
(
1
,
Math
.
ceil
(
rows
.
length
/
pageSize
.
value
)
||
1
)
if
(
currentPage
.
value
>
maxPage
)
{
currentPage
.
value
=
maxPage
}
})
function
formatFileSize
(
bytes
)
{
if
(
bytes
==
null
||
bytes
===
0
)
{
return
'0 B'
}
const
units
=
[
'B'
,
'KB'
,
'MB'
,
'GB'
]
let
n
=
bytes
let
i
=
0
while
(
n
>=
1024
&&
i
<
units
.
length
-
1
)
{
n
/=
1024
i
+=
1
}
return
`
${
n
<
10
&&
i
>
0
?
n
.
toFixed
(
1
)
:
Math
.
round
(
n
)}
${
units
[
i
]}
`
}
const
onBack
=
()
=>
{
router
.
push
({
name
:
'intelligentTranslation'
})
}
const
onFileChange
=
(
event
)
=>
{
const
{
files
}
=
event
.
target
if
(
!
files
?.
length
)
{
return
}
const
pdfOnly
=
Array
.
from
(
files
).
filter
(
(
f
)
=>
/
\.
pdf$/i
.
test
(
f
.
name
)
||
f
.
type
===
'application/pdf'
)
const
now
=
new
Date
()
const
pad
=
(
x
)
=>
String
(
x
).
padStart
(
2
,
'0'
)
const
uploadTime
=
`
${
now
.
getFullYear
()}
-
${
pad
(
now
.
getMonth
()
+
1
)}
-
${
pad
(
now
.
getDate
())}
${
pad
(
now
.
getHours
()
)}
:
${
pad
(
now
.
getMinutes
())}
`
pdfOnly
.
forEach
((
f
)
=>
{
records
.
value
.
unshift
({
id
:
`local-
${
Date
.
now
()}
-
${
Math
.
random
().
toString
(
36
).
slice
(
2
,
8
)}
`
,
name
:
f
.
name
,
uploadTime
,
sizeBytes
:
f
.
size
,
status
:
'翻译中'
})
})
if
(
fileInputRef
.
value
)
{
fileInputRef
.
value
.
value
=
''
}
ElMessage
.
success
(
pdfOnly
.
length
?
`已添加
${
pdfOnly
.
length
}
个文档`
:
'仅支持 PDF 格式'
)
}
const
onDelete
=
(
row
)
=>
{
const
idx
=
records
.
value
.
findIndex
((
r
)
=>
r
.
id
===
row
.
id
)
if
(
idx
!==
-
1
)
{
records
.
value
.
splice
(
idx
,
1
)
ElMessage
.
success
(
'已删除'
)
}
}
const
onExport
=
(
row
)
=>
{
ElMessage
.
info
(
`导出:
${
row
.
name
}
(占位)`
)
}
const
onEdit
=
(
row
)
=>
{
router
.
push
({
name
:
'intelligentTranslationDocument'
,
query
:
{
fileName
:
row
.
name
}
})
}
</
script
>
<
style
scoped
lang=
"scss"
>
.upload-records-page
{
display
:
flex
;
width
:
100%
;
min-height
:
calc
(
100vh
-
65px
);
box-sizing
:
border-box
;
background-color
:
var
(
--
bg-white-100
);
}
.upload-records-page
:deep
(
.intelligence-left-tab-bar
)
{
background-color
:
var
(
--
bg-white-100
);
align-self
:
stretch
;
}
.records-main
{
box-sizing
:
border-box
;
flex
:
1
;
min-width
:
0
;
display
:
flex
;
flex-direction
:
column
;
padding
:
16px
;
background-color
:
var
(
--
bg-white-100
);
}
.page-header
{
box-sizing
:
border-box
;
flex-shrink
:
0
;
width
:
calc
(
100%
+
32px
);
height
:
64px
;
margin
:
-16px
-16px
0
;
padding
:
0
24px
;
display
:
flex
;
align-items
:
center
;
background
:
var
(
--
bg-white-100
);
box-shadow
:
0
0
20px
0
rgba
(
25
,
69
,
130
,
0
.1
);
}
.page-header-inner
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
min-width
:
0
;
}
.workspace-toolbar-icon
{
width
:
24px
;
height
:
24px
;
flex-shrink
:
0
;
display
:
block
;
}
.workspace-toolbar-heading
{
font-family
:
'YouSheBiaoTiHei'
,
sans-serif
;
font-size
:
30px
;
font-weight
:
400
;
line-height
:
32px
;
letter-spacing
:
0
;
color
:
var
(
--
text-primary-80-color
);
}
.records-toolbar
{
box-sizing
:
border-box
;
flex-shrink
:
0
;
margin-top
:
18px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
16px
;
flex-wrap
:
wrap
;
}
.records-toolbar-left
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
min-width
:
0
;
}
.back-btn
{
box-sizing
:
border-box
;
width
:
32px
;
height
:
32px
;
padding
:
0
;
border
:
none
;
border-radius
:
6px
;
background
:
transparent
;
cursor
:
pointer
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
}
.back-btn
:hover
{
background
:
rgba
(
25
,
69
,
130
,
0
.06
);
}
.back-btn
.back-icon
{
width
:
16px
;
height
:
16px
;
display
:
block
;
object-fit
:
contain
;
}
.records-toolbar-title
{
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-size
:
18px
;
font-weight
:
600
;
line-height
:
26px
;
color
:
var
(
--
text-primary-80-color
,
#344054
);
}
.records-toolbar-right
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
flex-shrink
:
0
;
}
.records-search-input
{
width
:
240px
;
}
/* 覆盖 App.vue 全局 el-input box-shadow: none !important */
:deep
(
.records-search-input.el-input
.el-input__wrapper
)
{
min-height
:
32px
;
height
:
auto
;
background-color
:
var
(
--
bg-white-100
)
!
important
;
box-shadow
:
0
0
0
1px
var
(
--
bg-black-10
)
inset
!
important
;
}
:deep
(
.records-search-input.el-input
.el-input__wrapper
:hover
)
{
box-shadow
:
0
0
0
1px
var
(
--
bg-black-10
)
inset
!
important
;
}
:deep
(
.records-search-input.el-input
.el-input__wrapper
:active
)
{
box-shadow
:
0
0
0
1px
var
(
--
bg-black-10
)
inset
!
important
;
}
:deep
(
.records-search-input.el-input
.el-input__wrapper.is-focus
)
{
box-shadow
:
0
0
0
1px
var
(
--
el-input-focus-border-color
,
var
(
--
color-primary-100
))
inset
!
important
;
}
:deep
(
.records-search-input.el-input
.el-input__wrapper.is-focus
:hover
)
{
box-shadow
:
0
0
0
1px
var
(
--
el-input-focus-border-color
,
var
(
--
color-primary-100
))
inset
!
important
;
}
:deep
(
.records-search-input.el-input
.el-input__wrapper.is-focus
:active
)
{
box-shadow
:
0
0
0
1px
var
(
--
el-input-focus-border-color
,
var
(
--
color-primary-100
))
inset
!
important
;
}
.records-upload-btn
{
box-sizing
:
border-box
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
114px
;
height
:
32px
;
padding
:
0
;
border-radius
:
4px
;
border
:
none
;
background
:
var
(
--
color-primary-100
);
color
:
#fff
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
text-align
:
center
;
cursor
:
pointer
;
}
.records-upload-btn-inner
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
100%
;
height
:
100%
;
}
.records-upload-btn-main
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
gap
:
8px
;
}
.records-upload-text
{
flex-shrink
:
0
;
white-space
:
nowrap
;
}
.records-upload-icon
{
flex-shrink
:
0
;
display
:
inline-flex
;
line-height
:
0
;
color
:
inherit
;
}
.records-upload-icon
:deep
(
svg
)
{
width
:
16px
;
height
:
16px
;
display
:
block
;
}
.records-upload-input
{
display
:
none
;
}
.records-table-section
{
margin-top
:
18px
;
flex
:
1
;
min-height
:
0
;
display
:
flex
;
flex-direction
:
column
;
gap
:
16px
;
background-color
:
var
(
--
bg-white-100
);
}
.records-table
{
flex
:
1
;
min-height
:
200px
;
}
.cell-action
{
margin-right
:
8px
;
padding
:
0
;
border
:
none
;
background
:
none
;
color
:
#2563eb
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
cursor
:
pointer
;
}
.cell-action
:hover
{
text-decoration
:
underline
;
}
.cell-action--delete
{
color
:
var
(
--
color-red-100
);
}
.cell-action--delete
:hover
{
color
:
var
(
--
color-red-100
);
}
.status-cell
{
display
:
inline-flex
;
align-items
:
center
;
gap
:
6px
;
}
.status-cell-icon
{
width
:
16px
;
height
:
16px
;
object-fit
:
contain
;
flex-shrink
:
0
;
display
:
block
;
}
.status-cell--plain
{
display
:
inline
;
}
.records-pagination-row
{
display
:
flex
;
justify-content
:
center
;
width
:
100%
;
}
:deep
(
.records-table.el-table
)
{
--records-table-line
:
#e8ecf2
;
--el-table-bg-color
:
var
(
--
bg-white-100
);
--el-table-tr-bg-color
:
var
(
--
bg-white-100
);
--el-table-row-hover-bg-color
:
var
(
--
bg-white-100
);
--el-table-border-color
:
var
(
--
records-table-line
);
background-color
:
var
(
--
bg-white-100
);
border
:
none
!
important
;
}
:deep
(
.records-table.el-table
::before
),
:deep
(
.records-table.el-table
::after
)
{
display
:
none
;
}
:deep
(
.records-table
.el-table__inner-wrapper
)
{
border
:
none
!
important
;
}
/* 无外围框线、无竖线;保留行间水平分割线 */
:deep
(
.records-table
.el-table__header
tr
),
:deep
(
.records-table
.el-table__body
tr
)
{
height
:
48px
;
}
:deep
(
.records-table
.el-table__cell
)
{
border-left
:
none
!
important
;
border-right
:
none
!
important
;
border-top
:
none
!
important
;
border-bottom
:
1px
solid
var
(
--
records-table-line
)
!
important
;
box-sizing
:
border-box
;
padding
:
0
12px
;
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
left
;
vertical-align
:
middle
;
}
:deep
(
.records-table
.el-table__cell
.cell
)
{
font-family
:
'Source Han Sans CN'
,
sans-serif
;
font-weight
:
400
;
font-size
:
16px
;
line-height
:
24px
;
letter-spacing
:
0
;
text-align
:
left
;
}
:deep
(
.records-table
.el-table__body
tr
:last-child
.el-table__cell
)
{
border-bottom
:
none
!
important
;
}
:deep
(
.records-table
.el-table__border-left-patch
),
:deep
(
.records-table
.el-table__border-right-patch
)
{
display
:
none
;
}
:deep
(
.records-table
.el-table__fixed-right
::before
),
:deep
(
.records-table
.el-table__fixed
::before
)
{
box-shadow
:
none
;
}
:deep
(
.records-table
.el-table__header-wrapper
.el-table__cell
)
{
background-color
:
var
(
--
bg-black-2
)
!
important
;
}
:deep
(
.records-table
.el-table__header
th
)
{
background-color
:
var
(
--
bg-black-2
)
!
important
;
color
:
#344054
;
font-weight
:
400
;
}
:deep
(
.records-table
.el-table__body
tr
)
{
background-color
:
var
(
--
bg-white-100
);
}
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论