diff --git a/package-lock.json b/package-lock.json index 15d84c2..62964c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "@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", @@ -1897,6 +1899,21 @@ "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2018,6 +2035,25 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "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", @@ -2088,6 +2124,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2571,6 +2615,38 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2969,6 +3045,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -2984,6 +3079,17 @@ "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", @@ -3207,6 +3313,11 @@ "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", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 9ac6012..3eb9af0 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,10 @@ "@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", diff --git a/src/components/menu/MenuTree.tsx b/src/components/menu/MenuTree.tsx index e3a8ec3..2d79308 100644 --- a/src/components/menu/MenuTree.tsx +++ b/src/components/menu/MenuTree.tsx @@ -1,78 +1,90 @@ import './menu-tree.css'; -import {useState} from "react"; +import {useContext} from "react"; +import {Axios} from "../../context/Context.ts"; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' -import {faCaretRight, faCaretDown, faEdit, faPlus, faRemove} from '@fortawesome/free-solid-svg-icons'; +import {faCheck, faCaretRight, faCaretDown, faEdit, faPlus, faRemove} from '@fortawesome/free-solid-svg-icons'; import {IMenuTree, IMenuTreeItem} from "../../interfaces/menu/IMenuTree.ts"; - export default function MenuTree(props: IMenuTree) { - - const [menusArray, setMenuArray] = useState([ - { - id: '1', - name: '一级目录', - active: true, - level: 1, - children: [ - { - id: '1-1', - name: '二级目录', - active: false, - level: 1, - children: [ - { - id: '1-1-1', - name: '三级目录', - active: false, - level: 1, - children: [] - } - ] - } - ] - } - ]); + const axios = useContext(Axios); const triggerChildren = (item: IMenuTreeItem) => { - item.active = !item.active; - setMenuArray([ - ...menusArray - ]); + + const trigger = () => { + item.isOpen = !item.isOpen; + props.setMenuTreeArray(item); + } + + trigger(); } - const renderMenu = (children: Array, parent?: IMenuTreeItem) => { - if (!children || children.length == 0) { + const renderBtnGroup = (item: IMenuTreeItem) => { + if(item.isEdit) { + return ( + <> + { + props.handleEditSaveClick(item); + }}/> + { + props.handleEditCancelClick(item); + }}/> + + ) + } + return ( + <> + { + props.handleEditClick(item); + }}/> + { + props.handleAddClick(item); + }}/> + { + props.handleRemoveClick(item); + }}/> + + ); + } + + const renderLabel = (item: IMenuTreeItem) => { + if(item.isEdit) { + return { + item.name = e.target.value; + props.handleNameChange(item); + }}/> + } + const icon = item.isOpen ? faCaretDown : faCaretRight; + return ( + <> + {item.isParent ? { + triggerChildren(item) + }}/> : <>} + { + triggerChildren(item) + }}>{item.name} + + ) + } + + const renderMenu = (children: Array | null, parent?: IMenuTreeItem) => { + if (!children) { return <> } const lis = children.map((item, index) => { - const isParent = item.children && item.children.length > 0; - const icon = item.active ? faCaretDown : faCaretRight; const renderChildrenMenu = renderMenu(item.children, item); return (
  • -
    - {isParent ? { - triggerChildren(item) - }}/> : <>} - { - triggerChildren(item) - }}>{item.name} -
    -
    - - - -
    +
    {renderLabel(item)}
    +
    {renderBtnGroup(item)}
    {renderChildrenMenu}
  • ) }) - const active = parent ? parent.active : true; - return
      {lis}
    + const isOpen = parent ? parent.isOpen : true; + return
      {lis}
    } - const menuUl = renderMenu(menusArray); - return
    {menuUl}
    + return
    {renderMenu(props.menus)}
    } \ No newline at end of file diff --git a/src/components/menu/menu-tree.css b/src/components/menu/menu-tree.css index 3c05597..2a832e7 100644 --- a/src/components/menu/menu-tree.css +++ b/src/components/menu/menu-tree.css @@ -16,12 +16,18 @@ .menu-tree ul li .menu-title .label span { margin-left: 5px; } + +.menu-tree ul li .menu-title .label span { + +} + .menu-tree ul li .menu-title .icon-group { - width: 50px; + max-width: 50px; flex-shrink: 0; display: flex; - justify-content: space-between; + justify-content: right; } .menu-tree ul li .menu-title .icon-group .icon { cursor: pointer; + padding-left: 5px; } \ No newline at end of file diff --git a/src/context/Context.ts b/src/context/Context.ts new file mode 100644 index 0000000..20f4168 --- /dev/null +++ b/src/context/Context.ts @@ -0,0 +1,6 @@ +import {createContext} from "react"; +import axios from 'axios'; + +axios.defaults.baseURL = 'https://localhost:8080'; + +export const Axios = createContext(axios) \ No newline at end of file diff --git a/src/interfaces/menu/IMenuTree.ts b/src/interfaces/menu/IMenuTree.ts index 3184e01..5c8fbbc 100644 --- a/src/interfaces/menu/IMenuTree.ts +++ b/src/interfaces/menu/IMenuTree.ts @@ -1,15 +1,32 @@ -import {MouseEvent} from 'react'; - export interface IMenuTreeItem { id: string; name: string; - active: boolean; + oldName: string; level: number; - children: Array; + isEdit: boolean; + isOpen: boolean; + isParent: boolean; + children: Array | null; } export interface IMenuTree { menus: Array; + url: string; + + setMenuTreeArray(item: IMenuTreeItem): void; + + handleExpand(item: IMenuTreeItem): void; + + handleEditClick(item: IMenuTreeItem): void; + + handleAddClick(item: IMenuTreeItem): void; + + handleRemoveClick(item: IMenuTreeItem): void; + + handleEditSaveClick(item: IMenuTreeItem): void; + + handleEditCancelClick(item: IMenuTreeItem): void; + + handleNameChange(item: IMenuTreeItem): void; - handleClick(e: MouseEvent, item: IMenuTreeItem): void; } \ No newline at end of file diff --git a/src/mock/MockMenuTree.ts b/src/mock/MockMenuTree.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/route/index/index.tsx b/src/route/index/index.tsx index 01b033c..86cf0f8 100644 --- a/src/route/index/index.tsx +++ b/src/route/index/index.tsx @@ -1,8 +1,33 @@ import './index.css'; -import {MouseEvent} from "react"; +import {MouseEvent, useState} 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 | 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; + } + +} const projMenu: IMenuWithTopButton = { button: { @@ -43,6 +68,8 @@ const agentMenu: IMenuWithTopButton = { } export default function Index() { + const menuTrees: Array = []; + const [menuTreeArray, setMenuTreeArray] = useState(menuTrees); return (
    @@ -51,7 +78,73 @@ export default function Index() { list={projMenu.list} handleListItem={projMenu.handleListItem} /> - + + { + setMenuTreeArray([ + ...menuTreeArray + ]) + }} + handleExpand={(item) => { + }} + handleAddClick={(item) => { + item.isParent = true; + item.isOpen = true; + if (!item.children) { + item.children = new Array(); + } + 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 + ]) + }} + />