diff --git a/src/X1.WebUI/src/constants/api.ts b/src/X1.WebUI/src/constants/api.ts index 8a93493..d4affef 100644 --- a/src/X1.WebUI/src/constants/api.ts +++ b/src/X1.WebUI/src/constants/api.ts @@ -31,6 +31,7 @@ export const API_PATHS = { // 测试用例相关 TEST_CASES: '/test-cases', + TEST_CASE_SETS: '/test-case-sets', TEST_STEPS: '/test-steps', // 分析相关 diff --git a/src/X1.WebUI/src/constants/menuConfig.ts b/src/X1.WebUI/src/constants/menuConfig.ts index f9a80ca..ce355b0 100644 --- a/src/X1.WebUI/src/constants/menuConfig.ts +++ b/src/X1.WebUI/src/constants/menuConfig.ts @@ -19,6 +19,9 @@ export type Permission = | 'testcases.view' | 'testcases.manage' | 'testcases.create' + | 'testcasesets.view' + | 'testcasesets.manage' + | 'testcasesets.create' | 'teststeps.view' | 'teststeps.manage' | 'teststeps.create' @@ -97,6 +100,11 @@ export const menuItems: MenuItem[] = [ href: '/dashboard/testcases/list', permission: 'testcases.view', }, + { + title: '用例集', + href: '/dashboard/testcases/sets', + permission: 'testcasesets.view', + }, { title: '创建用例', href: '/dashboard/testcases/create', diff --git a/src/X1.WebUI/src/contexts/AuthContext.tsx b/src/X1.WebUI/src/contexts/AuthContext.tsx index 89a085b..15ae491 100644 --- a/src/X1.WebUI/src/contexts/AuthContext.tsx +++ b/src/X1.WebUI/src/contexts/AuthContext.tsx @@ -1,7 +1,7 @@ import { createContext, useContext, useReducer, ReactNode, useMemo, useEffect, useState } from 'react'; -import { AuthState, AuthContextType, LoginRequest, User, RegisterRequest } from '@/types/auth'; +import { AuthState, AuthContextType, LoginRequest, User as AuthUser, RegisterRequest } from '@/types/auth'; import { useSetRecoilState, useRecoilState } from 'recoil'; -import { userState } from '@/stores/userStore'; +import { userState, User as StoreUser } from '@/stores/userStore'; import { authService } from '@/services/authService'; import { useAuthSync } from '@/hooks/useAuthSync'; import { useAuthInit } from '@/hooks/useAuthInit'; @@ -24,14 +24,14 @@ const initialState: AuthState = { type AuthAction = | { type: 'LOGIN_START' } - | { type: 'LOGIN_SUCCESS'; payload: { user: User; accessToken: string; refreshToken: string; rememberMe: boolean } } + | { type: 'LOGIN_SUCCESS'; payload: { user: AuthUser; accessToken: string; refreshToken: string; rememberMe: boolean } } | { type: 'LOGIN_FAILURE'; payload: { error: string } } | { type: 'REGISTER_START' } | { type: 'REGISTER_SUCCESS' } | { type: 'REGISTER_FAILURE'; payload: { error: string } } | { type: 'LOGOUT' } | { type: 'CLEAR_ERROR' } - | { type: 'SET_USER'; payload: { user: User; accessToken: string; refreshToken: string } } + | { type: 'SET_USER'; payload: { user: AuthUser; accessToken: string; refreshToken: string } } | { type: 'SET_REMEMBER_ME'; payload: boolean }; // 获取默认权限 @@ -49,6 +49,9 @@ const getDefaultPermissions = (userPermissions: Record = {}) => 'testcases.view', 'testcases.manage', 'testcases.create', + 'testcasesets.view', + 'testcasesets.manage', + 'testcasesets.create', 'teststeps.view', 'teststeps.manage', 'teststeps.create', @@ -181,7 +184,20 @@ export function AuthProvider({ children }: { children: ReactNode }) { }, [state]); // 使用自定义 hooks - useAuthSync(state.user, setGlobalUser); + useAuthSync(state.user, (user: AuthUser | null) => { + if (user) { + const storeUser: StoreUser = { + id: user.id, + username: user.userName, + email: user.email, + roles: [], // 从用户权限中提取角色信息 + permissions: Object.keys(user.permissions || {}) + }; + setGlobalUser(storeUser); + } else { + setGlobalUser(null); + } + }); useAuthInit(dispatch); const authActions = useMemo(() => ({ diff --git a/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx b/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx index ecfc908..120f655 100644 --- a/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx +++ b/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx @@ -26,7 +26,7 @@ export default function ScenarioForm({ onSubmit, initialData, isEdit = false, is e.preventDefault(); if (isSubmitting) return; // 防止重复提交 - if (isEdit && initialData) { + if (isEdit && initialData?.scenarioId) { // 编辑模式,需要包含scenarioId const updateData: UpdateScenarioRequest = { ...formData, diff --git a/src/X1.WebUI/src/pages/testcasesets/TestCaseSetForm.tsx b/src/X1.WebUI/src/pages/testcasesets/TestCaseSetForm.tsx new file mode 100644 index 0000000..f5a8ed8 --- /dev/null +++ b/src/X1.WebUI/src/pages/testcasesets/TestCaseSetForm.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Textarea } from '@/components/ui/textarea'; +import { Checkbox } from '@/components/ui/checkbox'; +import { CreateTestCaseSetRequest, UpdateTestCaseSetRequest, TestCaseSet } from '@/services/testCaseSetService'; + +interface TestCaseSetFormProps { + onSubmit: (data: CreateTestCaseSetRequest | UpdateTestCaseSetRequest) => void; + initialData?: Partial; + isEdit?: boolean; + isSubmitting?: boolean; +} + +export default function TestCaseSetForm({ onSubmit, initialData, isEdit = false, isSubmitting = false }: TestCaseSetFormProps) { + const [formData, setFormData] = React.useState({ + code: initialData?.code || '', + name: initialData?.name || '', + version: initialData?.version || '', + description: initialData?.description || '', + versionUpdateInfo: initialData?.versionUpdateInfo || '', + remarks: initialData?.remarks || '', + isDisabled: initialData?.isDisabled ?? false + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (isSubmitting) return; // 防止重复提交 + + if (isEdit && initialData?.testCaseSetId) { + // 编辑模式,需要包含testCaseSetId + const updateData: UpdateTestCaseSetRequest = { + ...formData, + testCaseSetId: initialData.testCaseSetId + }; + onSubmit(updateData); + } else { + // 创建模式 + onSubmit(formData); + } + }; + + return ( +
+
+ + setFormData({ ...formData, code: e.target.value })} + placeholder="请输入用例集编码" + required + disabled={isSubmitting} + /> +
+ +
+ + setFormData({ ...formData, name: e.target.value })} + placeholder="请输入用例集名称" + required + disabled={isSubmitting} + /> +
+ +
+ + setFormData({ ...formData, version: e.target.value })} + placeholder="请输入版本号" + required + disabled={isSubmitting} + /> +
+ +
+ +