重构 API 层架构:统一 HTTP 请求、新增算力调度后端路由

- 请求拦截器统一 Auth 头不带 Bearer 前缀,新增 /suanli 前缀路由到算力调度后端
- 任务创建/轮询/历史接口迁移至 apis/display,改为 axios 调用替代原始 fetch
- 模型 API 分离为两层:apis 纯 HTTP 调用 + utils 缓存业务逻辑
- 新增历史任务列表接口 requestTaskHistory(支持 user_id/platform_code 筛选和分页)
- 响应拦截器兼容 status/code 双字段,用户信息兼容新旧 data 格式
- 移除免费次数(freeTimes)体系
- 更新 CLAUDE.md 文档
This commit is contained in:
王佑琳 2026-06-02 18:05:55 +08:00
parent 72267ab2c9
commit 5da5496492
13 changed files with 129 additions and 157 deletions

View File

@ -3,8 +3,7 @@ VITE_BASE = '/'
# 主服务
VITE_API_PREFIX = '/api'
VITE_API_BASE_URL = 'http://test.xueai.art/api' # http://huanda.xueai.art http://106.54.11.219/api 43.248.131.153:8003
VITE_API_WS_URL = 'ws://test.xueai.art/api'
VITE_API_BASE_URL = 'http://test.xueai.art/newapi/api' # http://huanda.xueai.art http://106.54.11.219/api 43.248.131.153:8003
# 支付服务
VITE_API_PAY_PREFIX = '/pay'
@ -12,7 +11,8 @@ VITE_API_PAY_TARGET = 'http://test.xueai.art' # http://43.248.133.202 test.xue
# 任务处理模块
VITE_API_WORKFLOW_UPLOAD = 'http://43.248.97.19:4000/aigc/workflow/file/upload' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow
VITE_API_WORKFLOW_WS = 'ws://43.248.97.19:4000/testworkflow'
VITE_API_TASK_PREFIX = '/suanli'
VITE_API_TASK_TARGET = 'http://test.xueai.art'
# 是否开启开发者工具
VITE_OPEN_DEVTOOLS = false

View File

@ -7,7 +7,6 @@ VITE_BUILD_MOCK = false
# 主服务
VITE_API_PREFIX = '/api'
VITE_API_BASE_URL = 'https://sxwz.xueai.art/api'
VITE_API_WS_URL = 'wss://sxwz.xueai.art/api'
# 支付服务
VITE_API_PAY_PREFIX = '/pay'
@ -15,7 +14,8 @@ VITE_API_PAY_TARGET = 'https://sxwz.xueai.art' # http://43.248.133.202
# 任务处理模块
VITE_API_WORKFLOW_UPLOAD = 'https://designtools.xueai.art/workflow/file/upload' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow
VITE_API_WORKFLOW_WS = 'wss://talkingdraw.xueai.art/testworkflow'
VITE_API_TASK_PREFIX = '/suanli'
VITE_API_TASK_TARGET = 'http://test.xueai.art'
# 模型资源
VITE_API_MODEL_RESOURCE = 'https://resources.xueai.art/AIGC'

View File

@ -16,7 +16,7 @@ Vue 3 (Composition API) + Vite 7 + Pinia + Vue Router + Element Plus + Less + pn
## 架构概览
AI 绘画/视频生成前端操作平台,通过 HTTP 接口对接后端和第三方 AI 平台RunningHub提交生成任务并轮询结果。
AI 绘画/视频生成前端操作平台,通过 HTTP 接口对接算力调度后端suanli和第三方 AI 平台RunningHub提交生成任务并轮询结果。
### 关键目录
@ -25,36 +25,70 @@ src/
├── main.js # 入口:创建 Vue 应用,安装 Pinia/Router/VueVirtualScroller
├── router/index.js # 路由定义 + token 验证守卫
├── stores/ # Pinia 状态管理
│ ├── user.js # 用户认证、信息、免费次数
│ ├── user.js # 用户认证、信息
│ └── display.js # 生成历史列表、UI 状态(滚动、画布等)
├── apis/ # HTTP API 模块auth/display通过 axios 实例调用
├── apis/ # HTTP API 层:纯请求封装,不含业务逻辑
│ ├── auth/ # 认证相关登录、token 校验、用户信息)
│ └── display/ # 任务创建/轮询/历史、平台模型、收藏/删除
├── components/ # 通用组件
│ ├── dialogBox/ # 生成参数输入面板(核心交互入口),含模型选择、比例、上传等子组件
│ ├── virtual-scroller/# 虚拟滚动列表组件自定义实现reverse 模式)
│ └── canvas/ # 图片画布编辑(圆/矩形选区,局部重绘)
├── views/ # 页面home、login
├── utils/
│ ├── request.js # Axios 实例,拦截器处理 token 和不同服务的 baseURL 路由
│ ├── websocket.js # 任务生成入口HTTP POST 创建任务 + 20s 轮询获取结果
│ ├── request.js # Axios 实例 + 拦截器:统一 Auth不带 Bearer+ 按前缀路由 baseURL
│ ├── websocket.js # 任务生成入口:组装参数 → 调用 API → 20s 轮询直至完成/失败
│ ├── modelApi.js # 模型业务层localStorage 每日缓存 + 模型名称→UUID 查找
│ ├── createTask.js # 调用平台适配器 Playload() 构造任务 body
│ ├── modelConfig.js # 从远程 JSON 加载 workflow 配置localStorage 每日缓存
│ ├── modelApi.js # 从 /suanli/v1/platforms/:code/models 获取模型列表及 UUID
│ └── auth.ts # token 存取工具localStorage
├── config/
│ ├── index.js # 平台配置入口,导出 runninghub 适配器
│ └── runninghub/ # RunningHub 平台适配器Playload() 构造和 result() 解析
```
### API 层设计原则
- `src/apis/` 中的函数只做**纯 HTTP 调用**`service.get/post/delete` 等不包含缓存、localStorage、业务判断等逻辑
- 缓存、数据转换等业务逻辑放在 `src/utils/` 中,调用 apis 层的原始函数
示例:`utils/modelApi.js` 导入 `apis/display` 的原始 `fetchPlatformModels`,在其上叠加 localStorage 每日缓存和 `getModelId` 查找逻辑。
### 核心数据流
1. 用户在 `dialogBox` 中设置参数(模型、提示词、比例、上传图片等)
2. 点击生成 → `dialogBox:handleStart()` 组装 data含 modelId、params、imgs、request
3. 调用 `websocket.js:generate(data, generateData)`
4. `generate()` 内部先通过 `createTask(data)``runninghub.Playload()` 构造 RunningHub workflow payload 作为 body
5. 调用 `modelApi.getModelId(type, modelName)``/suanli/v1/platforms/:code/models` 查找模型 UUID带 localStorage 每日缓存)
6. POST `/api/v1/tasks` 提交任务(`{ model_id, body, request }`),携带 `X-Session-Id` 用于预扣费
5. 调用 `modelApi.getModelId(type, modelName)` 查找模型 UUID带 localStorage 每日缓存)
6. 调用 `requestCreateTask(body, sessionId)` → POST `/suanli/v1/tasks``{ model_id, body, request }`,携带 `X-Session-Id` 用于预扣费)
7. 返回 task_id → `displayStore.addGeneratingItem()` 在前端列表插入"生成中"条目
8. 每 20 秒轮询 GET `/api/v1/tasks/{task_id}`completed 时调用 `updateItemToSuccess()` 更新列表
8. 每 20 秒轮询 `requestTaskStatus(taskId)` → GET `/suanli/v1/tasks/{task_id}`completed 时调用 `updateItemToSuccess()` 更新列表
### 接口速查
| 函数 | 端点 | 用途 |
|------|------|------|
| `requestCreateTask` | POST `/suanli/v1/tasks` | 创建生成任务 |
| `requestTaskStatus` | GET `/suanli/v1/tasks/:id` | 查询单个任务状态 |
| `requestTaskHistory` | GET `/suanli/v1/tasks/history` | 历史任务列表(支持 `user_id`/`platform_code`/`page`/`pageSize` |
| `fetchPlatformModels` | GET `/suanli/v1/platforms/:code/models` | 获取平台模型列表 |
| `cancelOrCollect` | POST `/collect/toggle` | 收藏/取消收藏 |
| `deleteGenerateHistory` | DELETE `/taskRecordHistory/delete` | 删除历史记录 |
### 请求拦截器路由
拦截器统一设置 `Authorization: <token>`(不带 Bearer 前缀),根据 URL 前缀切换后端:
| URL 前缀 | 环境变量 | 示例 target |
|----------|----------|-------------|
| `/suanli` | `VITE_API_TASK_TARGET` | `http://test.xueai.art` |
| `/pay` | `VITE_API_PAY_TARGET` | `http://test.xueai.art` |
| 其他 | `VITE_API_BASE_URL`(默认) | `http://test.xueai.art/newapi/api` |
### 响应格式兼容
响应拦截器同时识别 `code === 0``status === 0` 两种成功状态。用户信息接口兼容新旧两种格式:`data.userInfo` 嵌套(新)和 `data` 扁平(旧),在 store 的 `getInfo()` 中做了 `const u = res.data.userInfo || res.data` 的兼容处理。
### 平台编码映射
@ -63,28 +97,14 @@ src/
| Painting | `ai_painting_talk` |
| Video | `ai_video_talk` |
映射函数 `getPlatformCode()` 位于 `utils/modelApi.js`
### 自动导入
- `unplugin-auto-import`:自动导入 Vue/Router/Pinia API`.vue` 中无需手动 import
- `unplugin-vue-components`:自动注册 `src/components/` 下的组件和 Element Plus 组件
- Element Plus 图标通过 `unplugin-icons` 按需加载
### 环境变量
`VITE_API_BASE_URL` 定义主 API 地址(含 `/api` 后缀)。请求拦截器根据 URL 前缀自动切换后端服务:
| 前缀 | 用途 |
|------|------|
| `/pay` | 支付服务 |
| `/api`(默认) | 主服务(含任务创建 `/api/v1/tasks` |
新增的 `/suanli` 接口使用 `VITE_API_BASE_URL` 去掉 `/api` 后缀作为基础 URL。
### 路由守卫
`src/router/index.js``beforeEach` 守卫检查 token 存在性和有效性(调用 `/auth/check/token`),无效则跳转 `/login`。支持通过 URL query `?token=xxx` 传入 token。
### Authorization 头
- 通过 axios 的请求auth/display 等):拦截器自动加 `Bearer` 前缀
- 通过 fetch 的请求(任务创建/轮询/模型列表):直接传 token**不加** `Bearer` 前缀
`src/router/index.js``beforeEach` 守卫检查 token 存在性和有效性(调用 `POST /login/validateToken`),无效则跳转 `/login`。支持通过 URL query `?token=xxx` 传入 token。

3
components.d.ts vendored
View File

@ -11,7 +11,10 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
2: typeof import('./src/components/virtual-scroller/VirtualScroller copy 2.vue')['default']
3: typeof import('./src/components/virtual-scroller/VirtualScroller copy 3.vue')['default']
Canvas: typeof import('./src/components/canvas/index.vue')['default']
copy: typeof import('./src/components/virtual-scroller/VirtualScroller copy.vue')['default']
DialogBox: typeof import('./src/components/dialogBox/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']

View File

@ -38,14 +38,10 @@ export function logout() {
/** @desc 获取用户信息 */
export const getUserInfo = () => {
return service.get(`${BASE_URL}/user/info`)
}
/** @desc 获取路由信息 */
export const getUserRoute = () => {
return service.get(`${BASE_URL}/route`)
return service.get(`/sysUser/currentUser
`)
}
export const checkUsertoken = () => {
return service.get(`${BASE_URL}/check/token`)
return service.post(`/login/validateToken`)
}

View File

@ -1,9 +1,6 @@
import service from '@/utils/request'
// 获取生成历史列表
export function getGenerateHistoryList(query) {
return service.get('/taskRecordHistory', { params: query })
}
// ==================== 历史记录 APIaxios ====================
// 取消或收藏
export function cancelOrCollect(query) {
@ -15,7 +12,28 @@ export function deleteGenerateHistory(query) {
return service.delete('/taskRecordHistory/delete', { params: query })
}
// 获取免费次数
export function getFreeTimes(id) {
return service.get('/plantformBalance/userBalances', { params: { id } })
}
// ==================== 任务 APIaxios经由 /suanli 前缀路由到算力调度后端) ====================
// 创建生成任务HTTP POST /suanli/v1/tasks
export function requestCreateTask(body, sessionId) {
return service.post('/suanli/v1/tasks', body, {
headers: { 'X-Session-Id': sessionId }
})
}
// 查询任务状态 / 获取历史任务结果HTTP GET /suanli/v1/tasks/:id
export function requestTaskStatus(taskId) {
return service.get(`/suanli/v1/tasks/${taskId}`)
}
// 获取历史任务列表HTTP GET /suanli/v1/tasks/history支持平台筛选和分页
export function requestTaskHistory(params) {
return service.get('/suanli/v1/tasks/history', { params })
}
// ==================== 平台模型 API ====================
// 获取平台模型列表(原始 HTTP 调用,不含缓存逻辑)
export function fetchPlatformModels(code) {
return service.get(`/suanli/v1/platforms/${code}/models`)
}

View File

@ -161,7 +161,7 @@
<script setup>
import { generate } from '@/utils/websocket'
import { useDisplayStore, useUserStore } from '@/stores'
import { useDisplayStore } from '@/stores'
import request from '@/utils/request'
import { getModelId } from '@/utils/modelApi'
@ -742,7 +742,6 @@ const handleSend = async () => {
modelName: 'GPT',
modelId,
quantity: 1,
free: useUserStore().freeTimes,
params: [
{ name: 'prompt', data: inputText.value + '并且去除掉图1中的框' },
{ name: 'index', data: 1 },

View File

@ -83,7 +83,7 @@ import ImageUploader from './imageUploader/index.vue'
import VideoImageUploader from './videoImageUploader/index.vue'
import Time from './Time/index.vue'
import { Sender } from 'vue-element-plus-x'
import { useDisplayStore, useUserStore } from '@/stores'
import { useDisplayStore } from '@/stores'
import { generate } from '@/utils/websocket'
import { useRouter } from 'vue-router'
import { fetchModelConfig } from '@/utils/modelConfig'
@ -106,8 +106,6 @@ const props = defineProps({
const router = useRouter()
const useDisplay = useDisplayStore()
const useUser = useUserStore()
const isgerenate = ref(false)
const model = ref() //
@ -234,7 +232,6 @@ const handleStart = async () => {
modelName: model.value,
modelId: modelId || modelDisplayConfig.value?.modelId || '',
quantity: quantity.value,
free: useUser.freeTimes,
params: [
{ name: 'prompt', data: prompt.value},
{ name: 'quantity', data: quantity.value},
@ -301,16 +298,8 @@ watch(() => props.type, (newType) => {
} else {
model.value = 'flux'
}
const chargeType = newType === 'Painting' ? 1 : 4
useUser.fetchFreeTimes(chargeType)
}, { immediate: true })
//
onMounted(async () => {
const chargeType = props.type === 'Painting' ? 1 : 4
await useUser.fetchFreeTimes(chargeType)
console.log('免费次数', useUser.freeTimes)
})
</script>
<style lang="less" scoped>

View File

@ -4,7 +4,6 @@ import {
getUserInfo as getUserInfoApi,
logout as logoutApi
} from '@/apis/auth'
import { getFreeTimes } from '@/apis/display'
import { clearToken, getToken, setToken } from '@/utils/auth'
const storeSetup = () => {
@ -34,7 +33,6 @@ const storeSetup = () => {
const dept = ref({}) // 当前用户所在部门集合
const isLogin = ref(false)
const freeTimes = ref(0) // 免费次数
// 重置token
const resetToken = () => {
@ -45,49 +43,40 @@ const storeSetup = () => {
// 检查token有效性
const checkTokenValid = async () => {
const res = await checkUsertokenApi()
console.log('checkTokenValid:', res) // 打印响应数据以进行调试
if (res.code === '401' || res.success === false) {
// 检查响应数据是否存在,以避免空响应导致的错误
console.error('Token is invalid:', res.message)// 打印错误信息以进行调试
console.log('checkTokenValid:', res)
if (res.code === '401' || res.status === '401' || res.success === false) {
console.error('Token is invalid:', res.message)
return false
}
console.log('Token is valid') // 打印成功信息以进行调试
console.log('Token is valid')
return true
}
// 获取用户信息
const getInfo = async () => {
const res = await getUserInfoApi()
Object.assign(userInfo, res.data)
// userInfo.avatar = getAvatar(res.data.avatar, res.data.gender)
userInfo.username = res.data.username
if (typeof res.data.routers === 'string' && res.data.routers.trim() !== '') {
userInfo.routers = res.data.routers.split(',').map((item) => item.trim()) // 补充trim处理更完善
// 兼容新旧格式:新格式 data.userInfo 嵌套,旧格式 data 扁平
const u = res.data.userInfo || res.data
Object.assign(userInfo, u)
userInfo.id = u.userId || u.id
userInfo.username = u.userName || u.username
if (typeof u.routers === 'string' && u.routers.trim() !== '') {
userInfo.routers = u.routers.split(',').map((item) => item.trim())
} else {
userInfo.routers = []
}
if (res.data.roles && res.data.roles.length) {
roles.value = res.data.roles
permissions.value = res.data.permissions
// 角色和权限在 data 层级(非 userInfo 内)
const roleList = res.data.roles || u.roles
if (roleList?.length) {
roles.value = roleList
permissions.value = res.data.permissions || u.permissions || []
}
}
// 获取免费次数
const fetchFreeTimes = async (chargeType = 1) => {
if (userInfo.id) {
const res = await getFreeTimes(userInfo.id)
const balanceList = res.data || []
const target = balanceList.find((item) => item.chargeType === chargeType)
freeTimes.value = target?.balance || 0
return freeTimes.value
}
return 0
}
// 登录
const accountLogin = async (req) => {
const res = await accountLoginApi(req)
if (res.data == null || res.code === '500' || res.success === false) {
if (res.data == null || res.code === '500' || res.status === 500 || res.success === false) {
// eslint-disable-next-line no-undef
ElMessage({
title: '提示',
@ -134,14 +123,12 @@ const storeSetup = () => {
dept,
username,
isLogin,
freeTimes,
accountLogin,
logout,
logoutCallBack,
getInfo,
resetToken,
checkTokenValid,
fetchFreeTimes
checkTokenValid
}
}

View File

@ -1,4 +1,4 @@
import { getToken } from '@/utils/auth'
import { fetchPlatformModels as fetchModelsRaw } from '@/apis/display'
const CACHE_PREFIX = 'platform_models_'
@ -53,13 +53,7 @@ export function getPlatformCode(type) {
}
}
// suanli 接口的基础 URL不带 /api 后缀)
function getSuanliBaseUrl() {
const apiBase = import.meta.env.VITE_API_BASE_URL || ''
return apiBase.replace(/\/api$/, '')
}
// 获取平台模型列表
// 获取平台模型列表(带 localStorage 每日缓存)
export async function fetchPlatformModels(code) {
const cached = getFromCache(code)
if (cached) {
@ -68,19 +62,7 @@ export async function fetchPlatformModels(code) {
}
try {
const token = getToken()
const baseUrl = getSuanliBaseUrl()
const url = `${baseUrl}/suanli/v1/platforms/${code}/models`
console.log(`从远程获取平台模型列表: ${url}`)
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': token
}
})
const result = await response.json()
const result = await fetchModelsRaw(code)
if (result.code === 0 && result.data?.models) {
saveToCache(code, result.data.models)

View File

@ -29,21 +29,19 @@ const StatusCodeMessage = {
service.interceptors.request.use(
(config) => {
const token = getToken()
if (token) {
if (!config.headers) {
config.headers = {}
}
config.headers.Authorization = `Bearer ${token}`
if (!config.headers) {
config.headers = {}
}
// console.log(config.baseURL)
if (config.url?.startsWith(import.meta.env.VITE_API_PAY_PREFIX)) { // 支付服务路由
// 统一 Auth 头不带 Bearer 前缀
if (token) config.headers.Authorization = token
if (config.url?.startsWith(import.meta.env.VITE_API_TASK_PREFIX)) { // 算力调度后端
config.baseURL = import.meta.env.VITE_API_TASK_TARGET
} else if (config.url?.startsWith(import.meta.env.VITE_API_PAY_PREFIX)) { // 支付服务路由
config.baseURL = import.meta.env.VITE_API_PAY_TARGET
} else if (config.url?.startsWith(import.meta.env.VITE_API_AIGC_PREFIX)) { // 资源服务路由
// config.url = config.url.replace(import.meta.env.VITE_API_AIGC_PREFIX, '')
config.baseURL = import.meta.env.VITE_API_AIGC_TARGET
} else if (config.url?.startsWith(import.meta.env.VITE_API_MUSIC_WORKFLOW_PREFIX)) { // 音频生成平台工作流服务路由
config.url = config.url.replace(import.meta.env.VITE_API_MUSIC_WORKFLOW_PREFIX, '')
config.baseURL = import.meta.env.VITE_API_MUSIC_WORKFLOW_TARGET
}
return config
},
@ -57,11 +55,11 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response) => {
const { data } = response
const { success, code, msg } = data
if (success || code === 0) {
const { success, code, status, msg, message } = data
if (success || code === 0 || status === 0) {
console.log('msg: \n', msg)
return response.data
} else if (code === 401 && response.config.url !== '/auth/check/token`') { // 判断code=401时进行页面刷新但是不对检验token这个路由的请求判断防止出现死循环
} else if (code === 401 && response.config.url !== '/login/validateToken`') { // 判断code=401时进行页面刷新但是不对检验token这个路由的请求判断防止出现死循环
userError()
}
console.log('CodeMessage: \n', StatusCodeMessage[code])

View File

@ -1,9 +1,9 @@
import { ElNotification } from 'element-plus'
import { h } from 'vue'
import { useDisplayStore, useUserStore } from '@/stores'
import { getToken } from '@/utils/auth'
import { useDisplayStore } from '@/stores'
import { createTask } from '@/utils/createTask'
import { userError } from '@/utils/tokenError'
import { requestCreateTask, requestTaskStatus } from '@/apis/display'
export function getChargeType(chargeType) {
switch (chargeType) {
@ -63,8 +63,6 @@ const activePollIntervals = new Set()
export async function generate(data, generateData) {
const useDisplay = useDisplayStore()
const token = getToken()
const baseUrl = import.meta.env.VITE_API_BASE_URL
let taskId = null
let pollInterval = null
@ -94,17 +92,7 @@ export async function generate(data, generateData) {
}
// POST 创建任务
const createResponse = await fetch(`${baseUrl}/v1/tasks`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': token,
'X-Session-Id': sessionId
},
body: JSON.stringify(requestBody)
})
const createResult = await createResponse.json()
const createResult = await requestCreateTask(requestBody, sessionId)
if (createResult.code !== 0) {
ElNotification({
@ -131,14 +119,7 @@ export async function generate(data, generateData) {
// 轮询任务状态
const pollTask = async () => {
try {
const pollResponse = await fetch(`${baseUrl}/v1/tasks/${taskId}`, {
method: 'GET',
headers: {
'Authorization': token
}
})
const pollResult = await pollResponse.json()
const pollResult = await requestTaskStatus(taskId)
if (pollResult.code !== 0) return
@ -153,7 +134,6 @@ export async function generate(data, generateData) {
const urls = taskData.outputs?.images?.map(img => img.url) || []
if (urls.length > 0) {
useDisplay.updateItemToSuccess(taskId, urls)
if (useUserStore().freeTimes) await useUserStore().fetchFreeTimes()
websocketSuccess()
} else {
websocketError(4403, '未获取到生成结果')

View File

@ -73,9 +73,10 @@ import RefreshOverlay from './components/RefreshOverlay.vue'
import Select from '@/components/Select/index.vue'
import { VirtualScroller } from '@/components/virtual-scroller'
import Canvas from '@/components/canvas/index.vue'
import { getGenerateHistoryList } from '@/apis/display'
import { requestTaskHistory } from '@/apis/display'
import { useRouter } from 'vue-router'
import { getChargeType } from '@/utils/websocket'
import { getPlatformCode } from '@/utils/modelApi'
const props = defineProps({
if: {
@ -163,12 +164,11 @@ const fetchHistory = async (isLoadMore = false) => {
try {
const pageToFetch = isLoadMore ? currentPage.value + 1 : 1
const result = await getGenerateHistoryList({
userId: userStore.userInfo.id,
chargeType: chargeType.value,
page: pageToFetch,
size: 10,
sort: 'createTime,desc'
const result = await requestTaskHistory({
user_id: userStore.userInfo.id,
platform_code: getPlatformCode(props.type),
page: pageToFetch,
pageSize: 10
})
const dataList = result.data?.list || result.data || []