You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用户端提交个人信息按钮无响应问题排查求助

用户端个人信息提交无反馈问题排查

问题背景

系统支持两种个人信息录入方式:

  • 管理员创建User并录入信息:功能正常
  • 用户注册登录后自行填写提交:点击按钮无任何success/error反馈

流程:邮箱密码注册 → 登录账号 → 填写个人信息 → 通过session获取session.user.email和session.user.id随表单提交

相关代码

FormPage.tsx

const router = useRouter()
const form = useForm<AccountSchemaType>({
    resolver: zodResolver(AccountSchema),
    defaultValues: {
        first_name: '',
        middle_name: '',
        last_name: '',
        sex: '',
        position: '',
        position_other: '',
        classification: '',

        //DO
        section_or_unit: '',
        division_office: '',

        //Rest
        years_in_service: '',
        undergraduate_course: '',
        date_graduated: '',
        master_degree: '',
        doctorate_degree: '',

        //School
        school: '',
    }
})


const { mutate, isPending } = useMutation({
    mutationFn: CreateAccountAction,
    onSuccess: (data) => {
        if(data?.success){
            toast({
                title: "Success",
                description: data.success,
                variant: 'default'
            })
        }
        router.refresh()

        if(data?.error){
            toast({
                title:"Error",
                description: data.error,
                variant: "destructive"
            })
        }
    },
    onError: (error: Error) => {
        console.error("Client error:", error)
        toast({
            title: "Error",
            description: "Unable to create a new account. Please try again.",
            variant: "destructive"
        })
    }
})


const onSubmit = useCallback((values: AccountSchemaType) => {
    mutate(values)
}, [mutate])

createAccounts.ts

export async function CreateAccountAction(form: AccountSchemaType) {
    // Get the current session
    const session = await auth()

    // Check if user is authenticated
    if (!session || !session.user || !session.user.id) {
        return {error: "You are not authorized"}
    }

    // Additional check to ensure email exists
    if (!session.user.email) {
        return {error: "Email already existing"}
    }

    const parsedBody = AccountSchema.safeParse(form)

    if (!parsedBody.success) {
        return {error: "Something went wrong"}
    }

    const {
        first_name,
        middle_name,
        last_name,
        sex,
        position,
        position_other,
        classification,
        section_or_unit,
        division_office,
        years_in_service,
        undergraduate_course,
        date_graduated,
        doctorate_degree,
        master_degree,
        school
    } = parsedBody.data

    try {
        const newAccount = await db.account.create({
            data: {
                first_name,
                middle_name,
                last_name,
                sex,
                position,
                position_other,
                classification,
                section_or_unit,
                undergraduate_course,
                division_office,
                years_in_service,
                date_graduated,
                master_degree,
                doctorate_degree,
                school,
                // Use non-null assertion since we checked earlier
                email: session.user.email!,
                accountId: session.user.id,
            }
        })

        return {success: "Updated Account", newAccount}

    } catch (error) {
        console.error("Error creating account:", error)
        throw new Error("Failed to create account")
    }
}

zod-schema.ts

export const AccountSchema = z.object({
    first_name: z.string().min(1, 'First name is required'),
    middle_name: z.string().min(1, 'Middle name is required'),
    last_name: z.string().min(1, 'Last name is required'),
    sex: z.string().min(2, { message: "Select input field" }),
    email: z.string().email(),
    position: z.string().min(1, 'Position is required'),
    position_other: z.string().optional(),
    classification: z.string().min(1, 'Classification is required'),


    //Divsion
    section_or_unit: z.string().optional(),
    division_office: z.string().optional(),


    //Rest
    years_in_service: z.string().min(1, {message: "Select Years"}),
    undergraduate_course: z.string().optional(),
    date_graduated: z.string().optional(),
    doctorate_degree: z.string().optional(),
    master_degree: z.string().optional(),

    //School
    school: z.string().optional(),


});


export type AccountSchemaType = z.infer<typeof AccountSchema>

Schema.Prisma

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String    @unique
  password      String?
  emailVerified DateTime?
  image         String?
  role          String    @default("teacher") // "DO / school_admin / teacher"
  affiliation   String?
  createdAt     DateTime  @default(now())
  updateAt      DateTime  @updatedAt
  account       Account?
}

model Account {
  id               String  @id @default(cuid())
  first_name       String
  middle_name      String
  last_name        String
  sex              String
  email            String
  position         String
  position_other   String?
  classification   String
  years_in_service String

  section_or_unit      String?
  undergraduate_course String?
  date_graduated       String?
  doctorate_degree     String?
  master_degree        String?

  school          String?
  division_office String?

  account   User   @relation(fields: [accountId], references: [id])
  accountId String @unique

  createdAt DateTime @default(now())
  updateAt  DateTime @default(now())

  @@unique([email])
}

问题分析及修复方案

1. 前端表单校验失败(核心原因)

AccountSchema中定义了email: z.string().email()为必填字段,但前端表单的defaultValues未包含email,且用户提交的表单也不会携带该字段(email从session获取)。React Hook Form 结合 Zod Resolver 会在前端自动校验,校验不通过时会阻止表单提交,导致点击按钮无任何反馈。

修复:
删除AccountSchema中的email字段(或设为可选z.string().email().optional()),因为action已从session获取email,无需前端传递:

export const AccountSchema = z.object({
    first_name: z.string().min(1, 'First name is required'),
    middle_name: z.string().min(1, 'Middle name is required'),
    last_name: z.string().min(1, 'Last name is required'),
    sex: z.string().min(2, { message: "Select input field" }),
    // 移除email字段
    position: z.string().min(1, 'Position is required'),
    position_other: z.string().optional(),
    classification: z.string().min(1, 'Classification is required'),

    // 其余字段保持不变
    // ...
});

2. Action中错误文案逻辑错误

createAccounts.ts中:

if (!session.user.email) {
    return {error: "Email already existing"}
}

逻辑与文案不符,应改为:

if (!session.user.email) {
    return {error: "Missing email in session"}
}

3. 前端Toast显示时机问题

onSuccess中先执行router.refresh()再处理错误Toast,页面刷新会导致Toast来不及显示。调整顺序:

onSuccess: (data) => {
    if(data?.success){
        toast({
            title: "Success",
            description: data.success,
            variant: 'default'
        })
    }

    if(data?.error){
        toast({
            title:"Error",
            description: data.error,
            variant: "destructive"
        })
    }
    
    // 最后执行页面刷新
    router.refresh()
},

4. 数据库唯一性冲突处理

Account模型中@@unique([email]),若用户重复提交会触发数据库错误。当前action中catch后throw error,前端onError会触发,但需确保错误信息能准确传递给用户,可修改catch逻辑:

catch (error) {
    console.error("Error creating account:", error)
    // 检测是否为唯一性冲突错误
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
        return {error: "Account already exists for this email"}
    }
    return {error: "Failed to create account"}
    // 不要throw,避免进入onError,统一通过返回值处理
}

同时前端onSuccess处理所有返回的success/error,onError仅处理网络等意外错误。

内容的提问来源于stack exchange,提问作者Stykgwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:05:59