下载完成
@@ -230,8 +238,8 @@ export default function PlaywrightDownloadDialog({
{/* Initial Loading State */}
{isDownloading && !progress && (
-
-
正在初始化下载...
+
+
正在初始化下载...
)}
@@ -246,24 +254,25 @@ export default function PlaywrightDownloadDialog({
isAlertDialog
>
-
+
确定要取消下载吗?
这将退出应用程序,您需要重新启动来继续下载。
-
-
diff --git a/src/renderer/src/components/UpdateDialog.tsx b/src/renderer/src/components/UpdateDialog.tsx
index 9939152..e62faec 100644
--- a/src/renderer/src/components/UpdateDialog.tsx
+++ b/src/renderer/src/components/UpdateDialog.tsx
@@ -3,6 +3,9 @@ import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { DownloadCloud, LoaderCircle, RefreshCw } from 'lucide-react'
import Modal from './ui/Modal'
+import { Button } from './ui/Button'
+import { ScrollArea } from './ui/shadcn/scroll-area'
+import { Card, CardContent, CardHeader, CardTitle } from './ui/shadcn/card'
import { useUpdateDialogState } from '../hooks/useUpdateDialogState'
import type { UserType } from '../../../main/types/user.types'
import type {
@@ -44,7 +47,7 @@ export default function UpdateDialog({
const renderReleaseList = (title: string, releases: UpdateRelease[]) => {
if (releases.length === 0) {
return (
-
+
暂无版本
)
@@ -52,7 +55,7 @@ export default function UpdateDialog({
return (
-
{title}
+
{title}
{releases.map((release) => {
const selected = isSelectedRelease(release)
return (
@@ -65,19 +68,19 @@ export default function UpdateDialog({
}}
className={`w-full rounded-lg border px-3 py-3 text-left transition ${
selected
- ? 'border-blue-500 bg-blue-50 text-blue-900'
- : 'border-slate-200 bg-white text-slate-700 hover:border-slate-300'
+ ? 'border-primary bg-primary/10 text-primary'
+ : 'border-border bg-background text-foreground hover:bg-accent'
}`}
>
V{release.version}
-
+
{release.channel}
-
{release.publishedAt}
+
{release.publishedAt}
{release.notesSummary && (
-
{release.notesSummary}
+
{release.notesSummary}
)}
)
@@ -96,21 +99,23 @@ export default function UpdateDialog({
disableEscapeKey={isBusy}
>
-
-
- 当前版本
V{status?.currentVersion}
-
+
+
+ 当前版本 V{status?.currentVersion}
+
{status?.currentChannel ?? __APP_CHANNEL__}
-
void onRefreshCatalog()}
- className="inline-flex items-center gap-2 rounded-md border border-slate-200 px-3 py-1.5 text-xs text-slate-600 hover:bg-slate-100"
disabled={isBusy}
+ className="gap-2"
>
刷新
-
+
{catalog?.mode === 'admin' ? (
@@ -119,104 +124,106 @@ export default function UpdateDialog({
{renderReleaseList('Stable', catalog.channels?.stable ?? [])}
{renderReleaseList('Preview', catalog.channels?.preview ?? [])}
-
-
-
+
+
+
{selectedRelease
? `${selectedRelease.channel.toUpperCase()} V${selectedRelease.version}`
: '请选择一个版本'}
-
-
+
+
{selectedRelease?.notesSummary ?? '选择版本后可查看详细更新说明。'}
-
-
- {isLoadingChangelog ? (
-
-
- 正在加载更新说明...
-
- ) : (
-
- {changelog || '暂无更新说明。'}
-
- )}
-
-
+
+
+
+ {isLoadingChangelog ? (
+
+
+ 正在加载更新说明...
+
+ ) : (
+
+
+ {changelog || '暂无更新说明。'}
+
+
+ )}
+
+
+
) : (
-
-
-
+
+
+
{selectedRelease ? `发现稳定版 V${selectedRelease.version}` : '暂无可用更新'}
-
-
+
+
{status?.currentChannel === 'preview'
? '当前设备正在运行预览版,普通用户将更新回稳定版。'
: '更新包已在后台准备完成,确认后将自动关闭并重启应用。'}
-
-
- {isLoadingChangelog ? (
-
-
- 正在加载更新说明...
-
- ) : (
-
- {changelog || '暂无更新说明。'}
-
- )}
-
-
+
+
+
+ {isLoadingChangelog ? (
+
+
+ 正在加载更新说明...
+
+ ) : (
+
+
+ {changelog || '暂无更新说明。'}
+
+
+ )}
+
+
+
)}
{status?.error && (
-
+
{status.error}
)}
-
-
{status?.message ?? ' '}
+
+
{status?.message ?? ' '}
-
稍后
-
+
{userType === 'Admin' ? (
-
selectedRelease
? void onDownloadAndInstallAdminRelease(selectedRelease)
: undefined
}
- className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-blue-300"
disabled={!selectedRelease || isBusy}
+ loading={isBusy}
+ className="gap-2"
>
- {isBusy ? (
-
- ) : (
-
- )}
+ {isBusy ? null : }
下载并更新
-
+
) : (
- void onInstallUserRelease()}
- className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-blue-300"
disabled={!selectedRelease || isBusy}
+ loading={isBusy}
+ className="gap-2"
>
- {isBusy ? (
-
- ) : (
-
- )}
+ {isBusy ? null : }
立即更新
-
+
)}
diff --git a/src/renderer/src/components/cleaner/CleanerSidebar.tsx b/src/renderer/src/components/cleaner/CleanerSidebar.tsx
index 6198900..e9e29b6 100644
--- a/src/renderer/src/components/cleaner/CleanerSidebar.tsx
+++ b/src/renderer/src/components/cleaner/CleanerSidebar.tsx
@@ -1,5 +1,8 @@
import React from 'react'
import { DatabaseZap, Layers, Users } from 'lucide-react'
+import { Card } from '../ui/shadcn/card'
+import { ScrollArea } from '../ui/shadcn/scroll-area'
+import { cn } from '@renderer/lib/utils'
interface CleanerSidebarProps {
valMode: 'full' | 'filtered'
@@ -18,15 +21,20 @@ export function CleanerSidebar({
}: CleanerSidebarProps): React.JSX.Element {
return (
-
-
+
+
校验数据来源
)
}
diff --git a/src/renderer/src/components/cleaner/CleanerToolbar.tsx b/src/renderer/src/components/cleaner/CleanerToolbar.tsx
index 93292f0..8807db8 100644
--- a/src/renderer/src/components/cleaner/CleanerToolbar.tsx
+++ b/src/renderer/src/components/cleaner/CleanerToolbar.tsx
@@ -10,6 +10,8 @@ import {
Settings2,
Square
} from 'lucide-react'
+import { Button } from '../ui/Button'
+import { Separator } from '../ui/shadcn/separator'
import type { ValidationResult } from '../../hooks/cleaner/types'
interface CleanerToolbarProps {
@@ -48,25 +50,29 @@ export function CleanerToolbar({
}: CleanerToolbarProps): React.JSX.Element {
return (
<>
-
-
+ void handleValidation()}
disabled={isValidationRunning}
- className="w-full bg-slate-100 hover:bg-slate-200 text-slate-800 py-3 rounded-lg font-medium transition-colors shadow-sm flex justify-center items-center gap-2 border border-slate-200 disabled:opacity-50"
+ className="w-full gap-2"
+ loading={isValidationRunning}
>
{isValidationRunning ? '正在获取...' : '获取并校验物料状态'}
-
+
-
+
-
setSelectedItems(new Set(filteredResults.map((r) => r.materialCode)))}
- className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
+ variant="secondary"
+ className="gap-1"
>
- 全选
-
-
全选
+
+
{
const visibleCodes = new Set(filteredResults.map((r) => r.materialCode))
setSelectedItems((prev) => {
@@ -77,14 +83,16 @@ export function CleanerToolbar({
return next
})
}}
- className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
+ variant="secondary"
+ className="gap-1"
>
- 取消
-
+
取消
+
-
+
-
{
const checkedCodes = filteredResults
.filter((r) => selectedItems.has(r.materialCode))
@@ -93,49 +101,58 @@ export function CleanerToolbar({
setHiddenItems((prev) => new Set([...prev, ...checkedCodes]))
}
}}
- className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
+ variant="secondary"
+ className="gap-1"
>
隐藏勾选
-
-
+ setHiddenItems(new Set())}
- className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
+ variant="secondary"
+ className="gap-1"
>
显示全部
-
+
-
+
-
void handleConfirmDeletion()}
disabled={isRunning || validationResults.length === 0}
- className="text-xs bg-indigo-50 border border-indigo-200 text-indigo-700 px-3 py-1.5 rounded shadow-sm hover:bg-indigo-100 flex items-center gap-1.5 font-medium transition-colors disabled:opacity-50"
+ className="gap-1.5 bg-indigo-50 text-indigo-700 border-indigo-200 hover:bg-indigo-100 dark:bg-indigo-900/20 dark:text-indigo-400 dark:border-indigo-800"
>
确认删除 (同步数据库)
-
+
- setIsTypeDialogOpen(true)}
ref={typeManagementButtonRef}
- className="text-xs bg-white border border-slate-300 text-slate-700 px-3 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1.5"
+ variant="secondary"
+ className="gap-1.5"
>
类型管理
-
-
+ void handleExportResults()}
disabled={isExporting || filteredResults.length === 0}
- className="text-xs bg-blue-50 border border-blue-200 text-blue-700 px-3 py-1.5 rounded shadow-sm hover:bg-blue-100 flex items-center gap-1.5 font-medium disabled:opacity-50"
+ loading={isExporting}
+ className="gap-1.5 bg-blue-50 text-blue-700 border-blue-200 hover:bg-blue-100 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800"
>
{isExporting ? '导出中...' : '导出结果'}
-
-
+ setIsReportViewerOpen(true)}
- className="text-xs bg-emerald-50 border border-emerald-200 text-emerald-700 px-3 py-1.5 rounded shadow-sm hover:bg-emerald-100 flex items-center gap-1.5 font-medium transition-colors"
+ className="gap-1.5 bg-emerald-50 text-emerald-700 border-emerald-200 hover:bg-emerald-100 dark:bg-emerald-900/20 dark:text-emerald-400 dark:border-emerald-800"
>
查看报告
-
+
>
diff --git a/src/renderer/src/components/ui/Button.tsx b/src/renderer/src/components/ui/Button.tsx
index dafeec2..7007b50 100644
--- a/src/renderer/src/components/ui/Button.tsx
+++ b/src/renderer/src/components/ui/Button.tsx
@@ -34,32 +34,37 @@ const sizeMap: Record
= {
lg: 'lg'
}
-export function Button({
- variant = 'primary',
- size = 'md',
- loading = false,
- icon,
- children,
- className = '',
- disabled,
- ...props
-}: ButtonProps) {
- const newVariant = variantMap[variant]
- const newSize = sizeMap[size]
+const Button = React.forwardRef(
+ ({
+ variant = 'primary',
+ size = 'md',
+ loading = false,
+ icon,
+ children,
+ className = '',
+ disabled,
+ ...props
+ }, ref) => {
+ const newVariant = variantMap[variant]
+ const newSize = sizeMap[size]
- return (
-
- {icon && !loading && {icon}}
- {children}
-
- )
-}
+ return (
+
+ {icon && !loading && {icon}}
+ {children}
+
+ )
+ }
+)
+Button.displayName = 'Button'
+export { Button }
export default Button
diff --git a/src/renderer/src/components/ui/ConfirmDialog.tsx b/src/renderer/src/components/ui/ConfirmDialog.tsx
index 3262c5c..9ea4e3f 100644
--- a/src/renderer/src/components/ui/ConfirmDialog.tsx
+++ b/src/renderer/src/components/ui/ConfirmDialog.tsx
@@ -2,12 +2,23 @@
* ConfirmDialog Component
*
* A confirmation dialog component for displaying confirmation prompts.
- * Extends the Modal component with consistent styling and behavior.
+ * Now uses shadcn/ui AlertDialog component for better accessibility and styling.
+ *
+ * Migrated to use shadcn/ui AlertDialog
*/
import React, { useCallback, useEffect } from 'react'
import { AlertTriangle, Info, AlertCircle } from 'lucide-react'
-import { Modal } from './Modal'
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from './shadcn/alert-dialog'
import { Button } from './Button'
export type ConfirmDialogVariant = 'danger' | 'warning' | 'info'
@@ -29,18 +40,18 @@ const variantStyles: Record<
> = {
danger: {
icon: ,
- iconColor: 'text-red-500',
- buttonVariant: 'danger'
+ iconColor: 'text-destructive',
+ buttonVariant: 'danger' as const
},
warning: {
icon: ,
- iconColor: 'text-yellow-500',
- buttonVariant: 'primary'
+ iconColor: 'text-amber-500 dark:text-amber-400',
+ buttonVariant: 'primary' as const
},
info: {
icon: ,
- iconColor: 'text-blue-500',
- buttonVariant: 'primary'
+ iconColor: 'text-primary',
+ buttonVariant: 'primary' as const
}
}
@@ -78,40 +89,56 @@ export function ConfirmDialog({
const styles = variantStyles[variant]
+ // For AlertDialog, we use open/onOpenChange
+ const handleOpenChange = (open: boolean) => {
+ if (!open) {
+ onCancel()
+ }
+ }
+
return (
-
-
- {/* Icon */}
-
{styles.icon}
+
+
+
+
+ {/* Icon */}
+
{styles.icon}
- {/* Message */}
-
- {typeof message === 'string' ? (
-
{message}
- ) : (
- message
- )}
-
-
+ {/* Title and Description */}
+
+
{title}
+
+
+ {typeof message === 'string' ? (
+
{message}
+ ) : (
+ message
+ )}
+
+
+
+
+
- {/* Buttons */}
-
-
- {cancelText}
-
-
- {confirmText}
-
-
-
+ {/* Buttons */}
+
+
+
+ {cancelText}
+
+
+
+
+ {confirmText}
+
+
+
+
+
)
}