refactor(app): clean up the client code
This commit is contained in:
parent
9495917e97
commit
efb0320427
@ -1,5 +1,3 @@
|
||||
import { values } from 'lodash';
|
||||
import { stringify } from 'querystring';
|
||||
import { request } from 'umi';
|
||||
|
||||
interface ActionParams {
|
||||
@ -24,6 +22,7 @@ interface Resource {
|
||||
[name: string]: (params?: ActionParams) => Promise<any>;
|
||||
}
|
||||
|
||||
// TODO 待改进,提供一个封装度更完整的 SDK,request 可以自由替换
|
||||
class ApiClient {
|
||||
resource(name: string): Resource {
|
||||
const proxy: any = new Proxy(
|
||||
|
@ -32,6 +32,7 @@ export const request: RequestConfig = {
|
||||
],
|
||||
};
|
||||
|
||||
// TODO:目前这块是写在代码里的,待改进
|
||||
const pathnames = ['/login', '/register', '/lostpassword', '/resetpassword'];
|
||||
|
||||
export async function getInitialState() {
|
||||
|
@ -1,49 +0,0 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import ViewFactory from '@/components/views';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
export function Create(props) {
|
||||
console.log(props);
|
||||
const { title, viewName, collection_name } = props.schema;
|
||||
const { activeTab = {}, item = {}, associatedName, associatedKey } = props;
|
||||
const { associationField } = activeTab;
|
||||
|
||||
const params = {};
|
||||
|
||||
if (associationField && associationField.target) {
|
||||
params['resourceName'] = associationField.name;
|
||||
params['resourceTarget'] = associationField.target;
|
||||
params['associatedName'] = associatedName;
|
||||
params['associatedKey'] = associatedKey;
|
||||
} else {
|
||||
params['resourceName'] = props.resourceName;
|
||||
params['resourceTarget'] = collection_name;
|
||||
params['resourceKey'] = item.itemId;
|
||||
}
|
||||
|
||||
console.log(params);
|
||||
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<ViewFactory
|
||||
{...props}
|
||||
reference={drawerRef}
|
||||
viewName={viewName}
|
||||
{...params}
|
||||
/>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Create;
|
@ -1,28 +0,0 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Button, Popconfirm } from 'antd';
|
||||
import ViewFactory from '@/components/views';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
|
||||
export function Destroy(props) {
|
||||
console.log(props);
|
||||
const { onTrigger } = props;
|
||||
const { title, viewId, isBulk = true } = props.schema;
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<Popconfirm
|
||||
title="确认删除吗?"
|
||||
onConfirm={() => {
|
||||
console.log('destroy', onTrigger);
|
||||
onTrigger && onTrigger();
|
||||
}}
|
||||
>
|
||||
<Button icon={<DeleteOutlined />} type={'ghost'} danger>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Destroy;
|
@ -1,75 +0,0 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Button, Popover } from 'antd';
|
||||
import ViewFactory from '@/components/views';
|
||||
import { FilterOutlined } from '@ant-design/icons';
|
||||
|
||||
export function Filter(props) {
|
||||
console.log(props);
|
||||
const drawerRef = useRef<any>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { title, viewName, collection_name } = props.schema;
|
||||
const {
|
||||
filterCount,
|
||||
activeTab = {},
|
||||
item = {},
|
||||
associatedName,
|
||||
associatedKey,
|
||||
} = props;
|
||||
const { associationField } = activeTab;
|
||||
|
||||
const params = {};
|
||||
|
||||
if (associationField && associationField.target) {
|
||||
params['resourceName'] = associationField.target;
|
||||
params['resourceTarget'] = associationField.target;
|
||||
params['associatedName'] = associatedName;
|
||||
params['associatedKey'] = associatedKey;
|
||||
} else {
|
||||
params['resourceName'] = collection_name;
|
||||
params['resourceKey'] = item.itemId;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popover
|
||||
// title="设置筛选"
|
||||
trigger="click"
|
||||
visible={visible}
|
||||
placement={'bottomLeft'}
|
||||
onVisibleChange={visible => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
className={'filters-popover'}
|
||||
style={{}}
|
||||
overlayStyle={{
|
||||
minWidth: 500,
|
||||
}}
|
||||
content={
|
||||
<>
|
||||
<div
|
||||
className={'popover-button-mask'}
|
||||
onClick={() => setVisible(false)}
|
||||
></div>
|
||||
<ViewFactory
|
||||
{...props}
|
||||
setVisible={setVisible}
|
||||
viewName={'filter'}
|
||||
{...params}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
icon={<FilterOutlined />}
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
{filterCount ? `${filterCount} 个${title}项` : title}
|
||||
</Button>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Filter;
|
@ -1,46 +0,0 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import ViewFactory from '@/components/views';
|
||||
import { EditOutlined } from '@ant-design/icons';
|
||||
|
||||
export function Update(props) {
|
||||
const { title, viewName, resourceName, collection_name } = props.schema;
|
||||
const { resourceKey, activeTab = {}, item = {} } = props;
|
||||
const { associationField } = activeTab;
|
||||
|
||||
const params = {};
|
||||
|
||||
if (associationField && associationField.target) {
|
||||
params['resourceName'] = associationField.target;
|
||||
params['resourceTarget'] = associationField.target;
|
||||
params['associatedName'] = resourceName;
|
||||
params['associatedKey'] = item.itemId;
|
||||
} else {
|
||||
params['resourceName'] = collection_name;
|
||||
params['resourceKey'] = item.itemId || resourceKey;
|
||||
}
|
||||
const drawerRef = useRef<any>();
|
||||
return (
|
||||
<>
|
||||
<ViewFactory
|
||||
{...props}
|
||||
reference={drawerRef}
|
||||
viewName={viewName}
|
||||
mode={'update'}
|
||||
{...params}
|
||||
/>
|
||||
<Button
|
||||
icon={<EditOutlined />}
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
drawerRef.current.setVisible(true);
|
||||
drawerRef.current.getData(item.itemId || resourceKey);
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Update;
|
@ -1,22 +1,363 @@
|
||||
import React from 'react';
|
||||
import Create from './Create';
|
||||
import Update from './Update';
|
||||
import Destroy from './Destroy';
|
||||
import Filter from './Filter';
|
||||
// import { Space } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Space, Button, Popconfirm, Popover } from 'antd';
|
||||
import {
|
||||
FilterOutlined,
|
||||
PlusOutlined,
|
||||
SelectOutlined,
|
||||
DeleteOutlined,
|
||||
PrinterOutlined,
|
||||
ExportOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import Drawer from '@/components/drawer';
|
||||
import View from '@/components/views';
|
||||
import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
import ReactToPrint from 'react-to-print';
|
||||
import api from '@/api-client';
|
||||
import './style.less';
|
||||
|
||||
const ACTIONS = new Map<string, any>();
|
||||
|
||||
export function Create(props) {
|
||||
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
|
||||
const { title, pageTitle, viewName, transform, componentProps = {} } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size={size}
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: pageTitle || title,
|
||||
content: ({ resolve, closeWithConfirm }) => (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
onValueChange={() => {
|
||||
closeWithConfirm && closeWithConfirm(true);
|
||||
}}
|
||||
associatedKey={associatedKey}
|
||||
viewName={viewName}
|
||||
onReset={resolve}
|
||||
onDraft={async item => {
|
||||
const values = transform ? {} : item;
|
||||
for (const [sourceKey, targetKey] of Object.entries<string>(
|
||||
transform || {},
|
||||
)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(values, targetKey, value);
|
||||
}
|
||||
await resolve();
|
||||
console.log('onFinish', values);
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
onFinish={async item => {
|
||||
const values = transform ? {} : item;
|
||||
for (const [sourceKey, targetKey] of Object.entries<string>(
|
||||
transform || {},
|
||||
)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(values, targetKey, value);
|
||||
}
|
||||
await resolve();
|
||||
console.log('onFinish', values);
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}}
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Update(props) {
|
||||
const { onFinish, data, schema = {}, associatedKey, ...restProps } = props;
|
||||
const { title, viewName } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: title,
|
||||
content: ({ resolve, closeWithConfirm }) => (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
associatedKey={associatedKey}
|
||||
data={data}
|
||||
viewName={viewName}
|
||||
onReset={resolve}
|
||||
onDraft={async values => {
|
||||
await resolve();
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
onValueChange={() => {
|
||||
closeWithConfirm && closeWithConfirm(true);
|
||||
}}
|
||||
onFinish={async values => {
|
||||
await resolve();
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}}
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Add(props) {
|
||||
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
|
||||
console.log({ associatedKey }, 'add');
|
||||
const { filter, title, viewName, transform, componentProps = {} } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size={size}
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: title,
|
||||
content: ({ resolve }) => {
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
return (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
defaultFilter={filter}
|
||||
viewName={viewName}
|
||||
associatedKey={associatedKey}
|
||||
onSelected={values => {
|
||||
console.log(values);
|
||||
setSelectedRows(
|
||||
values.map(item => {
|
||||
if (!transform) {
|
||||
return;
|
||||
}
|
||||
const data = {};
|
||||
for (const [sourceKey, targetKey] of Object.entries<
|
||||
string
|
||||
>(transform)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(data, targetKey, value);
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Drawer.Footer>
|
||||
<Space>
|
||||
<Button onClick={resolve}>取消</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={async () => {
|
||||
console.log({ schema, onFinish });
|
||||
onFinish && (await onFinish(selectedRows));
|
||||
resolve();
|
||||
}}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</Space>
|
||||
</Drawer.Footer>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}}
|
||||
icon={<SelectOutlined />}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Destroy(props) {
|
||||
const { size, schema = {}, onFinish } = props;
|
||||
const { title, componentProps = {} } = schema;
|
||||
return (
|
||||
<Popconfirm
|
||||
title="确认删除吗?"
|
||||
onConfirm={async e => {
|
||||
onFinish && (await onFinish());
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size={size}
|
||||
danger
|
||||
type={'ghost'}
|
||||
icon={<DeleteOutlined />}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
|
||||
export function Filter(props) {
|
||||
const { schema = {}, onFinish } = props;
|
||||
const { title, fields = [] } = schema;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [data, setData] = useState({});
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
console.log('Filter', { visible, data });
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<div
|
||||
style={{
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
zIndex: 1000,
|
||||
position: 'fixed',
|
||||
background: 'rgba(0, 0, 0, 0)',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}
|
||||
onClick={() => setVisible(false)}
|
||||
></div>
|
||||
)}
|
||||
<Popover
|
||||
// title="设置筛选"
|
||||
trigger="click"
|
||||
visible={visible}
|
||||
defaultVisible={visible}
|
||||
placement={'bottomLeft'}
|
||||
destroyTooltipOnHide
|
||||
onVisibleChange={visible => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
className={'filters-popover'}
|
||||
style={{}}
|
||||
overlayStyle={{
|
||||
minWidth: 500,
|
||||
}}
|
||||
content={
|
||||
<>
|
||||
<View
|
||||
data={data}
|
||||
onFinish={async values => {
|
||||
if (values) {
|
||||
const items = values.filter.and || values.filter.or;
|
||||
setFilterCount(Array.isArray(items) ? items.length : 0);
|
||||
setData(values);
|
||||
onFinish && (await onFinish(values));
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
schema={{
|
||||
type: 'filterForm',
|
||||
fields: [
|
||||
{
|
||||
dataIndex: ['filter'],
|
||||
name: 'filter',
|
||||
interface: 'json',
|
||||
type: 'json',
|
||||
component: {
|
||||
type: 'filter',
|
||||
'x-component-props': {
|
||||
fields,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button icon={<FilterOutlined />}>
|
||||
{filterCount ? `${filterCount} 个${title}项` : title}
|
||||
</Button>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Print(props) {
|
||||
const { schema = {}, contentRef } = props;
|
||||
const { title, buttonProps = {} } = schema;
|
||||
return (
|
||||
<ReactToPrint
|
||||
trigger={() => (
|
||||
<Button {...buttonProps} icon={<PrinterOutlined />}>
|
||||
{title}
|
||||
</Button>
|
||||
)}
|
||||
content={() => contentRef.current}
|
||||
pageStyle={`
|
||||
@page {
|
||||
margin: 1cm;
|
||||
}
|
||||
table { page-break-inside:auto }
|
||||
tr { page-break-inside:avoid; page-break-after:auto }
|
||||
`}
|
||||
documentTitle={' '}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Export(props) {
|
||||
const { schema = {}, onFinish } = props;
|
||||
const { title, buttonProps = {} } = schema;
|
||||
return (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
icon={<ExportOutlined />}
|
||||
onClick={async () => {
|
||||
onFinish && (await onFinish({ fields: [] }));
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function Actions(props) {
|
||||
const { onTrigger = {}, actions = [], style, ...restProps } = props;
|
||||
return (
|
||||
actions.length > 0 && (
|
||||
<div className={'action-buttons'} style={style}>
|
||||
{actions.map(
|
||||
action =>
|
||||
ACTIONS.has(action.type) && (
|
||||
<div key={action.__index} className={`${action.type}-action-button action-button`}>
|
||||
<Action
|
||||
{...restProps}
|
||||
onFinish={onTrigger[action.type]}
|
||||
schema={action}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Actions;
|
||||
|
||||
export function registerAction(type: string, Action: any) {
|
||||
ACTIONS.set(type, Action);
|
||||
}
|
||||
|
||||
registerAction('update', Update);
|
||||
registerAction('create', Create);
|
||||
registerAction('destroy', Destroy);
|
||||
registerAction('filter', Filter);
|
||||
|
||||
export function getAction(type: string) {
|
||||
return ACTIONS.get(type);
|
||||
}
|
||||
@ -29,25 +370,10 @@ export function Action(props) {
|
||||
return Component && <Component {...props} />;
|
||||
}
|
||||
|
||||
export function Actions(props) {
|
||||
const { onTrigger = {}, style, schema, actions = [], ...restProps } = props;
|
||||
console.log(onTrigger);
|
||||
return (
|
||||
actions.length > 0 && (
|
||||
<div className={'action-buttons'} style={style}>
|
||||
{actions.map(action => (
|
||||
<div className={`${action.name}-action-button action-button`}>
|
||||
<Action
|
||||
{...restProps}
|
||||
view={schema}
|
||||
schema={action}
|
||||
onTrigger={onTrigger[action.name]}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Actions;
|
||||
registerAction('add', Add);
|
||||
registerAction('update', Update);
|
||||
registerAction('create', Create);
|
||||
registerAction('destroy', Destroy);
|
||||
registerAction('filter', Filter);
|
||||
registerAction('print', Print);
|
||||
registerAction('export', Export);
|
||||
|
@ -12,4 +12,4 @@
|
||||
left: 24px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
|
||||
import ReactDOM, { createPortal } from 'react-dom';
|
||||
import { createForm } from '@formily/core';
|
||||
// import { FormProvider } from '@formily/react'
|
||||
import { isNum, isStr, isBool, isFn } from '@formily/shared';
|
||||
import { Modal, Drawer } from 'antd';
|
||||
import { DrawerProps } from 'antd/lib/drawer';
|
@ -8,9 +8,8 @@ import {
|
||||
isStr,
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import ViewFactory from '@/components/views';
|
||||
import Drawer from '@/components/pages/AdminLoader/Drawer';
|
||||
import View from '@/components/pages/AdminLoader/View';
|
||||
import Drawer from '@/components/drawer';
|
||||
import View from '@/components/views';
|
||||
|
||||
function transform({ value, multiple, labelField, valueField = 'id' }) {
|
||||
let selectedKeys = [];
|
@ -21,7 +21,6 @@ import { Cascader } from './cascader';
|
||||
import { Icon } from './icons';
|
||||
import { ColorSelect } from './color-select';
|
||||
import { Permissions } from './permissions';
|
||||
import { DraggableTable } from './draggable-table';
|
||||
import { Values } from './values';
|
||||
import { Automations } from './automations';
|
||||
import { Wysiwyg } from './wysiwyg';
|
||||
@ -61,7 +60,6 @@ export const setup = () => {
|
||||
drawerSelect: DrawerSelect,
|
||||
colorSelect: ColorSelect,
|
||||
subTable: SubTable,
|
||||
draggableTable: DraggableTable,
|
||||
values: Values,
|
||||
wysiwyg: Wysiwyg,
|
||||
markdown: Markdown,
|
@ -11,7 +11,7 @@ import {
|
||||
} from '../shared';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { Spin } from '@nocobase/client';
|
||||
import { Spin } from '@/components/spin';
|
||||
import get from 'lodash/get';
|
||||
|
||||
function RemoteSelectComponent(props) {
|
@ -1,15 +1,5 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Table as AntdTable, Button, Space, Popconfirm } from 'antd';
|
||||
import { Actions } from '@/components/actions';
|
||||
import ViewFactory from '@/components/views';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { components, fields2columns } from '@/components/views/SortableTable';
|
||||
import Form from './Form';
|
||||
import { Spin } from '@nocobase/client';
|
||||
import maxBy from 'lodash/maxBy';
|
||||
import View from '@/components/pages/AdminLoader/View';
|
||||
import View from '@/components/views';
|
||||
|
||||
export interface SimpleTableProps {
|
||||
schema?: any;
|
@ -9,7 +9,6 @@ import {
|
||||
isStr,
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import ViewFactory from '@/components/views';
|
||||
import Table from './Table';
|
||||
|
||||
export const SubTable = connect({
|
@ -1,109 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from '@formily/react-schema-renderer';
|
||||
import moment from 'moment';
|
||||
import { Select, Table } from 'antd';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
mapStyledProps,
|
||||
mapTextComponent,
|
||||
compose,
|
||||
isStr,
|
||||
isArr,
|
||||
} from '../shared';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { Spin } from '@nocobase/client';
|
||||
import { fields2columns, components } from '@/components/views/SortableTable';
|
||||
|
||||
function DraggableTableComponent(props) {
|
||||
const {
|
||||
mode = 'showInDetail',
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
rowKey = 'id',
|
||||
fields = [],
|
||||
resourceName,
|
||||
associatedKey,
|
||||
filter,
|
||||
labelField,
|
||||
valueField = 'id',
|
||||
objectValue,
|
||||
placeholder,
|
||||
} = props;
|
||||
const { data = [], loading = true, mutate } = useRequest(
|
||||
() => {
|
||||
return api.resource(resourceName).list({
|
||||
associatedKey,
|
||||
filter,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
refreshDeps: [resourceName, associatedKey],
|
||||
},
|
||||
);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
const onTableChange = (selectedRowKeys: React.ReactText[]) => {
|
||||
onChange(selectedRowKeys);
|
||||
};
|
||||
const tableProps: any = {};
|
||||
tableProps.rowSelection = {
|
||||
selectedRowKeys: Array.isArray(value)
|
||||
? value
|
||||
: data.map(item => item[rowKey]),
|
||||
onChange: onTableChange,
|
||||
};
|
||||
const dataSource = data.filter(item => {
|
||||
return get(item, ['component', mode]);
|
||||
});
|
||||
// const sortIds = get(value, 'sort')||[];
|
||||
// let values = [];
|
||||
// sortIds.forEach(id => {
|
||||
// const item = dataSource.find(item => item[rowKey] === id);
|
||||
// if (item) {
|
||||
// values.push(item);
|
||||
// }
|
||||
// });
|
||||
// if (values.length === 0) {
|
||||
// values = dataSource;
|
||||
// }
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
scroll={{ y: 300 }}
|
||||
size={'small'}
|
||||
dataSource={dataSource}
|
||||
rowKey={rowKey}
|
||||
loading={loading}
|
||||
columns={fields2columns(fields || [])}
|
||||
pagination={false}
|
||||
{...tableProps}
|
||||
// components={components({
|
||||
// data: {
|
||||
// list: data,
|
||||
// },
|
||||
// mutate: (values) => {
|
||||
// onChange({
|
||||
// sort: values.list.map(item => item[rowKey]),
|
||||
// selectedRowKeys: get(value, 'selectedRowKeys')||data.map(item => item[rowKey]),
|
||||
// });
|
||||
// mutate(values.list);
|
||||
// // setDataSource(values.list);
|
||||
// console.log('mutate', values);
|
||||
// },
|
||||
// rowKey,
|
||||
// onMoved: async ({resourceKey, target}) => {
|
||||
// }
|
||||
// })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const DraggableTable = connect({
|
||||
getProps: mapStyledProps,
|
||||
getComponent: mapTextComponent,
|
||||
})(DraggableTableComponent);
|
||||
|
||||
export default DraggableTable;
|
@ -1,163 +0,0 @@
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
forwardRef,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { Button, Drawer } from 'antd';
|
||||
import { Tooltip, Input, Space, Modal } from 'antd';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import {
|
||||
SchemaForm,
|
||||
SchemaMarkupField as Field,
|
||||
createFormActions,
|
||||
FormSpy,
|
||||
LifeCycleTypes,
|
||||
createAsyncFormActions,
|
||||
Submit,
|
||||
Reset,
|
||||
FormButtonGroup,
|
||||
registerFormFields,
|
||||
FormValidator,
|
||||
setValidationLanguage,
|
||||
} from '@formily/antd';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { useRequest } from 'umi';
|
||||
import api from '@/api-client';
|
||||
import { Spin } from '@nocobase/client';
|
||||
import cleanDeep from 'clean-deep';
|
||||
import scopes from '@/components/views/Form/scopes';
|
||||
|
||||
const actions = createFormActions();
|
||||
|
||||
export default forwardRef((props: any, ref) => {
|
||||
console.log(props);
|
||||
const { target, onFinish } = props;
|
||||
const { data: schema = {}, loading } = useRequest(() =>
|
||||
api.resource(target).getView({
|
||||
resourceKey: 'form',
|
||||
}),
|
||||
);
|
||||
const [state, setState] = useState<any>({});
|
||||
const [form, setForm] = useState<any>({});
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [data, setData] = useState({});
|
||||
const [title, setTitle] = useState('创建子字段');
|
||||
const [index, setIndex] = useState();
|
||||
useImperativeHandle(ref, () => ({
|
||||
setVisible,
|
||||
setData,
|
||||
setTitle,
|
||||
setIndex,
|
||||
}));
|
||||
console.log({ onFinish });
|
||||
const { fields = {} } = schema;
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<Drawer
|
||||
{...props}
|
||||
destroyOnClose
|
||||
visible={visible}
|
||||
className={'noco-drawer'}
|
||||
width={'40%'}
|
||||
onClose={() => {
|
||||
if (changed) {
|
||||
Modal.confirm({
|
||||
title: '表单内容发生变化,确定不保存吗?',
|
||||
onOk() {
|
||||
setChanged(false);
|
||||
setVisible(false);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setChanged(false);
|
||||
setVisible(false);
|
||||
}
|
||||
}}
|
||||
title={title}
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'right',
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(false);
|
||||
setChanged(false);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={async () => {
|
||||
await form.submit();
|
||||
// const { values = {} } = await actions.submit();
|
||||
// setVisible(false);
|
||||
// onFinish && onFinish(values, index);
|
||||
}}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<SchemaForm
|
||||
colon={true}
|
||||
layout={'vertical'}
|
||||
initialValues={data}
|
||||
onChange={values => {
|
||||
setChanged(true);
|
||||
}}
|
||||
onSubmit={async values => {
|
||||
setVisible(false);
|
||||
setChanged(false);
|
||||
onFinish && onFinish(values, index);
|
||||
}}
|
||||
// actions={actions}
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: fields,
|
||||
}}
|
||||
expressionScope={scopes}
|
||||
>
|
||||
<FormSpy
|
||||
selector={[
|
||||
LifeCycleTypes.ON_FORM_MOUNT,
|
||||
LifeCycleTypes.ON_FORM_SUBMIT_START,
|
||||
LifeCycleTypes.ON_FORM_SUBMIT_END,
|
||||
]}
|
||||
reducer={(state, action) => {
|
||||
switch (action.type) {
|
||||
case LifeCycleTypes.ON_FORM_SUBMIT_START:
|
||||
return {
|
||||
...state,
|
||||
submitting: true,
|
||||
};
|
||||
case LifeCycleTypes.ON_FORM_SUBMIT_END:
|
||||
return {
|
||||
...state,
|
||||
submitting: false,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ state, form }) => {
|
||||
setState(state);
|
||||
setForm(form);
|
||||
return <div />;
|
||||
}}
|
||||
</FormSpy>
|
||||
</SchemaForm>
|
||||
</Drawer>
|
||||
);
|
||||
});
|
@ -1,60 +1,108 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Layout, Menu, Breadcrumb } from 'antd';
|
||||
import { Link, useLocation } from 'umi';
|
||||
import { Link as UmiLink, useLocation } from 'umi';
|
||||
import Icon from '@/components/icons';
|
||||
import './style.less';
|
||||
|
||||
function pathcamp(path1: string, path2: string) {
|
||||
return true;
|
||||
if (path1 === path2) {
|
||||
return true;
|
||||
}
|
||||
return path1.indexOf(`${path2}/`) === 0;
|
||||
}
|
||||
|
||||
function Link(props: any) {
|
||||
const { to, children } = props;
|
||||
if (/^http/.test(to)) {
|
||||
return (
|
||||
<a target={'_blank'} href={to}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return <UmiLink {...props} to={`/admin/${to}`} />;
|
||||
}
|
||||
|
||||
export default (props: any) => {
|
||||
const { items = [], hideChildren, ...restProps } = props;
|
||||
const location = useLocation();
|
||||
let paths = items.map(item => item.path);
|
||||
const {
|
||||
menuId,
|
||||
currentPageName,
|
||||
items = [],
|
||||
hideChildren,
|
||||
...restProps
|
||||
} = props;
|
||||
if (items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const toPaths = data => {
|
||||
const paths = [];
|
||||
data.forEach(item => {
|
||||
if (item.path && item.path === currentPageName) {
|
||||
paths.push(`${item.name}`);
|
||||
}
|
||||
if (item.paths && item.paths.includes(currentPageName)) {
|
||||
paths.push(`${item.name}`);
|
||||
}
|
||||
paths.push(...toPaths(item.children || []));
|
||||
});
|
||||
return paths;
|
||||
};
|
||||
const keys = toPaths(items);
|
||||
console.log({ menuId, currentPageName, items, keys });
|
||||
const [loading, setLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [menuId]);
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
const renderChildren = items => {
|
||||
return items.map(item => {
|
||||
const { children = [] } = item;
|
||||
// const subItems = children.filter(child => child.showInMenu);
|
||||
if (!hideChildren && children.length) {
|
||||
return (
|
||||
<Menu.SubMenu
|
||||
key={`${item.name}`}
|
||||
icon={item.icon && <Icon type={item.icon} />}
|
||||
title={<>{item.title}</>}
|
||||
>
|
||||
{renderChildren(children)}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Menu.Item
|
||||
icon={item.icon && <Icon type={item.icon} />}
|
||||
key={`${item.name}`}
|
||||
>
|
||||
<Link to={item.path}>{item.title}</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Menu
|
||||
defaultSelectedKeys={paths
|
||||
.filter(path => pathcamp(location.pathname, path))
|
||||
.concat(location.pathname)}
|
||||
defaultOpenKeys={paths
|
||||
.filter(path => pathcamp(location.pathname, path))
|
||||
.concat(location.pathname)}
|
||||
defaultSelectedKeys={keys}
|
||||
defaultOpenKeys={keys}
|
||||
// selectedKeys={keys}
|
||||
// openKeys={keys}
|
||||
onOpenChange={openKeys => {
|
||||
console.log({ openKeys });
|
||||
}}
|
||||
onSelect={info => {
|
||||
console.log({ info });
|
||||
}}
|
||||
onDeselect={info => {
|
||||
console.log({ info });
|
||||
}}
|
||||
{...restProps}
|
||||
>
|
||||
{items.map(item => {
|
||||
if (!item.showInMenu) {
|
||||
return null;
|
||||
}
|
||||
const { children = [] } = item;
|
||||
const subItems = children.filter(child => child.showInMenu);
|
||||
if (!hideChildren && subItems.length > 1) {
|
||||
return (
|
||||
<Menu.SubMenu
|
||||
key={`${item.path}`}
|
||||
icon={<Icon type={item.icon} />}
|
||||
title={<>{item.title}</>}
|
||||
>
|
||||
{subItems.map((child: any) => (
|
||||
<Menu.Item key={child.path}>
|
||||
<Link to={child.path}>{child.title}</Link>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Menu.Item icon={<Icon type={item.icon} />} key={item.path}>
|
||||
<Link to={item.path}>{item.title}</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
{renderChildren(items)}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
@ -1,378 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Space, Button, Popconfirm, Popover } from 'antd';
|
||||
import {
|
||||
FilterOutlined,
|
||||
PlusOutlined,
|
||||
SelectOutlined,
|
||||
DeleteOutlined,
|
||||
PrinterOutlined,
|
||||
ExportOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import Drawer from '@/components/pages/AdminLoader/Drawer';
|
||||
import View from '@/components/pages/AdminLoader/View';
|
||||
import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
import ReactToPrint from 'react-to-print';
|
||||
import api from '@/api-client';
|
||||
|
||||
const ACTIONS = new Map<string, any>();
|
||||
|
||||
export function Create(props) {
|
||||
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
|
||||
const { title, pageTitle, viewName, transform, componentProps = {} } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size={size}
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: pageTitle || title,
|
||||
content: ({ resolve, closeWithConfirm }) => (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
onValueChange={() => {
|
||||
closeWithConfirm && closeWithConfirm(true);
|
||||
}}
|
||||
associatedKey={associatedKey}
|
||||
viewName={viewName}
|
||||
onReset={resolve}
|
||||
onDraft={async item => {
|
||||
const values = transform ? {} : item;
|
||||
for (const [sourceKey, targetKey] of Object.entries<string>(
|
||||
transform || {},
|
||||
)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(values, targetKey, value);
|
||||
}
|
||||
await resolve();
|
||||
console.log('onFinish', values);
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
onFinish={async item => {
|
||||
const values = transform ? {} : item;
|
||||
for (const [sourceKey, targetKey] of Object.entries<string>(
|
||||
transform || {},
|
||||
)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(values, targetKey, value);
|
||||
}
|
||||
await resolve();
|
||||
console.log('onFinish', values);
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}}
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Update(props) {
|
||||
const { onFinish, data, schema = {}, associatedKey, ...restProps } = props;
|
||||
const { title, viewName } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: title,
|
||||
content: ({ resolve, closeWithConfirm }) => (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
associatedKey={associatedKey}
|
||||
data={data}
|
||||
viewName={viewName}
|
||||
onReset={resolve}
|
||||
onDraft={async values => {
|
||||
await resolve();
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
onValueChange={() => {
|
||||
closeWithConfirm && closeWithConfirm(true);
|
||||
}}
|
||||
onFinish={async values => {
|
||||
await resolve();
|
||||
onFinish && (await onFinish(values));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}}
|
||||
icon={<PlusOutlined />}
|
||||
type={'primary'}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Add(props) {
|
||||
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
|
||||
console.log({ associatedKey }, 'add');
|
||||
const { filter, title, viewName, transform, componentProps = {} } = schema;
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
size={size}
|
||||
onClick={() => {
|
||||
Drawer.open({
|
||||
title: title,
|
||||
content: ({ resolve }) => {
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
return (
|
||||
<div>
|
||||
<View
|
||||
{...restProps}
|
||||
defaultFilter={filter}
|
||||
viewName={viewName}
|
||||
associatedKey={associatedKey}
|
||||
onSelected={values => {
|
||||
console.log(values);
|
||||
setSelectedRows(
|
||||
values.map(item => {
|
||||
if (!transform) {
|
||||
return;
|
||||
}
|
||||
const data = {};
|
||||
for (const [sourceKey, targetKey] of Object.entries<
|
||||
string
|
||||
>(transform)) {
|
||||
const value = get({ data: item }, sourceKey);
|
||||
set(data, targetKey, value);
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Drawer.Footer>
|
||||
<Space>
|
||||
<Button onClick={resolve}>取消</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={async () => {
|
||||
console.log({ schema, onFinish });
|
||||
onFinish && (await onFinish(selectedRows));
|
||||
resolve();
|
||||
}}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</Space>
|
||||
</Drawer.Footer>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
}}
|
||||
icon={<SelectOutlined />}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Destroy(props) {
|
||||
const { size, schema = {}, onFinish } = props;
|
||||
const { title, componentProps = {} } = schema;
|
||||
return (
|
||||
<Popconfirm
|
||||
title="确认删除吗?"
|
||||
onConfirm={async e => {
|
||||
onFinish && (await onFinish());
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size={size}
|
||||
danger
|
||||
type={'ghost'}
|
||||
icon={<DeleteOutlined />}
|
||||
{...componentProps}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
|
||||
export function Filter(props) {
|
||||
const { schema = {}, onFinish } = props;
|
||||
const { title, fields = [] } = schema;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [data, setData] = useState({});
|
||||
const [filterCount, setFilterCount] = useState(0);
|
||||
console.log('Filter', { visible, data });
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<div
|
||||
style={{
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
zIndex: 1000,
|
||||
position: 'fixed',
|
||||
background: 'rgba(0, 0, 0, 0)',
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}
|
||||
onClick={() => setVisible(false)}
|
||||
></div>
|
||||
)}
|
||||
<Popover
|
||||
// title="设置筛选"
|
||||
trigger="click"
|
||||
visible={visible}
|
||||
defaultVisible={visible}
|
||||
placement={'bottomLeft'}
|
||||
destroyTooltipOnHide
|
||||
onVisibleChange={visible => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
className={'filters-popover'}
|
||||
style={{}}
|
||||
overlayStyle={{
|
||||
minWidth: 500,
|
||||
}}
|
||||
content={
|
||||
<>
|
||||
<View
|
||||
data={data}
|
||||
onFinish={async values => {
|
||||
if (values) {
|
||||
const items = values.filter.and || values.filter.or;
|
||||
setFilterCount(Array.isArray(items) ? items.length : 0);
|
||||
setData(values);
|
||||
onFinish && (await onFinish(values));
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
schema={{
|
||||
type: 'filterForm',
|
||||
fields: [
|
||||
{
|
||||
dataIndex: ['filter'],
|
||||
name: 'filter',
|
||||
interface: 'json',
|
||||
type: 'json',
|
||||
component: {
|
||||
type: 'filter',
|
||||
'x-component-props': {
|
||||
fields,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button icon={<FilterOutlined />}>
|
||||
{filterCount ? `${filterCount} 个${title}项` : title}
|
||||
</Button>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Print(props) {
|
||||
const { schema = {}, contentRef } = props;
|
||||
const { title, buttonProps = {} } = schema;
|
||||
return (
|
||||
<ReactToPrint
|
||||
trigger={() => (
|
||||
<Button {...buttonProps} icon={<PrinterOutlined />}>
|
||||
{title}
|
||||
</Button>
|
||||
)}
|
||||
content={() => contentRef.current}
|
||||
pageStyle={`
|
||||
@page {
|
||||
margin: 1cm;
|
||||
}
|
||||
table { page-break-inside:auto }
|
||||
tr { page-break-inside:avoid; page-break-after:auto }
|
||||
`}
|
||||
documentTitle={' '}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Export(props) {
|
||||
const { schema = {}, onFinish } = props;
|
||||
const { title, buttonProps = {} } = schema;
|
||||
return (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
icon={<ExportOutlined />}
|
||||
onClick={async () => {
|
||||
onFinish && (await onFinish({ fields: [] }));
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function Actions(props) {
|
||||
const { onTrigger = {}, actions = [], style, ...restProps } = props;
|
||||
return (
|
||||
actions.length > 0 && (
|
||||
<div className={'action-buttons'} style={style}>
|
||||
{actions.map(
|
||||
action =>
|
||||
ACTIONS.has(action.type) && (
|
||||
<div key={action.__index} className={`${action.type}-action-button action-button`}>
|
||||
<Action
|
||||
{...restProps}
|
||||
onFinish={onTrigger[action.type]}
|
||||
schema={action}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Actions;
|
||||
|
||||
export function registerAction(type: string, Action: any) {
|
||||
ACTIONS.set(type, Action);
|
||||
}
|
||||
|
||||
export function getAction(type: string) {
|
||||
return ACTIONS.get(type);
|
||||
}
|
||||
|
||||
export function Action(props) {
|
||||
const { schema = {} } = props;
|
||||
// cnsole.log(schema);
|
||||
const { type } = schema;
|
||||
const Component = getAction(type);
|
||||
return Component && <Component {...props} />;
|
||||
}
|
||||
|
||||
registerAction('add', Add);
|
||||
registerAction('update', Update);
|
||||
registerAction('create', Create);
|
||||
registerAction('destroy', Destroy);
|
||||
registerAction('filter', Filter);
|
||||
registerAction('print', Print);
|
||||
registerAction('export', Export);
|
@ -1,100 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Layout, Breadcrumb, Drawer } from 'antd';
|
||||
import { Link } from 'umi';
|
||||
import './style.less';
|
||||
import Menu from '../menu';
|
||||
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons';
|
||||
import { useLocalStorageState } from 'ahooks';
|
||||
import { useResponsive } from 'ahooks';
|
||||
|
||||
export function SideMenuLayout(props: any) {
|
||||
const { currentPageName, menu = [], menuId } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
// console.log(menu);
|
||||
const [collapsed, setCollapsed] = useLocalStorageState(
|
||||
`nocobase-menu-collapsed-${menuId}`,
|
||||
false,
|
||||
);
|
||||
const responsive = useResponsive();
|
||||
const isMobile = responsive.small && !responsive.middle && !responsive.large;
|
||||
document.body.className = collapsed ? 'collapsed' : '';
|
||||
return (
|
||||
<Layout style={{ height: 'calc(100vh - 48px)' }}>
|
||||
{!isMobile && (
|
||||
<Layout.Sider
|
||||
className={`nb-sider${collapsed ? ' collapsed' : ''}`}
|
||||
theme={'light'}
|
||||
>
|
||||
<Menu
|
||||
menuId={menuId}
|
||||
currentPageName={currentPageName}
|
||||
items={menu}
|
||||
mode={'inline'}
|
||||
/>
|
||||
<div
|
||||
onClick={() => {
|
||||
setCollapsed(!collapsed);
|
||||
setVisible(true);
|
||||
document.body.className = collapsed ? 'collapsed' : '';
|
||||
}}
|
||||
className={'menu-toggle'}
|
||||
>
|
||||
{React.createElement(
|
||||
collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
|
||||
{
|
||||
style: { fontSize: 16 },
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</Layout.Sider>
|
||||
)}
|
||||
<Layout.Content id={'content'}>
|
||||
{props.children}
|
||||
{isMobile && (
|
||||
<Drawer
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setCollapsed(!collapsed);
|
||||
setVisible(false);
|
||||
document.body.className = collapsed ? 'collapsed' : '';
|
||||
}}
|
||||
placement={'left'}
|
||||
closable={false}
|
||||
bodyStyle={{ padding: 0 }}
|
||||
>
|
||||
<Menu
|
||||
onSelect={() => {
|
||||
setCollapsed(false);
|
||||
setVisible(false);
|
||||
document.body.className = collapsed ? 'collapsed' : '';
|
||||
}}
|
||||
currentPageName={currentPageName}
|
||||
menuId={menuId}
|
||||
items={menu}
|
||||
mode={'inline'}
|
||||
/>
|
||||
</Drawer>
|
||||
)}
|
||||
{isMobile && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setCollapsed(!collapsed);
|
||||
setVisible(true);
|
||||
document.body.className = collapsed ? 'collapsed' : '';
|
||||
}}
|
||||
className={'menu-toggle'}
|
||||
>
|
||||
{React.createElement(
|
||||
collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
|
||||
{
|
||||
style: { fontSize: 16 },
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default SideMenuLayout;
|
@ -1,21 +0,0 @@
|
||||
.nb-sider {
|
||||
position: relative;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
|
||||
&.collapsed {
|
||||
margin-left: -200px;
|
||||
}
|
||||
.ant-menu-light {
|
||||
border-right-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
position: fixed;
|
||||
bottom: 12px;
|
||||
left: 12px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Layout, Dropdown, Avatar, Drawer } from 'antd';
|
||||
import './style.less';
|
||||
import { history, Link, request, useModel } from 'umi';
|
||||
import { UserOutlined, CodeOutlined, MenuOutlined } from '@ant-design/icons';
|
||||
import AvatarDropdown from '@/components/pages/AvatarDropdown';
|
||||
import Menu from '../menu';
|
||||
import { ReactComponent as Logo } from './logo-white.svg';
|
||||
import { useResponsive, useLocalStorageState } from 'ahooks';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export function TopMenuLayout(props: any) {
|
||||
const { currentPageName, menu = [] } = props;
|
||||
console.log({ menu });
|
||||
// const [visible, setVisible] = useState(false);
|
||||
const [visible, setVisible] = useLocalStorageState(
|
||||
`nocobase-nav-visible`,
|
||||
false,
|
||||
);
|
||||
const responsive = useResponsive();
|
||||
const isMobile = responsive.small && !responsive.middle && !responsive.large;
|
||||
const {
|
||||
initialState = {},
|
||||
loading,
|
||||
error,
|
||||
refresh,
|
||||
setInitialState,
|
||||
} = useModel('@@initialState');
|
||||
const logoUrl = get(initialState, 'systemSettings.logo.url');
|
||||
console.log({ logoUrl });
|
||||
return (
|
||||
<Layout style={{ height: '100vh' }}>
|
||||
<Layout.Header
|
||||
style={{ height: 48, lineHeight: '48px', padding: 0 }}
|
||||
className="nb-header"
|
||||
>
|
||||
<div className="logo" style={{ width: 200, height: 48, float: 'left' }}>
|
||||
{!logoUrl ? <Logo /> : <img src={logoUrl} />}
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<Menu
|
||||
currentPageName={currentPageName}
|
||||
hideChildren={true}
|
||||
items={menu}
|
||||
className={'noco-top-menu'}
|
||||
style={{ float: 'left' }}
|
||||
theme="dark"
|
||||
mode="horizontal"
|
||||
/>
|
||||
)}
|
||||
{!isMobile && <AvatarDropdown />}
|
||||
{isMobile && (
|
||||
<MenuOutlined
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: '#fff',
|
||||
position: 'absolute',
|
||||
right: 16,
|
||||
top: 16,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isMobile && (
|
||||
<Drawer
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
placement={'right'}
|
||||
closable={false}
|
||||
bodyStyle={{ background: '#001529', padding: 0 }}
|
||||
>
|
||||
<Menu
|
||||
currentPageName={currentPageName}
|
||||
onSelect={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
mode={'inline'}
|
||||
hideChildren={true}
|
||||
items={menu}
|
||||
className={'noco-top-menu'}
|
||||
style={{ float: 'left' }}
|
||||
theme="dark"
|
||||
/>
|
||||
<AvatarDropdown />
|
||||
</Drawer>
|
||||
)}
|
||||
</Layout.Header>
|
||||
<Layout.Content>{props.children}</Layout.Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default TopMenuLayout;
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,37 +0,0 @@
|
||||
.logo {
|
||||
// font-family: 'Michroma', sans-serif;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 18px;
|
||||
letter-spacing: 2px;
|
||||
font-weight: 300;
|
||||
padding: 0 15px;
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
svg {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.noco-top-menu.ant-menu-dark.ant-menu-horizontal > .ant-menu-item:hover,
|
||||
.noco-top-menu.ant-menu.ant-menu-dark .ant-menu-item-selected,
|
||||
.noco-top-menu.ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.ant-layout-content {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ant-layout-header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ant-menu-dark.ant-menu-horizontal {
|
||||
margin-right: 100px;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user