refactor: migrate key forms to shadcn field

This commit is contained in:
hailin
2026-07-02 15:30:01 +08:00
parent 2de36c3da6
commit 09df56c21b
6 changed files with 66 additions and 35 deletions

View File

@@ -1,11 +1,11 @@
import * as React from 'react'
import { AlertCircle, LockKeyhole, UserRound } from 'lucide-react'
import { AppField } from '@/components/app/AppField'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useAuth } from './AuthProvider'
@@ -70,33 +70,33 @@ export function LoginPage() {
<AlertDescription>{error}</AlertDescription>
</Alert>
) : null}
<div className="grid gap-2">
<Label htmlFor="username">账号</Label>
<AppField label="账号" htmlFor="username" invalid={Boolean(error)} required>
<div className="relative">
<UserRound className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
id="username"
autoComplete="username"
aria-invalid={Boolean(error)}
className="pl-9"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="password">密码</Label>
</AppField>
<AppField label="密码" htmlFor="password" invalid={Boolean(error)} required>
<div className="relative">
<LockKeyhole className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
id="password"
type="password"
autoComplete="current-password"
aria-invalid={Boolean(error)}
className="pl-9"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
</div>
</div>
</AppField>
<Button type="submit" className="mt-1 w-full" disabled={submitting}>
{submitting ? '登录中...' : '登录'}
</Button>

View File

@@ -100,15 +100,16 @@ export function SolutionCreatePage() {
<div className="grid gap-4">
<AppSection title="基础信息">
<div className="grid gap-4 md:grid-cols-2">
<AppField label="方案标题">
<AppField label="方案标题" htmlFor="solution-title" required>
<Input
id="solution-title"
required
placeholder="例如:认证回调阻塞处理方案"
value={form.title}
onChange={(event) => updateField('title', event.target.value)}
/>
</AppField>
<AppField label="状态">
<AppField label="状态" required>
<Select value={form.status} onValueChange={(value) => updateField('status', value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder="选择状态" />
@@ -121,15 +122,17 @@ export function SolutionCreatePage() {
</SelectContent>
</Select>
</AppField>
<AppField label="关联系统">
<AppField label="关联系统" htmlFor="solution-related-system">
<Input
id="solution-related-system"
placeholder="例如:CRM、认证中心、支付系统"
value={form.related_system}
onChange={(event) => updateField('related_system', event.target.value)}
/>
</AppField>
<AppField label="环境范围">
<AppField label="环境范围" htmlFor="solution-environment-scope">
<Input
id="solution-environment-scope"
placeholder="例如:生产环境 / Chrome"
value={form.environment_scope}
onChange={(event) => updateField('environment_scope', event.target.value)}
@@ -139,8 +142,9 @@ export function SolutionCreatePage() {
</AppSection>
<AppSection title="问题特征">
<AppField label="问题特征">
<AppField label="问题特征" htmlFor="solution-problem-signature" required>
<Textarea
id="solution-problem-signature"
required
placeholder="描述可复用的问题表现、报错特征、触发条件。"
value={form.problem_signature}
@@ -151,16 +155,18 @@ export function SolutionCreatePage() {
<AppSection title="根因与方案">
<div className="grid gap-4">
<AppField label="根因分析">
<AppField label="根因分析" htmlFor="solution-root-cause" required>
<Textarea
id="solution-root-cause"
required
placeholder="说明问题的根因或判断依据。"
value={form.root_cause}
onChange={(event) => updateField('root_cause', event.target.value)}
/>
</AppField>
<AppField label="处理方案">
<AppField label="处理方案" htmlFor="solution-content" required>
<Textarea
id="solution-content"
required
placeholder="写清楚处理步骤、验证方式和回滚注意事项。"
value={form.solution}
@@ -171,8 +177,9 @@ export function SolutionCreatePage() {
</AppSection>
<AppSection title="风险提示">
<AppField label="风险提示">
<AppField label="风险提示" htmlFor="solution-risk-note">
<Textarea
id="solution-risk-note"
placeholder="可选:说明操作风险、影响范围或上线前确认事项。"
value={form.risk_note}
onChange={(event) => updateField('risk_note', event.target.value)}

View File

@@ -1,5 +1,6 @@
import * as React from 'react'
import { AppField } from '@/components/app/AppField'
import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable'
import { AppEmptyState } from '@/components/app/AppEmptyState'
import { AppPageHeader } from '@/components/app/AppPageHeader'
@@ -74,9 +75,11 @@ export function SystemRolesPage() {
<div className="mx-auto flex max-w-7xl flex-col gap-4">
<AppPageHeader title="角色权限" />
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[minmax(0,360px)_auto]" onSubmit={handleCreate}>
<Input placeholder="角色名称" value={roleName} onChange={(event) => setRoleName(event.target.value)} required />
<Button type="submit">创建角色</Button>
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[minmax(0,360px)_auto] md:items-end" onSubmit={handleCreate}>
<AppField label="角色名称" htmlFor="system-role-name" required>
<Input id="system-role-name" value={roleName} onChange={(event) => setRoleName(event.target.value)} required />
</AppField>
<Button type="submit" className="w-full md:w-auto">创建角色</Button>
</form>
{error ? <AppEmptyState title="无法加载角色" description={error} /> : null}

View File

@@ -1,5 +1,6 @@
import * as React from 'react'
import { AppField } from '@/components/app/AppField'
import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable'
import { AppEmptyState } from '@/components/app/AppEmptyState'
import { AppPageHeader } from '@/components/app/AppPageHeader'
@@ -82,11 +83,17 @@ export function SystemUserGroupsPage() {
<div className="mx-auto flex max-w-7xl flex-col gap-4">
<AppPageHeader title="用户组管理" />
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_2fr_auto]" onSubmit={handleCreate}>
<Input placeholder="用户组名称" value={form.name} onChange={(event) => setForm({ ...form, name: event.target.value })} required />
<Input placeholder="编码" value={form.code} onChange={(event) => setForm({ ...form, code: event.target.value })} required />
<Input placeholder="说明" value={form.description} onChange={(event) => setForm({ ...form, description: event.target.value })} />
<Button type="submit">创建用户组</Button>
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_2fr_auto] md:items-end" onSubmit={handleCreate}>
<AppField label="用户组名称" htmlFor="system-group-name" required>
<Input id="system-group-name" value={form.name} onChange={(event) => setForm({ ...form, name: event.target.value })} required />
</AppField>
<AppField label="编码" htmlFor="system-group-code" required>
<Input id="system-group-code" value={form.code} onChange={(event) => setForm({ ...form, code: event.target.value })} required />
</AppField>
<AppField label="说明" htmlFor="system-group-description">
<Input id="system-group-description" value={form.description} onChange={(event) => setForm({ ...form, description: event.target.value })} />
</AppField>
<Button type="submit" className="w-full md:w-auto">创建用户组</Button>
</form>
{error ? <AppEmptyState title="无法加载用户组" description={error} /> : null}

View File

@@ -1,5 +1,6 @@
import * as React from 'react'
import { AppField } from '@/components/app/AppField'
import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable'
import { AppEmptyState } from '@/components/app/AppEmptyState'
import { AppFilterBar } from '@/components/app/AppFilterBar'
@@ -141,12 +142,20 @@ export function SystemUsersPage() {
<div className="mx-auto flex max-w-7xl flex-col gap-4">
<AppPageHeader title="用户管理" />
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_1fr_1fr_auto]" onSubmit={handleCreateUser}>
<Input placeholder="账号" value={form.username} onChange={(event) => setForm({ ...form, username: event.target.value })} required />
<Input placeholder="显示名称" value={form.display_name} onChange={(event) => setForm({ ...form, display_name: event.target.value })} />
<Input placeholder="邮箱" value={form.email} onChange={(event) => setForm({ ...form, email: event.target.value })} />
<Input placeholder="初始密码" type="password" value={form.password} onChange={(event) => setForm({ ...form, password: event.target.value })} required />
<Button type="submit">创建用户</Button>
<form className="grid gap-3 rounded-lg border border-border bg-surface p-3 md:grid-cols-[1fr_1fr_1fr_1fr_auto] md:items-end" onSubmit={handleCreateUser}>
<AppField label="账号" htmlFor="system-user-username" required>
<Input id="system-user-username" value={form.username} onChange={(event) => setForm({ ...form, username: event.target.value })} required />
</AppField>
<AppField label="显示名称" htmlFor="system-user-display-name">
<Input id="system-user-display-name" value={form.display_name} onChange={(event) => setForm({ ...form, display_name: event.target.value })} />
</AppField>
<AppField label="邮箱" htmlFor="system-user-email">
<Input id="system-user-email" value={form.email} onChange={(event) => setForm({ ...form, email: event.target.value })} />
</AppField>
<AppField label="初始密码" htmlFor="system-user-password" required>
<Input id="system-user-password" type="password" value={form.password} onChange={(event) => setForm({ ...form, password: event.target.value })} required />
</AppField>
<Button type="submit" className="w-full md:w-auto">创建用户</Button>
</form>
<AppFilterBar>

View File

@@ -61,8 +61,9 @@ export function TicketCreatePage() {
) : null}
<AppSection title="基础信息">
<AppField label="工单标题">
<AppField label="工单标题" htmlFor="ticket-title" required>
<Input
id="ticket-title"
required
placeholder="例如:生产环境登录后跳转失败"
value={form.title}
@@ -73,7 +74,7 @@ export function TicketCreatePage() {
<AppSection title="影响与优先级">
<div className="grid gap-4 md:grid-cols-2">
<AppField label="优先级">
<AppField label="优先级" required>
<Select value={form.priority} onValueChange={(value) => updateField('priority', value as TicketInput['priority'])}>
<SelectTrigger className="w-full">
<SelectValue placeholder="请选择优先级" />
@@ -88,8 +89,9 @@ export function TicketCreatePage() {
</SelectContent>
</Select>
</AppField>
<AppField label="影响范围">
<AppField label="影响范围" htmlFor="ticket-impact-scope">
<Input
id="ticket-impact-scope"
placeholder="例如:单个用户、某部门、全部用户"
value={form.impact_scope}
onChange={(event) => updateField('impact_scope', event.target.value)}
@@ -100,15 +102,17 @@ export function TicketCreatePage() {
<AppSection title="环境与系统">
<div className="grid gap-4 md:grid-cols-2">
<AppField label="关联系统">
<AppField label="关联系统" htmlFor="ticket-related-system">
<Input
id="ticket-related-system"
placeholder="例如:CRM、财务系统、数据中台"
value={form.related_system}
onChange={(event) => updateField('related_system', event.target.value)}
/>
</AppField>
<AppField label="运行环境">
<AppField label="运行环境" htmlFor="ticket-environment">
<Input
id="ticket-environment"
placeholder="例如:生产环境、测试环境、Windows 终端"
value={form.environment}
onChange={(event) => updateField('environment', event.target.value)}
@@ -118,8 +122,9 @@ export function TicketCreatePage() {
</AppSection>
<AppSection title="问题描述">
<AppField label="详细描述">
<AppField label="详细描述" htmlFor="ticket-description" required>
<Textarea
id="ticket-description"
required
placeholder="请描述影响范围、报错信息、操作步骤和已尝试的处理方式。"
value={form.description}