Skip to content

Commit

Permalink
feat(platform): add detail route
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Nov 10, 2022
1 parent dbf9c5d commit a06e2a3
Show file tree
Hide file tree
Showing 30 changed files with 656 additions and 253 deletions.
3 changes: 2 additions & 1 deletion packages/hooks/src/useImmer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ export type DraftFunction<S> = (draft: S) => void;
export type Updater<S> = (arg: S | DraftFunction<S>) => void;
export type ImmerHook<S> = [S, Updater<S>];

export function useImmer<S>(): ImmerHook<S | undefined>;
export function useImmer<S = any>(initialValue: S | (() => S)): ImmerHook<S>;
export function useImmer(initialValue: any) {
export function useImmer(initialValue?: any) {
const [val, updateValue] = useState(() => freeze(typeof initialValue === 'function' ? initialValue() : initialValue, true));
const setValue = useCallback((updater: any) => {
if (typeof updater === 'function') updateValue(produce(updater));
Expand Down
121 changes: 45 additions & 76 deletions packages/platform/src/app/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import AppExceptionRoute from './routes/exception/Exception';
import AppLayout from './routes/layout/Layout';
import AppLoginRoute from './routes/login/Login';

const AppAMapRoute = React.lazy(() => import('./routes/dashboard/amap/AMap'));
const AppEChartsRoute = React.lazy(() => import('./routes/dashboard/echarts/ECharts'));

const AppStandardTableRoute = React.lazy(() => import('./routes/list/standard-table/StandardTable'));

const AppACLRoute = React.lazy(() => import('./routes/test/acl/ACL'));
const AppHttpRoute = React.lazy(() => import('./routes/test/http/Http'));
const ROUTES = {
'/dashboard/amap': React.lazy(() => import('./routes/dashboard/amap/AMap')),
'/dashboard/echarts': React.lazy(() => import('./routes/dashboard/echarts/ECharts')),
'/list/standard-table': React.lazy(() => import('./routes/list/standard-table/StandardTable')),
'/list/standard-table/:id': React.lazy(() => import('./routes/list/standard-table/detail/Detail')),
'/test/acl': React.lazy(() => import('./routes/test/acl/ACL')),
'/test/http': React.lazy(() => import('./routes/test/http/Http')),
};

export interface RouteStateContextData {
matchRoutes: RouteMatch<string, RouteItem>[] | null;
Expand All @@ -35,13 +36,6 @@ export type CanActivateFn = (route: RouteItem) => true | React.ReactElement;

export interface RouteData {
title?: string;
breadcrumb?:
| {
title?: string;
link?: boolean;
separator?: React.ReactNode;
}
| false;
acl?:
| {
control: Control | Control[];
Expand Down Expand Up @@ -76,17 +70,13 @@ export function AppRoutes() {
path: LOGIN_PATH,
element: <AppLoginRoute />,
data: {
title: t('login', { ns: 'title' }),
title: t('Login', { ns: 'title' }),
},
},
{
path: '/',
element: <AppLayout />,
data: {
breadcrumb: {
title: t('home', { ns: 'title' }),
link: true,
},
canActivate: [tokenGuard],
canActivateChild: [tokenGuard],
},
Expand All @@ -97,103 +87,82 @@ export function AppRoutes() {
},
{
path: 'dashboard',
data: {
breadcrumb: {
title: t('dashboard.', { ns: 'title' }),
},
},
children: [
{
index: true,
element: <Navigate to="/exception/404" replace />,
},
{
path: 'amap',
element: (
<React.Suspense fallback={<AppFCPLoader />}>
<AppAMapRoute />
</React.Suspense>
),
element: <React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/dashboard/amap'])}</React.Suspense>,
data: {
title: t('dashboard.amap', { ns: 'title' }),
breadcrumb: {
link: true,
},
acl: ROUTES_ACL.dashboard.amap,
title: t('AMap', { ns: 'title' }),
acl: ROUTES_ACL['/dashboard/amap'],
canActivate: [ACLGuard],
},
},
{
path: 'echarts',
element: (
<React.Suspense fallback={<AppFCPLoader />}>
<AppEChartsRoute />
</React.Suspense>
),
element: <React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/dashboard/echarts'])}</React.Suspense>,
data: {
title: t('dashboard.echarts', { ns: 'title' }),
breadcrumb: {
link: true,
},
acl: ROUTES_ACL.dashboard.echarts,
title: t('ECharts', { ns: 'title' }),
acl: ROUTES_ACL['/dashboard/echarts'],
canActivate: [ACLGuard],
},
},
],
},
{
path: 'list',
data: {
breadcrumb: {
title: t('list.', { ns: 'title' }),
},
},
children: [
{
index: true,
element: <Navigate to="/exception/404" replace />,
},
{
path: 'standard-table',
element: <React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/list/standard-table'])}</React.Suspense>,
data: {
title: t('Standard Table', { ns: 'title' }),
acl: ROUTES_ACL['/list/standard-table'],
canActivate: [ACLGuard],
},
},
{
path: 'standard-table/:id',
element: (
<React.Suspense fallback={<AppFCPLoader />}>
<AppStandardTableRoute />
</React.Suspense>
<React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/list/standard-table/:id'])}</React.Suspense>
),
data: {
title: t('list.standard-table', { ns: 'title' }),
breadcrumb: {
link: true,
},
acl: ROUTES_ACL.list['standard-table'],
title: t('Device Detail', { ns: 'title' }),
acl: ROUTES_ACL['/list/standard-table/:id'],
canActivate: [ACLGuard],
},
},
],
},
{
path: 'test',
data: {
breadcrumb: {
title: t('test.', { ns: 'title' }),
},
},
children: [
{
index: true,
element: <Navigate to="/exception/404" replace />,
},
{
path: 'acl',
element: (
<React.Suspense fallback={<AppFCPLoader />}>
<AppACLRoute />
</React.Suspense>
),
element: <React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/test/acl'])}</React.Suspense>,
data: {
title: t('test.acl', { ns: 'title' }),
acl: ROUTES_ACL.test.acl,
title: t('ACL', { ns: 'title' }),
acl: ROUTES_ACL['/test/acl'],
canActivate: [ACLGuard],
},
},
{
path: 'http',
element: (
<React.Suspense fallback={<AppFCPLoader />}>
<AppHttpRoute />
</React.Suspense>
),
element: <React.Suspense fallback={<AppFCPLoader />}>{React.createElement(ROUTES['/test/http'])}</React.Suspense>,
data: {
title: t('test.http', { ns: 'title' }),
acl: ROUTES_ACL.test.http,
title: t('Http', { ns: 'title' }),
acl: ROUTES_ACL['/test/http'],
canActivate: [ACLGuard],
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,49 @@
import type { DBreadcrumbItem } from '@react-devui/ui/components/breadcrumb';

import { isUndefined } from 'lodash';
import { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';

import { DBreadcrumb } from '@react-devui/ui';
import { getClassName } from '@react-devui/utils';

import { RouteStateContext } from '../../Routes';

export interface AppRouteHeaderBreadcrumbProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
aList: DBreadcrumbItem<string>[];
aHome?: DBreadcrumbItem<string>;
aSeparator?: React.ReactNode;
}

export function AppRouteHeaderBreadcrumb(props: AppRouteHeaderBreadcrumbProps): JSX.Element | null {
const {
aList,
aHome,
aSeparator,

...restProps
} = props;

const { matchRoutes } = useContext(RouteStateContext);

const breadcrumb = (() => {
if (matchRoutes) {
const list: DBreadcrumbItem<string>[] = [];
for (const match of matchRoutes) {
if (match.route.data) {
const { title: _title, breadcrumb = {} } = match.route.data;
if (breadcrumb) {
const title = breadcrumb.title ?? _title;
if (!isUndefined(title)) {
const link = breadcrumb.link ?? false;
list.push({
id: title,
title: link ? (
<Link className="app-route-header__breadcrumb-link" to={match.pathname}>
{title}
</Link>
) : (
title
),
link,
separator: breadcrumb.separator,
});
}
}
}
}
return list;
}
})();
const { t } = useTranslation();

const home: DBreadcrumbItem<string> = aHome ?? {
id: '/',
title: t('Home', { ns: 'title' }),
link: true,
};

return (
<div {...restProps} className={getClassName(restProps.className, 'app-route-header__breadcrumb')}>
{breadcrumb && <DBreadcrumb dList={breadcrumb} dSeparator={aSeparator} />}
<DBreadcrumb
dList={[home].concat(aList).map((item) => ({
...item,
title: item.link ? (
<Link className="app-route-header__breadcrumb-link" to={item.id}>
{item.title}
</Link>
) : (
item.title
),
}))}
dSeparator={aSeparator}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import { nth } from 'lodash';
import { useContext } from 'react';
import React, { useContext } from 'react';
import { useNavigate } from 'react-router-dom';

import { ArrowLeftOutlined } from '@react-devui/icons';
import { getClassName } from '@react-devui/utils';

import { RouteStateContext } from '../../Routes';

export interface AppRouteHeaderHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
aBack?: boolean;
aActions?: React.ReactNode[];
}

export function AppRouteHeaderHeader(props: AppRouteHeaderHeaderProps): JSX.Element | null {
const {
children,
aBack = false,
aActions,

...restProps
} = props;

const { matchRoutes } = useContext(RouteStateContext);

const navigate = useNavigate();

const title = (() => {
if (matchRoutes) {
const { title } = nth(matchRoutes, -1)!.route.data ?? {};
Expand All @@ -29,8 +35,20 @@ export function AppRouteHeaderHeader(props: AppRouteHeaderHeaderProps): JSX.Elem

return (
<div {...restProps} className={getClassName(restProps.className, 'app-route-header__header')}>
<div className="app-route-header__header-title">{children ?? title}</div>
<div className="app-route-header__header-actions">{aActions}</div>
<div className="app-route-header__header-title-container">
{aBack && (
<button className="app-route-header__header-back">
<ArrowLeftOutlined
onClick={() => {
navigate(-1);
}}
dSize={20}
/>
</button>
)}
<div className="app-route-header__header-title">{children ?? title}</div>
</div>
<div className="app-route-header__header-actions">{React.Children.map(aActions, (c) => c)}</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function AppTableFilter(props: AppTableFilterProps): JSX.Element | null {
{t('components.table-filter.Reset')}
</DButton>
{aFilterList && (
<div className="d-flex align-items-center">
<div className="d-flex align-items-center" style={{ width: 120 }}>
<DButton
className="me-2"
dType="link"
Expand All @@ -90,7 +90,9 @@ export function AppTableFilter(props: AppTableFilterProps): JSX.Element | null {
{aFilterList.map(({ label, node }) => (
<div key={label} className="app-table-filter__filter">
<label className="app-table-filter__filter-label">
<div style={{ width: labelWidth }}>{label}:</div>
<div className="app-colon" style={{ width: labelWidth }}>
{label}
</div>
</label>
{React.cloneElement(node, {
style: { ...node.props.style, maxWidth: `calc(100% - ${isNumber(labelWidth) ? labelWidth + 'px' : labelWidth})` },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

import { useMount } from '@react-devui/hooks';
import { DCard } from '@react-devui/ui';
Expand All @@ -9,6 +10,7 @@ import { barOptions, lineOptions, nightingaleOptions, pieOptions, scatterOptions

export default function ECharts(): JSX.Element | null {
const [options, setOptions] = useState<echarts.EChartsOption[]>([]);
const { t } = useTranslation();

useMount(() => {
setOptions([lineOptions, stackedLineOptions, barOptions, stackedBarOptions, pieOptions, nightingaleOptions, scatterOptions]);
Expand All @@ -17,7 +19,12 @@ export default function ECharts(): JSX.Element | null {
return (
<>
<AppRouteHeader>
<AppRouteHeader.Breadcrumb />
<AppRouteHeader.Breadcrumb
aList={[
{ id: '/dashboard', title: t('Dashboard', { ns: 'title' }) },
{ id: '/dashboard/echarts', title: t('ECharts', { ns: 'title' }) },
]}
/>
<AppRouteHeader.Header />
</AppRouteHeader>
<div className={styles['app-echarts']}>
Expand Down
Loading

0 comments on commit a06e2a3

Please sign in to comment.