feat: 支持路由参数
This commit is contained in:
@@ -26,23 +26,26 @@
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
interface Meta {
|
interface Meta {
|
||||||
displayName?: string; // 展示在菜单上的名字
|
displayName?: string; // 展示在菜单上的名字, 可以用 {{language_id}} 包起来,用于国际化显示
|
||||||
icon?: string; // 展示在菜单上的图标
|
icon?: string; // 展示在菜单上的图标
|
||||||
headMenu?: // 是否展示在头部菜单 或 所属头部菜单的名字
|
headMenu?: // 是否展示在头部菜单 或 所属头部菜单的名字
|
||||||
| boolean
|
| boolean
|
||||||
| {
|
| {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string; // 可以用 {{language_id}} 包起来,用于国际化显示
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
order?: number; // 菜单序号,数字越小越靠前
|
||||||
};
|
};
|
||||||
sideMenu?: // 是否展示在侧边菜单 或 所属侧边菜单的名字
|
sideMenu?: // 是否展示在侧边菜单 或 所属侧边菜单的名字
|
||||||
| boolean
|
| boolean
|
||||||
| {
|
| {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string; // 可以用 {{language_id}} 包起来,用于国际化显示
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
order?: number; // 菜单序号,数字越小越靠前
|
||||||
};
|
};
|
||||||
order?: number; // 菜单排列序号,数字越小越靠前
|
order?: number; // 菜单序号,数字越小越靠前
|
||||||
|
route?: string; // 当前页面路由名字,支持 ':id' 等路由参数,不同则默认等于目录名
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ function buildNestedStructure(filePaths) {
|
|||||||
const parts = filePath.split('/').slice(2, -1); // 去掉 'src/pages' 'meta.json'
|
const parts = filePath.split('/').slice(2, -1); // 去掉 'src/pages' 'meta.json'
|
||||||
|
|
||||||
let current = root;
|
let current = root;
|
||||||
|
let parentFullRoute = '';
|
||||||
|
|
||||||
parts.forEach((part, index) => {
|
parts.forEach((part, index) => {
|
||||||
const find = current.find((item) => item._path === part);
|
const find = current.find((item) => item._path === part);
|
||||||
@@ -25,9 +26,15 @@ function buildNestedStructure(filePaths) {
|
|||||||
|
|
||||||
if (index === parts.length - 1) {
|
if (index === parts.length - 1) {
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
Object.assign(current, JSON.parse(content), {
|
const contentJSON = JSON.parse(content);
|
||||||
|
Object.assign(current, contentJSON, {
|
||||||
_fullpath: parts.join('/'),
|
_fullpath: parts.join('/'),
|
||||||
|
_fullroute: parentFullRoute
|
||||||
|
? `${parentFullRoute}/${contentJSON.route || part}`
|
||||||
|
: contentJSON.route || part,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
parentFullRoute = current._fullroute || parentFullRoute;
|
||||||
}
|
}
|
||||||
current = current._children;
|
current = current._children;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const parseMeta2Routes = (meta: Meta[]) => {
|
|||||||
() => import(`@/pages/${item._fullpath}/${toPascalCase(item._path)}`),
|
() => import(`@/pages/${item._fullpath}/${toPascalCase(item._path)}`),
|
||||||
);
|
);
|
||||||
const route: RouteObject = {
|
const route: RouteObject = {
|
||||||
path: item._fullpath,
|
path: item._fullroute,
|
||||||
element: <Element />,
|
element: <Element />,
|
||||||
};
|
};
|
||||||
routes.push(route);
|
routes.push(route);
|
||||||
|
|||||||
2
template/src/global.d.ts
vendored
2
template/src/global.d.ts
vendored
@@ -23,6 +23,8 @@ interface Meta {
|
|||||||
order?: number; // 菜单序号,数字越小越靠前
|
order?: number; // 菜单序号,数字越小越靠前
|
||||||
};
|
};
|
||||||
order?: number; // 菜单序号,数字越小越靠前
|
order?: number; // 菜单序号,数字越小越靠前
|
||||||
|
route?: string; // 当前页面路由名字,支持 ':id' 等路由参数,不同则默认等于目录名
|
||||||
|
_fullroute?: string; // 完整路由,最终路由
|
||||||
_path: string; // 目录名
|
_path: string; // 目录名
|
||||||
_fullpath?: string; // 完整目录结构
|
_fullpath?: string; // 完整目录结构
|
||||||
_children: Meta[];
|
_children: Meta[];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useAppDispatch, useAppSelector } from '@/app/hooks';
|
import { useAppDispatch, useAppSelector } from '@/app/hooks';
|
||||||
import { User } from '@/pages/demo/demoAPI';
|
import { User } from '@/pages/demo/demoAPI';
|
||||||
import { fetchUsers, selectDemo } from '@/pages/demo/demoSlice';
|
import { fetchUsers, selectDemo } from '@/pages/demo/demoSlice';
|
||||||
|
import withPage from '@/utils/withPage';
|
||||||
import { EditTableColumnsType, Table } from '@df/toco-ui';
|
import { EditTableColumnsType, Table } from '@df/toco-ui';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
@@ -43,4 +44,4 @@ const Demo = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Demo;
|
export default withPage(Demo);
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
import { useAppSelector } from '@/app/hooks';
|
import { useAppSelector } from '@/app/hooks';
|
||||||
|
import withPage from '@/utils/withPage';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import * as tocoServices from './testAPI';
|
import * as tocoServices from './testAPI';
|
||||||
|
|
||||||
const Test = (props) => {
|
const Test = (props) => {
|
||||||
const tocoStore = useAppSelector((state) => state);
|
const tocoStore = useAppSelector((state) => state);
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 一些常用变量或方法
|
// 一些常用变量或方法
|
||||||
console.log('navigate', navigate);
|
|
||||||
console.log('props', props);
|
console.log('props', props);
|
||||||
console.log('store', tocoStore);
|
console.log('store', tocoStore);
|
||||||
console.log('services', tocoServices);
|
console.log('services', tocoServices);
|
||||||
console.log('refs', tocoRefs);
|
console.log('refs', tocoRefs);
|
||||||
console.log('modals', tocoModals);
|
console.log('modals', tocoModals);
|
||||||
}, [navigate, props, tocoStore]);
|
}, [props, tocoStore]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -30,4 +28,4 @@ const Test = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Test;
|
export default withPage(Test);
|
||||||
|
|||||||
25
template/src/pages/yyy/xxx/Xxx.js
Normal file
25
template/src/pages/yyy/xxx/Xxx.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useAppSelector } from '@/app/hooks';
|
||||||
|
import withPage from '@/utils/withPage';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import * as tocoServices from './xxxAPI';
|
||||||
|
|
||||||
|
const Xxx = (props) => {
|
||||||
|
const tocoStore = useAppSelector((state) => state);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 一些常用变量或方法
|
||||||
|
console.log('props', props);
|
||||||
|
console.log('services', tocoServices);
|
||||||
|
console.log('refs', tocoRefs);
|
||||||
|
console.log('modals', tocoModals);
|
||||||
|
console.log('store', tocoStore);
|
||||||
|
}, [props, tocoStore]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>xxx</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withPage(Xxx);
|
||||||
8
template/src/pages/yyy/xxx/meta.json
Normal file
8
template/src/pages/yyy/xxx/meta.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"displayName": "xxx",
|
||||||
|
"icon": "RobotOutlined",
|
||||||
|
"headMenu": false,
|
||||||
|
"sideMenu": false,
|
||||||
|
"order": 0,
|
||||||
|
"route": "xx/:oo"
|
||||||
|
}
|
||||||
26
template/src/pages/yyy/xxx/xxxAPI.ts
Normal file
26
template/src/pages/yyy/xxx/xxxAPI.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import axiosInstance from '@/app/request';
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
name: {
|
||||||
|
first: string;
|
||||||
|
last: string;
|
||||||
|
};
|
||||||
|
phone: string;
|
||||||
|
gender: 'female' | 'male';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUsers = async (): Promise<User[]> => {
|
||||||
|
const response: AxiosResponse<{
|
||||||
|
info: any;
|
||||||
|
results: User[];
|
||||||
|
}> = await axiosInstance.get(
|
||||||
|
`https://randomuser.me/api/?results=8&seed=toco`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
errorHandler: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response.data?.results || [];
|
||||||
|
};
|
||||||
41
template/src/utils/withPage.tsx
Normal file
41
template/src/utils/withPage.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
useLocation,
|
||||||
|
useNavigate,
|
||||||
|
useParams,
|
||||||
|
useSearchParams,
|
||||||
|
} from 'react-router-dom';
|
||||||
|
|
||||||
|
const withPage = (BaseComponent: React.ComponentType) => {
|
||||||
|
const WithPageComponent = (props: any) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const params = useParams();
|
||||||
|
const location = useLocation();
|
||||||
|
const [searchParamsOri] = useSearchParams();
|
||||||
|
const searchParams = useMemo(() => {
|
||||||
|
const params = {};
|
||||||
|
for (const [key, value] of searchParamsOri.entries()) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}, [searchParamsOri]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseComponent
|
||||||
|
{...props}
|
||||||
|
location={location}
|
||||||
|
params={params}
|
||||||
|
search={searchParams}
|
||||||
|
navigate={navigate}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
WithPageComponent.displayName =
|
||||||
|
BaseComponent.displayName ?? 'WithPageComponent';
|
||||||
|
|
||||||
|
return WithPageComponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withPage;
|
||||||
Reference in New Issue
Block a user