Skip to content

Commit 5cf9789

Browse files
committed
Refactor frontend into monorepo
1 parent d78e4c0 commit 5cf9789

57 files changed

Lines changed: 765 additions & 480 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/packages/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Frontend Workspace Packages
2+
3+
- `@panwatch/api`: 统一的 HTTP 请求入口与领域 API(认证、版本、股票等)。
4+
- `@panwatch/base-ui`: 基础 UI 组件与样式工具(原 `src/components/ui/*` 已迁移)。
5+
- `@panwatch/biz-ui`: 业务组件与业务复用逻辑(原 `src/components/*` 业务组件已迁移)。
6+
7+
当前前端页面已统一从 `@panwatch/api` 发起接口请求,避免在页面中直接调用 `fetch`

frontend/packages/api/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@panwatch/api",
3+
"private": true,
4+
"version": "0.1.0",
5+
"type": "module",
6+
"exports": {
7+
".": "./src/index.ts"
8+
}
9+
}

frontend/packages/api/src/app.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { fetchAPI } from './client'
2+
3+
export interface VersionInfo {
4+
version: string
5+
}
6+
7+
export const appApi = {
8+
version: () => fetchAPI<VersionInfo>('/version'),
9+
}

frontend/packages/api/src/auth.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { fetchAPI } from './client'
2+
3+
export interface AuthStatus {
4+
initialized: boolean
5+
}
6+
7+
export interface AuthTokenPayload {
8+
token: string
9+
expires_at: string
10+
}
11+
12+
export interface LoginPayload {
13+
username: string
14+
password: string
15+
}
16+
17+
export const authApi = {
18+
status: () => fetchAPI<AuthStatus>('/auth/status'),
19+
login: (payload: LoginPayload) =>
20+
fetchAPI<AuthTokenPayload>('/auth/login', {
21+
method: 'POST',
22+
body: JSON.stringify(payload),
23+
}),
24+
setup: (payload: LoginPayload) =>
25+
fetchAPI<AuthTokenPayload>('/auth/setup', {
26+
method: 'POST',
27+
body: JSON.stringify(payload),
28+
}),
29+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const API_BASE = '/api'
2+
3+
interface ApiResponse<T> {
4+
code: number
5+
success?: boolean
6+
data: T
7+
message: string
8+
}
9+
10+
export function getToken(): string | null {
11+
return localStorage.getItem('token')
12+
}
13+
14+
export function logout() {
15+
localStorage.removeItem('token')
16+
localStorage.removeItem('token_expires')
17+
window.location.href = '/login'
18+
}
19+
20+
export function isAuthenticated(): boolean {
21+
const token = getToken()
22+
if (!token) return false
23+
24+
const expires = localStorage.getItem('token_expires')
25+
if (expires && new Date(expires) < new Date()) {
26+
logout()
27+
return false
28+
}
29+
return true
30+
}
31+
32+
export async function fetchAPI<T>(path: string, options?: RequestInit): Promise<T> {
33+
const headers: Record<string, string> = {}
34+
35+
const token = getToken()
36+
if (token) {
37+
headers['Authorization'] = `Bearer ${token}`
38+
}
39+
40+
if (options?.body) {
41+
headers['Content-Type'] = 'application/json'
42+
}
43+
44+
const res = await fetch(`${API_BASE}${path}`, {
45+
headers,
46+
...options,
47+
})
48+
49+
if (res.status === 401) {
50+
logout()
51+
throw new Error('登录已过期')
52+
}
53+
54+
const body: ApiResponse<T> = await res.json().catch(() => ({
55+
code: res.status,
56+
data: null as T,
57+
message: `HTTP ${res.status}`,
58+
}))
59+
if (body.code !== 0 || body.success === false) {
60+
throw new Error(body.message || `HTTP ${res.status}`)
61+
}
62+
return body.data
63+
}
64+
65+
export const apiClient = {
66+
request: fetchAPI,
67+
}

frontend/packages/api/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './client'
2+
export * from './types'
3+
export * from './stocks'
4+
export * from './app'
5+
export * from './auth'
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { fetchAPI } from './client'
2+
3+
export interface StockAgentInfo {
4+
agent_name: string
5+
schedule: string
6+
ai_model_id: number | null
7+
notify_channel_ids: number[]
8+
}
9+
10+
export interface StockItem {
11+
id: number
12+
symbol: string
13+
name: string
14+
market: string
15+
sort_order?: number
16+
agents?: StockAgentInfo[]
17+
}
18+
19+
export interface StockCreatePayload {
20+
symbol: string
21+
name: string
22+
market: string
23+
}
24+
25+
export const stocksApi = {
26+
list: () => fetchAPI<StockItem[]>('/stocks'),
27+
create: (payload: StockCreatePayload) =>
28+
fetchAPI<StockItem>('/stocks', {
29+
method: 'POST',
30+
body: JSON.stringify(payload),
31+
}),
32+
remove: (id: number) => fetchAPI<{ ok: boolean }>(`/stocks/${id}`, { method: 'DELETE' }),
33+
}

frontend/packages/api/src/types.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export interface AIModel {
2+
id: number
3+
name: string
4+
service_id: number
5+
model: string
6+
is_default: boolean
7+
}
8+
9+
export interface AIService {
10+
id: number
11+
name: string
12+
base_url: string
13+
api_key: string
14+
models: AIModel[]
15+
}
16+
17+
export interface NotifyChannel {
18+
id: number
19+
name: string
20+
type: string
21+
config: Record<string, string>
22+
enabled: boolean
23+
is_default: boolean
24+
}
25+
26+
export interface DataSource {
27+
id: number
28+
name: string
29+
type: string
30+
provider: string
31+
config: Record<string, unknown>
32+
enabled: boolean
33+
priority: number
34+
supports_batch: boolean
35+
test_symbols: string[]
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@panwatch/base-ui",
3+
"private": true,
4+
"version": "0.1.0",
5+
"type": "module",
6+
"exports": {
7+
".": "./src/index.ts"
8+
}
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { type ClassValue, clsx } from 'clsx'
2+
import { twMerge } from 'tailwind-merge'
3+
4+
export function cn(...inputs: ClassValue[]) {
5+
return twMerge(clsx(inputs))
6+
}

0 commit comments

Comments
 (0)