chore: initial commit

This commit is contained in:
2026-07-22 17:50:30 +08:00
parent d46cdd676e
commit 3abf272e33
190 changed files with 18073 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
import { useId, useState } from 'react'
import { zodResolver } from '@hookform/resolvers/zod'
import { Controller, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { createSysUser, updateSysUser } from '@/api'
import { FileUpload } from '@/components/file-upload'
import { Button } from '@/components/ui/button.tsx'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog.tsx'
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field'
import { Input } from '@/components/ui/input'
import { Spinner } from '@/components/ui/spinner.tsx'
import {
sysUserFormSchema,
type SysUser,
type SysUserFormInput,
type SysUserFormOutput,
} from '@/schemas'
interface Props {
currentRow?: SysUser | null
onClose: () => void
onConfirm: () => void
open: boolean
}
export function ActionsDialog({ open, currentRow, onClose, onConfirm }: Props) {
const isEdit = !!currentRow?.id
const formId = useId()
const [loading, setLoading] = useState(false)
const form = useForm<SysUserFormInput, undefined, SysUserFormOutput>({
resolver: zodResolver(sysUserFormSchema),
defaultValues: currentRow
? {
...currentRow,
avatar: currentRow.avatar_id
? [{ id: currentRow.avatar_id, file_path: currentRow.avatar_url }]
: [],
}
: {
username: '',
account: '',
password: '',
confirmPassword: '',
avatar: [],
},
})
const onSubmit = async (values: SysUserFormOutput) => {
try {
setLoading(true)
if (values.id) {
await updateSysUser(values.id, values)
} else {
await createSysUser(values)
}
toast('操作成功!')
onConfirm()
} finally {
setLoading(false)
}
}
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent onCloseAutoFocus={() => form.reset()}>
<DialogHeader>
<DialogTitle>{isEdit ? '编辑' : '创建'}</DialogTitle>
<DialogDescription />
</DialogHeader>
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
<FieldGroup className='gap-4'>
<Controller
name='avatar'
control={form.control}
render={({ field, fieldState }) => {
return (
<Field data-invalid={fieldState.invalid}>
<FieldLabel></FieldLabel>
<FileUpload defaultFiles={field.value} onChange={field.onChange} />
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)
}}
/>
<Controller
name='username'
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor='username'></FieldLabel>
<Input
{...field}
id='username'
aria-invalid={fieldState.invalid}
placeholder='请输入用户名称'
autoComplete='off'
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
{!isEdit && (
<>
<Controller
name='account'
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor='account'></FieldLabel>
<Input
{...field}
id='account'
aria-invalid={fieldState.invalid}
placeholder='请输入账户名称'
autoComplete='off'
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
<Controller
name='password'
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor='password'></FieldLabel>
<Input
{...field}
id='password'
aria-invalid={fieldState.invalid}
placeholder='请输入用户密码'
autoComplete='off'
type='password'
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
<Controller
name='confirmPassword'
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor='confirmPassword'></FieldLabel>
<Input
{...field}
id='confirmPassword'
aria-invalid={fieldState.invalid}
placeholder='请输入确认密码'
autoComplete='off'
type='password'
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
</>
)}
</FieldGroup>
</form>
<DialogFooter>
<DialogClose asChild>
<Button disabled={loading} variant='outline'>
</Button>
</DialogClose>
<Button disabled={loading} form={formId} type='submit'>
{loading && <Spinner data-icon='inline-start' />}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}