提交 9435564d authored 作者: 张伊明's avatar 张伊明

wip: save immature local changes before syncing pre

Made-with: Cursor
上级 12c1ff12
...@@ -77,6 +77,7 @@ import Menu11 from "@/assets/icons/overview/menu11.png"; ...@@ -77,6 +77,7 @@ import Menu11 from "@/assets/icons/overview/menu11.png";
import Menu12 from "@/assets/icons/overview/menu12.png"; import Menu12 from "@/assets/icons/overview/menu12.png";
import Tool1 from './tool1.svg' import Tool1 from './tool1.svg'
import Tool2 from './tool2.svg' import Tool2 from './tool2.svg'
import Tool3 from '@/assets/icons/tool-item-icon2.png'
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore";
const store = useWrittingAsstaintStore(); const store = useWrittingAsstaintStore();
...@@ -323,6 +324,11 @@ const toolList = ref([ ...@@ -323,6 +324,11 @@ const toolList = ref([
icon: Tool2, icon: Tool2,
path: "/writtingAsstaint" path: "/writtingAsstaint"
}, },
{
title: "智能翻译",
icon: Tool3,
path: "/intelligent-translation"
},
]) ])
const handleToModule = (item, index) => { const handleToModule = (item, index) => {
...@@ -590,7 +596,8 @@ onMounted(() => { ...@@ -590,7 +596,8 @@ onMounted(() => {
position: absolute; position: absolute;
z-index: 999999; z-index: 999999;
width: 273px; width: 273px;
height: 171px; height: auto;
min-height: 171px;
top: 52px; top: 52px;
left: 300px; left: 300px;
box-sizing: border-box; box-sizing: border-box;
...@@ -599,16 +606,17 @@ onMounted(() => { ...@@ -599,16 +606,17 @@ onMounted(() => {
backdrop-filter: blur(30px); backdrop-filter: blur(30px);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
padding: 8px 0 12px 32px;
.menu-content { .menu-content {
width: 562px; width: 100%;
height: 348px; height: auto;
margin-top: 8px; margin-top: 0;
margin-left: 72px; margin-left: 0;
.menu-item { .menu-item {
margin-top: 36px; margin-top: 20px;
width: 280px; width: 220px;
height: 24px; height: 24px;
display: flex; display: flex;
cursor: pointer; cursor: pointer;
......
<template> <template>
<div class="intelligenceLeftTabBar"> <div class="intelligence-left-tab-bar">
<div class="navBox" :class="{navBoxShow:isNavMenuShow}"> <div class="nav-box" :class="{ 'nav-box-show': isNavMenuShow }">
<div class="navList" v-for="(item,index) in navList " :key="index" :class="{on:navPath==item.path}" @click="onNavListClick(item.path)"> <div
<div class="icon" :style="{background:`url(${item.img})no-repeat`,backgroundSize:'24px 24px',backgroundPosition:'17px 17px'}"></div> v-for="item in navList"
<span class="text-tip-1" style="white-space: nowrap; ">{{ item.name }}</span> :key="item.path"
</div> class="nav-list"
</div> :class="{ 'on': navPath === item.path }"
@click="onNavListClick(item.path)"
<img class="show" src="@/assets/icons/muenShow.png" :style="isNavMenuShow?'transform: scaleX(1)':''" alt="" @click="()=>{isNavMenuShow=!isNavMenuShow}"> >
</div> <div class="icon" :style="getIconStyle(item.img)"></div>
<span class="text-tip-1 nav-text">{{ item.name }}</span>
</div>
</div>
<img
class="show"
:class="{ 'show-expanded': isNavMenuShow }"
src="@/assets/icons/muenShow.png"
alt=""
@click="onToggleNavMenu"
/>
</div>
</template> </template>
<script setup> <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 muen1 from '@/assets/icons/tool-item-icon1.png'
import muen2 from '@/assets/icons/tool-item-icon2.png' import muen2 from '@/assets/icons/tool-item-icon2.png'
import muen3 from '@/assets/icons/tool-item-icon3.png' import muen3 from '@/assets/icons/tool-item-icon3.png'
import muen4 from '@/assets/icons/tool-item-icon4.png' import muen4 from '@/assets/icons/tool-item-icon4.png'
import { onMounted, onUnmounted, ref, nextTick } from "vue"; const isNavMenuShow = ref(false)
import { useRoute } from "vue-router"; const navPath = ref('')
import { ElMessage } from "element-plus";
import { useWrittingAsstaintStore } from "@/stores/writtingAsstaintStore"; const navList = ref([
const isNavMenuShow=ref(false) {
const navList=ref([ img: muen1,
{ path: '/writtingAsstaint',
img:muen1, name: '智能写报'
path:'/writtingAsstaint', },
name:'智能写报' {
}, img: muen2,
{ path: '/intelligent-translation',
img:muen2, name: '智能翻译'
path:'/writtingAsstaint1', },
name:'智能翻译' {
}, img: muen3,
{ path: '/writtingAsstaint2',
img:muen3, name: '智能查询'
path:'/writtingAsstaint2', },
name:'智能查询' {
}, img: muen4,
{ path: '/writtingAsstaint3',
img:muen4, name: '智能对话'
path:'/writtingAsstaint3', }
name:'智能对话'
},
]) ])
const navPath=ref()
const route=useRoute() const route = useRoute()
if(route.path){ const router = useRouter()
navPath.value=route.path
if (route.path) {
navPath.value = route.path
} }
const onNavListClick=(path)=>{
if(path=='/writtingAsstaint'){ const getIconStyle = (img) => ({
navPath.value=path background: `url(${img}) no-repeat`,
}else{ backgroundSize: '24px 24px',
ElMessage.error('正在开发中') 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.intelligenceLeftTabBar{ .intelligence-left-tab-bar {
padding: 5px 0; padding: 5px 0;
border-right: 1px solid rgb(234, 236, 238); border-right: 1px solid rgb(234, 236, 238);
position: relative; position: relative;
.navBox{
height: 100%; .nav-box {
width: 65px; height: 100%;
transition: all 0.3s; width: 65px;
padding: 0 3px; transition: all 0.3s;
.navList{ padding: 0 3px;
display: flex;
align-items: center; .nav-list {
cursor: pointer; display: flex;
border-radius: 8px; align-items: center;
overflow: hidden; cursor: pointer;
height: 60px; border-radius: 8px;
.icon{ overflow: hidden;
width: 60px; height: 60px;
height: 60px;
flex-shrink: 0; .icon {
border-radius: 10px; width: 60px;
margin-right: 15px; height: 60px;
} flex-shrink: 0;
} border-radius: 10px;
.on{ margin-right: 15px;
background-color: var(--color-primary-10); }
color: var(--color-primary-100);
font-weight: Bold;
}
}
.navBoxShow{
width: 200px;
transition: all 0.3s;
} }
.show{
position: absolute; .on {
width: 24px; background-color: var(--color-primary-10);
height: 24px; color: var(--color-primary-100);
right: 21px; font-weight: 700;
bottom: 21px;
cursor: pointer;
transform: scaleX(-1)
} }
} }
.nav-box-show {
width: 200px;
transition: all 0.3s;
}
.nav-text {
white-space: nowrap;
}
</style> .show {
\ No newline at end of file position: absolute;
width: 24px;
height: 24px;
right: 21px;
bottom: 21px;
cursor: pointer;
transform: scaleX(-1);
}
.show-expanded {
transform: scaleX(1);
}
}
</style>
// 智能翻译
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
# 智能翻译开发任务拆分(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] 左侧导航“智能翻译”支持直接跳转并高亮
# 智能翻译页面实现计划
## 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. 代码结构清晰,便于后续样式和接口替换
<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>
<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>
<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>
<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>
<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>
<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>
<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>
差异被折叠。
// 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)
})
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论