backup
This commit is contained in:
parent
a2db003a01
commit
c5abcf2166
|
@ -1,4 +1,4 @@
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ four more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
|
@ -9,16 +9,25 @@
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@heroicons/react": "^2.0.18",
|
||||||
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
|
"antd": "^5.16.1",
|
||||||
|
"axios": "^1.6.8",
|
||||||
|
"next": "14.1.3",
|
||||||
|
"postcss": "8.4.31",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"next": "14.1.3"
|
"tailwindcss": "3.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@vercel/style-guide": "^5.0.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.1.3"
|
"eslint-config-next": "14.1.3",
|
||||||
|
"prettier": "3.0.3",
|
||||||
|
"prettier-plugin-tailwindcss": "0.5.4",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Page() {
|
||||||
|
return <p>Customer Page</p>;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Page() {
|
||||||
|
return <p>Invoice Page</p>;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import SideNav from '@/app/ui/dashboard/sidenav';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
|
||||||
|
<div className="w-full flex-none md:w-64">
|
||||||
|
<SideNav />
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Page() {
|
||||||
|
return <p>Dashboard Page</p>;
|
||||||
|
}
|
|
@ -1,107 +0,0 @@
|
||||||
:root {
|
|
||||||
--max-width: 1100px;
|
|
||||||
--border-radius: 12px;
|
|
||||||
--font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
|
|
||||||
"Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
|
|
||||||
"Fira Mono", "Droid Sans Mono", "Courier New", monospace;
|
|
||||||
|
|
||||||
--foreground-rgb: 0, 0, 0;
|
|
||||||
--background-start-rgb: 214, 219, 220;
|
|
||||||
--background-end-rgb: 255, 255, 255;
|
|
||||||
|
|
||||||
--primary-glow: conic-gradient(
|
|
||||||
from 180deg at 50% 50%,
|
|
||||||
#16abff33 0deg,
|
|
||||||
#0885ff33 55deg,
|
|
||||||
#54d6ff33 120deg,
|
|
||||||
#0071ff33 160deg,
|
|
||||||
transparent 360deg
|
|
||||||
);
|
|
||||||
--secondary-glow: radial-gradient(
|
|
||||||
rgba(255, 255, 255, 1),
|
|
||||||
rgba(255, 255, 255, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 239, 245, 249;
|
|
||||||
--tile-end-rgb: 228, 232, 233;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#00000080,
|
|
||||||
#00000040,
|
|
||||||
#00000030,
|
|
||||||
#00000020,
|
|
||||||
#00000010,
|
|
||||||
#00000010,
|
|
||||||
#00000080
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 238, 240, 241;
|
|
||||||
--callout-border-rgb: 172, 175, 176;
|
|
||||||
--card-rgb: 180, 185, 188;
|
|
||||||
--card-border-rgb: 131, 134, 135;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--foreground-rgb: 255, 255, 255;
|
|
||||||
--background-start-rgb: 0, 0, 0;
|
|
||||||
--background-end-rgb: 0, 0, 0;
|
|
||||||
|
|
||||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
|
||||||
--secondary-glow: linear-gradient(
|
|
||||||
to bottom right,
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0),
|
|
||||||
rgba(1, 65, 255, 0.3)
|
|
||||||
);
|
|
||||||
|
|
||||||
--tile-start-rgb: 2, 13, 46;
|
|
||||||
--tile-end-rgb: 2, 5, 19;
|
|
||||||
--tile-border: conic-gradient(
|
|
||||||
#ffffff80,
|
|
||||||
#ffffff40,
|
|
||||||
#ffffff30,
|
|
||||||
#ffffff20,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff10,
|
|
||||||
#ffffff80
|
|
||||||
);
|
|
||||||
|
|
||||||
--callout-rgb: 20, 20, 20;
|
|
||||||
--callout-border-rgb: 108, 108, 108;
|
|
||||||
--card-rgb: 100, 100, 100;
|
|
||||||
--card-border-rgb: 200, 200, 200;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
max-width: 100vw;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: rgb(var(--foreground-rgb));
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
transparent,
|
|
||||||
rgb(var(--background-end-rgb))
|
|
||||||
)
|
|
||||||
rgb(var(--background-start-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Inter } from "next/font/google";
|
|
||||||
import "./globals.css";
|
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
import "@/app/ui/globals.css";
|
||||||
|
// import { inter } from '@/app/ui/fonts';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Create Next App",
|
||||||
|
@ -15,8 +14,8 @@ export default function RootLayout({
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html>
|
||||||
<body className={inter.className}>{children}</body>
|
<body>{children}</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export type Invoice = {
|
||||||
|
id: string;
|
||||||
|
customer_id: string;
|
||||||
|
amount: number;
|
||||||
|
date: string;
|
||||||
|
// In TypeScript, this is called a string union type.
|
||||||
|
// It means that the "status" property can only be one of the two strings: 'pending' or 'paid'.
|
||||||
|
status: 'pending' | 'paid';
|
||||||
|
};
|
||||||
|
type Status={
|
||||||
|
success:boolean;
|
||||||
|
code:number ;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
export type ResultPage<T> = {
|
||||||
|
content:T[];
|
||||||
|
totalPages:number;
|
||||||
|
totalElements:number;
|
||||||
|
|
||||||
|
}
|
||||||
|
export type ResponseVO<T>={
|
||||||
|
data:T;
|
||||||
|
timeStamp:number;
|
||||||
|
status:Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DataType ={
|
||||||
|
key: React.ReactNode;
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
state: number;
|
||||||
|
priority: number;
|
||||||
|
type:number;
|
||||||
|
action?:React.ReactNode;
|
||||||
|
children: DataType[];
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
const invoices = [
|
||||||
|
{
|
||||||
|
customer_id: customers[0].id,
|
||||||
|
amount: 15795,
|
||||||
|
status: 'pending',
|
||||||
|
date: '2022-12-06',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
customer_id: customers[1].id,
|
||||||
|
amount: 20348,
|
||||||
|
status: 'pending',
|
||||||
|
date: '2022-11-14',
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {unstable_noStore as noStore} from 'next/cache';
|
||||||
|
import axios, {AxiosResponse} from "axios";
|
||||||
|
import {DataType, ResponseVO, ResultPage} from "@/app/lib/definitions";
|
||||||
|
|
||||||
|
export async function taskTreeResult():Promise<ResponseVO<ResultPage<DataType>>> {
|
||||||
|
noStore();
|
||||||
|
try {
|
||||||
|
// 使用 Axios 发送 POST 请求获取数据
|
||||||
|
const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await axios.post('http://localhost:8090/task/tree', {
|
||||||
|
pageSize: 10,
|
||||||
|
pageNumber: 1
|
||||||
|
});
|
||||||
|
// 从响应中提取数据并返回
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
// 处理错误
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
// 返回一个默认值或者抛出错误
|
||||||
|
throw new Error('Failed to fetch data');
|
||||||
|
}
|
||||||
|
}
|
121
src/app/page.tsx
121
src/app/page.tsx
|
@ -1,94 +1,51 @@
|
||||||
import Image from "next/image";
|
import AcmeLogo from '@/app/ui/acme-logo';
|
||||||
import styles from "./page.module.css";
|
import { ArrowRightIcon } from '@heroicons/react/24/outline';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<main className={styles.main}>
|
<main className="flex min-h-screen flex-col p-6">
|
||||||
<div className={styles.description}>
|
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
|
||||||
<p>
|
<AcmeLogo/>
|
||||||
Get started by editing
|
</div>
|
||||||
<code className={styles.code}>src/app/page.tsx</code>
|
<div className="mt-4 flex grow flex-col gap-4 md:flex-row">
|
||||||
</p>
|
<div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
|
||||||
<div>
|
<p
|
||||||
<a
|
className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
>
|
||||||
By{" "}
|
<strong>Welcome to Acme.</strong> This is the example for the{' '}
|
||||||
<Image
|
<a href="https://nextjs.org/learn/" className="text-blue-500">
|
||||||
src="/vercel.svg"
|
Next.js Learn Course
|
||||||
alt="Vercel Logo"
|
|
||||||
className={styles.vercelLogo}
|
|
||||||
width={100}
|
|
||||||
height={24}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</a>
|
</a>
|
||||||
|
, brought to you by Vercel.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
href="/login"
|
||||||
|
className="flex items-center gap-5 self-start rounded-lg bg-blue-500 px-6 py-3 text-sm font-medium text-white transition-colors hover:bg-blue-400 md:text-base"
|
||||||
|
>
|
||||||
|
<span>Log in</span>
|
||||||
|
{/*<ArrowRightIcon className="w-5 md:w-6"/>*/}
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
|
||||||
|
{/* Add Hero Images Here */}
|
||||||
<div className={styles.center}>
|
|
||||||
<Image
|
<Image
|
||||||
className={styles.logo}
|
src="/hero-desktop.png"
|
||||||
src="/next.svg"
|
width={1000}
|
||||||
alt="Next.js Logo"
|
height={760}
|
||||||
width={180}
|
alt="Screenshots of the dashboard project showing desktop version"
|
||||||
height={37}
|
className="hidden md:block"
|
||||||
priority
|
/>
|
||||||
|
<Image
|
||||||
|
src="/hero-mobile.png"
|
||||||
|
width={560}
|
||||||
|
height={620}
|
||||||
|
alt="Screenshot of the dashboard project showing mobile version"
|
||||||
|
className="block md:hidden"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Docs <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Learn <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Templates <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>Explore starter templates for Next.js.</p>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
className={styles.card}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
Deploy <span>-></span>
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import SideNav from '@/app/ui/dashboard/sidenav';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
|
||||||
|
<div className="w-full flex-none md:w-64">
|
||||||
|
<SideNav />
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Page() {
|
||||||
|
return <p>Dashboard Page</p>;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import SideNav from '@/app/ui/dashboard/sidenav';
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div>{children}</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import TreeTable from "@/app/ui/task/project/TreeTable";
|
||||||
|
import {DetailForm} from "@/app/ui/task/project/DetailForm";
|
||||||
|
|
||||||
|
const Page: React.FC = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TreeTable/>
|
||||||
|
<DetailForm itemId={12} operationId={"1"}></DetailForm>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { GlobeAltIcon } from '@heroicons/react/24/outline';
|
||||||
|
// import { lusitana } from '@/app/ui/fonts';
|
||||||
|
|
||||||
|
export default function AcmeLogo() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex flex-row items-center leading-none text-white`}
|
||||||
|
>
|
||||||
|
{/*<GlobeAltIcon className="h-12 w-12 rotate-[15deg]" />*/}
|
||||||
|
<p className="text-[44px]">Acme</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {
|
||||||
|
UserGroupIcon,
|
||||||
|
HomeIcon,
|
||||||
|
DocumentDuplicateIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
|
// Map of links to display in the side navigation.
|
||||||
|
// Depending on the size of the application, this would be stored in a database.
|
||||||
|
const links = [
|
||||||
|
{ name: 'Home', href: '/dashboard', icon: HomeIcon },
|
||||||
|
{
|
||||||
|
name: 'Invoices',
|
||||||
|
href: '/dashboard/invoices',
|
||||||
|
icon: DocumentDuplicateIcon,
|
||||||
|
},
|
||||||
|
{ name: 'Customers', href: '/dashboard/customers', icon: UserGroupIcon },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function NavLinks() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{links.map((link) => {
|
||||||
|
const LinkIcon = link.icon;
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={link.name}
|
||||||
|
href={link.href}
|
||||||
|
className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
|
||||||
|
>
|
||||||
|
{/*<LinkIcon className="w-6" />*/}
|
||||||
|
<p className="hidden md:block">{link.name}</p>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Link from 'next/link';
|
||||||
|
import NavLinks from '@/app/ui/dashboard/nav-links';
|
||||||
|
import AcmeLogo from '@/app/ui/acme-logo';
|
||||||
|
import { PowerIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
|
export default function SideNav() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full flex-col px-3 py-4 md:px-2">
|
||||||
|
<Link
|
||||||
|
className="mb-2 flex h-20 items-end justify-start rounded-md bg-blue-600 p-4 md:h-40"
|
||||||
|
href="/public"
|
||||||
|
>
|
||||||
|
<div className="w-32 text-white md:w-40">
|
||||||
|
<AcmeLogo />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
|
||||||
|
<NavLinks />
|
||||||
|
<div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
|
||||||
|
<form>
|
||||||
|
<button className="flex h-[48px] w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
|
||||||
|
<PowerIcon className="w-6" />
|
||||||
|
<div className="hidden md:block">Sign Out</div>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// import { Inter,Lusitana } from 'next/font/google';
|
||||||
|
//
|
||||||
|
// export const inter = Inter({ subsets: ['latin'] });
|
||||||
|
// export const lusitana = Lusitana({
|
||||||
|
// weight: ['400', '700'],
|
||||||
|
// subsets: ['latin'],
|
||||||
|
// });
|
|
@ -0,0 +1,18 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
input[type='number'] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
'use client'
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Cascader,
|
||||||
|
Checkbox,
|
||||||
|
ColorPicker,
|
||||||
|
DatePicker,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Radio,
|
||||||
|
Select,
|
||||||
|
Slider,
|
||||||
|
Switch,
|
||||||
|
TreeSelect,
|
||||||
|
Upload,
|
||||||
|
} from 'antd';
|
||||||
|
export interface DetailFormProps {
|
||||||
|
itemId: number,
|
||||||
|
operationId: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DetailForm: React.FC<DetailFormProps> = (props) => {
|
||||||
|
|
||||||
|
|
||||||
|
const [componentDisabled, setComponentDisabled] =
|
||||||
|
useState<boolean>(props.operationId==='1');
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
const normFile = (e: any) => {
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return e?.fileList;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Checkbox
|
||||||
|
checked={componentDisabled}
|
||||||
|
onChange={(e) => setComponentDisabled(e.target.checked)}
|
||||||
|
>
|
||||||
|
Form disabled
|
||||||
|
</Checkbox>
|
||||||
|
<Form
|
||||||
|
labelCol={{span: 4}}
|
||||||
|
wrapperCol={{span: 14}}
|
||||||
|
layout="horizontal"
|
||||||
|
disabled={componentDisabled}
|
||||||
|
style={{maxWidth: 600}}
|
||||||
|
>
|
||||||
|
<Form.Item label="Checkbox" name="disabled" valuePropName="checked">
|
||||||
|
<Checkbox>Checkbox</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Radio">
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="apple"> Apple </Radio>
|
||||||
|
<Radio value="pear"> Pear </Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Input">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Select">
|
||||||
|
<Select>
|
||||||
|
<Select.Option value="demo">Demo</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="TreeSelect">
|
||||||
|
<TreeSelect
|
||||||
|
treeData={[
|
||||||
|
{title: 'Light', value: 'light', children: [{title: 'Bamboo', value: 'bamboo'}]},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Cascader">
|
||||||
|
<Cascader
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'zhejiang',
|
||||||
|
label: 'Zhejiang',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 'hangzhou',
|
||||||
|
label: 'Hangzhou',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="DatePicker">
|
||||||
|
<DatePicker/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="RangePicker">
|
||||||
|
<RangePicker/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="InputNumber">
|
||||||
|
<InputNumber/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="TextArea">
|
||||||
|
<TextArea rows={4}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Switch" valuePropName="checked">
|
||||||
|
<Switch/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Upload" valuePropName="fileList" getValueFromEvent={normFile}>
|
||||||
|
<Upload action="/upload.do" listType="picture-card">
|
||||||
|
<button style={{border: 0, background: 'none'}} type="button">
|
||||||
|
<PlusOutlined/>
|
||||||
|
<div style={{marginTop: 8}}>Upload</div>
|
||||||
|
</button>
|
||||||
|
</Upload>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Button">
|
||||||
|
<Button>Button</Button>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Slider">
|
||||||
|
<Slider/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="ColorPicker">
|
||||||
|
<ColorPicker/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import React, {Fragment} from "react";
|
||||||
|
import {Button, Dropdown, MenuProps, Modal, Popconfirm, Space} from "antd";
|
||||||
|
import {DownOutlined, QuestionCircleOutlined} from "@ant-design/icons";
|
||||||
|
import {DetailForm} from "@/app/ui/task/project/DetailForm";
|
||||||
|
|
||||||
|
export interface OperationButtonProps {
|
||||||
|
itemId: number
|
||||||
|
}
|
||||||
|
interface OperationModelProps {
|
||||||
|
operationId: string,
|
||||||
|
openModal:boolean
|
||||||
|
}
|
||||||
|
class OperationButton extends React.Component<OperationButtonProps,OperationModelProps> {
|
||||||
|
|
||||||
|
constructor(props: OperationButtonProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
operationId: '',
|
||||||
|
openModal:false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const handleCancel =()=>{
|
||||||
|
this.setState({...this.state,openModal:false})
|
||||||
|
}
|
||||||
|
const items: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: <a onClick={(e) => {
|
||||||
|
this.setState({...this.state,openModal:true})
|
||||||
|
}}>详情</a>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: <a onClick={(e) => {
|
||||||
|
this.setState({...this.state,openModal:true})
|
||||||
|
}}>添加下级</a>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: <a onClick={(e) => {
|
||||||
|
this.setState({...this.state,openModal:true})
|
||||||
|
}}>修改</a>,
|
||||||
|
}
|
||||||
|
,
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: <Popconfirm
|
||||||
|
title="删除任务"
|
||||||
|
description="确认要删除任务?"
|
||||||
|
icon={<QuestionCircleOutlined style={{color: 'red'}}/>}
|
||||||
|
okText="确认"
|
||||||
|
cancelText="取消"
|
||||||
|
>删除</Popconfirm>,
|
||||||
|
}
|
||||||
|
,
|
||||||
|
{
|
||||||
|
key: '5',
|
||||||
|
label: <Popconfirm
|
||||||
|
title="完成任务"
|
||||||
|
description="确认要完成任务?"
|
||||||
|
okText="确认"
|
||||||
|
cancelText="取消"
|
||||||
|
>完成</Popconfirm>,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return <Fragment>
|
||||||
|
<Dropdown menu={{items}}>
|
||||||
|
<a onClick={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
}}>
|
||||||
|
<Space>
|
||||||
|
操作<DownOutlined/>
|
||||||
|
</Space>
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
<Modal
|
||||||
|
open={this.state.openModal}
|
||||||
|
title="Title"
|
||||||
|
// open={open}
|
||||||
|
// onOk={handleOk}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
// footer={[
|
||||||
|
// <Button key="back" onClick={handleCancel}>
|
||||||
|
// Return
|
||||||
|
// </Button>,
|
||||||
|
// <Button key="submit" type="primary" loading={loading} onClick={handleOk}>
|
||||||
|
// Submit
|
||||||
|
// </Button>,
|
||||||
|
// <Button
|
||||||
|
// key="link"
|
||||||
|
// href="https://google.com"
|
||||||
|
// type="primary"
|
||||||
|
// loading={loading}
|
||||||
|
// onClick={handleOk}
|
||||||
|
// >
|
||||||
|
// Search on Google
|
||||||
|
// </Button>,
|
||||||
|
// ]}
|
||||||
|
>
|
||||||
|
<DetailForm itemId={this.props.itemId} operationId={'1'}/>
|
||||||
|
</Modal>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OperationButton;
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
'use client'
|
||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
import {Button, ColorPicker, Dropdown, MenuProps, Space, Switch, Table} from 'antd';
|
||||||
|
import type { TableColumnsType, TableProps } from 'antd';
|
||||||
|
import { taskTreeResult} from "@/app/lib/task/project/data";
|
||||||
|
import {DataType, ResponseVO, ResultPage} from "@/app/lib/definitions";
|
||||||
|
import {DownOutlined} from "@ant-design/icons";
|
||||||
|
import OperationButton from "@/app/ui/task/project/OperationButton";
|
||||||
|
|
||||||
|
type TableRowSelection<T> = TableProps<T>['rowSelection'];
|
||||||
|
|
||||||
|
|
||||||
|
const columns: TableColumnsType<DataType> = [
|
||||||
|
{
|
||||||
|
title: '任务编码',
|
||||||
|
dataIndex: 'code',
|
||||||
|
key: 'code',
|
||||||
|
width: '10%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: '20%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
width: '30%',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
width: '10%',
|
||||||
|
key: 'state',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '优先级',
|
||||||
|
dataIndex: 'priority',
|
||||||
|
width: '10%',
|
||||||
|
key: 'priority',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
width: '10%',
|
||||||
|
key: 'action',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// rowSelection objects indicates the need for row selection
|
||||||
|
const rowSelection: TableRowSelection<DataType> = {
|
||||||
|
onChange: (selectedRowKeys, selectedRows) => {
|
||||||
|
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||||
|
},
|
||||||
|
onSelect: (record, selected, selectedRows) => {
|
||||||
|
console.log(record, selected, selectedRows);
|
||||||
|
},
|
||||||
|
onSelectAll: (selected, selectedRows, changeRows) => {
|
||||||
|
console.log(selected, selectedRows, changeRows);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
function recursionActionChild(children:DataType[]){
|
||||||
|
if (children.length===0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
children.forEach(item=>{
|
||||||
|
item.key=item.id;
|
||||||
|
item.action=(<OperationButton itemId={item.id}></OperationButton>)
|
||||||
|
recursionActionChild(item.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const TreeTable: React.FC = () => {
|
||||||
|
// const [checkStrictly, setCheckStrictly] = useState(false);
|
||||||
|
const [data, setData] = useState<DataType[]>([]);
|
||||||
|
const [pageNumber, setPageNumber] = useState<number>(1);
|
||||||
|
const [pageSize, setPageSize] = useState<number>(10);
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("useEffect::taskTreeResult")
|
||||||
|
taskTreeResult().then((result:ResponseVO<ResultPage<DataType>>)=>{
|
||||||
|
if (result.status.success){
|
||||||
|
recursionActionChild(result.data.content);
|
||||||
|
setData(result.data.content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*<Space align="center" style={{ marginBottom: 16 }}>*/}
|
||||||
|
{/* CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />*/}
|
||||||
|
{/*</Space>*/}
|
||||||
|
<Button type="primary">添加主线任务</Button>
|
||||||
|
<ColorPicker defaultValue="#1677ff" showText/>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
// rowSelection={{ ...rowSelection, checkStrictly}}
|
||||||
|
dataSource={data}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TreeTable;
|
Loading…
Reference in New Issue