refactor(frontend): move views into app and pages structure

Reorganize the frontend around app-level routing and page modules so the runtime and feature screens share a clearer navigation and composition layout for future work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 22:13:12 +08:00
parent a27736a832
commit b024a2bcb5
25 changed files with 2628 additions and 1656 deletions

View File

@@ -1,5 +1,12 @@
import api from './index'
export interface MessageAttachment {
id: string
name: string
type: string
size: number
}
export interface Message {
id: string
role: 'user' | 'assistant'
@@ -7,6 +14,7 @@ export interface Message {
model?: string
tokens_used?: number
created_at: string
attachments?: MessageAttachment[]
}
export interface Conversation {

View File

@@ -1,27 +1,81 @@
import axios from 'axios'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000',
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:9527',
timeout: 30000,
})
function createRequestId() {
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
return crypto.randomUUID()
}
return `req-${Date.now()}-${Math.random().toString(16).slice(2)}`
}
function isDev() {
return Boolean(import.meta.env.DEV)
}
function debugLog(stage: string, payload: Record<string, unknown>) {
if (!isDev()) return
console.debug(`[api:${stage}]`, payload)
}
// 请求拦截器:添加 Token
api.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token')
const requestId = createRequestId()
config.headers = config.headers || {}
config.headers['X-Request-ID'] = requestId
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
;(config as typeof config & { metadata?: { startedAt: number; requestId: string } }).metadata = {
startedAt: Date.now(),
requestId,
}
debugLog('request', {
requestId,
method: config.method,
url: config.url,
params: config.params,
data: config.data,
})
return config
})
// 响应拦截器:处理错误
api.interceptors.response.use(
(response) => response,
(response) => {
const metadata = (response.config as typeof response.config & { metadata?: { startedAt: number; requestId: string } }).metadata
debugLog('response', {
requestId: response.headers['x-request-id'] || metadata?.requestId,
method: response.config.method,
url: response.config.url,
status: response.status,
durationMs: metadata ? Date.now() - metadata.startedAt : undefined,
data: response.data,
})
return response
},
async (error) => {
const metadata = (error.config as typeof error.config & { metadata?: { startedAt: number; requestId: string } })?.metadata
const requestId = error.response?.headers?.['x-request-id'] || metadata?.requestId
if (error.response?.status === 401) {
localStorage.removeItem('access_token')
window.location.href = '/login'
}
debugLog('error', {
requestId,
method: error.config?.method,
url: error.config?.url,
status: error.response?.status,
durationMs: metadata ? Date.now() - metadata.startedAt : undefined,
detail: error.response?.data,
})
if (requestId) {
error.requestId = requestId
}
return Promise.reject(error)
}
)