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)
|
// - 用户可读错误信息(errorMessage)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import {useMemo} from 'react';
|
||||||
import { useAppContext } from '@/contexts/app-context';
|
import {useAppContext} from '@/contexts/app-context';
|
||||||
import {
|
import {
|
||||||
TaskAPI,
|
TaskAPI,
|
||||||
ModelAPI,
|
ModelAPI,
|
||||||
fetchBanners,
|
fetchBanners,
|
||||||
MODEL_CATEGORY_LABELS,
|
MODEL_CATEGORY_LABELS,
|
||||||
type TaskListRequestParams,
|
type TaskListRequestParams,
|
||||||
type TaskInfoItemResponseBody,
|
type TaskInfoItemResponseBody,
|
||||||
type CreatTaskRequestBody,
|
type CreatTaskRequestBody,
|
||||||
type ModelsListResponseBody,
|
type ModelsListResponseBody,
|
||||||
type Banner,
|
type Banner,
|
||||||
type ModelCategory,
|
type ModelCategory,
|
||||||
} from '@/services/modules';
|
} from '@/services/modules';
|
||||||
import { useAsyncData, useAsyncMutation } from './use-async';
|
import {useAsyncData, useAsyncMutation} from './use-async';
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Banner
|
// Banner
|
||||||
@@ -34,13 +34,13 @@ import { useAsyncData, useAsyncMutation } from './use-async';
|
|||||||
|
|
||||||
/** 首页轮播图数据 Hook */
|
/** 首页轮播图数据 Hook */
|
||||||
export function useBannerList() {
|
export function useBannerList() {
|
||||||
const { isLoggedIn } = useAppContext();
|
const {isLoggedIn} = useAppContext();
|
||||||
|
|
||||||
return useAsyncData<Banner[]>(
|
return useAsyncData<Banner[]>(
|
||||||
fetchBanners,
|
fetchBanners,
|
||||||
[],
|
[],
|
||||||
{ enabled: isLoggedIn, label: 'banner-list' },
|
{enabled: isLoggedIn, label: 'banner-list'},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -49,53 +49,53 @@ export function useBannerList() {
|
|||||||
|
|
||||||
/** 模型列表数据 Hook(按类别分别请求后合并) */
|
/** 模型列表数据 Hook(按类别分别请求后合并) */
|
||||||
export function useModelList() {
|
export function useModelList() {
|
||||||
const { isLoggedIn } = useAppContext();
|
const {isLoggedIn} = useAppContext();
|
||||||
|
|
||||||
// 使用单个 fetcher 返回合并后的结果
|
// 使用单个 fetcher 返回合并后的结果
|
||||||
const result = useAsyncData<ModelsListResponseBody[]>(
|
const result = useAsyncData<ModelsListResponseBody[]>(
|
||||||
async () => {
|
async () => {
|
||||||
const categories: ModelCategory[] = ['txt2img', 'img2img', 'img2video', 'txt2audio'];
|
const categories: ModelCategory[] = ["image_to_image", "text_to_image", "image_to_video", "text_to_audio"];
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
categories.map((category) =>
|
categories.map((category) =>
|
||||||
ModelAPI.fetchModels({
|
ModelAPI.fetchModels({
|
||||||
provider_name: '',
|
provider_name: '',
|
||||||
category,
|
category,
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 50,
|
page_size: 50,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const allModels: ModelsListResponseBody[] = [];
|
const allModels: ModelsListResponseBody[] = [];
|
||||||
results.forEach((r) => {
|
results.forEach((r) => {
|
||||||
if (r.status === 'fulfilled') {
|
if (r.status === 'fulfilled') {
|
||||||
allModels.push(...r.value);
|
allModels.push(...r.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 按优先级降序排列
|
// 按优先级降序排列
|
||||||
allModels.sort((a, b) => b.priority - a.priority);
|
allModels.sort((a, b) => b.priority - a.priority);
|
||||||
return allModels;
|
return allModels;
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
{ enabled: isLoggedIn, label: 'model-list' },
|
{enabled: isLoggedIn, label: 'model-list'},
|
||||||
);
|
);
|
||||||
|
|
||||||
// 按类别分组
|
// 按类别分组
|
||||||
const groupedModels = useMemo(() => {
|
const groupedModels = useMemo(() => {
|
||||||
const groups = new Map<string, ModelsListResponseBody[]>();
|
const groups = new Map<string, ModelsListResponseBody[]>();
|
||||||
result.data?.forEach((model) => {
|
result.data?.forEach((model) => {
|
||||||
const cat = model.category_name || 'other';
|
const cat = model.category_name || 'other';
|
||||||
if (!groups.has(cat)) groups.set(cat, []);
|
if (!groups.has(cat)) groups.set(cat, []);
|
||||||
groups.get(cat)!.push(model);
|
groups.get(cat)!.push(model);
|
||||||
});
|
});
|
||||||
return groups;
|
return groups;
|
||||||
}, [result.data]);
|
}, [result.data]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
/** 按类别分组后的模型 */
|
/** 按类别分组后的模型 */
|
||||||
groupedModels,
|
groupedModels,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -103,10 +103,10 @@ export function useModelList() {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
export interface UseTaskListParams {
|
export interface UseTaskListParams {
|
||||||
/** 完整的 API 请求参数(由调用方根据 antd Table onChange 构建) */
|
/** 完整的 API 请求参数(由调用方根据 antd Table onChange 构建) */
|
||||||
requestParams: TaskListRequestParams;
|
requestParams: TaskListRequestParams;
|
||||||
/** 外部传入的刷新信号(任意变化触发重新请求) */
|
/** 外部传入的刷新信号(任意变化触发重新请求) */
|
||||||
refreshSignal?: number;
|
refreshSignal?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,14 +116,14 @@ export interface UseTaskListParams {
|
|||||||
* 本 Hook 仅负责数据获取、竞态控制、错误日志。
|
* 本 Hook 仅负责数据获取、竞态控制、错误日志。
|
||||||
*/
|
*/
|
||||||
export function useTaskList(params: UseTaskListParams) {
|
export function useTaskList(params: UseTaskListParams) {
|
||||||
const { isLoggedIn } = useAppContext();
|
const {isLoggedIn} = useAppContext();
|
||||||
const { requestParams, refreshSignal = 0 } = params;
|
const {requestParams, refreshSignal = 0} = params;
|
||||||
|
|
||||||
return useAsyncData(
|
return useAsyncData(
|
||||||
() => TaskAPI.getTaskList(requestParams),
|
() => TaskAPI.getTaskList(requestParams),
|
||||||
[requestParams, refreshSignal],
|
[requestParams, refreshSignal],
|
||||||
{ enabled: isLoggedIn, label: 'task-list' },
|
{enabled: isLoggedIn, label: 'task-list'},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -132,13 +132,13 @@ export function useTaskList(params: UseTaskListParams) {
|
|||||||
|
|
||||||
/** 任务详情 Hook(taskId 为 null 时不请求) */
|
/** 任务详情 Hook(taskId 为 null 时不请求) */
|
||||||
export function useTaskDetail(taskId: string | null | undefined) {
|
export function useTaskDetail(taskId: string | null | undefined) {
|
||||||
const { isLoggedIn } = useAppContext();
|
const {isLoggedIn} = useAppContext();
|
||||||
|
|
||||||
return useAsyncData<TaskInfoItemResponseBody>(
|
return useAsyncData<TaskInfoItemResponseBody>(
|
||||||
() => TaskAPI.getTaskInfo(taskId!),
|
() => TaskAPI.getTaskInfo(taskId!),
|
||||||
[taskId],
|
[taskId],
|
||||||
{ enabled: isLoggedIn && !!taskId, label: 'task-detail' },
|
{enabled: isLoggedIn && !!taskId, label: 'task-detail'},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -147,20 +147,20 @@ export function useTaskDetail(taskId: string | null | undefined) {
|
|||||||
|
|
||||||
/** 提交任务 Mutation Hook */
|
/** 提交任务 Mutation Hook */
|
||||||
export function useSubmitTask(options?: {
|
export function useSubmitTask(options?: {
|
||||||
onSuccess?: (data: TaskInfoItemResponseBody) => void;
|
onSuccess?: (data: TaskInfoItemResponseBody) => void;
|
||||||
onError?: (err: Error) => void;
|
onError?: (err: Error) => void;
|
||||||
}) {
|
}) {
|
||||||
return useAsyncMutation<TaskInfoItemResponseBody, [CreatTaskRequestBody]>(
|
return useAsyncMutation<TaskInfoItemResponseBody, [CreatTaskRequestBody]>(
|
||||||
(body) => TaskAPI.submitTask(body),
|
(body) => TaskAPI.submitTask(body),
|
||||||
{
|
{
|
||||||
label: 'submit-task',
|
label: 'submit-task',
|
||||||
onSuccess: options?.onSuccess,
|
onSuccess: options?.onSuccess,
|
||||||
onError: options?.onError,
|
onError: options?.onError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 重新导出类型和工具 ----------
|
// ---------- 重新导出类型和工具 ----------
|
||||||
|
|
||||||
export { MODEL_CATEGORY_LABELS };
|
export {MODEL_CATEGORY_LABELS};
|
||||||
export type { ModelCategory, ModelsListResponseBody, TaskInfoItemResponseBody, Banner };
|
export type {ModelCategory, ModelsListResponseBody, TaskInfoItemResponseBody, Banner};
|
||||||
|
|||||||
Reference in New Issue
Block a user