feat: 忘记密码/修改密码 + 设置页结构重组 + barrel cleanup + LoginPage 版本统一
新增: - ForgotPasswordModal — 手机号+短信验证码重置密码 (AuthAPI.resetPassword) - ChangePasswordModal — 当前密码验证后修改密码 (AuthAPI.changePassword) - useSmsCooldown Hook — 通用短信发送冷却逻辑,供注册/忘记密码复用 - PasswordSetting — 设置页账号安全区块,双选项:修改密码 / 短信验证码重置 修改: - LoginForm \"忘记密码?\" 链接接入 ForgotPasswordModal(替换 TODO) - LoginPage 移除 edition 业务版本 UI 区分,登录/注册 Tab 全局统一 - 设置页 Section 组件移入 blocks/ 子目录(git mv 保留历史) - auth.ts sendSms URL 修正: /sms/send → /send-sms 清理: - settings/index.ts 移除 4 个未使用 re-export,精简为仅 SettingsPage - navbar/index.ts 移除未使用 re-export,精简为仅 NavBar 文档: - WORKLOG.md 新增 2026-06-08 工作日志 - ARCHITECTURE.md / PROJECT.md 更新设置页目录结构与新组件 - TODO.md 忘记密码流程标记已完成 + 短信人机验证列入后期优化
This commit is contained in:
@@ -12,21 +12,21 @@
|
||||
// - 用户可读错误信息(errorMessage)
|
||||
// ============================================================
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import {useMemo} from 'react';
|
||||
import {useAppContext} from '@/contexts/app-context';
|
||||
import {
|
||||
TaskAPI,
|
||||
ModelAPI,
|
||||
fetchBanners,
|
||||
MODEL_CATEGORY_LABELS,
|
||||
type TaskListRequestParams,
|
||||
type TaskInfoItemResponseBody,
|
||||
type CreatTaskRequestBody,
|
||||
type ModelsListResponseBody,
|
||||
type Banner,
|
||||
type ModelCategory,
|
||||
TaskAPI,
|
||||
ModelAPI,
|
||||
fetchBanners,
|
||||
MODEL_CATEGORY_LABELS,
|
||||
type TaskListRequestParams,
|
||||
type TaskInfoItemResponseBody,
|
||||
type CreatTaskRequestBody,
|
||||
type ModelsListResponseBody,
|
||||
type Banner,
|
||||
type ModelCategory,
|
||||
} from '@/services/modules';
|
||||
import { useAsyncData, useAsyncMutation } from './use-async';
|
||||
import {useAsyncData, useAsyncMutation} from './use-async';
|
||||
|
||||
// ============================================================
|
||||
// Banner
|
||||
@@ -34,13 +34,13 @@ import { useAsyncData, useAsyncMutation } from './use-async';
|
||||
|
||||
/** 首页轮播图数据 Hook */
|
||||
export function useBannerList() {
|
||||
const { isLoggedIn } = useAppContext();
|
||||
const {isLoggedIn} = useAppContext();
|
||||
|
||||
return useAsyncData<Banner[]>(
|
||||
fetchBanners,
|
||||
[],
|
||||
{ enabled: isLoggedIn, label: 'banner-list' },
|
||||
);
|
||||
return useAsyncData<Banner[]>(
|
||||
fetchBanners,
|
||||
[],
|
||||
{enabled: isLoggedIn, label: 'banner-list'},
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -49,53 +49,53 @@ export function useBannerList() {
|
||||
|
||||
/** 模型列表数据 Hook(按类别分别请求后合并) */
|
||||
export function useModelList() {
|
||||
const { isLoggedIn } = useAppContext();
|
||||
const {isLoggedIn} = useAppContext();
|
||||
|
||||
// 使用单个 fetcher 返回合并后的结果
|
||||
const result = useAsyncData<ModelsListResponseBody[]>(
|
||||
async () => {
|
||||
const categories: ModelCategory[] = ['txt2img', 'img2img', 'img2video', 'txt2audio'];
|
||||
const results = await Promise.allSettled(
|
||||
categories.map((category) =>
|
||||
ModelAPI.fetchModels({
|
||||
provider_name: '',
|
||||
category,
|
||||
page: 1,
|
||||
page_size: 50,
|
||||
}),
|
||||
),
|
||||
);
|
||||
// 使用单个 fetcher 返回合并后的结果
|
||||
const result = useAsyncData<ModelsListResponseBody[]>(
|
||||
async () => {
|
||||
const categories: ModelCategory[] = ["image_to_image", "text_to_image", "image_to_video", "text_to_audio"];
|
||||
const results = await Promise.allSettled(
|
||||
categories.map((category) =>
|
||||
ModelAPI.fetchModels({
|
||||
provider_name: '',
|
||||
category,
|
||||
page: 1,
|
||||
page_size: 50,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const allModels: ModelsListResponseBody[] = [];
|
||||
results.forEach((r) => {
|
||||
if (r.status === 'fulfilled') {
|
||||
allModels.push(...r.value);
|
||||
}
|
||||
});
|
||||
// 按优先级降序排列
|
||||
allModels.sort((a, b) => b.priority - a.priority);
|
||||
return allModels;
|
||||
},
|
||||
[],
|
||||
{ enabled: isLoggedIn, label: 'model-list' },
|
||||
);
|
||||
const allModels: ModelsListResponseBody[] = [];
|
||||
results.forEach((r) => {
|
||||
if (r.status === 'fulfilled') {
|
||||
allModels.push(...r.value);
|
||||
}
|
||||
});
|
||||
// 按优先级降序排列
|
||||
allModels.sort((a, b) => b.priority - a.priority);
|
||||
return allModels;
|
||||
},
|
||||
[],
|
||||
{enabled: isLoggedIn, label: 'model-list'},
|
||||
);
|
||||
|
||||
// 按类别分组
|
||||
const groupedModels = useMemo(() => {
|
||||
const groups = new Map<string, ModelsListResponseBody[]>();
|
||||
result.data?.forEach((model) => {
|
||||
const cat = model.category_name || 'other';
|
||||
if (!groups.has(cat)) groups.set(cat, []);
|
||||
groups.get(cat)!.push(model);
|
||||
});
|
||||
return groups;
|
||||
}, [result.data]);
|
||||
// 按类别分组
|
||||
const groupedModels = useMemo(() => {
|
||||
const groups = new Map<string, ModelsListResponseBody[]>();
|
||||
result.data?.forEach((model) => {
|
||||
const cat = model.category_name || 'other';
|
||||
if (!groups.has(cat)) groups.set(cat, []);
|
||||
groups.get(cat)!.push(model);
|
||||
});
|
||||
return groups;
|
||||
}, [result.data]);
|
||||
|
||||
return {
|
||||
...result,
|
||||
/** 按类别分组后的模型 */
|
||||
groupedModels,
|
||||
};
|
||||
return {
|
||||
...result,
|
||||
/** 按类别分组后的模型 */
|
||||
groupedModels,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -103,10 +103,10 @@ export function useModelList() {
|
||||
// ============================================================
|
||||
|
||||
export interface UseTaskListParams {
|
||||
/** 完整的 API 请求参数(由调用方根据 antd Table onChange 构建) */
|
||||
requestParams: TaskListRequestParams;
|
||||
/** 外部传入的刷新信号(任意变化触发重新请求) */
|
||||
refreshSignal?: number;
|
||||
/** 完整的 API 请求参数(由调用方根据 antd Table onChange 构建) */
|
||||
requestParams: TaskListRequestParams;
|
||||
/** 外部传入的刷新信号(任意变化触发重新请求) */
|
||||
refreshSignal?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,14 +116,14 @@ export interface UseTaskListParams {
|
||||
* 本 Hook 仅负责数据获取、竞态控制、错误日志。
|
||||
*/
|
||||
export function useTaskList(params: UseTaskListParams) {
|
||||
const { isLoggedIn } = useAppContext();
|
||||
const { requestParams, refreshSignal = 0 } = params;
|
||||
const {isLoggedIn} = useAppContext();
|
||||
const {requestParams, refreshSignal = 0} = params;
|
||||
|
||||
return useAsyncData(
|
||||
() => TaskAPI.getTaskList(requestParams),
|
||||
[requestParams, refreshSignal],
|
||||
{ enabled: isLoggedIn, label: 'task-list' },
|
||||
);
|
||||
return useAsyncData(
|
||||
() => TaskAPI.getTaskList(requestParams),
|
||||
[requestParams, refreshSignal],
|
||||
{enabled: isLoggedIn, label: 'task-list'},
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -132,13 +132,13 @@ export function useTaskList(params: UseTaskListParams) {
|
||||
|
||||
/** 任务详情 Hook(taskId 为 null 时不请求) */
|
||||
export function useTaskDetail(taskId: string | null | undefined) {
|
||||
const { isLoggedIn } = useAppContext();
|
||||
const {isLoggedIn} = useAppContext();
|
||||
|
||||
return useAsyncData<TaskInfoItemResponseBody>(
|
||||
() => TaskAPI.getTaskInfo(taskId!),
|
||||
[taskId],
|
||||
{ enabled: isLoggedIn && !!taskId, label: 'task-detail' },
|
||||
);
|
||||
return useAsyncData<TaskInfoItemResponseBody>(
|
||||
() => TaskAPI.getTaskInfo(taskId!),
|
||||
[taskId],
|
||||
{enabled: isLoggedIn && !!taskId, label: 'task-detail'},
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -147,20 +147,20 @@ export function useTaskDetail(taskId: string | null | undefined) {
|
||||
|
||||
/** 提交任务 Mutation Hook */
|
||||
export function useSubmitTask(options?: {
|
||||
onSuccess?: (data: TaskInfoItemResponseBody) => void;
|
||||
onError?: (err: Error) => void;
|
||||
onSuccess?: (data: TaskInfoItemResponseBody) => void;
|
||||
onError?: (err: Error) => void;
|
||||
}) {
|
||||
return useAsyncMutation<TaskInfoItemResponseBody, [CreatTaskRequestBody]>(
|
||||
(body) => TaskAPI.submitTask(body),
|
||||
{
|
||||
label: 'submit-task',
|
||||
onSuccess: options?.onSuccess,
|
||||
onError: options?.onError,
|
||||
},
|
||||
);
|
||||
return useAsyncMutation<TaskInfoItemResponseBody, [CreatTaskRequestBody]>(
|
||||
(body) => TaskAPI.submitTask(body),
|
||||
{
|
||||
label: 'submit-task',
|
||||
onSuccess: options?.onSuccess,
|
||||
onError: options?.onError,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- 重新导出类型和工具 ----------
|
||||
|
||||
export { MODEL_CATEGORY_LABELS };
|
||||
export type { ModelCategory, ModelsListResponseBody, TaskInfoItemResponseBody, Banner };
|
||||
export {MODEL_CATEGORY_LABELS};
|
||||
export type {ModelCategory, ModelsListResponseBody, TaskInfoItemResponseBody, Banner};
|
||||
|
||||
Reference in New Issue
Block a user