feat: 实现基础设施层
axios 请求封装及七个业务模块 API,Pinia 状态管理(auth/system/models/tools),Mock 适配器与数据,以及流式对话、轮询、倒计时组合式函数。
This commit is contained in:
45
frontend/src/stores/auth.ts
Normal file
45
frontend/src/stores/auth.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { login as loginApi } from '@/api/modules/system'
|
||||
import { SESSION_TIMEOUT } from '@/constants'
|
||||
|
||||
/**
|
||||
* 认证 store
|
||||
* 沿用原项目 localStorage 的登录时间戳 + 5 分钟会话超时机制
|
||||
*/
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const username = ref<string>(localStorage.getItem('username') || '')
|
||||
const loginTime = ref<number>(parseInt(localStorage.getItem('loginTime') || '0', 10) || 0)
|
||||
|
||||
const isLoggedIn = computed(() => {
|
||||
if (!loginTime.value) return false
|
||||
return Date.now() - loginTime.value < SESSION_TIMEOUT
|
||||
})
|
||||
|
||||
/** 登录 */
|
||||
async function login(user: string, password: string) {
|
||||
await loginApi(user, password)
|
||||
username.value = user
|
||||
loginTime.value = Date.now()
|
||||
localStorage.setItem('username', user)
|
||||
localStorage.setItem('loginTime', String(loginTime.value))
|
||||
}
|
||||
|
||||
/** 续期会话(活跃时刷新) */
|
||||
function refresh() {
|
||||
if (isLoggedIn.value) {
|
||||
loginTime.value = Date.now()
|
||||
localStorage.setItem('loginTime', String(loginTime.value))
|
||||
}
|
||||
}
|
||||
|
||||
/** 退出 */
|
||||
function logout() {
|
||||
username.value = ''
|
||||
loginTime.value = 0
|
||||
localStorage.removeItem('username')
|
||||
localStorage.removeItem('loginTime')
|
||||
}
|
||||
|
||||
return { username, loginTime, isLoggedIn, login, refresh, logout }
|
||||
})
|
||||
34
frontend/src/stores/models.ts
Normal file
34
frontend/src/stores/models.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { getModelList } from '@/api/modules/model'
|
||||
import type { ModelItem } from '@/types'
|
||||
|
||||
/**
|
||||
* 模型列表缓存 store
|
||||
* 列表页根据 base_model id 渲染模型名时使用
|
||||
*/
|
||||
export const useModelsStore = defineStore('models', () => {
|
||||
const list = ref<ModelItem[]>([])
|
||||
const loaded = ref(false)
|
||||
|
||||
async function load(force = false) {
|
||||
if (loaded.value && !force) return
|
||||
try {
|
||||
list.value = (await getModelList()) || []
|
||||
loaded.value = true
|
||||
} catch {
|
||||
list.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据 id 获取模型名 */
|
||||
function getModelName(modelId: string | number): string {
|
||||
if (!modelId) return '-'
|
||||
const model = list.value.find(
|
||||
(m) => m.id == modelId || m.id === String(modelId) || m.id === Number(modelId),
|
||||
)
|
||||
return model ? model.name : `模型${modelId}`
|
||||
}
|
||||
|
||||
return { list, loaded, load, getModelName }
|
||||
})
|
||||
36
frontend/src/stores/system.ts
Normal file
36
frontend/src/stores/system.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { getHealth } from '@/api/modules/system'
|
||||
import type { HealthMetrics } from '@/types'
|
||||
|
||||
/**
|
||||
* 顶部栏系统监控 store
|
||||
* 30s 轮询 CPU/内存/磁盘使用率
|
||||
*/
|
||||
export const useSystemStore = defineStore('system', () => {
|
||||
const metrics = ref<HealthMetrics>({})
|
||||
let timer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
async function fetchMetrics() {
|
||||
try {
|
||||
metrics.value = await getHealth()
|
||||
} catch {
|
||||
// 静默失败,顶部栏非关键
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (timer) return
|
||||
fetchMetrics()
|
||||
timer = setInterval(fetchMetrics, 30000)
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
|
||||
return { metrics, fetchMetrics, start, stop }
|
||||
})
|
||||
48
frontend/src/stores/tools.ts
Normal file
48
frontend/src/stores/tools.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { CustomTool } from '@/types'
|
||||
|
||||
/**
|
||||
* 自定义工具 store(localStorage 持久化)
|
||||
* 原 web 项目 customTools 仅存本地,无后端
|
||||
*/
|
||||
export const useToolsStore = defineStore('tools', () => {
|
||||
const STORAGE_KEY = 'customTools'
|
||||
const tools = ref<CustomTool[]>(loadFromStorage())
|
||||
|
||||
function loadFromStorage(): CustomTool[] {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 持久化
|
||||
watch(
|
||||
tools,
|
||||
(val) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(val))
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
function addTool(tool: CustomTool) {
|
||||
tools.value.push(tool)
|
||||
}
|
||||
|
||||
function updateTool(id: string, data: Partial<CustomTool>) {
|
||||
const idx = tools.value.findIndex((t) => t.id === id)
|
||||
if (idx !== -1) tools.value[idx] = { ...tools.value[idx], ...data }
|
||||
}
|
||||
|
||||
function removeTool(id: string) {
|
||||
tools.value = tools.value.filter((t) => t.id !== id)
|
||||
}
|
||||
|
||||
function getTool(id: string): CustomTool | undefined {
|
||||
return tools.value.find((t) => t.id === id)
|
||||
}
|
||||
|
||||
return { tools, addTool, updateTool, removeTool, getTool }
|
||||
})
|
||||
Reference in New Issue
Block a user