This commit is contained in:
asp_ly 2024-12-27 23:07:46 +08:00
parent 03dd04d9f5
commit 871dd32c91
8 changed files with 172 additions and 133 deletions

View File

@ -37,6 +37,7 @@
"@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.4",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"@types/recharts": "^1.8.29", "@types/recharts": "^1.8.29",
"ajv": "^8.17.1", "ajv": "^8.17.1",
@ -60,8 +61,8 @@
"@types/dagre": "^0.7.52", "@types/dagre": "^0.7.52",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/node": "^20.17.10", "@types/node": "^20.17.10",
"@types/react": "^18.2.43", "@types/react": "^18.3.18",
"@types/react-dom": "^18.2.17", "@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0", "@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
@ -2650,6 +2651,39 @@
} }
} }
}, },
"node_modules/@radix-ui/react-toast": {
"version": "1.2.4",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.4.tgz",
"integrity": "sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collection": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-portal": "1.1.3",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-visually-hidden": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": { "node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
@ -3378,21 +3412,21 @@
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA=="
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "18.3.12", "version": "18.3.18",
"resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.12.tgz", "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-dom": { "node_modules/@types/react-dom": {
"version": "18.3.1", "version": "18.3.5",
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.5.tgz",
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
"devOptional": true, "devOptional": true,
"dependencies": { "peerDependencies": {
"@types/react": "*" "@types/react": "^18.0.0"
} }
}, },
"node_modules/@types/recharts": { "node_modules/@types/recharts": {

View File

@ -39,6 +39,7 @@
"@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.4",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"@types/recharts": "^1.8.29", "@types/recharts": "^1.8.29",
"ajv": "^8.17.1", "ajv": "^8.17.1",
@ -62,8 +63,8 @@
"@types/dagre": "^0.7.52", "@types/dagre": "^0.7.52",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/node": "^20.17.10", "@types/node": "^20.17.10",
"@types/react": "^18.2.43", "@types/react": "^18.3.18",
"@types/react-dom": "^18.2.17", "@types/react-dom": "^18.3.5",
"@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0", "@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",

View File

@ -1,17 +1,17 @@
import React from 'react'; import React from 'react';
import { RouterProvider } from 'react-router-dom'; import {RouterProvider} from 'react-router-dom';
import { ConfigProvider } from 'antd'; import {ConfigProvider} from 'antd';
import zhCN from 'antd/locale/zh_CN'; import zhCN from 'antd/locale/zh_CN';
import router from './router'; import router from './router';
import { Toaster } from "@/components/ui/toaster";
const App: React.FC = () => { const App: React.FC = () => {
return ( return (
<ConfigProvider locale={zhCN}> <React.Fragment>
<RouterProvider router={router} /> <ConfigProvider locale={zhCN}>
<Toaster /> <RouterProvider router={router}/>
</ConfigProvider> </ConfigProvider>
); </React.Fragment>
);
}; };
export default App; export default App;

View File

@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport <ToastPrimitives.Viewport
ref={ref} ref={ref}
className={cn( className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", "fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
className className
)} )}
{...props} {...props}
@ -23,13 +23,13 @@ const ToastViewport = React.forwardRef<
ToastViewport.displayName = ToastPrimitives.Viewport.displayName ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva( const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full",
{ {
variants: { variants: {
variant: { variant: {
default: "border bg-background text-foreground", default: "bg-background border",
destructive: destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground", "group destructive border-destructive bg-destructive text-destructive-foreground",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -60,7 +60,7 @@ const ToastAction = React.forwardRef<
<ToastPrimitives.Action <ToastPrimitives.Action
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className className
)} )}
{...props} {...props}

View File

@ -13,20 +13,18 @@ export function Toaster() {
return ( return (
<ToastProvider> <ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) { {toasts.map(({ id, title, description, action, ...props }) => (
return ( <Toast key={id} {...props}>
<Toast key={id} {...props}> <div className="grid gap-1">
<div className="grid gap-1"> {title && <ToastTitle>{title}</ToastTitle>}
{title && <ToastTitle>{title}</ToastTitle>} {description && (
{description && ( <ToastDescription>{description}</ToastDescription>
<ToastDescription>{description}</ToastDescription> )}
)} </div>
</div> {action}
{action} <ToastClose />
<ToastClose /> </Toast>
</Toast> ))}
)
})}
<ToastViewport /> <ToastViewport />
</ToastProvider> </ToastProvider>
) )

View File

@ -3,89 +3,90 @@
@tailwind utilities; @tailwind utilities;
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 0 0% 3.9%; --foreground: 0 0% 3.9%;
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 0 0% 3.9%; --card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%; --popover: 0 0% 100%;
--input: 0 0% 89.8%; --popover-foreground: 0 0% 3.9%;
--ring: 0 0% 3.9%;
--primary: 0 0% 9%;
--radius: 0.5rem; --primary-foreground: 0 0% 98%;
--chart-1: 12 76% 61%; --secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--chart-2: 173 58% 39%;
--muted: 0 0% 96.1%;
--chart-3: 197 37% 24%; --muted-foreground: 0 0% 45.1%;
--chart-4: 43 74% 66%; --accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--chart-5: 27 87% 67%;
} --destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
.dark {
--background: 0 0% 3.9%; --border: 0 0% 89.8%;
--foreground: 0 0% 98%; --input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%; --radius: 0.5rem;
--popover: 0 0% 3.9%; --chart-1: 12 76% 61%;
--popover-foreground: 0 0% 98%;
--chart-2: 173 58% 39%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%; --chart-3: 197 37% 24%;
--secondary: 0 0% 14.9%; --chart-4: 43 74% 66%;
--secondary-foreground: 0 0% 98%;
--chart-5: 27 87% 67%;
--muted: 0 0% 14.9%; }
--muted-foreground: 0 0% 63.9%;
.dark {
--accent: 0 0% 14.9%; --background: 0 0% 3.9%;
--accent-foreground: 0 0% 98%; --foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%; --card: 0 0% 3.9%;
--destructive-foreground: 0 0% 98%; --card-foreground: 0 0% 98%;
--border: 0 0% 14.9%; --popover: 0 0% 3.9%;
--input: 0 0% 14.9%; --popover-foreground: 0 0% 98%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%; --primary: 0 0% 98%;
--chart-2: 160 60% 45%; --primary-foreground: 0 0% 9%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%; --secondary: 0 0% 14.9%;
--chart-5: 340 75% 55%; --secondary-foreground: 0 0% 98%;
}
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
} }
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
} }
body {
@apply bg-background text-foreground; body {
} @apply bg-background text-foreground;
}
} }

View File

@ -1,13 +1,15 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router-dom'; import {RouterProvider} from 'react-router-dom';
import { Provider } from 'react-redux'; import {Provider} from 'react-redux';
import router from './router'; import router from './router';
import store from './store'; import store from './store';
import './index.css'; import './index.css';
import {Toaster} from "@/components/ui/toaster.tsx";
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}> <Provider store={store}>
<RouterProvider router={router} /> <RouterProvider router={router}/>
</Provider> <Toaster/>
</Provider>
); );

View File

@ -5,6 +5,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogFooter, DialogFooter,
DialogDescription,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import {Button} from "@/components/ui/button"; import {Button} from "@/components/ui/button";
import { import {
@ -101,25 +102,24 @@ const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
toast({ toast({
title: "操作成功", title: "操作成功",
description: "项目组更新成功", description: "项目组更新成功",
duration: 3000, variant: "default",
}); });
onSuccess(); onSuccess?.();
} else { } else {
await createProjectGroup(values); await createProjectGroup(values);
toast({ toast({
title: "操作成功", title: "操作成功",
description: "项目组创建成功", description: "项目组创建成功",
duration: 3000, variant: "default",
}); });
onSuccess(); onSuccess?.();
} }
} catch (error) { } catch (error) {
console.error('操作失败:', error); console.error('操作失败:', error);
toast({ toast({
variant: "destructive",
title: "操作失败", title: "操作失败",
description: error instanceof Error ? error.message : `项目组${isEdit ? '更新' : '创建'}失败`, description: error instanceof Error ? error.message : `项目组${isEdit ? '更新' : '创建'}失败`,
duration: 3000, variant: "destructive",
}); });
} finally { } finally {
setLoading(false); setLoading(false);
@ -131,6 +131,9 @@ const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
<DialogContent className="sm:max-w-[600px]"> <DialogContent className="sm:max-w-[600px]">
<DialogHeader> <DialogHeader>
<DialogTitle>{isEdit ? '编辑' : '新建'}</DialogTitle> <DialogTitle>{isEdit ? '编辑' : '新建'}</DialogTitle>
<DialogDescription>
{isEdit ? '编辑现有项目组信息' : '创建一个新的项目组'}
</DialogDescription>
</DialogHeader> </DialogHeader>
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4"> <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
@ -276,7 +279,7 @@ const ProjectGroupModal: React.FC<ProjectGroupModalProps> = ({
> >
</Button> </Button>
<Button type="submit" loading={loading}> <Button type="submit" disabled={loading}>
</Button> </Button>
</DialogFooter> </DialogFooter>