Compare commits

..

10 Commits

33 changed files with 833 additions and 232 deletions

View File

@ -6,12 +6,18 @@
1. 层级展示任务
2. 可在操作中四象线展示子任务
![任务树](tree.png)
![任务树](help/tree.png)
## 四象线
1. 向上重要
2. 向左紧急
![四象线](four.png)
![四象线](help/four.png)
## 日历
1. 月,周,日展示
2. 只有期望开始时间和期望结束时间都填写的时候才会在日历中展示
![](help/week.png)
![](help/day.png)
![](help/month.png)
## 项目启动
### 后端服务启动
1. 后端启动应用需求dockerredismysql8。
@ -38,3 +44,5 @@ docker build -t task-manager-nginx .
```shell
docker run -d -p 3001:3001 --restart unless-stopped --name task-manager-nginx task-manager-nginx
```
## 查看页面
http://taskmanagerserver.com:3001

View File

@ -2,7 +2,7 @@ FROM openjdk:8
WORKDIR /app
COPY task-manager-server.jar /app/task-manager-server.jar
EXPOSE 8090
CMD ["java", "-jar", "task-manager-server.jar"]
CMD ["java", "-jar", "task-manager-server.jar","--spring.profiles.active=docker"]
# 指定文件名 当前路径
# docker build -t task-manager-server -f Dockerfile-server .
# docker run -d -p 8090:8090 --restart unless-stopped -v ./hosts:/etc/hosts --name task-manager-server task-manager-server

Binary file not shown.

BIN
help/day.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

View File

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 176 KiB

BIN
help/month.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

BIN
help/week.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

View File

@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// Middleware cannot be used with "output: export".
output: 'export',
// Optional: Change links `/me` -> `/me/` and emit `/me.html` -> `/me/index.html`
// trailingSlash: true,
@ -8,7 +9,7 @@ const nextConfig = {
// skipTrailingSlashRedirect: true,
// Optional: Change the output directory `out` -> `dist`
// distDir: 'dist',
distDir: 'docker/out',
};
export default nextConfig;

206
package-lock.json generated
View File

@ -16,12 +16,15 @@
"next": "14.1.3",
"postcss": "8.4.31",
"react": "^18",
"react-big-calendar": "^1.12.2",
"react-dom": "^18",
"sass": "^1.77.3",
"tailwindcss": "3.3.3"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-big-calendar": "^1.8.9",
"@types/react-dom": "^18",
"@vercel/style-guide": "^5.0.1",
"eslint": "^8",
@ -1371,6 +1374,15 @@
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rc-component/color-picker": {
"version": "1.5.3",
"resolved": "https://registry.npmmirror.com/@rc-component/color-picker/-/color-picker-1.5.3.tgz",
@ -1483,6 +1495,17 @@
"react-dom": ">=16.9.0"
}
},
"node_modules/@restart/hooks": {
"version": "0.4.16",
"resolved": "https://registry.npmmirror.com/@restart/hooks/-/hooks-0.4.16.tgz",
"integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
"dependencies": {
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.7.2",
"resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz",
@ -1508,6 +1531,12 @@
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
}
},
"node_modules/@types/date-arithmetic": {
"version": "4.1.4",
"resolved": "https://registry.npmmirror.com/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz",
"integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -1538,20 +1567,29 @@
"node_modules/@types/prop-types": {
"version": "15.7.11",
"resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.11.tgz",
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
"dev": true
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
},
"node_modules/@types/react": {
"version": "18.2.64",
"resolved": "https://registry.npmmirror.com/@types/react/-/react-18.2.64.tgz",
"integrity": "sha512-MlmPvHgjj2p3vZaxbQgFUQFvD8QiZwACfGqEdDSWou5yISWxDQ4/74nCAwsUiX7UFLKZz3BbVSPj+YxeoGGCfg==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-big-calendar": {
"version": "1.8.9",
"resolved": "https://registry.npmmirror.com/@types/react-big-calendar/-/react-big-calendar-1.8.9.tgz",
"integrity": "sha512-HIHLUxR3PzWHrFdZ00VnCMvDjAh5uzlL0vMC2b7tL3bKaAJsqq9T8h+x0GVeDbZfMfHAd1cs5tZBhVvourNJXQ==",
"dev": true,
"dependencies": {
"@types/date-arithmetic": "*",
"@types/prop-types": "*",
"@types/react": "*"
}
},
"node_modules/@types/react-dom": {
"version": "18.2.21",
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.2.21.tgz",
@ -1564,8 +1602,7 @@
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.8.tgz",
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
"dev": true
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
},
"node_modules/@types/semver": {
"version": "7.5.8",
@ -1573,6 +1610,11 @@
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
"dev": true
},
"node_modules/@types/warning": {
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/@types/warning/-/warning-3.0.3.tgz",
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.21.0",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
@ -2401,6 +2443,14 @@
"resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
@ -2496,6 +2546,11 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
"node_modules/date-arithmetic": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/date-arithmetic/-/date-arithmetic-4.1.0.tgz",
"integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg=="
},
"node_modules/dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
@ -2564,7 +2619,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -2621,6 +2675,15 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@ -3884,6 +3947,11 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/globalize": {
"version": "0.1.1",
"resolved": "https://registry.npmmirror.com/globalize/-/globalize-0.1.1.tgz",
"integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA=="
},
"node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
@ -4025,6 +4093,11 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "4.3.6",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz",
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
@ -4086,6 +4159,14 @@
"node": ">= 0.4"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-array-buffer": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
@ -4606,6 +4687,11 @@
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -4635,6 +4721,19 @@
"node": "14 || >=16.14"
}
},
"node_modules/luxon": {
"version": "3.4.4",
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz",
"integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
"engines": {
"node": ">=12"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
@ -4717,6 +4816,25 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
"version": "0.5.45",
"resolved": "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.45.tgz",
"integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==",
"dependencies": {
"moment": "^2.29.4"
},
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@ -5978,6 +6096,33 @@
"node": ">=0.10.0"
}
},
"node_modules/react-big-calendar": {
"version": "1.12.2",
"resolved": "https://registry.npmmirror.com/react-big-calendar/-/react-big-calendar-1.12.2.tgz",
"integrity": "sha512-cPVcwH5V1YiC6QKaV4afvpuZ2DtP8+TocnZY98nGodqq8bfjVDiP3Ch+TewBZzj9mg7JbewHdufDZXZBqQl1lw==",
"dependencies": {
"@babel/runtime": "^7.20.7",
"clsx": "^1.2.1",
"date-arithmetic": "^4.1.0",
"dayjs": "^1.11.7",
"dom-helpers": "^5.2.1",
"globalize": "^0.1.1",
"invariant": "^2.2.4",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"luxon": "^3.2.1",
"memoize-one": "^6.0.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.40",
"prop-types": "^15.8.1",
"react-overlays": "^5.2.1",
"uncontrollable": "^7.2.1"
},
"peerDependencies": {
"react": "^16.14.0 || ^17 || ^18",
"react-dom": "^16.14.0 || ^17 || ^18"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.2.0.tgz",
@ -6000,6 +6145,25 @@
"resolved": "https://registry.npmmirror.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-overlays": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/react-overlays/-/react-overlays-5.2.1.tgz",
"integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==",
"dependencies": {
"@babel/runtime": "^7.13.8",
"@popperjs/core": "^2.11.6",
"@restart/hooks": "^0.4.7",
"@types/warning": "^3.0.0",
"dom-helpers": "^5.2.0",
"prop-types": "^15.7.2",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"react": ">=16.3.0",
"react-dom": ">=16.3.0"
}
},
"node_modules/reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
@ -6307,6 +6471,22 @@
"node": ">=10"
}
},
"node_modules/sass": {
"version": "1.77.3",
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.77.3.tgz",
"integrity": "sha512-WJHo+jmFp0dwRuymPmIovuxHaBntcCyja5hCB0yYY9wWrViEp4kF5Cdai98P72v6FzroPuABqu+ddLMbQWmwzA==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz",
@ -7064,6 +7244,20 @@
"which-boxed-primitive": "^1.0.2"
}
},
"node_modules/uncontrollable": {
"version": "7.2.1",
"resolved": "https://registry.npmmirror.com/uncontrollable/-/uncontrollable-7.2.1.tgz",
"integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@types/react": ">=16.9.11",
"invariant": "^2.2.4",
"react-lifecycles-compat": "^3.0.4"
},
"peerDependencies": {
"react": ">=15.0.0"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",

View File

@ -17,12 +17,15 @@
"next": "14.1.3",
"postcss": "8.4.31",
"react": "^18",
"react-big-calendar": "^1.12.2",
"react-dom": "^18",
"sass": "^1.77.3",
"tailwindcss": "3.3.3"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-big-calendar": "^1.8.9",
"@types/react-dom": "^18",
"@vercel/style-guide": "^5.0.1",
"eslint": "^8",

View File

@ -1,5 +1,6 @@
import type { Metadata } from "next";
import "@/ui/globals.css";
import Script from "next/script";
export const metadata: Metadata = {
title: "任务管理",
@ -17,7 +18,9 @@ export default function RootLayout({
}>) {
return (
<html>
<body style={{margin: 0}}>{children}</body>
<Script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"/>
<Script src="https://cdn.jsdelivr.net/npm/dayjs@1/locale/zh-cn.js"/>
<body style={{margin: 0}}>{children}</body>
</html>
);
);
}

View File

View File

@ -1,12 +1,16 @@
'use client'
import {usePathname, useRouter} from "next/navigation";
import { useRouter} from "next/navigation";
import dayjs from "dayjs";
import {useEffect} from "react";
export default function Home() {
console.log('app.usePathname()', usePathname());
const { replace } = useRouter();
replace("/task/four")
return (
<main className="flex min-h-screen flex-col p-6">
</main>
);
const {replace} = useRouter();
useEffect(()=>{
replace("/task/four")
},[])
dayjs.locale('zh-cn')
return (
<main className="flex min-h-screen flex-col p-6">
</main>
);
}

View File

@ -1,12 +0,0 @@
export const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request: Request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}

View File

@ -0,0 +1,10 @@
import CalShow from "@/ui/task/calendar/CalShow";
const Page: React.FC = () => {
return (
<>
<CalShow/>
</>
);
};
export default Page;

View File

@ -44,7 +44,7 @@ export type DataType ={
expectedTimeRange?:(string|Dayjs|undefined)[];
actualStartTime?:Date;
actualEndTime?:Date;
actualTimeRange?:(string|Dayjs)[]
actualTimeRange?:(string|Dayjs|undefined)[]
children: DataType[]|undefined;
}
export type DictType={
@ -54,3 +54,8 @@ export type DictType={
order:number;
color:string;
}
export type SearchObject={
name: string,
value: any,
operateType:string,
}

View File

@ -0,0 +1,6 @@
import {Event} from "react-big-calendar";
export interface TaskEvent extends Event {
id?: any;
state?:any;
priority?:any;
}

View File

@ -1,7 +1,6 @@
import {unstable_noStore as noStore} from 'next/cache';
import axios, {AxiosResponse} from "axios";
import {DataType, DictType, ResponseVO, ResultPage} from "@/lib/definitions";
import {message} from "antd";
export async function getTaskTreeResult(requestParam:string): Promise<ResponseVO<ResultPage<DataType>>> {
noStore();
try {
@ -217,5 +216,6 @@ export enum OPERATION_BUTTON_TYPE {
DELETE,
COMPLETE,
SHOW_FOUR,
SHOW_CALENDAR,
ADD,
}

View File

@ -1,12 +0,0 @@
import { GlobeAltIcon } from '@heroicons/react/24/outline';
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>
);
}

View File

@ -1,37 +0,0 @@
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>
);
})}
</>
);
}

View File

@ -1,29 +0,0 @@
import Link from 'next/link';
import NavLinks from '@/ui/dashboard/nav-links';
import AcmeLogo from '@/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>
);
}

View File

@ -1,7 +0,0 @@
// import { Inter,Lusitana } from 'next/font/google';
//
// export const inter = Inter({ subsets: ['latin'] });
// export const lusitana = Lusitana({
// weight: ['400', '700'],
// subsets: ['latin'],
// });

View File

@ -4,6 +4,7 @@ import {DownOutlined, QuestionCircleOutlined} from "@ant-design/icons";
import {DetailForm} from "@/ui/task/four/DetailForm";
import {commonUpdate, deleteTask, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
import Link from "next/link";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
export interface OperationButtonProps {
itemId: number,
@ -14,7 +15,7 @@ export interface OperationButtonProps {
}
interface OperationModelProps {
operationId: number | undefined,
operationId: number,
pPid: number,
pid: number,
openModal: boolean
@ -27,7 +28,7 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
this.state = {
pid: props.pid,
pPid: props.pPid,
operationId: undefined,
operationId: 0,
openModal: false
};
}
@ -89,12 +90,12 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
cancelText="取消"
onConfirm={() => {
commonUpdate({
updateColoumList:[{
updateColumnList:[{
name:'state',
code:'state',
value:'7'
}],
conditionColoumList:[{
conditionColumnList:[{
name:'id',
code:'id',
operateType:'=',
@ -113,6 +114,10 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
{
key: OPERATION_BUTTON_TYPE.SHOW_FOUR,
label: <Link href={"/task/four?pid=" + this.props.itemId}></Link>,
},
{
key: OPERATION_BUTTON_TYPE.SHOW_CALENDAR,
label: <Link href={"/task/calendar?pid=" + this.props.itemId}></Link>,
}
];
return <Fragment>
@ -125,42 +130,17 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
</Space>
</a>
</Dropdown>
<Modal
maskClosable={false}
destroyOnClose={true}
open={this.state.openModal}
title={this.state.operationId === OPERATION_BUTTON_TYPE.DETAIL ? '任务详情' :
{this.state.openModal&&<DetailModelForm
haveButton={false}
itemId={this.state.operationId === OPERATION_BUTTON_TYPE.UPDATE||this.state.operationId === OPERATION_BUTTON_TYPE.DETAIL?this.props.itemId:undefined}
pPid={this.props.pPid}
pid={this.state.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ?this.props.itemId:undefined}
operationId={this.state.operationId}
description={this.state.operationId === OPERATION_BUTTON_TYPE.DETAIL ? '任务详情' :
this.state.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ? '添加支线任务' :
this.state.operationId === OPERATION_BUTTON_TYPE.UPDATE ? '修改任务' : '未知操作'}
// open={open}
// onOk={handleOk}
onCancel={handleCancel}
footer={[]}
width={800}
// 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={this.state.operationId}
handleCancel={handleCancel}
pPid={this.props.pPid}
/>
</Modal>
open={this.state.openModal}
reloadData={handleCancel}/>}
</Fragment>
}
}

View File

@ -1,4 +1,4 @@
import React, {useContext} from "react";
import React, {Fragment, useContext} from "react";
import {Button, DatePicker, Select, Space} from "antd";
import {usePathname, useRouter} from "next/navigation";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
@ -23,25 +23,39 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
console.log('usePathname()', usePathname());
const data = useContext(LocalContext);
const {RangePicker} = DatePicker;
const expectStartTimeParseResult:RequestDateType[] = data.expectedStartTime.length>0?JSON.parse(data.expectedStartTime):[undefined,undefined]
expectStartTimeParseResult.map(item => item&&item.value ? dayjs(item.value.toString()) : undefined)
const defaultExpectStartTime:[start: Dayjs | null | undefined, end: Dayjs | null | undefined] = [
expectStartTimeParseResult[0]&&expectStartTimeParseResult[0].value ? dayjs(expectStartTimeParseResult[0].value.toString()) : undefined,
expectStartTimeParseResult[1]&&expectStartTimeParseResult[1].value ? dayjs(expectStartTimeParseResult[1].value.toString()) : undefined
const expectStartTimeParseResult: RequestDateType[] = data.expectedStartTime.length > 0 ? JSON.parse(data.expectedStartTime) : [undefined, undefined]
expectStartTimeParseResult.map(item => item && item.value ? dayjs(item.value.toString()) : undefined)
const defaultExpectStartTime: [start: Dayjs | null | undefined, end: Dayjs | null | undefined] = [
expectStartTimeParseResult[0] && expectStartTimeParseResult[0].value ? dayjs(expectStartTimeParseResult[0].value.toString()) : undefined,
expectStartTimeParseResult[1] && expectStartTimeParseResult[1].value ? dayjs(expectStartTimeParseResult[1].value.toString()) : undefined
];
return <Space style={{marginTop: 0 ,"height": "42px", "alignContent": "center"}}>
<DetailModelForm operationId={OPERATION_BUTTON_TYPE.ADD} description='添加主线任务' reloadData={refreshData}/>
{usePathname().startsWith("/task/project") ?
<>
<Button type="primary" onClick={() => {
replace("/task/four");
// setCurrentPath("/task/four");
}}></Button>
</> : <>
<Button type="primary" onClick={() => {
replace("/task/project");
// setCurrentPath("/task/project")
}}></Button>
return <Space style={{marginTop: 0, "height": "42px", "alignContent": "center"}}>
<DetailModelForm haveButton={true} open={false} operationId={OPERATION_BUTTON_TYPE.ADD}
description='添加主线任务' reloadData={refreshData}/>
{
!usePathname().startsWith("/task/project") &&
<Button type="primary" onClick={() => {
replace("/task/project");
// setCurrentPath("/task/project")
}}></Button>
}
{
!usePathname().startsWith("/task/four") &&
<Button type="primary" onClick={() => {
replace("/task/four");
// setCurrentPath("/task/four");
}}></Button>
}
{
!usePathname().startsWith("/task/calendar") &&
<Button type="primary" onClick={() => {
replace("/task/calendar");
// setCurrentPath("/task/project")
}}></Button>
}
{
/*日历需要状态*/
!usePathname().startsWith("/task/project") && <Fragment>
<span style={{whiteSpace: 'nowrap'}}>:</span>
<Select
mode="multiple"
@ -57,6 +71,11 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
return {label: item.name, value: item.code}
})}
/>
</Fragment>
}
{
/*四相线需要状态时间*/
usePathname().startsWith("/task/four") && <Fragment>
<span style={{whiteSpace: 'nowrap'}}>:</span>
<RangePicker
placeholder={['开始时间', '结束时间']}
@ -75,7 +94,7 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
'value': dates[0],
'operateType': ">="
});
}else{
} else {
expectStartTimeList.push(undefined)
}
if (dates[1]) {
@ -84,13 +103,15 @@ export const TitleOperation: React.FC<TitleOperationProps> = ({
'value': dates[1].add(1, 'day'),
'operateType': "<"
})
}else{
} else {
expectStartTimeList.push(undefined)
}
setExpectedStartTime(JSON.stringify(expectStartTimeList))
}}
/>
</>
</Fragment>
}
</Space>
}

View File

@ -0,0 +1,314 @@
'use client'
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from "react";
import {Calendar, dayjsLocalizer, Event, SlotInfo, View} from 'react-big-calendar'
// https://day.js.org/docs/zh-CN/get-set/get-set
import dayjs, {Dayjs} from 'dayjs'
import 'react-big-calendar/lib/css/react-big-calendar.css'
import 'react-big-calendar/lib/sass/styles.scss'
import 'react-big-calendar/lib/addons/dragAndDrop/styles.scss'
import '@/ui/task/calendar/index.modules.css'
import {commonUpdate, getTaskTreeResult, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
import {useSearchParams} from "next/dist/client/components/navigation";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
import {SearchObject} from "@/lib/definitions";
import LocalContext from "@/ui/LocalContent";
import withDragAndDrop, {EventInteractionArgs} from "react-big-calendar/lib/addons/dragAndDrop";
import {TaskEvent} from "@/lib/task/calendar/data";
import {number} from "prop-types";
import {DATE_TIME} from "@/lib/constants";
/**
* https://github.com/jquense/react-big-calendar?tab=readme-ov-file
* @constructor
*/
const localizer = dayjsLocalizer(dayjs)
const DragAndDropCalendar = withDragAndDrop(Calendar)
const CalShow: React.FC = () => {
dayjs.locale('zh-cn')
const [view, setView] = useState<View>('week');
const [date, setDate] = useState<Date>(new Date());
const clickRef = useRef<number|undefined|null>(null)
// 展示在页面的任务,默认获取当前月的信息。
const [events, setEvents] = useState<TaskEvent[]>([]);
const [open, setOpen] = useState(false);
const [description, setDescription] = useState('');
const [operationId, setOperationId] = useState(-1);
const [itemId, setItemId] = useState(-1);
const [expectedStartTime, setExpectedStartTime] = useState<Dayjs>();
const [expectedEndTime, setExpectedEndTime] = useState<Dayjs>();
const [range, setRange] = useState<{start: Date; end: Date}>({
start: dayjs(date).startOf('week').toDate(),
end: dayjs(date).endOf('week').toDate()
});
let state: string = useContext(LocalContext).taskState
const handleViewChange = (newView: View) => {
setView(newView);
};
var pid = useSearchParams().get('pid');
function clearClickTimeout(){
clickRef && typeof clickRef.current=== 'number' &&!isNaN(clickRef.current) && isFinite(clickRef.current)&&window.clearTimeout(clickRef.current)
}
const handleNavigate = (newDate: Date) => {
console.log('handleNavigate', newDate)
setDate(newDate);
const searchList: SearchObject[] = []
if (pid != null) {
searchList.push({name: "pid", value: pid, operateType: "="}, {
name: 'TREE',
value: "false",
operateType: "TREE"
});
}
searchList.push({name: "expectedStartTime", value: range.start, operateType: ">="})
searchList.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
loadData(searchList);
};
useEffect(() => {
console.log("CalShow:useEffect:range",range)
const searchListE = []
if (pid != null) {
searchListE.push({name: "pid", value: pid, operateType: "="}, {
name: 'TREE',
value: "false",
operateType: "TREE"
});
}
searchListE.push({name: 'expectedStartTime', value: range.start, operateType: ">="})
searchListE.push({name: 'expectedStartTime', value: range.end, operateType: "<="})
loadData(searchListE);
/**
* What Is This?
* This is to prevent a memory leak, in the off chance that you
* teardown your interface prior to the timed method being called.
*/
return () => {
clearClickTimeout()
}
}, [useContext(LocalContext),range]);
const message = {
week: '周',
work_week: '工作周',
day: '天',
month: '月',
previous: '前',
next: '后',
today: '当下',
agenda: '日程'
}
const loadData = (searchList: SearchObject[]) => {
if (state.length > 0) {
searchList.push({name: 'state', value: state, operateType: "IN"})
}
searchList.push({name: 'expectedEndTime', value: dayjs(date).endOf('month'), operateType: "NOT NULL"})
let request = JSON.stringify({
pageSize: 9999,
pageNumber: 1,
data: searchList
})
getTaskTreeResult(request).then(responseD => {
if (responseD.status.success) {
let result:TaskEvent[] =responseD.data.content.map<TaskEvent>(taskState => {
return {
start: dayjs(taskState.expectedStartTime).toDate(),
end: dayjs(taskState.expectedEndTime).toDate(),
title: taskState.name,
resource: taskState.id,
id:taskState.id,
state:taskState.state,
priority:taskState.priority
}
});
console.log('responseD.data.content:',result)
setEvents([...result])
}
})
}
const reloadData = () => {
setOpen(false)
handleNavigate(expectedStartTime ? expectedStartTime.toDate() : date)
}
const handleSelectSlot = useCallback(
({start, end}: SlotInfo) => {
setExpectedEndTime(dayjs(end))
setExpectedStartTime(dayjs(start))
setOperationId(OPERATION_BUTTON_TYPE.ADD)
setDescription("添加任务")
setOpen(true);
},
[setEvents]
)
const handleSelectEvent = useCallback(
(event: Event, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout()
clickRef.current = window.setTimeout(()=> {
// window.alert(event.title);
console.log(event)
setOperationId(OPERATION_BUTTON_TYPE.DETAIL)
setDescription("任务详情")
setItemId(event.resource)
setOpen(true);
},250)
},
[]
)
const {defaultDate, scrollToTime} = useMemo(
() => ({
defaultDate: new Date(2015, 3, 12),
scrollToTime: new Date(1970, 1, 1, 6),
}),
[]
)
const doubleClick = (event: TaskEvent, e: React.SyntheticEvent<HTMLElement>) => {
clearClickTimeout()
clickRef.current = window.setTimeout(()=>{
// 数据落库
commonUpdate({
updateColumnList: [{
name: '任务状态',
code: 'state',
value: 7
}],
conditionColumnList: [{
name: 'id',
code: 'id',
operateType: '=',
value: event.resource
}]
})
setEvents((prev: TaskEvent[]) => {
const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource);
const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource);
let result: TaskEvent[] = [];
if (existing !== undefined&&filtered !== undefined) {
result= [...filtered, {...existing, state:7}];
}
console.log('result',result)
return result;
})
},250)
}
const moveEvent = useCallback(
({event, start, end, isAllDay: droppedOnAllDaySlot = false}: EventInteractionArgs<TaskEvent>) => {
console.log("onEventResize || onEventDrop :",start,end)
const {allDay} = event
if (!allDay && droppedOnAllDaySlot) {
event.allDay = true
}
// 数据落库
commonUpdate({
updateColumnList: [{
name: 'expectedStartTime',
code: '',
value: start.toLocaleString()
}, {
name: 'expectedEndTime',
code: '',
value: end.toLocaleString()
}],
conditionColumnList: [{
name: 'id',
code: 'id',
operateType: '=',
value: event.resource
}]
})
setEvents((prev: TaskEvent[]) => {
const existing: TaskEvent | undefined = prev.find((ev: TaskEvent) => ev.resource === event.resource);
const filtered: TaskEvent[] | undefined = prev.filter((ev: TaskEvent) => ev.resource !== event.resource);
if (start instanceof Date && end instanceof Date && existing !== undefined) {
return [...filtered, {...existing, start, end, allDay}];
}
if (filtered !== undefined) {
return [...filtered];
}
return [];
})
},
[setEvents]
)
const eventPropGetter = useCallback(
(event:TaskEvent) => ({
...(event.state===7
? { className: 'completeTask' }
: event.priority===3?{ className: 'importantUrgentTask' }:
event.priority===2?{ className: 'importantNotUrgentTask' }:
event.priority===1?{ className: 'notImportantUrgentTask' }:
{ className: 'notImportantNotUrgentTask' }),
}),
[setEvents]
)
const rangeChange = (rangeLet: Date[]|{ start: Date; end: Date },current?:View|undefined)=>{
console.log("rangeChange:",rangeLet,(current?current:view))
// view 为天的时候类型为数组index:0为当天
if ((current?current:view)==="day"&&Array.isArray(rangeLet)) {
if (range.start.valueOf()>rangeLet[0].valueOf()){
setRange({...range,start:rangeLet[0]})
}else if (range.end.valueOf()<rangeLet[0].valueOf()){
setRange({...range,end:rangeLet[0]})
}
}
// 为周的时候类型为数组,周一到周日七天
if ((current?current:view)==="week"&&Array.isArray(rangeLet)){
if (range.start.valueOf()>rangeLet[0].valueOf()){
setRange({...range,start:rangeLet[0]})
}
if (range.end.valueOf()<rangeLet[6].valueOf()){
setRange({...range,end:rangeLet[6]})
}
}
// 为周的时候类型为对象
if ((current?current:view)==="month"&& rangeLet && !Array.isArray(rangeLet)){
if (range.start.valueOf()>rangeLet.start.valueOf()){
setRange({...range,start:rangeLet.start})
}
if (range.end.valueOf()<rangeLet.end.valueOf()){
setRange({...range,end:rangeLet.end})
}
}
}
return <div className="App" style={{height: '90vh'}}>
{open && <DetailModelForm operationId={operationId} description={description} open={open} haveButton={false}
itemId={itemId} pid={pid?Number(pid):0}
reloadData={reloadData} expectedStartTime={expectedStartTime}
expectedEndTime={expectedEndTime}/>}
<DragAndDropCalendar
// 本地设置
localizer={localizer}
messages={message}
// 修改style
eventPropGetter={eventPropGetter}
events={events}
// 界面
view={view}
// 界面改变
onView={handleViewChange}
onRangeChange={rangeChange}
// 时间
date={date}
// 条目信息改变
onNavigate={handleNavigate}
// 点击
selectable
scrollToTime={scrollToTime}
// 双击
onDoubleClickEvent={doubleClick}
// 点击任务
onSelectEvent={handleSelectEvent}
// 点击空白处
onSelectSlot={handleSelectSlot}
// 改变时间长短
resizable
onEventResize={moveEvent}
onEventDrop={moveEvent}
/>
</div>
}
export default CalShow;

View File

@ -0,0 +1,20 @@
.completeTask{
background-color: deepskyblue !important;
color: black !important;
}
.importantUrgentTask{
background-color: red !important;
color: black !important;
}
.notImportantUrgentTask{
background-color: gray !important;
color: black !important;
}
.importantNotUrgentTask{
background-color: yellow !important;
color: black !important;
}
.notImportantNotUrgentTask{
background-color: green !important;
color: black !important;
}

View File

@ -1,9 +1,9 @@
'use client'
import React, {useContext, useEffect, useState} from 'react';
import React, {useContext} from 'react';
import {ConfigProvider, Table} from 'antd';
import type {TableColumnsType, TableProps} from 'antd';
import {getTaskTreeResult, taskPriorityList, taskStateList, taskTreeResult} from "@/lib/task/project/data";
import {DataType, ResponseVO, ResultPage} from "@/lib/definitions";
import {taskPriorityList, taskStateList} from "@/lib/task/project/data";
import {DataType} from "@/lib/definitions";
import OperationButton from "@/ui/task/OperationButton";
import "@/ui/task/four/detailForm.modules.css"
import LocalContext from "@/ui/LocalContent";

View File

@ -1,4 +1,4 @@
import { PlusOutlined } from '@ant-design/icons';
import {PlusOutlined, QuestionCircleOutlined} from '@ant-design/icons';
import {
ModalForm,
ProForm,
@ -6,33 +6,72 @@ import {
ProFormSelect,
ProFormText, ProFormTextArea, ProFormTreeSelect,
} from '@ant-design/pro-components';
import { Button, Form, message } from 'antd';
import React, {useState} from "react";
import {Button, Form, message, Popconfirm} from 'antd';
import React, {useEffect, useState} from "react";
import {
addTask,
addTask, deleteTask, getTask,
getTaskTreeResult,
OPERATION_BUTTON_TYPE,
taskPriorityList,
taskStateList
taskStateList, updateTask
} from "@/lib/task/project/data";
import {DataType} from "@/lib/definitions";
import dayjs from "dayjs";
import dayjs, {Dayjs} from "dayjs";
export type DetailModelFormProps={
// 当前内容id
itemId?: number,
pid?:number,
// 祖宗任务id
pPid?:number,
// 操作id
operationId: number,
// 标题描述
description:string,
// 是否打开界面,用于非按钮操作
open:boolean,
// 使用按钮操作
haveButton:boolean,
expectedStartTime?:Dayjs,
expectedEndTime?:Dayjs,
// 重新加载数据
reloadData?: () => void
}
export type PidSelectTree= { label: string; value: number;pPid:number; children?: PidSelectTree[] }
export type PidSelectTree= { label: string; value: number;pid:number; children?: PidSelectTree[] }
export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
console.log("DetailModelForm:props:",props)
const [form] = Form.useForm<DataType>();
const [pPid, setPPid] = useState<number>(0);
const [pid, setPid] = useState<number>(props.pid?props.pid:0);
const [editFormDisable, setEditFormDisable] = useState(props.operationId === OPERATION_BUTTON_TYPE.DETAIL)
useEffect(() => {
if (props.itemId!=undefined&&(
props.operationId === OPERATION_BUTTON_TYPE.DETAIL || props.operationId === OPERATION_BUTTON_TYPE.UPDATE)) {
getTask(props.itemId).then(task => {
console.log('DetailModelForm:getTask(props.itemId)', props.itemId, task);
if (task.status.success) {
// setTaskMessage(task.data)
task.data.state = taskStateList.find(taskState => taskState.code === task.data.state?.toString())?.name;
task.data.priority = taskPriorityList.find(taskPriority => taskPriority.code === task.data.priority?.toString())?.name;
task.data.actualTimeRange = [task.data.actualStartTime ? dayjs(task.data.actualStartTime) : undefined,
task.data.actualEndTime ? dayjs(task.data.actualEndTime) : undefined];
task.data.expectedTimeRange = [task.data.expectedStartTime ? dayjs(task.data.expectedStartTime) : undefined,
task.data.expectedEndTime ? dayjs(task.data.expectedEndTime) : undefined];
form.setFieldsValue(task.data)
} else {
message.error(task.status.message);
props.reloadData?.()
}
})
}else if(props.operationId === OPERATION_BUTTON_TYPE.ADD|| props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD){
let data={'expectedTimeRange':[props.expectedStartTime?props.expectedStartTime:dayjs(), props.expectedEndTime],'pid':props.pid};
form.setFieldsValue(data)
}
}, [props])
function childReduce(child:DataType[]):PidSelectTree[]{
const result:PidSelectTree[] = [];
child.map(data=> {
const resultData:PidSelectTree = {label:data.name,value:data.id,pPid:data.pPid};
const resultData:PidSelectTree = {label:data.name,value:data.id,pid:data.pid};
if (data.children){
resultData.children=childReduce(data.children);
}
@ -40,26 +79,65 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
})
return result;
}
// 如果不是添加任务需要回显
return (
<ModalForm<DataType>
title={
props.operationId === OPERATION_BUTTON_TYPE.DETAIL ? "任务详情":
props.operationId === OPERATION_BUTTON_TYPE.ADD?"添加任务":
props.operationId === OPERATION_BUTTON_TYPE.ADD?"修改任务":''
}
trigger={
title={props.description}
open={props.open&&!props.haveButton}
trigger={props.haveButton?
<Button type="primary">
<PlusOutlined />
{props.description}
</Button>
</Button>:undefined
}
form={form}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => console.log('run'),
onCancel: () => {
console.log('run');
props.reloadData?.();
},
}}
// submitTimeout={2000}
submitter={props.itemId!==undefined&&props.itemId!==-1?{
render: (prop, defaultDoms) => {
return [
editFormDisable?<Button
key="edit"
onClick={() => {
// props.submit();
setEditFormDisable(false)
}}
>
</Button>:undefined,
props.operationId === OPERATION_BUTTON_TYPE.DETAIL||props.operationId === OPERATION_BUTTON_TYPE.UPDATE?<Popconfirm
key ='delete'
title="删除任务"
description="确认要删除任务?"
icon={<QuestionCircleOutlined style={{color: 'red'}}/>}
okText="确认"
cancelText="取消"
onConfirm={() => {
if (props.itemId!==undefined) {
deleteTask(props.itemId).then((response => {
console.log('response', response)
if (response.status.success) {
message.success("删除任务成功:" + response.data)
props.reloadData?.()
}
}));
}
}}
><Button type="primary" danger>
</Button>
</Popconfirm>:undefined
,
...defaultDoms
];
},
}:undefined}
onFinish={async (values) => {
console.log('Received values of form: ', values);
if (values.pid===undefined){
@ -77,42 +155,71 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
if (values.actualTimeRange?.[1]!=undefined) {
values.actualEndTime=dayjs(values.actualTimeRange[1]).toDate()
}
values.pPid=pPid;
values.pid=pid;
var result:boolean=false;
await addTask(values).then(response => {
console.log('response', response)
if (response.status.success) {
message.success("添加任务成功:" + response.data)
// 树任务重新刷新
// 四象限任务重新刷新
// 如果可以直接更新列表而不请求。。。。。。
console.log('props.reloadData?.()',props.reloadData)
props.reloadData?.()
result= true
}else {
message.error(response.status.message)
result= false
let state = taskStateList.find(taskState => taskState.name === values.state?.toString());
if (state) {
values.state = state.code
}
let priority = taskPriorityList.find(taskPriority => taskPriority.name === values.priority?.toString())
if (priority) {
values.priority = priority.code
}
// todo 修改
if (props.operationId === OPERATION_BUTTON_TYPE.UPDATE||(props.operationId === OPERATION_BUTTON_TYPE.DETAIL&&!editFormDisable)) {
await updateTask(values).then(response => {
console.log('response', response)
if (response.status.success) {
message.success("修改任务成功:" + response.data)
// 树任务重新刷新
// 四象限任务重新刷新
// 如果可以直接更新列表而不请求。。。。。。
console.log('props.reloadData?.()',props.reloadData)
props.reloadData?.()
result= true
}else {
message.error(response.status.message)
result= false
}
}
}
);
);
}else {
await addTask(values).then(response => {
console.log('response', response)
if (response.status.success) {
message.success("添加任务成功:" + response.data)
// 树任务重新刷新
// 四象限任务重新刷新
// 如果可以直接更新列表而不请求。。。。。。
console.log('props.reloadData?.()',props.reloadData)
props.reloadData?.()
result= true
}else {
message.error(response.status.message)
result= false
}
}
);
}
return result;
}}
>
<ProFormText width="sm" name="id" hidden={true} label="主键" />
<ProFormText width="sm" name="code" hidden={true} label="任务编码" />
<ProFormText width="sm" name="code" initialValue={props.itemId} hidden={true} label="任务编码" />
<ProFormText width="sm" name="pPid" initialValue={props.pPid} hidden={true} label="祖宗id" />
<ProForm.Group>
<ProFormTreeSelect
width="md"
request={() =>{
return getTaskTreeResult(JSON.stringify(
{pageSize:1000,pageNumber:1,data:[{code:'pid',value:'0',operateType:'='},{code:'',value:true,operateType: "TREE"}]}
{pageSize:1000,pageNumber:1,data:[{code:'pid',value:'0',operateType:'='},{code:'state',value:'8,9',operateType:'IN'},{code:'',value:true,operateType: "TREE"}]}
)).then(result=> childReduce(result.data.content))
}}
name="pid"
label="父级任务"
fieldProps={{onSelect: (e,node) => {console.log('onSelect',e,node);setPPid(node.pPid)}}}
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
fieldProps={{onSelect: (e,node) => {console.log('onSelect',e,node);setPid(e)}}}
disabled ={editFormDisable}
/>
<ProFormText
width="md"
@ -120,7 +227,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label="任务名称"
tooltip="最长为 24 位"
placeholder="请输入任务名称"
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
</ProForm.Group>
<ProFormTextArea
@ -129,7 +236,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label="任务描述"
// tooltip="最长为 24 位"
placeholder="请输入任务描述"
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
<ProForm.Group>
@ -144,7 +251,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
name="priority"
label="任务优先级"
initialValue='3'
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
<ProFormSelect
width="sm"
@ -157,7 +264,7 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
name="state"
label="任务状态"
initialValue='8'
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
</ProForm.Group>
@ -168,14 +275,14 @@ export const DetailModelForm: React.FC<DetailModelFormProps> = (props) => {
label="期望时间"
fieldProps={{allowEmpty:[true, true],showTime:true,needConfirm:false}}
placeholder={['开始时间','结束时间']}
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
<ProFormDateTimeRangePicker
name="actualTimeRange"
label="实际时间"
fieldProps={ {allowEmpty:[true, true],showTime:true,needConfirm:false}}
placeholder={['开始时间','结束时间']}
disabled ={props.operationId === OPERATION_BUTTON_TYPE.DETAIL}
disabled ={editFormDisable}
/>
</ProForm.Group>

View File

@ -0,0 +1,6 @@
.odd {
background-color: #fff;
}
.even {
background-color: rgb(209, 192, 192);
}

View File

@ -17,6 +17,7 @@ import {
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
import OperationButton from "@/ui/task/OperationButton";
import dayjs from "dayjs";
import '@/ui/task/project/TreeTablePro.modules.css'
const TreeTablePro: React.FC = () => {
const actionRef = useRef<ActionType>();
@ -131,7 +132,7 @@ const TreeTablePro: React.FC = () => {
}
];
let toolBarRenderList = [
<DetailModelForm key={1} operationId={OPERATION_BUTTON_TYPE.ADD} description='添加主线任务' reloadData={()=>{
<DetailModelForm open={false} haveButton={true} key={1} operationId={OPERATION_BUTTON_TYPE.ADD} description='添加主线任务' reloadData={()=>{
actionRef.current?.reload( false);
}}/>,
<Switch key={2} checkedChildren="树" unCheckedChildren="列表" checked={switchChecked}
@ -207,6 +208,7 @@ const TreeTablePro: React.FC = () => {
// },
}}
rowKey="id"
rowClassName={(record, i) => (i % 2 === 1 ? "even" : "odd")}
pagination={{
current: current,
pageSize: pageSize,
@ -217,7 +219,7 @@ const TreeTablePro: React.FC = () => {
}
}}
dateFormatter="string"
scroll={{y: 600}}
scroll={{y: 580}}
// headerTitle="任务管理"
toolBarRender={()=>toolBarRenderList}>
</ProTable>

View File

@ -1,6 +1,10 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -18,9 +22,19 @@
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"docker/out/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}