feat:四象限拖拽

This commit is contained in:
1708-huayu 2025-03-19 18:59:45 +08:00
parent 39d791e156
commit d2e9e4c4ae
20 changed files with 1159 additions and 30 deletions

View File

@ -10,6 +10,8 @@ const nextConfig = {
// Optional: Change the output directory `out` -> `dist`
distDir: 'docker/out',
// 严格模式下react-beautiful-dnd无法使用
reactStrictMode:false,
};
export default nextConfig;

259
package-lock.json generated
View File

@ -5,11 +5,13 @@
"requires": true,
"packages": {
"": {
"name": "assistant-todo",
"version": "0.1.0",
"dependencies": {
"@ant-design/pro-components": "^2.7.1",
"@heroicons/react": "^2.0.18",
"@tailwindcss/forms": "^0.5.7",
"@types/react-beautiful-dnd": "^13.1.8",
"antd": "^5.16.1",
"axios": "^1.6.8",
"dayjs": "^1.11.13",
@ -34,6 +36,7 @@
"eslint-config-next": "14.1.3",
"prettier": "3.0.3",
"prettier-plugin-tailwindcss": "0.5.4",
"react-beautiful-dnd": "^13.1.1",
"typescript": "^5"
}
},
@ -1540,6 +1543,16 @@
"integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==",
"dev": true
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.6",
"resolved": "https://registry.npmmirror.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
"integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==",
"dev": true,
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz",
@ -1588,6 +1601,14 @@
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-beautiful-dnd": {
"version": "13.1.8",
"resolved": "https://registry.npmmirror.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz",
"integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==",
"dependencies": {
"@types/react": "*"
}
},
"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",
@ -1608,6 +1629,18 @@
"@types/react": "*"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.34",
"resolved": "https://registry.npmmirror.com/@types/react-redux/-/react-redux-7.1.34.tgz",
"integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
"dev": true,
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.8.tgz",
@ -2533,6 +2566,15 @@
"node": ">= 8"
}
},
"node_modules/css-box-model": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/css-box-model/-/css-box-model-1.2.1.tgz",
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
"dev": true,
"dependencies": {
"tiny-invariant": "^1.0.6"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@ -4087,6 +4129,15 @@
"node": ">= 0.4"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@ -5525,6 +5576,12 @@
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"node_modules/raf-schd": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/raf-schd/-/raf-schd-4.0.3.tgz",
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
"dev": true
},
"node_modules/rc-cascader": {
"version": "3.24.1",
"resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.24.1.tgz",
@ -6113,6 +6170,32 @@
"node": ">=0.10.0"
}
},
"node_modules/react-beautiful-dnd": {
"version": "13.1.1",
"resolved": "https://registry.npmmirror.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
"deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.9.2",
"css-box-model": "^1.2.0",
"memoize-one": "^5.1.1",
"raf-schd": "^4.0.2",
"react-redux": "^7.2.0",
"redux": "^4.0.4",
"use-memo-one": "^1.1.1"
},
"peerDependencies": {
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-beautiful-dnd/node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"dev": true
},
"node_modules/react-big-calendar": {
"version": "1.12.2",
"resolved": "https://registry.npmmirror.com/react-big-calendar/-/react-big-calendar-1.12.2.tgz",
@ -6193,6 +6276,37 @@
"react-dom": ">=16.3.0"
}
},
"node_modules/react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-7.2.9.tgz",
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"peerDependencies": {
"react": "^16.8.3 || ^17 || ^18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true
},
"node_modules/reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
@ -6316,6 +6430,15 @@
"node": ">=8.10.0"
}
},
"node_modules/redux": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/redux/-/redux-4.2.1.tgz",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
@ -7078,6 +7201,12 @@
"node": ">=12.22"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"dev": true
},
"node_modules/tinycolor2": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
@ -7343,6 +7472,15 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-memo-one": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/use-memo-one/-/use-memo-one-1.1.3.tgz",
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
"dev": true,
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
@ -8666,6 +8804,16 @@
"integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==",
"dev": true
},
"@types/hoist-non-react-statics": {
"version": "3.3.6",
"resolved": "https://registry.npmmirror.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
"integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==",
"dev": true,
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz",
@ -8714,6 +8862,14 @@
"csstype": "^3.0.2"
}
},
"@types/react-beautiful-dnd": {
"version": "13.1.8",
"resolved": "https://registry.npmmirror.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz",
"integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==",
"requires": {
"@types/react": "*"
}
},
"@types/react-big-calendar": {
"version": "1.8.9",
"resolved": "https://registry.npmmirror.com/@types/react-big-calendar/-/react-big-calendar-1.8.9.tgz",
@ -8734,6 +8890,18 @@
"@types/react": "*"
}
},
"@types/react-redux": {
"version": "7.1.34",
"resolved": "https://registry.npmmirror.com/@types/react-redux/-/react-redux-7.1.34.tgz",
"integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
"dev": true,
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.8.tgz",
@ -9464,6 +9632,15 @@
"which": "^2.0.1"
}
},
"css-box-model": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/css-box-model/-/css-box-model-1.2.1.tgz",
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
"dev": true,
"requires": {
"tiny-invariant": "^1.0.6"
}
},
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@ -10674,6 +10851,15 @@
"function-bind": "^1.1.2"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"requires": {
"react-is": "^16.7.0"
}
},
"hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@ -11717,6 +11903,12 @@
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"raf-schd": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/raf-schd/-/raf-schd-4.0.3.tgz",
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
"dev": true
},
"rc-cascader": {
"version": "3.24.1",
"resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.24.1.tgz",
@ -12117,6 +12309,29 @@
"loose-envify": "^1.1.0"
}
},
"react-beautiful-dnd": {
"version": "13.1.1",
"resolved": "https://registry.npmmirror.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2",
"css-box-model": "^1.2.0",
"memoize-one": "^5.1.1",
"raf-schd": "^4.0.2",
"react-redux": "^7.2.0",
"redux": "^4.0.4",
"use-memo-one": "^1.1.1"
},
"dependencies": {
"memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"dev": true
}
}
},
"react-big-calendar": {
"version": "1.12.2",
"resolved": "https://registry.npmmirror.com/react-big-calendar/-/react-big-calendar-1.12.2.tgz",
@ -12182,6 +12397,28 @@
"warning": "^4.0.3"
}
},
"react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-7.2.9.tgz",
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"dependencies": {
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true
}
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/reactcss/-/reactcss-1.2.3.tgz",
@ -12282,6 +12519,15 @@
"picomatch": "^2.2.1"
}
},
"redux": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/redux/-/redux-4.2.1.tgz",
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2"
}
},
"reflect.getprototypeof": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz",
@ -12884,6 +13130,12 @@
"resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz",
"integrity": "sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg=="
},
"tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"dev": true
},
"tinycolor2": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz",
@ -13080,6 +13332,13 @@
"punycode": "^2.1.0"
}
},
"use-memo-one": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/use-memo-one/-/use-memo-one-1.1.3.tgz",
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
"dev": true,
"requires": {}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",

View File

@ -12,6 +12,7 @@
"@ant-design/pro-components": "^2.7.1",
"@heroicons/react": "^2.0.18",
"@tailwindcss/forms": "^0.5.7",
"@types/react-beautiful-dnd": "^13.1.8",
"antd": "^5.16.1",
"axios": "^1.6.8",
"dayjs": "^1.11.13",
@ -36,7 +37,7 @@
"eslint-config-next": "14.1.3",
"prettier": "3.0.3",
"prettier-plugin-tailwindcss": "0.5.4",
"typescript": "^5",
"react-beautiful-dnd": "^13.1.8"
"react-beautiful-dnd": "^13.1.1",
"typescript": "^5"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

View File

@ -0,0 +1,58 @@
/**
* A layout is UI that is shared between multiple routes. On navigation, layouts preserve state, remain interactive, and do not re-render.
*/
'use client'
import React, { useState} from 'react';
import {DragDropContext, DropResult} from 'react-beautiful-dnd';
import {DroppableTable} from "@/ui/task/drag/DroppableTable";
import {DataType, Request} from "@/lib/definitions";
import {useEffect} from "react";
import {TaskSelectVO} from "@/lib/task/drag/data";
import {selectTaskAPI} from "@/lib/task/drag/service";
export default function Layout({children}: { children: React.ReactNode }) {
const [allTaskList, setAllTaskList] = useState<DataType[]>([]);
useEffect(() => {
addData()
},[])
async function addData() {
const requestParam: Request<TaskSelectVO> = {
pageSize: 1000,
pageNumber: 1,
data: {state:'9'}
}
const res = await selectTaskAPI(requestParam)
setAllTaskList(res.data.content)
}
// 处理拖拽结束事件
const onDragEnd = (result: DropResult) => {
console.log('拖拽结束')
const {source, destination} = result;
console.log('Source Droppable ID:', source.droppableId);
console.log('Destination Droppable ID:', destination?.droppableId);
if (!destination || !['table1', 'table2'].includes(destination.droppableId)) return;
// 如果拖拽到无效区域,直接返回
if (!destination) return;
// 如果拖拽到同一个表格的同一个位置,直接返回
if (source.droppableId === destination.droppableId && source.index === destination.index) {
return;
}
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div style={{display: 'flex',flexWrap: 'wrap',boxSizing: 'border-box',width:'100vw'}}>
{/* 不紧急不重要 */}
<DroppableTable tableCode='1' taskList={allTaskList.filter(task => task.priority == '1')}/>
{/* 紧急不重要 */}
<DroppableTable tableCode="2" taskList={allTaskList.filter(task => task.priority == '2')}/>
{/* 不紧急重要 */}
<DroppableTable tableCode='3' taskList={allTaskList.filter(task => task.priority == '3')}/>
{/* 紧急重要 */}
<DroppableTable tableCode='4' taskList={allTaskList.filter(task => task.priority == '4')}/>
</div>
</DragDropContext>
);
}

View File

@ -0,0 +1,11 @@
/**
* The .js, .jsx, or .tsx file extensions can be used for Pages.
* A page is always the leaf of the route subtree.
* A page.js file is required to make a route segment publicly accessible.
* Pages are Server Components by default, but can be set to a Client Component.
* Pages can fetch data. View the Data Fetching section for more information.
* @constructor
*/
export default function Page() {
return <p>Dashboard Page</p>;
}

View File

@ -0,0 +1,3 @@
export default function Page() {
return <p>Dashboard test</p>;
}

View File

@ -1,15 +1,11 @@
import React from "react";
import {Dayjs} from "dayjs";
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';
};
export type Request<T>={
data:T,
pageSize:number,
pageNumber:number,
}
type Status={
success:boolean;
code:number ;
@ -19,7 +15,6 @@ export type ResultPage<T> = {
content:T[];
totalPages:number;
totalElements:number;
}
export type ResponseVO<T>={
data:T;
@ -27,20 +22,26 @@ export type ResponseVO<T>={
status:Status;
}
export type DataType ={
key: React.ReactNode;
export type TaskMessage ={
id: string;
pid:string;
pName:string;
pPid:string;
code: string;
name: string;
description: string;
state: number|string|undefined;
priority: number|string|undefined;
state: string;
priority: string;
}
export type DataType = TaskMessage&{
key: React.ReactNode;
type:number;
action?:React.ReactNode;
expectedStartTime?:Date|string;
expectedEndTime?:Date;
expectedStartTime?:string;
expectedEndTime?:string;
expectedTimeRange?:(string|Dayjs|undefined)[];
actualStartTime?:Date;
actualEndTime?:Date;

View File

@ -0,0 +1,6 @@
export type TaskSelectVO = {
name?: string;
state?: string;
priority?: string;
allOverdueTasks?:boolean;
}

View File

@ -0,0 +1,15 @@
import {DataType, Request, ResponseVO, ResultPage} from "@/lib/definitions";
import {unstable_noStore as noStore} from "next/dist/server/web/spec-extension/unstable-no-store";
import {AxiosResponse} from "axios";
import {httpReq} from "@/utils/axiosReq";
import {TaskSelectVO} from "@/lib/task/drag/data";
export async function selectTaskAPI(requestParam: Request<TaskSelectVO>):
Promise<ResponseVO<ResultPage<DataType>>> {
noStore();
// 使用 Axios 发送 PUT 请求获取数据
const response: AxiosResponse<ResponseVO<ResultPage<DataType>>> = await httpReq.post(
process.env.NEXT_PUBLIC_TODO_REQUEST_URL + '/task/select', requestParam);
// 从响应中提取数据并返回
return response.data;
}

View File

@ -156,7 +156,7 @@ export const taskStateList: DictType[] = [
code: '8',
name: '新建',
order: 8,
color: 'RED'
color: 'primary'
},
{
id: 9,
@ -164,6 +164,13 @@ export const taskStateList: DictType[] = [
name: '进行中',
order: 9,
color: 'YELLOW'
},
{
id:10,
code:'10',
name:'已逾期',
order:10,
color: 'RED'
}
]
@ -177,4 +184,5 @@ export enum OPERATION_BUTTON_TYPE {
SHOW_FOUR,
SHOW_CALENDAR,
ADD,
UPDATE_PRIORITY
}

View File

@ -1,12 +1,18 @@
import React, {Fragment} from "react";
import {Button, Dropdown, MenuProps, message, Modal, Popconfirm, Space} from "antd";
import {Button, Dropdown, MenuProps, message, Modal, Popconfirm, Radio, Space} from "antd";
import {DownOutlined, QuestionCircleOutlined} from "@ant-design/icons";
import {commonUpdate, deleteTask, OPERATION_BUTTON_TYPE} from "@/lib/task/project/data";
import {
commonUpdate,
deleteTask,
OPERATION_BUTTON_TYPE,
taskPriorityList,
} from "@/lib/task/project/data";
import Link from "next/link";
import {DetailModelForm} from "@/ui/task/project/DetailModelForm";
export interface OperationButtonProps {
itemId: string,
priority?:string,
pid: string,
pPid: string,
operationId?: string,
@ -17,7 +23,10 @@ interface OperationModelProps {
operationId: number,
pPid: string,
pid: string,
openModal: boolean
openModal: boolean,
updatePriority:boolean,
priority?:string,
updatePriorityValue?:string,
}
class OperationButton extends React.Component<OperationButtonProps, OperationModelProps> {
@ -28,7 +37,9 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
pid: props.pid,
pPid: props.pPid,
operationId: 0,
openModal: false
openModal: false,
updatePriority:false,
priority: props.priority,
};
}
@ -42,6 +53,36 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
const onClick: MenuProps['onClick'] = ({key}) => {
console.log(key)
};
const priorityHandleOk =async ()=>{
console.log(this.props.itemId,this.state.priority,this.state.updatePriorityValue)
if (this.state.priority==this.state.updatePriorityValue){
return
}
// 修改任务优先级
const res = await commonUpdate({
updateColumnList:[{
name:'priority',
code:'priority',
value:this.state.updatePriorityValue
}],
conditionColumnList:[{
name:'id',
code:'id',
operateType:'=',
value:this.props.itemId
}]
})
// 关闭弹窗
this.setState({updatePriority:false})
if (res.status.success) {
// 全局提示
message.success("完成任务成功")
// 更新任务
this.props.refreshDate?.()
}else {
message.error(res.status.message)
}
}
const items: MenuProps['items'] = [
{
key: OPERATION_BUTTON_TYPE.DETAIL,
@ -110,6 +151,12 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
}}
><a></a></Popconfirm>,
},
{
key: OPERATION_BUTTON_TYPE.UPDATE_PRIORITY,
label: <a onClick={(e) => {
this.setState({updatePriority: true})
}}></a>,
},
{
key: OPERATION_BUTTON_TYPE.SHOW_TREE,
label: <Link href={"/task/project?pid=" + this.props.itemId}></Link>,
@ -133,6 +180,28 @@ class OperationButton extends React.Component<OperationButtonProps, OperationMod
</Space>
</a>
</Dropdown>
<Modal
title="修改任务优先级"
open={this.state.updatePriority}
onOk={priorityHandleOk}
onCancel={()=>{this.setState({updatePriority:false})}}
destroyOnClose={true}
okText='确认'
cancelText='取消'
>
<Radio.Group
options={taskPriorityList.map(taskPriority=> {return {
label:taskPriority.name,value:taskPriority.code
}})}
defaultValue={this.state.priority}
optionType="button"
buttonStyle="solid"
onChange={e=>{
console.log({e})
this.setState({updatePriorityValue:e.target.value})
}}
/>
</Modal>
{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}

View File

@ -0,0 +1,321 @@
'use client'
import React, {useEffect, useState} from 'react';
import '@/ui/task/four/detailForm.modules.css'
import {
Button,
DatePicker,
Form,
Input,
message,
Select,
Space,
} from 'antd';
import {RangePickerProps} from "antd/es/date-picker";
import dayjs from "dayjs";
import {
addTask,
getTask,
OPERATION_BUTTON_TYPE,
taskPriorityList,
taskStateList,
updateTask
} from "@/lib/task/project/data";
import {DataType} from "@/lib/definitions";
export interface DetailFormProps {
itemId: string,
pPid: string,
operationId: number | undefined,
handleCancel: () => void,
setUpdatePriority?: (value: (((prevState: (string | undefined)) => (string | undefined)) | string | undefined)) => void
}
export const DetailForm: React.FC<DetailFormProps> = (props) => {
const [form] = Form.useForm();
const [componentDisabled] = useState<boolean>(props.operationId === OPERATION_BUTTON_TYPE.DETAIL);
const {RangePicker} = DatePicker;
const {TextArea} = Input;
// const [taskMessage,setTaskMessage]=useState<any>({name:"useState没效果吗,是这样的"});
let defaultPriority:string|undefined;
useEffect(() => {
if (props.operationId === OPERATION_BUTTON_TYPE.DETAIL || props.operationId === OPERATION_BUTTON_TYPE.UPDATE) {
getTask(props.itemId).then(task => {
console.log('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;
defaultPriority = task.data.priority
task.data.actualTimeRange = [task.data.actualStartTime ? dayjs(task.data.actualStartTime) : '',
task.data.actualEndTime ? dayjs(task.data.actualEndTime) : ''];
task.data.expectedTimeRange = [task.data.expectedStartTime ? dayjs(task.data.expectedStartTime) : '',
task.data.expectedEndTime ? dayjs(task.data.expectedEndTime) : ''];
form.setFieldsValue(task.data)
} else {
message.error(task.status.message);
props.handleCancel()
}
})
}else if(props.operationId === OPERATION_BUTTON_TYPE.ADD|| props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD){
let data={'expectedTimeRange':[dayjs(), undefined]};
form.setFieldsValue(data)
}
}, [])
const range = (start: number, end: number) => {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
};
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// return current && current < dayjs().endOf('day');
console.log('current', current, current && current < dayjs().endOf('day'), current < dayjs().endOf('day'))
return current < dayjs().startOf('day');
};
const disabledRangeTime: RangePickerProps['disabledTime'] = (_, type) => {
console.log('current', _)
if (type === 'start') {
return {
disabledHours: () => range(0, 60).splice(4, 20),
disabledMinutes: () => range(30, 60),
disabledSeconds: () => [55, 56],
};
}
return {
disabledHours: () => range(0, 60).splice(20, 4),
disabledMinutes: () => range(0, 31),
disabledSeconds: () => [55, 56],
};
};
const onFinish = (values: any) => {
console.log(values);
let request: DataType = {
key: values.id,
id: values.id,
pPid: props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ? props.pPid : values.pPid,
pid: props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD ? props.itemId : props.operationId === OPERATION_BUTTON_TYPE.UPDATE ? values.pid : 0,
code: values.code,
name: values.name,
description: values.description,
state: values.state,
priority: values.priority,
type: values.type,
children: []
}
if (values.expectedTimeRange) {
if (values.expectedTimeRange[0]) {
request.expectedStartTime = values.expectedTimeRange[0]
}
if (values.expectedTimeRange[1]) {
request.expectedEndTime = values.expectedTimeRange[1]
}
}
if (values.actualTimeRange) {
if (values.actualTimeRange[0]) {
request.actualStartTime = values.actualTimeRange[0]
}
if (values.actualTimeRange[1]) {
request.actualEndTime = values.actualTimeRange[1]
}
}
if (props.operationId === OPERATION_BUTTON_TYPE.ADD_CHILD) {
addTask(request).then(response => {
console.log('response', response)
if (response.status.success) {
message.success("添加任务成功:" + response.data)
props.handleCancel()
}
}
)
} else if (props.operationId === OPERATION_BUTTON_TYPE.UPDATE) {
var stateFind = taskStateList.find(taskState => taskState.name === request.state?.toString());
if (stateFind) {
request.state = stateFind.code;
}
var priorityFind = taskPriorityList.find(taskPriority => taskPriority.name === request.priority?.toString());
if (priorityFind) {
request.priority = priorityFind.code;
}
updateTask(request).then(response => {
console.log('response', response)
if (response.status.success) {
message.success("修改任务成功:" + response.data)
props.handleCancel()
}else {
message.error(response.status.message)
}
}
)
}
};
return (
<>
{/*<Checkbox*/}
{/* checked={componentDisabled}*/}
{/* onChange={(e) => setComponentDisabled(e.target.checked)}*/}
{/*>*/}
{/* Form disabled*/}
{/*</Checkbox>*/}
<Form
labelCol={{span: 4}}
wrapperCol={{span: 18}}
layout="horizontal"
disabled={componentDisabled}
size='large'
onFinish={onFinish}
// initialValues={taskMessage}
form={form}
// style={{maxWidth: '70%'}}
>
<Form.Item<DataType> name='id' label="id" hidden={true}>
<Input name='id'/>
</Form.Item>
<Form.Item<DataType> name='pid' label="pid" hidden={true}>
<Input name='pid'/>
</Form.Item>
<Form.Item<DataType> name='code' label="code" hidden={true}>
<Input name='code'/>
</Form.Item>
<Form.Item<DataType> name='pPid' label="pPid" hidden={true}>
<Input name='pPid'/>
</Form.Item>
<Form.Item<DataType> name='name' label="任务名称"
rules={[{required: true, message: '任务名称不能为空'}]}>
<Input name='name'/>
</Form.Item>
<Form.Item<DataType> name='description' label="任务描述">
<TextArea name='description' rows={4}/>
</Form.Item>
<Form.Item<DataType> name='priority' initialValue='3' label="任务优先级">
<Select
allowClear={true} options={
taskPriorityList.map(taskState => {
return {
'label': taskState.name,
'value': taskState.code
}
})
}>
</Select>
</Form.Item>
<Form.Item<DataType> name='state' initialValue='8' label="任务状态">
<Select allowClear={true} options={
taskStateList.map(taskState => {
return {
'label': taskState.name,
'value': taskState.code
}
})
}>
</Select>
</Form.Item>
<Form.Item<DataType> name={'expectedTimeRange'} label="期望">
<RangePicker
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
placeholder={['开始时间', '结束时间']}
allowEmpty={[true, true]}
needConfirm={false}
showTime={{
hideDisabledOptions: true,
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')],
}}
// format="YYYY-MM-DD HH:mm:ss"
/>
</Form.Item>
<Form.Item<DataType> name='actualTimeRange' label="实际">
<RangePicker
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
placeholder={['开始时间', '结束时间']}
allowEmpty={[true, true]}
needConfirm={false}
showTime={{
hideDisabledOptions: true,
defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('11:59:59', 'HH:mm:ss')],
}}
// format="YYYY-MM-DD HH:mm:ss"
/>
</Form.Item>
{/*<Form.Item label="InputNumber">*/}
{/* <InputNumber/>*/}
{/*</Form.Item>*/}
{/*<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="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="DateTimePicker">*/}
{/* <DatePicker/>*/}
{/*</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.Item>
<Space>
{props.operationId === OPERATION_BUTTON_TYPE.DETAIL ? (
// <Button htmlType="button">关闭</Button>
<>
</>
) : (<>
<Button type="primary" htmlType="submit"></Button>
<Button htmlType="button"></Button>
</>
)
}
</Space>
</Form.Item>
</Form>
</>
);
}

View File

@ -0,0 +1,159 @@
'use client'
import React, {CSSProperties, useEffect} from "react";
import {Draggable, Droppable} from "react-beautiful-dnd";
import {Table, TableProps, Tooltip} from "antd";
import {DataType} from "@/lib/definitions";
import './index.modules.css'
interface DroppableTableProps // extends DragDropContextProps
{
tableCode:string,
taskList:DataType[]
}
export const DroppableTable: React.FC<DroppableTableProps> = (props) => {
const [stateName, setStateName] = React.useState("");
useEffect(() => {
if(props.tableCode=='1'){
setStateName('不紧急不重要');
}else if (props.tableCode=='2'){
setStateName('紧急不重要');
}else if (props.tableCode=='3'){
setStateName('不紧急重要');
}else if (props.tableCode=='4'){
setStateName('紧急重要');
}
}, []);
const getItemStyle = (isDragging:any, draggableStyle:any):CSSProperties => {
console.log({draggableStyle})
return {
// some basic styles to make the items look a bit nicer
userSelect: "none",
// change background colour if dragging
background: isDragging ? "lightgreen" : "grey",
// styles we need to apply on draggables
...draggableStyle,
height:80
}};
const getListStyle = (isDraggingOver:boolean) => ({
background: isDraggingOver ? "lightblue" : "lightgrey",
});
// 渲染表格行
// @ts-ignore
const renderTableRow = (data: DataType[], droppableId: string): TableProps<Item>['components']['body']['row'] =>
(props:any) => {
// console.log({props, droppableId});
const { children, ...restProps } = props;
const record = data.find((item) => item.id === restProps['data-row-key']);
const index = data.findIndex((item) => item.id === restProps['data-row-key']);
if (!record) {
// console.error('Invalid record at index:', index);
return null; // 或者返回一个占位符
}
return (
<Draggable key={record.id} draggableId={record.id} index={index}>
{(provided,snapshot) => (
<tr
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
{/*<td>{record.code}</td>*/}
<td>{record.name}</td>
<td>{record?.description}</td>
<td>{record.state}</td>
<td>:{record.expectedStartTime}<br/>
:{record.expectedEndTime}</td>
</tr>
)}
</Draggable>
);
};
const calculateScrollHeight = () => {
// 计算50%视窗高度减去21px
const height = `calc(50vh - 80px)`;
return { y: height };
};
return (
<Droppable droppableId={props.tableCode} key={props.tableCode}>
{(provided,snapshot) => (
<div ref={provided.innerRef}
{...provided.droppableProps}
style={getListStyle(snapshot.isDraggingOver)}
className="droppable-table"
>
<Table<DataType>
dataSource={props.taskList}
scroll={calculateScrollHeight()}
columns={[
// {
// title: '任务编码',
// dataIndex: 'code',
// key: 'code',
// width: '10%',
// },
{
title: stateName,
dataIndex: 'name',
key: 'name',
width: '10%',
ellipsis: {
showTitle: false,
},
render: (address) => (
<Tooltip placement="topLeft" title={address}>
{address}
</Tooltip>
),
},
{
title: '任务描述',
dataIndex: 'description',
key: 'description',
width: '20%',
ellipsis: {
showTitle: false,
},
render: (description) => (
<Tooltip placement="topLeft" title={description}>
{description}
</Tooltip>
),
},
{
title: '任务状态',
dataIndex: 'state',
width: '5%',
key: 'state',
},
{
title: '期望时间',
dataIndex: 'expectedStartTime',
width: '10%',
key: 'expectedStartTime',
},
]}
rowKey="id"
components={{
body: {
row: renderTableRow(props.taskList, props.tableCode),
},
}}
pagination={false}
/>
{provided.placeholder}
</div>
)}
</Droppable>
)
}

View File

@ -0,0 +1,193 @@
'use client'
import React, {useContext} from 'react';
import {ConfigProvider, Table} from 'antd';
import type {TableColumnsType, TableProps} from 'antd';
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";
import {useSearchParams} from "next/dist/client/components/navigation";
import {RequestDateType} from "@/ui/task/RequestDateType";
type TableRowSelection<T> = TableProps<T>['rowSelection'];
// 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);
},
};
interface TableSearchType {
priority: string,
resultDataTypeList: DataType[],
refreshDate?: () => void,
loadingState:boolean
}
const TreeTable: React.FC<TableSearchType> = (props) => {
const dataLocalContext = useContext(LocalContext);
console.log('dataLocalContext', dataLocalContext)
const checkLocal:boolean = useSearchParams().get('pid')!=undefined;
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',
filtered: checkLocal&&dataLocalContext.taskState.length > 0,
defaultFilteredValue: dataLocalContext.taskState.split(','),
onFilter: (text, record) => {
// console.log('&record.priority.toString()===props.priority',record.priority,text,props.priority)
// &record.priority.toString()===props.priority 不重要紧急 0 0
if (dataLocalContext.taskState.length === 0) {
return true;
}
var dataLocalContextTaskStateList = dataLocalContext.taskState.split(",");
console.log('dataLocalContextTaskStateList', dataLocalContext.taskState.length > 0, dataLocalContextTaskStateList);
return taskStateList.filter(taskState => dataLocalContextTaskStateList.includes(taskState.code))
.find(taskState => taskState.name === record.state) != undefined;
}
},
{
title: '优先级',
dataIndex: 'priority',
width: '10%',
key: 'priority',
hidden: true,
filters: taskPriorityList.map(taskState => {
return {
'text': taskState.name,
'value': taskState.code
}
}),
filtered: true,
defaultFilteredValue: [props.priority],
onFilter: (text, record) => {
console.log('&record.priority.toString()===props.priority',record.priority,text,props.priority,props.priority === record.priority)
// &record.priority.toString()===props.priority 不重要紧急 0 0
return props.priority === record.priority+''
}
},
{
title: '期望开始时间',
dataIndex: 'expectedStartTime',
width: '10%',
key: 'expectedStartTime',
filtered: checkLocal&&dataLocalContext.expectedStartTime.length > 0,
// defaultFilteredValue: JSON.parse(dataLocalContext.expectedStartTime),
onFilter: (text, record) => {
console.log('dataLocalContext.expectedStartTime',dataLocalContext.expectedStartTime)
if (dataLocalContext.expectedStartTime.length === 0) {
return true;
}
const expectStartTimeParseResult:RequestDateType[] = JSON.parse(dataLocalContext.expectedStartTime);
console.log('expectStartTimeParseResult',expectStartTimeParseResult)
if (expectStartTimeParseResult[0]!=undefined){
let le:boolean;
if (expectStartTimeParseResult[0].operateType==='<='){
le=record.expectedStartTime!=undefined&&record.expectedStartTime<=expectStartTimeParseResult[0].value
}else {
le=record.expectedStartTime!=undefined&&record.expectedStartTime>=expectStartTimeParseResult[0].value
}
if (!le){
return le;
}
}
if(expectStartTimeParseResult[1]!=undefined){
let ge:boolean = record.expectedStartTime!=undefined&&record.expectedStartTime<=expectStartTimeParseResult[1].value
if (!ge){
return ge;
}
}
return true;
}
},
{
title: '操作',
dataIndex: 'action',
width: '10%',
key: 'action',
},
];
return (
<>
<ConfigProvider
theme={{
components: {
Table: {
headerBg: taskPriorityList.find((item) => item.code === props.priority)?.color
/* 这里是你的组件 token */
},
},
}}
>
<Table
columns={columns}
// rowSelection={{ ...rowSelection, checkStrictly}}
dataSource={props.resultDataTypeList.filter(resultDataType=>{
resultDataType.action= <OperationButton itemId={resultDataType.id} pid={resultDataType.pid} pPid={resultDataType.pPid} refreshDate={props.refreshDate}/>
if (dataLocalContext.expectedStartTime.length === 0) {
return true;
}
const expectStartTimeParseResult:RequestDateType[] = JSON.parse(dataLocalContext.expectedStartTime);
if (expectStartTimeParseResult[0]!=undefined){
let le:boolean;
if (expectStartTimeParseResult[0].operateType==='<='){
le=resultDataType.expectedStartTime!=undefined&&resultDataType.expectedStartTime<=expectStartTimeParseResult[0].value
}else {
le=resultDataType.expectedStartTime!=undefined&&resultDataType.expectedStartTime>=expectStartTimeParseResult[0].value
}
if (!le){
return le;
}
}
if(expectStartTimeParseResult[1]!=undefined){
let ge:boolean = resultDataType.expectedStartTime!=undefined&&resultDataType.expectedStartTime<=expectStartTimeParseResult[1].value
if (!ge){
return ge;
}
}
return true;
})}
pagination={{pageSize: 5}}
scroll={{y: 280}}
loading={props.loadingState}
/>
</ConfigProvider>
{/*<Space align="center" style={{ marginBottom: 16 }}>*/}
{/* CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />*/}
{/*</Space>*/}
{/*<ColorPicker defaultValue="#1677ff" showText/>*/}
</>
);
};
export default TreeTable;

View File

@ -0,0 +1,6 @@
.ant-picker-range-separator{
width: 60px;
}
.ant-empty-image{
overflow: hidden;
}

View File

@ -0,0 +1,5 @@
.droppable-table{
width: 50vw;
height: calc(50vh - 21px);
overflow-y: auto;
}

View File

@ -9,6 +9,7 @@ import "@/ui/task/four/detailForm.modules.css"
import LocalContext from "@/ui/LocalContent";
import {useSearchParams} from "next/dist/client/components/navigation";
import {RequestDateType} from "@/ui/task/RequestDateType";
import dayjs from "dayjs";
type TableRowSelection<T> = TableProps<T>['rowSelection'];
@ -73,7 +74,7 @@ const TreeTable: React.FC<TableSearchType> = (props) => {
console.log('dataLocalContextTaskStateList', dataLocalContext.taskState.length > 0, dataLocalContextTaskStateList);
return taskStateList.filter(taskState => dataLocalContextTaskStateList.includes(taskState.code))
.find(taskState => taskState.name === record.state) != undefined;
}
},
},
{
title: '优先级',
@ -96,9 +97,9 @@ const TreeTable: React.FC<TableSearchType> = (props) => {
}
},
{
title: '期望开始时间',
title: '期望时间',
dataIndex: 'expectedStartTime',
width: '10%',
width: '13%',
key: 'expectedStartTime',
filtered: checkLocal&&dataLocalContext.expectedStartTime.length > 0,
// defaultFilteredValue: JSON.parse(dataLocalContext.expectedStartTime),
@ -127,12 +128,25 @@ const TreeTable: React.FC<TableSearchType> = (props) => {
}
}
return true;
},
render:(value, record, index)=>{
let result = [];
result.push('起:');
if(record && record.expectedStartTime !== undefined){
result.push(dayjs(record.expectedStartTime).format('YYYY-MM-DD HH:mm'));
}
result.push(<br key="break1"/>); // 使用key来给每个br一个唯一的标识
result.push('止:');
if(record && record.expectedEndTime !== undefined){
result.push(dayjs(record.expectedEndTime).format('YYYY-MM-DD HH:mm'));
}
return result;
}
},
{
title: '操作',
dataIndex: 'action',
width: '10%',
width: '7%',
key: 'action',
},
];
@ -152,7 +166,7 @@ const TreeTable: React.FC<TableSearchType> = (props) => {
columns={columns}
// rowSelection={{ ...rowSelection, checkStrictly}}
dataSource={props.resultDataTypeList.filter(resultDataType=>{
resultDataType.action= <OperationButton itemId={resultDataType.id} pid={resultDataType.pid} pPid={resultDataType.pPid} refreshDate={props.refreshDate}/>
resultDataType.action= <OperationButton itemId={resultDataType.id} priority={resultDataType.priority} pid={resultDataType.pid} pPid={resultDataType.pPid} refreshDate={props.refreshDate}/>
if (dataLocalContext.expectedStartTime.length === 0) {
return true;
}