From 1f8e27076382ac3e616c72477e2ccaa16477887e Mon Sep 17 00:00:00 2001 From: WangYu7777777 <2443522725@qq.com> Date: Sat, 6 Dec 2025 22:01:24 +0800 Subject: [PATCH 1/3] docs: add Chinese documentation translations - Add Chinese translations for getting-started.md - Add Chinese translations for fronted-integration.md - Update README.md with Chinese documentation links - Create docs/zh-CN directory structure --- README.md | 5 + docs/zh-CN/fronted-integration-zh.md | 370 ++++++++++++++++ docs/zh-CN/frontend-integration-zh.md | 609 ++++++++++++++++++++++++++ docs/zh-CN/getting-started-zh.md | 145 ++++++ docs/zh-CN/getting-started-zh.md.bak | 226 ++++++++++ 5 files changed, 1355 insertions(+) create mode 100644 docs/zh-CN/fronted-integration-zh.md create mode 100644 docs/zh-CN/frontend-integration-zh.md create mode 100644 docs/zh-CN/getting-started-zh.md create mode 100644 docs/zh-CN/getting-started-zh.md.bak diff --git a/README.md b/README.md index af8d28b..06459e7 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,11 @@ docker run --rm -i \ And then you can configure the access. +## 中文文档 + +- [入门指南](docs/zh-CN/getting-started-zh.md) +- [前端集成指南](docs/zh-CN/frontend-integration-zh.md) + ## For Developers ### Quick Start Guides diff --git a/docs/zh-CN/fronted-integration-zh.md b/docs/zh-CN/fronted-integration-zh.md new file mode 100644 index 0000000..9f33a36 --- /dev/null +++ b/docs/zh-CN/fronted-integration-zh.md @@ -0,0 +1,370 @@ +# WebLedger 前端集成指南 + +本文档介绍如何将前端应用程序与 WebLedger 后端 API 集成。 + +## API 基础 + +### 基础 URL +- **开发环境**: `http://localhost:5000` +- **生产环境**: `https://your-domain.com` + +所有 API 端点都以 `/api` 为前缀。 + +### 身份验证 + +WebLedger 使用基于 JWT(JSON Web Tokens)的身份验证。 + +#### 1. 登录获取令牌 + +```http +POST /api/auth/login +Content-Type: application/json + +{ + "username": "your-username", + "password": "your-password" +} +``` + +成功响应: +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "expiresIn": 3600, + "user": { + "id": 1, + "username": "your-username" + } +} +``` + +#### 2. 在请求中使用令牌 + +```http +GET /api/ledger/entries +Authorization: Bearer +``` + +## React 集成示例 + +### 项目设置 + +```bash +# 创建新的 React 应用(TypeScript 模板) +npx create-react-app my-ledger-app --template typescript +cd my-ledger-app + +# 安装必要的依赖 +npm install axios +npm install @mui/material @emotion/react @emotion/styled +npm install @mui/icons-material +``` + +### 创建 API 服务 + +`src/services/api.ts`: +```typescript +import axios from 'axios'; + +const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000'; + +const api = axios.create({ + baseURL: API_BASE_URL, + timeout: 10000, +}); + +// 请求拦截器:添加认证令牌 +api.interceptors.request.use( + (config) => { + const token = localStorage.getItem('auth_token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// 响应拦截器:处理错误 + return Promise.reject(error); + } +); + +// API 方法 +export const authAPI = { + login: (username: string, password: string) => + api.post('/api/auth/login', { username, password }), + }, +}; + +export const ledgerAPI = { + getEntries: (params?: { startDate?: string; endDate?: string; category?: string }) => + api.get('/api/ledger/entries', { params }), + + createEntry: (entry: { + date: string; + description: string; + amount: number; + category: string; + notes?: string; + }) => api.post('/api/ledger/entries', entry), + + getCategories: () => api.get('/api/ledger/categories'), + + getSummary: (params?: { startDate?: string; endDate?: string }) => + api.get('/api/ledger/summary', { params }), +}; + +export default api; +``` + +### 登录组件示例 + +`src/components/Login.tsx`: +```typescript +import React, { useState } from 'react'; +import { TextField, Button, Box, Alert } from '@mui/material'; +import { authAPI } from '../services/api'; + +const Login: React.FC<{ onLoginSuccess: () => void }> = ({ onLoginSuccess }) => { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const response = await authAPI.login(username, password); + localStorage.setItem('auth_token', response.data.token); + onLoginSuccess(); + } catch (err) { + setError('登录失败,请检查用户名和密码'); + } + }; + + return ( + + {error && {error}} + setUsername(e.target.value)} + margin="normal" + required + /> + setPassword(e.target.value)} + margin="normal" + required + /> + + + ); +}; + +export default Login; +``` + +### 账目列表组件 + +`src/components/LedgerList.tsx`: +```typescript +import React, { useState, useEffect } from 'react'; +import { + Table, TableBody, TableCell, TableContainer, + TableHead, TableRow, Paper, Typography +} from '@mui/material'; +import { ledgerAPI } from '../services/api'; + +interface LedgerEntry { + id: number; + date: string; + description: string; + amount: number; + category: string; + balance: number; +} + +const LedgerList: React.FC = () => { + const [entries, setEntries] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchEntries(); + }, []); + + const fetchEntries = async () => { + try { + const response = await ledgerAPI.getEntries(); + setEntries(response.data); + } catch (error) { + console.error('获取账目失败:', error); + } finally { + setLoading(false); + } + }; + + if (loading) return 加载中...; + + return ( + + + + + 日期 + 描述 + 类别 + 金额 + 余额 + + + + {entries.map((entry) => ( + + {new Date(entry.date).toLocaleDateString()} + {entry.description} + {entry.category} + = 0 ? 'green' : 'red' }}> + {entry.amount >= 0 ? '+' : ''}{entry.amount.toFixed(2)} + + {entry.balance.toFixed(2)} + + ))} + +
+ + logout: () => { +
+ ); +}; + +export default LedgerList; +``` + +## Vue.js 集成示例 + +### Vue 3 + Composition API + +```javascript +// src/composables/useLedgerApi.js +import { ref } from 'vue'; +import axios from 'axios'; + +const API_BASE_URL = 'http://localhost:5000'; + + localStorage.removeItem('auth_token'); +api.interceptors.response.use( + window.location.href = '/login'; +export function useLedgerApi() { + const api = axios.create({ + baseURL: API_BASE_URL, + }); + + // 设置请求拦截器添加令牌 + api.interceptors.request.use(config => { + const token = localStorage.getItem('auth_token'); + if (token) { + } + (response) => response, + (error) => { + config.headers.Authorization = `Bearer ${token}`; + if (error.response?.status === 401) { + } + return config; + + }); + + const entries = ref([]); + const loading = ref(false); + + const fetchEntries = async () => { + loading.value = true; + try { + const response = await api.get('/api/ledger/entries'); + entries.value = response.data; + } catch (error) { + console.error('获取数据失败:', error); + } finally { + loading.value = false; + } + }; + + return { + entries, + loading, + fetchEntries, + }; +} +``` + +## 环境变量配置 + +在 React/Vue 项目根目录创建 `.env` 文件: + +```env +REACT_APP_API_URL=http://localhost:5000 +REACT_APP_APP_NAME=WebLedger Frontend +``` + +## 安全最佳实践 + +1. **令牌存储**: 使用 `localStorage` 或 `sessionStorage` 存储 JWT 令牌 +2. **HTTPS**: 生产环境始终使用 HTTPS +3. **输入验证**: 前端和后端都要验证用户输入 +4. **错误处理**: 优雅地处理 API 错误 +5. **加载状态**: 显示加载指示器 + +## 测试 API 连接 + +使用以下命令测试 API 是否可用: + +```bash +# 测试健康检查端点 +curl http://localhost:5000/health + +# 测试 API 端点(需要认证) +curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:5000/api/ledger/entries +``` + +## 故障排除 + +### CORS 问题 +如果遇到 CORS 错误,确保后端已正确配置 CORS: + +```csharp +// 在 .NET 后端 Program.cs 中 +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowFrontend", + policy => policy.WithOrigins("http://localhost:3000") + .AllowAnyHeader() + .AllowAnyMethod()); +}); +``` + +### 连接超时 +- 检查后端服务是否正在运行 +- 验证端口号是否正确 +- 检查防火墙设置 + + +## 更多资源 + +- [React 官方文档](https://reactjs.org/docs/getting-started.html) +- [Vue.js 官方文档](https://vuejs.org/guide/introduction.html) +- [Axios 文档](https://axios-http.com/docs/intro) +- [Material-UI 组件库](https://mui.com/material-ui/getting-started/) + +如需更多帮助,请查阅 [WebLedger GitHub 仓库](https://github.com/HIT-ReFreSH/WebLedger) 或提交 Issue。 + +--- + diff --git a/docs/zh-CN/frontend-integration-zh.md b/docs/zh-CN/frontend-integration-zh.md new file mode 100644 index 0000000..378403c --- /dev/null +++ b/docs/zh-CN/frontend-integration-zh.md @@ -0,0 +1,609 @@ +# Frontend Integration Guide + +This guide will help you build a modern frontend application (React or Vue) with TypeScript to connect to the WebLedger backend. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Quick Start with React + TypeScript](#quick-start-with-react--typescript) +- [Quick Start with Vue + TypeScript](#quick-start-with-vue--typescript) +- [Authentication Setup](#authentication-setup) +- [API Client Implementation](#api-client-implementation) +- [Example Usage](#example-usage) + +## Prerequisites + +- Node.js 18+ and npm/yarn/pnpm +- WebLedger backend running (see [Getting Started](./getting-started.md)) +- Access credentials (access and secret) + +## Quick Start with React + TypeScript + +### 1. Create React App with Vite + +```bash +npm create vite@latest my-ledger-app -- --template react-ts +cd my-ledger-app +npm install +``` + +### 2. Install Dependencies + +```bash +npm install axios +# Optional: for state management +npm install zustand +# Optional: for routing +npm install react-router-dom +``` + +### 3. Create API Client + +Create `src/api/client.ts`: + +```typescript +import axios, { AxiosInstance } from 'axios'; + +const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5143'; +const ACCESS_KEY = import.meta.env.VITE_WL_ACCESS || 'root'; +const SECRET_KEY = import.meta.env.VITE_WL_SECRET || ''; + +export const apiClient: AxiosInstance = axios.create({ + baseURL: API_BASE_URL, + headers: { + 'Content-Type': 'application/json', + 'wl-access': ACCESS_KEY, + 'wl-secret': SECRET_KEY, + }, +}); + +// Request interceptor for logging +apiClient.interceptors.request.use( + (config) => { + console.log('Request:', config.method?.toUpperCase(), config.url); + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// Response interceptor for error handling +apiClient.interceptors.response.use( + (response) => response, + (error) => { + console.error('API Error:', error.response?.data || error.message); + return Promise.reject(error); + } +); +``` + +### 4. Create Type Definitions + +Create `src/types/ledger.ts`: + +```typescript +export interface Entry { + id?: string; + type: string; + category: string; + amount: number; + givenTime: string; + description?: string; +} + +export interface Category { + name: string; + parent?: string; + description?: string; +} + +export interface SelectOption { + startTime?: string; + endTime?: string; + categories?: string[]; + types?: string[]; + limit?: number; + offset?: number; +} + +export interface LedgerType { + name: string; + defaultCategory: string; + description?: string; +} +``` + +### 5. Create API Service + +Create `src/api/ledgerService.ts`: + +```typescript +import { apiClient } from './client'; +import { Entry, Category, SelectOption, LedgerType } from '../types/ledger'; + +export const ledgerService = { + // Entry operations + async createEntry(entry: Entry): Promise { + const response = await apiClient.post('/ledger/entry', entry); + return response.data; + }, + + async deleteEntry(id: string): Promise { + await apiClient.delete(`/ledger/entry?id=${id}`); + }, + + async selectEntries(option: SelectOption): Promise { + const response = await apiClient.post('/ledger/select', option); + return response.data; + }, + + // Category operations + async addOrUpdateCategory(category: Category): Promise { + await apiClient.put('/ledger/category', category); + }, + + async deleteCategory(categoryName: string): Promise { + await apiClient.delete(`/ledger/category?category=${categoryName}`); + }, + + async listCategories(): Promise { + const response = await apiClient.get('/ledger/categories'); + return response.data; + }, + + // Type operations + async addOrUpdateType(type: LedgerType): Promise { + await apiClient.put('/ledger/type', type); + }, + + async listTypes(): Promise { + const response = await apiClient.get('/ledger/types'); + return response.data; + }, +}; +``` + +### 6. Environment Variables + +Create `.env.local`: + +```env +VITE_API_URL=http://localhost:5143 +VITE_WL_ACCESS=root +VITE_WL_SECRET=your-secret-key-here +``` + +### 7. Example Component + +Create `src/components/EntryForm.tsx`: + +```typescript +import React, { useState } from 'react'; +import { ledgerService } from '../api/ledgerService'; +import { Entry } from '../types/ledger'; + +export const EntryForm: React.FC = () => { + const [formData, setFormData] = useState>({ + type: '', + category: '', + amount: 0, + givenTime: new Date().toISOString(), + description: '', + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const id = await ledgerService.createEntry(formData as Entry); + console.log('Created entry:', id); + alert('Entry created successfully!'); + // Reset form + setFormData({ + type: '', + category: '', + amount: 0, + givenTime: new Date().toISOString(), + description: '', + }); + } catch (error) { + console.error('Failed to create entry:', error); + alert('Failed to create entry'); + } + }; + + return ( +
+
+ + setFormData({ ...formData, type: e.target.value })} + required + /> +
+
+ + setFormData({ ...formData, category: e.target.value })} + required + /> +
+
+ + setFormData({ ...formData, amount: parseFloat(e.target.value) })} + required + /> +
+
+ + setFormData({ ...formData, givenTime: new Date(e.target.value).toISOString() })} + required + /> +
+
+ +