完善页面

This commit is contained in:
WenC 2024-03-13 16:11:28 +08:00
parent 85a63d722b
commit 82acef1ffa
17 changed files with 468 additions and 257 deletions

148
package-lock.json generated
View File

@ -8,19 +8,14 @@
"name": "ai-copyright",
"version": "0.0.0",
"dependencies": {
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"antd": "^5.15.2",
"axios": "^1.6.7",
"localforage": "^1.10.0",
"match-sorter": "^6.3.4",
"mockjs": "^1.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"sort-by": "^1.2.0",
"stylus": "^0.63.0"
"sort-by": "^1.2.0"
},
"devDependencies": {
"@types/react": "^18.2.56",
@ -47,7 +42,10 @@
"node_modules/@adobe/css-tools": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz",
"integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ=="
"integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
@ -962,64 +960,6 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
"hasInstallScript": true,
"peer": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/react-fontawesome": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"react": ">=16.3"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@ -1917,7 +1857,8 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "2.0.1",
@ -2046,14 +1987,6 @@
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
"integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
"engines": {
"node": ">=18"
}
},
"node_modules/compute-scroll-into-view": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
@ -2062,7 +1995,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/convert-source-map": {
"version": "2.0.0",
@ -2106,6 +2040,7 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@ -2650,7 +2585,8 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/fsevents": {
"version": "2.3.3",
@ -2679,6 +2615,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@ -2710,6 +2647,7 @@
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -2719,6 +2657,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -2813,6 +2752,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@ -2821,7 +2761,8 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/is-extglob": {
"version": "2.1.1",
@ -3079,21 +3020,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mockjs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/mockjs/-/mockjs-1.1.0.tgz",
"integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
"dependencies": {
"commander": "*"
},
"bin": {
"random": "bin/random"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.7",
@ -3125,14 +3056,6 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-path": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/object-path/-/object-path-0.6.0.tgz",
@ -3145,6 +3068,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
@ -3221,6 +3145,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -3298,21 +3223,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -4101,7 +4011,10 @@
"node_modules/sax": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/scheduler": {
"version": "0.23.0",
@ -4194,6 +4107,9 @@
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">= 8"
}
@ -4245,6 +4161,9 @@
"version": "0.63.0",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.63.0.tgz",
"integrity": "sha512-OMlgrTCPzE/ibtRMoeLVhOY0RcNuNWh0rhAVqeKnk/QwcuUKQbnqhZ1kg2vzD8VU/6h3FoPTq4RJPHgLBvX6Bw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"@adobe/css-tools": "~4.3.3",
"debug": "^4.3.2",
@ -4475,7 +4394,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/yallist": {
"version": "3.1.1",

View File

@ -10,19 +10,14 @@
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"antd": "^5.15.2",
"axios": "^1.6.7",
"localforage": "^1.10.0",
"match-sorter": "^6.3.4",
"mockjs": "^1.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"sort-by": "^1.2.0",
"stylus": "^0.63.0"
"sort-by": "^1.2.0"
},
"devDependencies": {
"@types/react": "^18.2.56",

View File

@ -0,0 +1,78 @@
import './card-proj.css';
import {DownOutlined, EditOutlined, SearchOutlined, EyeOutlined, FolderOutlined} from '@ant-design/icons';
import {MenuProps} from 'antd';
import {Dropdown, Space} from 'antd';
const items: MenuProps['items'] = [
{
key: '1',
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
];
export default function CardProj() {
return (
<div className="card-proj">
<div className="title">
<div className="left">
<a href="/#"></a>
</div>
<div className="right">
<span className="context">SDFSDF</span>
<span className="status"></span>
<span className="date">2024-03-02 11:15:35</span>
</div>
</div>
<hr/>
<div className="body">
<div className="line">
<div className="left">
<span>500</span>
</div>
<div className="right">
<span>
<EditOutlined/>
<a href="/#"></a>
</span>
<span>
<SearchOutlined/>
<a href="/#"></a>
</span>
<span>
<EyeOutlined/>
<a href="/#"></a>
</span>
</div>
</div>
<div className="line">
<div className="left">
<span>
<Dropdown menu={{items}}>
<a onClick={(e) => e.preventDefault()}>
<Space> <DownOutlined/></Space>
</a>
</Dropdown>
</span>
<span>
<Dropdown menu={{items}}>
<a onClick={(e) => e.preventDefault()}>
<Space> <DownOutlined/></Space>
</a>
</Dropdown>
</span>
</div>
<div className="right">
<span>
<FolderOutlined />
<a href="/#"></a>
</span>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,76 @@
.card-proj {
border-radius: 6px;
border: 1px solid var(--color-border);
padding: 15px;
}
.card-proj .left {
position: unset;
}
.card-proj .right {
margin: 0;
}
.card-proj .title {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-proj hr {
border-color: var(--color-border);
border-style: dashed;
border-left: 0;
border-bottom: 0;
border-right: 0;
margin-block-start: 10px;
margin-block-end: 10px;
}
.card-proj .title .left {
padding: 5px;
font-weight: bold;
}
.card-proj .title .right {
padding: 5px;
}
.card-proj .title .right span {
margin-left: 10px;
}
.card-proj .title .right span:first-child {
margin-left: 0;
}
.card-proj .body {
width: unset;
border: unset !important;
padding: 0 !important;
}
.card-proj .body .line {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 0;
}
.card-proj .body .line .left {
display: flex;
}
.card-proj .body .line .left span {
margin-right: 10px;
}
.card-proj .body .line .right span {
margin-left: 5px;
}
.card-proj .body .line .right span a {
margin-left: 5px;
}

View File

@ -0,0 +1,39 @@
import './list-proj.css'
import CardProj from "../card/CardProj.tsx";
import {useRef, MutableRefObject} from "react";
import {Input, Pagination} from 'antd';
import type {SearchProps} from 'antd/es/input/Search';
const {Search} = Input;
const onSearch: SearchProps['onSearch'] = (value, _e, info) => console.log(info?.source, value);
export default function ListProj() {
const listProjRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
const listRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
const domHeight = window.innerHeight - 301;
console.log(domHeight)
return (
<div className="list-proj" ref={listProjRef}>
<div className="top">
<Search placeholder="按项目名搜索" onSearch={onSearch} style={{width: 200}}/>
</div>
<div className="body">
<div className="list" ref={listRef} style={{height: `${domHeight}px`}}>
<CardProj/>
<CardProj/>
<CardProj/>
<CardProj/>
<CardProj/>
<CardProj/>
</div>
<div className="page">
<Pagination defaultCurrent={1} total={50}/>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,35 @@
.list-proj {
width: 100%;
padding: 15px 0;
}
.list-proj .top {
padding: 0 15px 15px 15px;
display: flex;
justify-content: right;
border-bottom: 1px solid var(--color-border);
}
.list-proj .body {
width: unset;
margin: 0;
}
.list-proj .list {
padding: 15px;
overflow: auto;
}
.list-proj .body .card-proj {
margin-bottom: 10px;
}
.list-proj .body .card-proj:last-child {
margin-bottom: 0;
}
.list-proj .body .page {
border-top: 1px solid var(--color-border);
padding-top: 15px;
display: flex;
justify-content: right;
}

View File

@ -1,12 +1,15 @@
import './menu-tree.css';
import {useContext} from "react";
import {Axios} from "../../context/Context.ts";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {faCheck, faCaretRight, faCaretDown, faEdit, faPlus, faRemove} from '@fortawesome/free-solid-svg-icons';
import {
CaretRightOutlined,
CaretDownOutlined,
PlusOutlined,
CloseOutlined,
EditOutlined,
CheckOutlined
} from '@ant-design/icons';
import {IMenuTree, IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts";
export default function MenuTree(props: IMenuTree) {
const axios = useContext(Axios);
const triggerChildren = (item: IMenuTreeItem) => {
@ -18,14 +21,14 @@ export default function MenuTree(props: IMenuTree) {
trigger();
}
const renderBtnGroup = (item: IMenuTreeItem) => {
if(item.isEdit) {
const renderBtnGroup = (item: IMenuTreeItem, index: number, parent?: IMenuTreeItem) => {
if (item.isEdit) {
return (
<>
<FontAwesomeIcon className="icon" icon={faCheck} onClick={() => {
<CheckOutlined className="icon" onClick={() => {
props.handleEditSaveClick(item);
}}/>
<FontAwesomeIcon className="icon" icon={faRemove} onClick={() => {
<CloseOutlined className="icon" onClick={() => {
props.handleEditCancelClick(item);
}}/>
</>
@ -33,35 +36,44 @@ export default function MenuTree(props: IMenuTree) {
}
return (
<>
<FontAwesomeIcon className="icon" icon={faEdit} onClick={() => {
<EditOutlined className="icon" onClick={() => {
props.handleEditClick(item);
}}/>
<FontAwesomeIcon className="icon" icon={faPlus} onClick={() => {
<PlusOutlined className="icon" onClick={() => {
props.handleAddClick(item);
}}/>
<FontAwesomeIcon className="icon" icon={faRemove} onClick={() => {
props.handleRemoveClick(item);
<CloseOutlined className="icon" onClick={() => {
props.handleRemoveClick(item, index, parent);
}}/>
</>
);
}
const renderLabel = (item: IMenuTreeItem) => {
if(item.isEdit) {
return <input className="menu-name-input" value={item.name} onChange={(e) => {
item.name = e.target.value;
props.handleNameChange(item);
}}/>
if (item.isEdit) {
const width = 180 - 30 - item.level * 10;
return <input className="menu-name-input"
value={item.name}
style={{width: width}}
onChange={(e) => {
item.name = e.target.value;
props.handleNameChange(item);
}}/>
}
const icon = item.isOpen ? faCaretDown : faCaretRight;
const icon = item.isOpen ? <CaretDownOutlined onClick={() => {
triggerChildren(item)
}}/> : <CaretRightOutlined onClick={() => {
triggerChildren(item)
}}/>;
const width = 180 - 50 - (item.level * 10) - (5 + (item.isParent ? 8.75 : 0));
return (
<>
{item.isParent ? <FontAwesomeIcon className="icon" icon={icon} onClick={() => {
triggerChildren(item)
}}/> : <></>}
<span onDoubleClick={() => {
triggerChildren(item)
}}>{item.name}</span>
{item.isParent ? icon : <></>}
<span
style={{width: width}}
onDoubleClick={() => {
triggerChildren(item)
}}>{item.name}</span>
</>
)
}
@ -76,7 +88,7 @@ export default function MenuTree(props: IMenuTree) {
<li key={item.id}>
<div className="menu-title">
<div className="label">{renderLabel(item)}</div>
<div className="icon-group">{renderBtnGroup(item)}</div>
<div className="icon-group">{renderBtnGroup(item, index, parent)}</div>
</div>
{renderChildrenMenu}
</li>

View File

@ -0,0 +1,111 @@
import './menu-tree-with-top-button.css';
import MenuTree from "./MenuTree.tsx";
import {IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts";
import {useState} from "react";
class MenuTreeItem implements IMenuTreeItem {
children: Array<IMenuTreeItem> | null;
id: string;
isEdit: boolean;
isOpen: boolean;
isParent: boolean;
level: number;
name: string;
oldName: string;
constructor(id: string, name: string, level: number) {
this.id = id;
this.name = name;
this.oldName = name;
this.level = level;
this.isEdit = false;
this.isOpen = false;
this.isParent = false;
this.children = null;
}
}
export default function MenuTreeWithTopButton() {
const menuTrees: Array<IMenuTreeItem> = [];
const [menuTreeArray, setMenuTreeArray] = useState(menuTrees);
return (
<div className="menu-tree-with-top-button">
<button type="button" className="btn btn-blue" onClick={() => {
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, '一级目录', 1);
menuTreeItem.isParent = true;
setMenuTreeArray([
...menuTreeArray,
menuTreeItem,
]);
}}>
</button>
<MenuTree
menus={menuTreeArray}
url={''}
setMenuTreeArray={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
handleExpand={() => {
}}
handleAddClick={(item) => {
item.isParent = true;
item.isOpen = true;
if (!item.children) {
item.children = new Array<IMenuTreeItem>();
}
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, item.level + '级目录', item.level + 1);
menuTreeItem.isOpen = true;
item.children.push(menuTreeItem);
setMenuTreeArray([
...menuTreeArray
])
}
}
handleEditClick={(item) => {
item.isEdit = true;
setMenuTreeArray([
...menuTreeArray
])
}}
handleRemoveClick={(_item, index, parent) => {
if (parent) {
parent?.children?.splice(index, 1);
} else {
menuTreeArray.splice(index, 1);
}
setMenuTreeArray([
...menuTreeArray
])
}}
handleEditSaveClick={(item) => {
// 这里发请求,成功之后修改,失败还原
if (item.name === '') {
return;
}
item.oldName = item.name;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleEditCancelClick={(item) => {
item.name = item.oldName;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleNameChange={() => {
setMenuTreeArray([
...menuTreeArray
])
}}
/>
</div>
)
}

View File

@ -0,0 +1,7 @@
.menu-tree-with-top-button {
padding: 0 15px;
}
.menu-tree-with-top-button .btn {
width: 100%;
}

View File

@ -1,24 +1,43 @@
.menu-tree {
padding: 0 15px;
margin-top: 10px;
}
.menu-tree ul {
margin-left: 10px;
}
.menu-tree ul li {}
.menu-tree ul li {
}
.menu-tree ul li .menu-title {
display: flex;
justify-content: space-between;
align-items: center;
height: 22px;
line-height: 22px;
}
.menu-tree ul li .menu-title .label {
cursor: pointer;
display: flex;
align-items: center;
}
.menu-tree ul li .menu-title .label span {
margin-left: 5px;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
height: 22px;
line-height: 22px;
}
.menu-tree ul li .menu-title .label span {
.menu-tree ul li .menu-title .label .menu-name-input {
border: 0;
outline: 0;
height: 20px;
line-height: 20px;
text-decoration: 1px solid underline;
text-underline-offset: 2px;
background-color: transparent;
}
.menu-tree ul li .menu-title .icon-group {
@ -29,5 +48,5 @@
}
.menu-tree ul li .menu-title .icon-group .icon {
cursor: pointer;
padding-left: 5px;
padding-left: 2px;
}

View File

@ -8,7 +8,6 @@
.menu-with-top-button ul {
padding: 10px;
background-color: yellow;
}
.menu-with-top-button ul li {

View File

@ -1,5 +1,5 @@
:root {
--color-border: #eeeeee;
--color-border: #d9d9d9;
--color-box-shadow: rgba(0, 0, 0, 0.3);
--color-red: #ff5722;
--color-orange: #ffb800;
@ -19,6 +19,12 @@ html, body {
margin: 0;
padding: 0;
font-size: 14px;
background-color: #EEEEEE;
}
a {
color: var(--color-dark);
text-decoration-line: none;
}
ul {

View File

@ -21,11 +21,11 @@ export interface IMenuTree {
handleAddClick(item: IMenuTreeItem): void;
handleRemoveClick(item: IMenuTreeItem): void;
handleRemoveClick(item: IMenuTreeItem, index: number, parent?: IMenuTreeItem): void;
handleEditSaveClick(item: IMenuTreeItem): void;
handleEditCancelClick(item: IMenuTreeItem): void;
handleEditCancelClick(tem: IMenuTreeItem): void;
handleNameChange(item: IMenuTreeItem): void;

View File

@ -1,5 +1,5 @@
.body {
margin: 0 auto 10px auto;
background-color: red;
width: var(--width-workspace);
background-color: #FFFFFF;
}

View File

@ -12,7 +12,8 @@
}
.index .right {
margin-left: 235px;
margin-left: 220px;
border-left: 1px solid var(--color-border);
box-sizing: border-box;
height: 100%;
background-color: green;
}

View File

@ -1,33 +1,9 @@
import './index.css';
import {MouseEvent, useState} from "react";
import {MouseEvent} from "react";
import {IMenuListItem, IMenuWithTopButton} from "../../interfaces/menu/IMenuWithTopButton.ts";
import MenuWithTopButton from "../../components/menu/MenuWithTopButton.tsx";
import MenuTree from "../../components/menu/MenuTree.tsx";
import {IMenuTree, IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts";
import {name} from "axios";
class MenuTreeItem implements IMenuTreeItem {
children: Array<IMenuTreeItem> | null;
id: string;
isEdit: boolean;
isOpen: boolean;
isParent: boolean;
level: number;
name: string;
oldName: string;
constructor(id: string, name: string, level: number) {
this.id = id;
this.name = name;
this.oldName = name;
this.level = level;
this.isEdit = false;
this.isOpen = false;
this.isParent = false;
this.children = null;
}
}
import MenuTreeWithTopButton from "../../components/menu/MenuTreeWithTopButton.tsx";
import ListProj from "../../components/list/ListProj.tsx";
const projMenu: IMenuWithTopButton = {
button: {
@ -68,8 +44,6 @@ const agentMenu: IMenuWithTopButton = {
}
export default function Index() {
const menuTrees: Array<IMenuTreeItem> = [];
const [menuTreeArray, setMenuTreeArray] = useState(menuTrees);
return (
<div className="index">
<div className="left">
@ -78,80 +52,16 @@ export default function Index() {
list={projMenu.list}
handleListItem={projMenu.handleListItem}
/>
<button type="button" onClick={() => {
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, '一级目录', 1);
menuTreeItem.isParent = true;
setMenuTreeArray([
...menuTreeArray,
menuTreeItem,
]);
}}>
</button>
<MenuTree
menus={menuTreeArray}
url={''}
setMenuTreeArray={(item) => {
setMenuTreeArray([
...menuTreeArray
])
}}
handleExpand={(item) => {
}}
handleAddClick={(item) => {
item.isParent = true;
item.isOpen = true;
if (!item.children) {
item.children = new Array<IMenuTreeItem>();
}
const menuTreeItem = new MenuTreeItem(`${new Date().getTime()}`, item.level + '级目录', item.level + 1);
menuTreeItem.isOpen = true;
item.children.push(menuTreeItem);
setMenuTreeArray([
...menuTreeArray
])
}
}
handleEditClick={(item) => {
item.isEdit = true;
setMenuTreeArray([
...menuTreeArray
])
}}
handleRemoveClick={(item) => {
}}
handleEditSaveClick={(item) => {
console.log(item);
// 这里发请求,成功之后修改,失败还原
if(item.name === '') {
return;
}
item.oldName = item.name;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleEditCancelClick={(item) => {
item.name = item.oldName;
item.isEdit = false;
setMenuTreeArray([
...menuTreeArray
])
}}
handleNameChange={(item) => {
setMenuTreeArray([
...menuTreeArray
])
}}
/>
<MenuTreeWithTopButton />
<MenuWithTopButton
button={agentMenu.button}
list={agentMenu.list}
handleListItem={agentMenu.handleListItem}
/>
</div>
<div className="right"></div>
<div className="right">
<ListProj/>
</div>
</div>
)
}

View File

@ -1,7 +1,10 @@
import { defineConfig } from 'vite'
import {defineConfig} from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react()],
server: {
host: '0.0.0.0'
}
})