commit 8327fd09e87b61ccf585db715490ea0c011c3211 Author: dark Date: Tue Apr 16 00:51:35 2024 +0800 init diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..b3d98db --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,24 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: [ 'dist', '.eslintrc.cjs' ], + parser: '@typescript-eslint/parser', + plugins: [ 'react-refresh' ], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + '@typescript-eslint/ban-ts-comment': [ 'error', { + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': 'allow-with-description', + 'minimumDescriptionLength': 10 + } ], + '@typescript-eslint/no-explicit-any': 'off', + }, +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d6babe --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/index.html b/index.html new file mode 100644 index 0000000..1ae601c --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Crazy Pro + + +
+ + + diff --git a/mock/departments.ts b/mock/departments.ts new file mode 100644 index 0000000..6ae45f5 --- /dev/null +++ b/mock/departments.ts @@ -0,0 +1,21 @@ +export default [ + { + url: '/api/departments', + method: 'get', + response: () => { + return { + code: 200, + data: [ + { + id: '1', + name: '开发部' + }, + { + id: '2', + name: '测试部' + } + ] + } + } + } +] \ No newline at end of file diff --git a/mock/menus.ts b/mock/menus.ts new file mode 100644 index 0000000..83a8e00 --- /dev/null +++ b/mock/menus.ts @@ -0,0 +1,98 @@ +import { MockMethod } from 'vite-plugin-mock' + +export default [ + { + url: '/api/menus', + method: 'get', + response: () => { + return { + code: 200, + message: 'Success', + data: [ + { + id:1, + path: '/welcome', + name: '欢迎', + icon: 'ApplicationMenu', + component: './pages/dashboard', + type: 1, + order: 1, + }, + { + id:2, + path: '/admin', + name: '系统管理', + icon: 'SettingTwo', + access: 'canAdmin', + type: 1, + order: 2, + children: [ + { + id:3, + path: '/admin/menus', + name: '导航管理', + icon: 'AllApplication', + component: './pages/system/menus', + + type: 1, + }, + { + id:4, + path: '/admin/users', + name: '用户管理', + icon: 'PeoplesTwo', + component: './pages/system/users', + type: 1, + }, + { + id:5, + path: '/admin/departments', + name: '部门管理', + icon: 'Browser', + component: './pages/system/departments', + type: 1, + }, + { + id:6, + path: '/admin/roles', + name: '角色管理', + icon: 'Permissions', + component: './pages/system/roles', + type: 1, + }, + ], + }, + { + id:7, + name: '列表页', + icon: 'ListView', + path: '/list', + type: 1, + children: [ + { + id:8, + path: '/list/index', + name: '列表页面', + component: './pages/list/list', + type: 1, + }, + { + id:9, + path: '/list/tree', + name: '树形列表页面', + component: './pages/list/tree', + type: 1, + }, + ], + }, + { + id:10, + path: 'https://ant.design', + name: 'Ant Design 官网外链', + type: 3, + }, + ] + } + } + } +] as MockMethod[] \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b817871 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "pro", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host --port 3000", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons": "^5.3.6", + "@ant-design/pro-components": "^2.7.0", + "@formily/antd-v5": "^1.2.0", + "@formily/core": "^2.3.1", + "@formily/react": "^2.3.1", + "@icon-park/react": "^1.4.2", + "@tanstack/query-core": "^5.29.0", + "@tanstack/react-query": "^5.29.2", + "@tanstack/react-router": "^1.26.20", + "antd": "^5.16.1", + "axios": "^1.6.8", + "dayjs": "^1.11.10", + "jotai": "^2.8.0", + "jotai-tanstack-query": "^0.8.5", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "wonka": "^6.3.4" + }, + "devDependencies": { + "@tanstack/router-devtools": "^1.26.20", + "@tanstack/router-vite-plugin": "^1.26.16", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "mockjs": "^1.1.0", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vite-plugin-mock": "^3.0.1" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..5f9d2d3 --- /dev/null +++ b/src/App.css @@ -0,0 +1,20 @@ +.i-icon { + display: flex; +} +.ant-tree-iconEle{ + .i-icon { + display: inherit; + line-height: 28px; + } + +} + +.top-breadcrumb { + .item { + + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + } +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..5bfce9b --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,11 @@ +import './App.css' +import { RootProvider } from './routes.tsx' + +function App() { + + return ( + + ) +} + +export default App diff --git a/src/Auth.tsx b/src/Auth.tsx new file mode 100644 index 0000000..9250ab6 --- /dev/null +++ b/src/Auth.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { Props } from './types' +import { useAtomValue } from 'jotai' +import { authAtom } from './store/user.ts' + + +export type AuthProps = Props & { + //是否渲染没有权限的组件 + noAuth?: React.ReactNode + //权限key + authKey?: string[] +} + +export const Auth: React.FC = (props) => { + + const auth = useAtomValue(authAtom) + + if (!auth.isLogin) { + return null + } + + if (props.authKey && props.authKey.length > 0) { + if (props.authKey.some(key => !auth.authKey?.includes(key))) { + return props.noAuth || null + } + } + + return ( + <> + {props!.children} + + ) +} + +export default Auth \ No newline at end of file diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/breadcrumb/index.tsx b/src/components/breadcrumb/index.tsx new file mode 100644 index 0000000..236ab7b --- /dev/null +++ b/src/components/breadcrumb/index.tsx @@ -0,0 +1,74 @@ +import { Breadcrumb, BreadcrumbProps, Dropdown } from 'antd' +import { Link, useNavigate } from '@tanstack/react-router' +import { DownOutlined } from '@ant-design/icons' +import { getIcon } from '@/components/icon' +import { useCallback } from 'react' + +export const PageBreadcrumb = (props: BreadcrumbProps & { + showIcon?: boolean; +}) => { + + const nav = useNavigate() + const { items = [], showIcon = true, ...other } = props + + const renderIcon = useCallback((icon: any) => { + if (icon && showIcon) { + return getIcon(icon) + } + return null + }, []) + + const itemRender = (route) => { + + const isLast = route?.path === items[items.length - 1]?.path + + if (route.children) { + const items = route.children.map((item) => { + return { + ...item, + key: item.path, + label: item.name, + } + }) + return ( + { + nav({ + to: e.key + }) + } + }} + trigger={[ 'hover' ]}> + { + !route.component ? {renderIcon(route.icon)}{route.name} + + : {renderIcon(route.icon)}{route.name} + + + } + + + + ) + } + + return isLast ? ( + {renderIcon(route.icon)}{route.name} + ) : ( + {renderIcon(route.icon)}{route.name} + ) + } + + return ( + <> + + + ) +} + +export default PageBreadcrumb \ No newline at end of file diff --git a/src/components/error-boundary/index.tsx b/src/components/error-boundary/index.tsx new file mode 100644 index 0000000..a107b4e --- /dev/null +++ b/src/components/error-boundary/index.tsx @@ -0,0 +1,64 @@ +import React, { ErrorInfo } from 'react' +import { Button, Result } from 'antd' + +export class ErrorBoundary extends React.Component< + Record, + { hasError: boolean; errorInfo: string } +> { + state = { hasError: false, errorInfo: '' } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, errorInfo: error.message } + } + + componentDidCatch(error: any, errorInfo: ErrorInfo) { + // You can also log the error to an error reporting service + // eslint-disable-next-line no-console + console.log(error, errorInfo) + } + + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( + +
+

{this.state.errorInfo}

+
+ + + } + /> + ) + } + return this.props.children + } +} \ No newline at end of file diff --git a/src/components/icon/index.tsx b/src/components/icon/index.tsx new file mode 100644 index 0000000..2e3abd3 --- /dev/null +++ b/src/components/icon/index.tsx @@ -0,0 +1,52 @@ +import IconAll, { ALL_ICON_KEYS, IconType, IIconAllProps } from '@icon-park/react/es/all' +import React, { Fragment } from 'react' +import * as AntIcons from '@ant-design/icons/es/icons' +import { IconComponentProps } from '@ant-design/icons/es/components/Icon' + +export function Icon(props: Partial) { + + const { type, ...other } = props + const AntIcon = AntIcons[type as keyof typeof AntIcons] + if (AntIcon) { + return + } + + //如果是http或https链接,直接返回图片 + if (type && (type.startsWith('http') || type.startsWith('https') || type.startsWith('data:image'))) { + // @ts-ignore 没有办法把所有的属性都传递给img + return icon + } + + if (ALL_ICON_KEYS.indexOf(type as IconType) < 0) { + return null + } + + return ( + + + + ) +} + +// eslint-disable-next-line react-refresh/only-export-components +export const getIcon = (type: string, props?: Partial) => { + + if(React.isValidElement(type)){ + return type + } + //判断是否为json格式 + if (type && type.startsWith('{') && type.endsWith('}')) { + try { + const obj = JSON.parse(type) + type = obj.type + props = obj + } catch (e) { /* empty */ + } + } + + return +} + +export default Icon \ No newline at end of file diff --git a/src/hooks/useFetch.ts b/src/hooks/useFetch.ts new file mode 100644 index 0000000..da248fd --- /dev/null +++ b/src/hooks/useFetch.ts @@ -0,0 +1,23 @@ +import { atomFamily } from 'jotai/utils' +import { IDataProps } from '@/types' +import { useMemo, useRef } from 'react' +import { generateUUID } from '@/utils/uuid' + + +export type FetchProps = IDataProps & { + key?: string + initialValue?: T +} + +const factoryAtom = atomFamily((param: FetchProps) => param.initialValue) +export const useInnerAtom = (props: FetchProps) => { + const { initialValue, key } = props + // 生成唯一的key + const _key = useRef(key) + if (!_key.current) { + _key.current = generateUUID() + } + + return useMemo(() => factoryAtom({ initialValue }), [ _key.current ]) + +} \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/src/layout/FormPageLayout.tsx b/src/layout/FormPageLayout.tsx new file mode 100644 index 0000000..7045388 --- /dev/null +++ b/src/layout/FormPageLayout.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { createLazyRoute, Outlet } from '@tanstack/react-router' + +interface IFormPageLayoutProps { + children: React.ReactNode + +} + +const FormPageLayout: React.FC = (props) => { + return ( + <> + {props.children} + + + ) +} + +export default FormPageLayout + +export const GenRoute = (id: string) => createLazyRoute(id)({ + component: FormPageLayout, +}) diff --git a/src/layout/ListPageLayout.tsx b/src/layout/ListPageLayout.tsx new file mode 100644 index 0000000..b256269 --- /dev/null +++ b/src/layout/ListPageLayout.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { createLazyRoute, Outlet } from '@tanstack/react-router' + +interface IListPageLayoutProps { + children: React.ReactNode + +} + +const ListPageLayout: React.FC = (props) => { + return ( + <>{props.children} + + + ) +} + +export default ListPageLayout + +export const GenRoute = (id: string) => createLazyRoute(id)({ + component: ListPageLayout, +}) diff --git a/src/layout/RootLayout.tsx b/src/layout/RootLayout.tsx new file mode 100644 index 0000000..c8259b7 --- /dev/null +++ b/src/layout/RootLayout.tsx @@ -0,0 +1,144 @@ +import { + ProConfigProvider, + ProLayout, +} from '@ant-design/pro-components' +import { ConfigProvider, Dropdown } from 'antd' +import { useState } from 'react' +import defaultProps from './_defaultProps' +import { Link, Outlet, useRouteContext } from '@tanstack/react-router' +import Icon from '../components/icon' +import { MenuItem } from '@/types' +import PageBreadcrumb from '@/components/breadcrumb' +import { ErrorBoundary } from '@/components/error-boundary' + + +//根据menuData生成Breadcrumb所需的数据 +const getBreadcrumbData = (menuData: MenuItem[], pathname: string) => { + const breadcrumbData: any[] = [] + const findItem = (menuData: any[], pathname: string) => { + for (let i = 0; i < menuData.length; i++) { + if (menuData[i].path === pathname) { + breadcrumbData.push(menuData[i]) + return true + } + if (menuData[i].children) { + if (findItem(menuData[i].children, pathname)) { + breadcrumbData.push(menuData[i]) + return true + } + } + } + return false + } + findItem(menuData, pathname) + return breadcrumbData.reverse() +} + +export default () => { + + const { menuData } = useRouteContext({ + from: undefined, + strict: false, + select: (state) => state + }) + + const items = getBreadcrumbData(menuData, location.pathname) + + const [ pathname, setPathname ] = useState(location.pathname) + + return ( +
+ + { + return document.getElementById('crazy-pro-layout') || document.body + }} + > + } + title="Crazy Pro" + {...defaultProps} + route={{ + path: '/', + routes: menuData + }} + location={{ + pathname, + }} + token={{ + header: { + colorBgMenuItemSelected: 'rgba(0,0,0,0.04)', + }, + }} + menu={{ + collapsedShowGroupTitle: true, + }} + avatarProps={{ + src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg', + size: 'small', + title: '管理员', + render: (_, dom) => { + return ( + , + label: '退出登录', + }, + ], + }} + > + {dom} + + ) + }, + }} + actionsRender={(props) => { + if (props.isMobile) return [] + if (typeof window === 'undefined') return [] + return [] + }} + menuRender={(_, defaultDom) => ( + <> + {defaultDom} + + )} + menuItemRender={(item, dom) => { + return
{ + setPathname(item.path || '/welcome') + }} + > + + {dom} + +
+ }} + {...{ + 'layout': 'mix', + 'navTheme': 'light', + 'contentWidth': 'Fluid', + 'fixSiderbar': true, + 'colorPrimary': '#1677FF', + 'siderMenuType': 'group', + // layout: 'side', + }} + ErrorBoundary={ErrorBoundary} + > + +
+
+
+
+ ) +} diff --git a/src/layout/TreePageLayout.tsx b/src/layout/TreePageLayout.tsx new file mode 100644 index 0000000..ce294a2 --- /dev/null +++ b/src/layout/TreePageLayout.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { createLazyRoute, Outlet } from '@tanstack/react-router' + +interface ITreePageLayoutProps { + children: React.ReactNode + +} + +const TreePageLayout: React.FC = (props) => { + return ( + <> + {props.children} + + + ) +} + +export const GenRoute = (id: string) => createLazyRoute(id)({ + component: TreePageLayout, +}) diff --git a/src/layout/_defaultProps.tsx b/src/layout/_defaultProps.tsx new file mode 100644 index 0000000..b979036 --- /dev/null +++ b/src/layout/_defaultProps.tsx @@ -0,0 +1,69 @@ +export default { + route: { + path: '/', + routes: [], + }, + location: { + // pathname: '/', + }, + bgLayoutImgList: [ + { + src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png', + left: 85, + bottom: 100, + height: '303px', + }, + { + src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png', + bottom: -68, + right: -45, + height: '303px', + }, + { + src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png', + bottom: 0, + left: 0, + width: '331px', + }, + ], + appList: [ + { + icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', + title: 'Ant Design', + desc: '杭州市较知名的 UI 设计语言', + url: 'https://ant.design', + }, + { + icon: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png', + title: 'AntV', + desc: '蚂蚁集团全新一代数据可视化解决方案', + url: 'https://antv.vision/', + target: '_blank', + }, + { + icon: 'https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png', + title: 'umi', + desc: '插件化的企业级前端应用框架。', + url: 'https://umijs.org/zh-CN/docs', + }, + + { + icon: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg', + title: '语雀', + desc: '知识创作与分享工具', + url: 'https://www.yuque.com/', + }, + { + icon: 'https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg', + title: 'Kitchen ', + desc: 'Sketch 工具集', + url: 'https://kitchen.alipay.com/', + }, + { + icon: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png', + title: 'dumi', + desc: '为组件开发场景而生的文档工具', + url: 'https://d.umijs.org/zh-CN', + }, + ], +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..b85db59 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + + +ReactDOM.createRoot(document.getElementById('root')!).render( + // + + // , +) diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx new file mode 100644 index 0000000..60fb8a4 --- /dev/null +++ b/src/pages/dashboard/index.tsx @@ -0,0 +1,25 @@ + import { ProCard } from '@ant-design/pro-components' + import { createLazyRoute } from '@tanstack/react-router' + + +const Index = () => { + return ( + <> + + +

Dashboard

+ +
+ + ) +} + + export const Route = createLazyRoute('/welcome')({ + component: Index, + }) +export default Index \ No newline at end of file diff --git a/src/pages/list/index.tsx b/src/pages/list/index.tsx new file mode 100644 index 0000000..4763cee --- /dev/null +++ b/src/pages/list/index.tsx @@ -0,0 +1,10 @@ + +const Index = () => { + return ( +
+ 普通页面 +
+ ) +} + +export default Index \ No newline at end of file diff --git a/src/pages/list/list.tsx b/src/pages/list/list.tsx new file mode 100644 index 0000000..7335122 --- /dev/null +++ b/src/pages/list/list.tsx @@ -0,0 +1,15 @@ +import { createLazyRoute } from '@tanstack/react-router' +import { ProCard } from '@ant-design/pro-components' +const List = () => { + return ( + + 列表页面 + + ) +} +export const Route = createLazyRoute('/list/index')({ + component: List, +}) + + +export default List \ No newline at end of file diff --git a/src/pages/list/tree.tsx b/src/pages/list/tree.tsx new file mode 100644 index 0000000..005dcbc --- /dev/null +++ b/src/pages/list/tree.tsx @@ -0,0 +1,146 @@ +import { LightFilter, PageContainer, ProCard, ProColumns, ProTable } from '@ant-design/pro-components' +import { Tree, Input, Space, Button } from 'antd' +import { createLazyRoute } from '@tanstack/react-router' +import { departmentAtom } from '../../store/department.ts' +import { useAtomValue } from 'jotai' +import { getIcon } from '../../components/icon' +import dayjs from 'dayjs' + +//递归渲染树形结构,将name->title, id->key +const renderTree = (data: any[]) => { + return data?.map((item) => { + if (item.children) { + return { + title: item.name, + key: item.id, + children: renderTree(item.children), + } + } + return { + title: item.name, + key: item.id, + } + }) +} + + +const columns: ProColumns[] = [ + { + title: '姓名', + dataIndex: 'name', + render: (_) => {_}, + formItemProps: { + lightProps: { + labelFormatter: (value) => `app-${value}`, + }, + }, + }, + { + title: '帐号', + dataIndex: 'account', + }, + { + title: '创建者', + dataIndex: 'creator', + valueType: 'select', + search: false, + valueEnum: { + all: { text: '全部' }, + 付小小: { text: '付小小' }, + 曲丽丽: { text: '曲丽丽' }, + 林东东: { text: '林东东' }, + 陈帅帅: { text: '陈帅帅' }, + 兼某某: { text: '兼某某' }, + }, + }, + //操作 + { + title: '操作', + valueType: 'option', + render: (_, record) => { + return [ + { + alert('edit') + }}>编辑, + { + alert('delete') + }}>删除, + ] + } + }, +] + +const TreePage = () => { + + const { data, isError, isPending } = useAtomValue(departmentAtom) + + if (isError) { + return
Error
+ } + // if (isPending){ + // return
Loading
+ // } + + return ( + + + + , + ], + }} + + /> + + + + + + ) +} + + +export default TreePage \ No newline at end of file diff --git a/src/pages/system/departments/index.tsx b/src/pages/system/departments/index.tsx new file mode 100644 index 0000000..097a8b2 --- /dev/null +++ b/src/pages/system/departments/index.tsx @@ -0,0 +1,11 @@ +import { PageContainer } from '@ant-design/pro-components' + +const Departments = () => { + return ( + + + + ) +} + +export default Departments \ No newline at end of file diff --git a/src/pages/system/menus/index.tsx b/src/pages/system/menus/index.tsx new file mode 100644 index 0000000..b1ab2c8 --- /dev/null +++ b/src/pages/system/menus/index.tsx @@ -0,0 +1,60 @@ +import { PageContainer, ProCard } from '@ant-design/pro-components' +import { Button, Space, Tree } from 'antd' +import { useAtom, useAtomValue } from 'jotai' +import { menuDataAtom, selectedMenuAtom, selectedMenuIdAtom } from '@/store/system.ts' +import { formatterMenuData } from '@/utils/uuid.ts' +import { CloseOutlined, PlusOutlined } from '@ant-design/icons' + +const Menus = () => { + + const { data, isLoading } = useAtomValue(menuDataAtom) + const [ currentMenu, setCurrentMenu ] = useAtom(selectedMenuAtom) + const [ selectedKey, setSelectedKey ] = useAtom(selectedMenuIdAtom) + + const treeData = formatterMenuData(data!) + + return ( + + + +