Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,25 @@ uv run uvicorn app.main:app --port 8000

可选配置:复制 `backend/.env.example` 为 `backend/.env`,可修改数据库路径(.env 不提交仓库)。

## 前端启动(待前端负责人补充
## 前端启动(约 2 分钟

占位:Vue3 + Vite + Naive UI。启动命令与步骤由前端负责人补充到本节。
前置:安装 [bun](https://bun.sh/)(JS/TS 包与环境管理器,自带运行时,无需单独安装
Node.js)。Windows 可执行 `powershell -c "irm bun.sh/install.ps1 | iex"`,或按官网说明安装。

```bash
cd frontend
bun install # 首次需联网安装依赖
bun dev # 启动开发服务器
```

> 注意:不要直接双击 `frontend/index.html` 打开(会白屏),必须通过 `bun dev`
> 启动后访问 http://localhost:5173 。

启动后验证:

- 打开 http://127.0.0.1:5173 ,看到「文档翻译智能体」首页(产品介绍 + PDF 上传入口占位)。
- 先启动后端时,首页底部显示「后端状态:已连接」及任务数量;未启动则显示未连接。
- 前端 `/api` 由 Vite 代理到 http://127.0.0.1:8000(后端),无需额外 CORS 配置。

## 阶段 0 验收方式

Expand Down
24 changes: 24 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
13 changes: 13 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frontend(docwise-web 前端)

Vue 3 + Vite + Naive UI。技术约定:优先使用 bun。

## 启动

```bash
bun install
bun dev
```

开发服务器默认运行在 http://127.0.0.1:5173 ,`/api` 会代理到
http://127.0.0.1:8000(后端)。请先启动 backend 再访问首页。
178 changes: 178 additions & 0 deletions frontend/bun.lock

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>docwise-web · 文档翻译智能体</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
19 changes: 19 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"naive-ui": "^2.44.1",
"vue": "^3.5.40"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.8",
"vite": "^8.2.0"
}
}
7 changes: 7 additions & 0 deletions frontend/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/screenshots/homepage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<script setup>
import { onMounted, ref } from 'vue'
import {
zhCN,
dateZhCN,
NConfigProvider,
NLayout,
NLayoutHeader,
NLayoutContent,
NLayoutFooter,
NGrid,
NGi,
NCard,
NTag,
NUpload,
NText,
} from 'naive-ui'

const backendStatus = ref('checking')
const taskCount = ref(null)

const statusMeta = {
checking: { type: 'default', text: '检测中…' },
ok: { type: 'success', text: '已连接' },
down: { type: 'error', text: '未连接(请先启动后端)' },
}

onMounted(async () => {
try {
const res = await fetch('/api/health')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
await res.json()
backendStatus.value = 'ok'
try {
const tasksRes = await fetch('/api/tasks')
if (tasksRes.ok) {
const tasks = await tasksRes.json()
taskCount.value = tasks.length
}
} catch {
// 任务列表接口暂不可用时忽略
}
} catch {
backendStatus.value = 'down'
}
})

function handlePlaceholderUpload() {
// 阶段 0 骨架:PDF 上传接口由后端负责人在后续阶段提供
}
</script>

<template>
<n-config-provider :locale="zhCN" :date-locale="dateZhCN">
<n-layout class="page-layout">
<n-layout-header bordered class="page-header">
<div class="page-header-inner">
<span class="brand">docwise-web</span>
<span class="brand-sub">文档翻译智能体</span>
</div>
</n-layout-header>

<n-layout-content content-style="padding: 0">
<main class="page-main">
<section class="page-hero">
<h1>文档翻译智能体</h1>
<p class="tagline">Agent 是大脑,程序是手脚</p>
<p class="description">
上传英文文献 PDF,获得「像原文档的中文版」:
翻译、结构保留排版、质检一条龙。
</p>
<div class="tech-tags">
<n-tag round>Vue 3</n-tag>
<n-tag round type="info">Vite</n-tag>
<n-tag round type="success">Naive UI</n-tag>
</div>
</section>

<section class="page-section">
<n-card title="上传英文文献 PDF" class="upload-card">
<n-upload
accept="application/pdf"
:max="1"
:default-upload="false"
:custom-request="handlePlaceholderUpload"
>
<n-upload-dragger>
<div class="upload-hint">点击或拖拽 PDF 到此处</div>
<div class="upload-sub">阶段 0 骨架演示 · 翻译功能即将上线</div>
</n-upload-dragger>
</n-upload>
</n-card>
</section>

<section class="page-section">
<n-grid :cols="3" :x-gap="16" responsive="screen" item-responsive>
<n-gi span="3 s:1 m:1" v-for="feature in [
{ title: '翻译', desc: '英文文献自动翻译为中文,忠实原文表达。' },
{ title: '结构保留排版', desc: '图表、标题、公式布局尽量贴近原文档。' },
{ title: '质检一条龙', desc: '翻译完成后提供质量检查与修订建议。' },
]" :key="feature.title">
<n-card :title="feature.title" class="feature-card">
<n-text depth="3">{{ feature.desc }}</n-text>
</n-card>
</n-gi>
</n-grid>
</section>

<section class="page-section status-row">
<span>后端状态:</span>
<n-tag :type="statusMeta[backendStatus].type" :bordered="false">
{{ statusMeta[backendStatus].text }}
</n-tag>
<n-text v-if="backendStatus === 'ok' && taskCount !== null" depth="3" class="task-count">
任务列表共 {{ taskCount }} 条
</n-text>
</section>
</main>
</n-layout-content>

<n-layout-footer bordered class="page-footer">
© 2026 docwise-web · 阶段 0 骨架 · Vue3 + Vite + Naive UI
</n-layout-footer>
</n-layout>
</n-config-provider>
</template>
5 changes: 5 additions & 0 deletions frontend/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')
135 changes: 135 additions & 0 deletions frontend/src/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
:root {
font-family:
system-ui,
'Segoe UI',
Roboto,
'PingFang SC',
'Microsoft YaHei',
sans-serif;
color: #213547;
background-color: #f5f7fa;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

* {
box-sizing: border-box;
}

body {
margin: 0;
min-width: 320px;
}

.page-layout {
min-height: 100vh;
}

.page-header-inner {
max-width: 1080px;
margin: 0 auto;
padding: 14px 24px;
display: flex;
align-items: baseline;
gap: 12px;
}

.brand {
font-size: 20px;
font-weight: 700;
}

.brand-sub {
color: #667085;
font-size: 14px;
}

.page-main {
max-width: 1080px;
margin: 0 auto;
padding: 16px 24px 48px;
}

.page-hero {
text-align: center;
padding: 56px 0 40px;
}

.page-hero h1 {
margin: 0 0 8px;
font-size: 44px;
letter-spacing: -0.5px;
}

.tagline {
font-size: 18px;
color: #4f6ef7;
font-weight: 600;
margin: 0 0 16px;
}

.description {
max-width: 640px;
margin: 0 auto 24px;
color: #475467;
font-size: 16px;
line-height: 1.7;
}

.tech-tags {
display: flex;
justify-content: center;
gap: 8px;
}

.page-section {
margin-top: 24px;
}

.upload-card .n-upload-dragger {
padding: 40px 24px;
text-align: center;
}

.upload-hint {
font-size: 16px;
font-weight: 600;
}

.upload-sub {
margin-top: 8px;
color: #98a2b3;
font-size: 13px;
}

.feature-card {
height: 100%;
}

.status-row {
display: flex;
align-items: center;
gap: 8px;
margin-top: 32px;
padding: 16px;
background: #fff;
border: 1px solid #e4e7ec;
border-radius: 8px;
}

.task-count {
margin-left: 4px;
}

.page-footer {
text-align: center;
color: #667085;
font-size: 13px;
padding: 16px;
}

@media (max-width: 768px) {
.page-hero h1 {
font-size: 32px;
}
}
16 changes: 16 additions & 0 deletions frontend/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
},
})