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';
|
import { request } from 'umi';
|
||||||
|
|
||||||
interface ActionParams {
|
interface ActionParams {
|
||||||
@ -24,6 +22,7 @@ interface Resource {
|
|||||||
[name: string]: (params?: ActionParams) => Promise<any>;
|
[name: string]: (params?: ActionParams) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 待改进,提供一个封装度更完整的 SDK,request 可以自由替换
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
resource(name: string): Resource {
|
resource(name: string): Resource {
|
||||||
const proxy: any = new Proxy(
|
const proxy: any = new Proxy(
|
||||||
|
@ -32,6 +32,7 @@ export const request: RequestConfig = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO:目前这块是写在代码里的,待改进
|
||||||
const pathnames = ['/login', '/register', '/lostpassword', '/resetpassword'];
|
const pathnames = ['/login', '/register', '/lostpassword', '/resetpassword'];
|
||||||
|
|
||||||
export async function getInitialState() {
|
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 React, { useState } from 'react';
|
||||||
import Create from './Create';
|
import { Space, Button, Popconfirm, Popover } from 'antd';
|
||||||
import Update from './Update';
|
import {
|
||||||
import Destroy from './Destroy';
|
FilterOutlined,
|
||||||
import Filter from './Filter';
|
PlusOutlined,
|
||||||
// import { Space } from 'antd';
|
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';
|
import './style.less';
|
||||||
|
|
||||||
const ACTIONS = new Map<string, any>();
|
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) {
|
export function registerAction(type: string, Action: any) {
|
||||||
ACTIONS.set(type, Action);
|
ACTIONS.set(type, Action);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAction('update', Update);
|
|
||||||
registerAction('create', Create);
|
|
||||||
registerAction('destroy', Destroy);
|
|
||||||
registerAction('filter', Filter);
|
|
||||||
|
|
||||||
export function getAction(type: string) {
|
export function getAction(type: string) {
|
||||||
return ACTIONS.get(type);
|
return ACTIONS.get(type);
|
||||||
}
|
}
|
||||||
@ -29,25 +370,10 @@ export function Action(props) {
|
|||||||
return Component && <Component {...props} />;
|
return Component && <Component {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Actions(props) {
|
registerAction('add', Add);
|
||||||
const { onTrigger = {}, style, schema, actions = [], ...restProps } = props;
|
registerAction('update', Update);
|
||||||
console.log(onTrigger);
|
registerAction('create', Create);
|
||||||
return (
|
registerAction('destroy', Destroy);
|
||||||
actions.length > 0 && (
|
registerAction('filter', Filter);
|
||||||
<div className={'action-buttons'} style={style}>
|
registerAction('print', Print);
|
||||||
{actions.map(action => (
|
registerAction('export', Export);
|
||||||
<div className={`${action.name}-action-button action-button`}>
|
|
||||||
<Action
|
|
||||||
{...restProps}
|
|
||||||
view={schema}
|
|
||||||
schema={action}
|
|
||||||
onTrigger={onTrigger[action.name]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Actions;
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
|
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import ReactDOM, { createPortal } from 'react-dom';
|
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 { isNum, isStr, isBool, isFn } from '@formily/shared';
|
||||||
import { Modal, Drawer } from 'antd';
|
import { Modal, Drawer } from 'antd';
|
||||||
import { DrawerProps } from 'antd/lib/drawer';
|
import { DrawerProps } from 'antd/lib/drawer';
|
@ -8,9 +8,8 @@ import {
|
|||||||
isStr,
|
isStr,
|
||||||
isArr,
|
isArr,
|
||||||
} from '../shared';
|
} from '../shared';
|
||||||
import ViewFactory from '@/components/views';
|
import Drawer from '@/components/drawer';
|
||||||
import Drawer from '@/components/pages/AdminLoader/Drawer';
|
import View from '@/components/views';
|
||||||
import View from '@/components/pages/AdminLoader/View';
|
|
||||||
|
|
||||||
function transform({ value, multiple, labelField, valueField = 'id' }) {
|
function transform({ value, multiple, labelField, valueField = 'id' }) {
|
||||||
let selectedKeys = [];
|
let selectedKeys = [];
|
@ -21,7 +21,6 @@ import { Cascader } from './cascader';
|
|||||||
import { Icon } from './icons';
|
import { Icon } from './icons';
|
||||||
import { ColorSelect } from './color-select';
|
import { ColorSelect } from './color-select';
|
||||||
import { Permissions } from './permissions';
|
import { Permissions } from './permissions';
|
||||||
import { DraggableTable } from './draggable-table';
|
|
||||||
import { Values } from './values';
|
import { Values } from './values';
|
||||||
import { Automations } from './automations';
|
import { Automations } from './automations';
|
||||||
import { Wysiwyg } from './wysiwyg';
|
import { Wysiwyg } from './wysiwyg';
|
||||||
@ -61,7 +60,6 @@ export const setup = () => {
|
|||||||
drawerSelect: DrawerSelect,
|
drawerSelect: DrawerSelect,
|
||||||
colorSelect: ColorSelect,
|
colorSelect: ColorSelect,
|
||||||
subTable: SubTable,
|
subTable: SubTable,
|
||||||
draggableTable: DraggableTable,
|
|
||||||
values: Values,
|
values: Values,
|
||||||
wysiwyg: Wysiwyg,
|
wysiwyg: Wysiwyg,
|
||||||
markdown: Markdown,
|
markdown: Markdown,
|
@ -11,7 +11,7 @@ import {
|
|||||||
} from '../shared';
|
} from '../shared';
|
||||||
import { useRequest } from 'umi';
|
import { useRequest } from 'umi';
|
||||||
import api from '@/api-client';
|
import api from '@/api-client';
|
||||||
import { Spin } from '@nocobase/client';
|
import { Spin } from '@/components/spin';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
|
||||||
function RemoteSelectComponent(props) {
|
function RemoteSelectComponent(props) {
|
@ -1,15 +1,5 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import View from '@/components/views';
|
||||||
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';
|
|
||||||
|
|
||||||
export interface SimpleTableProps {
|
export interface SimpleTableProps {
|
||||||
schema?: any;
|
schema?: any;
|
@ -9,7 +9,6 @@ import {
|
|||||||
isStr,
|
isStr,
|
||||||
isArr,
|
isArr,
|
||||||
} from '../shared';
|
} from '../shared';
|
||||||
import ViewFactory from '@/components/views';
|
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
|
|
||||||
export const SubTable = connect({
|
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 { Layout, Menu, Breadcrumb } from 'antd';
|
||||||
import { Link, useLocation } from 'umi';
|
import { Link as UmiLink, useLocation } from 'umi';
|
||||||
import Icon from '@/components/icons';
|
import Icon from '@/components/icons';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
function pathcamp(path1: string, path2: string) {
|
function pathcamp(path1: string, path2: string) {
|
||||||
|
return true;
|
||||||
if (path1 === path2) {
|
if (path1 === path2) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return path1.indexOf(`${path2}/`) === 0;
|
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) => {
|
export default (props: any) => {
|
||||||
const { items = [], hideChildren, ...restProps } = props;
|
const {
|
||||||
const location = useLocation();
|
menuId,
|
||||||
let paths = items.map(item => item.path);
|
currentPageName,
|
||||||
|
items = [],
|
||||||
|
hideChildren,
|
||||||
|
...restProps
|
||||||
|
} = props;
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
const toPaths = data => {
|
||||||
<Menu
|
const paths = [];
|
||||||
defaultSelectedKeys={paths
|
data.forEach(item => {
|
||||||
.filter(path => pathcamp(location.pathname, path))
|
if (item.path && item.path === currentPageName) {
|
||||||
.concat(location.pathname)}
|
paths.push(`${item.name}`);
|
||||||
defaultOpenKeys={paths
|
}
|
||||||
.filter(path => pathcamp(location.pathname, path))
|
if (item.paths && item.paths.includes(currentPageName)) {
|
||||||
.concat(location.pathname)}
|
paths.push(`${item.name}`);
|
||||||
{...restProps}
|
}
|
||||||
>
|
paths.push(...toPaths(item.children || []));
|
||||||
{items.map(item => {
|
});
|
||||||
if (!item.showInMenu) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
const renderChildren = items => {
|
||||||
|
return items.map(item => {
|
||||||
const { children = [] } = item;
|
const { children = [] } = item;
|
||||||
const subItems = children.filter(child => child.showInMenu);
|
// const subItems = children.filter(child => child.showInMenu);
|
||||||
if (!hideChildren && subItems.length > 1) {
|
if (!hideChildren && children.length) {
|
||||||
return (
|
return (
|
||||||
<Menu.SubMenu
|
<Menu.SubMenu
|
||||||
key={`${item.path}`}
|
key={`${item.name}`}
|
||||||
icon={<Icon type={item.icon} />}
|
icon={item.icon && <Icon type={item.icon} />}
|
||||||
title={<>{item.title}</>}
|
title={<>{item.title}</>}
|
||||||
>
|
>
|
||||||
{subItems.map((child: any) => (
|
{renderChildren(children)}
|
||||||
<Menu.Item key={child.path}>
|
|
||||||
<Link to={child.path}>{child.title}</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Menu.Item icon={<Icon type={item.icon} />} key={item.path}>
|
<Menu.Item
|
||||||
|
icon={item.icon && <Icon type={item.icon} />}
|
||||||
|
key={`${item.name}`}
|
||||||
|
>
|
||||||
<Link to={item.path}>{item.title}</Link>
|
<Link to={item.path}>{item.title}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
);
|
);
|
||||||
})}
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
defaultSelectedKeys={keys}
|
||||||
|
defaultOpenKeys={keys}
|
||||||
|
// selectedKeys={keys}
|
||||||
|
// openKeys={keys}
|
||||||
|
onOpenChange={openKeys => {
|
||||||
|
console.log({ openKeys });
|
||||||
|
}}
|
||||||
|
onSelect={info => {
|
||||||
|
console.log({ info });
|
||||||
|
}}
|
||||||
|
onDeselect={info => {
|
||||||
|
console.log({ info });
|
||||||
|
}}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{renderChildren(items)}
|
||||||
</Menu>
|
</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;
|
|
||||||
}
|
|
@ -1,272 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import { Table as AntdTable, Card, Pagination } from 'antd';
|
|
||||||
import { useRequest, useHistory } from 'umi';
|
|
||||||
import api from '@/api-client';
|
|
||||||
import {
|
|
||||||
LoadingOutlined,
|
|
||||||
LeftOutlined,
|
|
||||||
RightOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import './style.less';
|
|
||||||
import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar';
|
|
||||||
import * as dates from 'react-big-calendar/lib/utils/dates';
|
|
||||||
import moment from 'moment';
|
|
||||||
const localizer = momentLocalizer(moment); // or globalizeLocalizer
|
|
||||||
import Drawer from '@/components/pages/AdminLoader/Drawer';
|
|
||||||
import { Details, DetailsPage } from '../Table';
|
|
||||||
import { Actions } from '../../Actions';
|
|
||||||
|
|
||||||
export const icon = <LoadingOutlined style={{ fontSize: 36 }} spin />;
|
|
||||||
|
|
||||||
export interface CalendarProps {
|
|
||||||
schema?: any;
|
|
||||||
activeTab?: any;
|
|
||||||
resourceName: string;
|
|
||||||
associatedName?: string;
|
|
||||||
associatedKey?: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let weekRangeFormat = ({ start, end }, culture, local) =>
|
|
||||||
// local.format(start, 'MMMM DD', culture) +
|
|
||||||
// ' – ' +
|
|
||||||
// local.format(end, dates.eq(start, end, 'month') ? 'DD' : 'MMMM DD', culture)
|
|
||||||
|
|
||||||
function toEvents(data, options: any = {}) {
|
|
||||||
const { startDateField, endDateField, labelField, idField = 'id' } = options;
|
|
||||||
if (!Array.isArray(data)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return data.map(item => ({
|
|
||||||
id: item[idField],
|
|
||||||
title: item[labelField],
|
|
||||||
allDay: true,
|
|
||||||
start: moment(
|
|
||||||
item[startDateField || 'createdAt'] || item.created_at,
|
|
||||||
).toDate(),
|
|
||||||
end: moment(
|
|
||||||
item[endDateField || startDateField || 'createdAt'] || item.created_at,
|
|
||||||
).toDate(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Calendar(props: CalendarProps) {
|
|
||||||
const { schema = {}, associatedKey, defaultFilter } = props;
|
|
||||||
|
|
||||||
const paginated = false;
|
|
||||||
|
|
||||||
const {
|
|
||||||
rowKey = 'id',
|
|
||||||
filter: schemaFilter,
|
|
||||||
sort,
|
|
||||||
appends,
|
|
||||||
resourceName,
|
|
||||||
labelField,
|
|
||||||
startDateField,
|
|
||||||
endDateField,
|
|
||||||
detailsOpenMode,
|
|
||||||
details = [],
|
|
||||||
actions = [],
|
|
||||||
} = schema;
|
|
||||||
|
|
||||||
console.log({ schema });
|
|
||||||
|
|
||||||
const [calendarView, setCalendarView] = useState('month');
|
|
||||||
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const { data, loading, mutate, refresh, run, params } = useRequest(
|
|
||||||
(params = {}, ...args) => {
|
|
||||||
const { current, pageSize, sorter, filter, ...restParams } = params as any;
|
|
||||||
console.log('paramsparamsparamsparamsparams', params, args);
|
|
||||||
return api
|
|
||||||
.resource(resourceName)
|
|
||||||
.list({
|
|
||||||
associatedKey,
|
|
||||||
page: paginated ? current : 1,
|
|
||||||
perPage: paginated ? pageSize : -1,
|
|
||||||
sorter,
|
|
||||||
sort,
|
|
||||||
'fields[appends]': appends,
|
|
||||||
// filter,
|
|
||||||
// ...actionDefaultParams,
|
|
||||||
filter: {
|
|
||||||
and: [
|
|
||||||
defaultFilter,
|
|
||||||
schemaFilter,
|
|
||||||
filter,
|
|
||||||
// __parent ? {
|
|
||||||
// collection_name: __parent,
|
|
||||||
// } : null,
|
|
||||||
].filter(obj => obj && Object.keys(obj).length),
|
|
||||||
},
|
|
||||||
// ...args2,
|
|
||||||
})
|
|
||||||
.then(({ data = [], meta = {} }) => {
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
list: data,
|
|
||||||
total: meta.count || data.length,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
paginated,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const events = toEvents(data?.list, {
|
|
||||||
idField: rowKey,
|
|
||||||
labelField,
|
|
||||||
startDateField,
|
|
||||||
endDateField,
|
|
||||||
});
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
allDay: '',
|
|
||||||
previous: (
|
|
||||||
<div>
|
|
||||||
<LeftOutlined />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
next: (
|
|
||||||
<div>
|
|
||||||
<RightOutlined />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
today: '今天',
|
|
||||||
month: '月',
|
|
||||||
week: '周',
|
|
||||||
day: '天',
|
|
||||||
agenda: '列表',
|
|
||||||
date: '日期',
|
|
||||||
time: '时间',
|
|
||||||
event: '事件',
|
|
||||||
noEventsInRange: '无',
|
|
||||||
showMore: count => `还有 ${count} 项`,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('events', data);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Actions
|
|
||||||
associatedKey={associatedKey}
|
|
||||||
onTrigger={{
|
|
||||||
async create(values) {
|
|
||||||
await refresh();
|
|
||||||
},
|
|
||||||
async filter(values) {
|
|
||||||
const items = values.filter.and || values.filter.or;
|
|
||||||
// @ts-ignore
|
|
||||||
run({ ...params[0], filter: values.filter });
|
|
||||||
// refresh();
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
actions={actions}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: '157px',
|
|
||||||
right: '168px',
|
|
||||||
marginBottom: 14,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<BigCalendar
|
|
||||||
popup
|
|
||||||
selectable
|
|
||||||
className={`view-${calendarView}`}
|
|
||||||
messages={messages as any}
|
|
||||||
formats={{
|
|
||||||
monthHeaderFormat: 'Y年M月',
|
|
||||||
agendaDateFormat: 'M月DD日',
|
|
||||||
dayHeaderFormat: 'Y年M月DD日',
|
|
||||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
|
||||||
if (dates.eq(start, end, 'month')) {
|
|
||||||
return local.format(start, 'Y年M月', culture);
|
|
||||||
}
|
|
||||||
return `${local.format(start, 'Y年M月', culture)} - ${local.format(
|
|
||||||
end,
|
|
||||||
'Y年M月',
|
|
||||||
culture,
|
|
||||||
)}`;
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
events={events}
|
|
||||||
onSelectSlot={slotInfo => {
|
|
||||||
// setFormMode('create');
|
|
||||||
// drawerRef.current.setVisible(true);
|
|
||||||
// console.log('onSelectSlot', slotInfo)
|
|
||||||
}}
|
|
||||||
onView={view => {
|
|
||||||
setCalendarView(view);
|
|
||||||
console.log(view);
|
|
||||||
}}
|
|
||||||
onSelectEvent={data => {
|
|
||||||
console.log({ data });
|
|
||||||
if (!detailsOpenMode || !details.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (detailsOpenMode === 'window') {
|
|
||||||
const paths = history.location.pathname.split('/');
|
|
||||||
history.push(`/admin/${paths[2]}/${data[rowKey]}/0`);
|
|
||||||
} else {
|
|
||||||
Drawer.open({
|
|
||||||
headerStyle:
|
|
||||||
details.length > 1
|
|
||||||
? {
|
|
||||||
paddingBottom: 0,
|
|
||||||
borderBottom: 0,
|
|
||||||
// paddingTop: 16,
|
|
||||||
// marginBottom: -4,
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
// title: details.length > 1 ? undefined : data[labelField],
|
|
||||||
title: data.title,
|
|
||||||
bodyStyle: {
|
|
||||||
// padding: 0,
|
|
||||||
},
|
|
||||||
content: ({ resolve, closeWithConfirm }) => (
|
|
||||||
<div>
|
|
||||||
<Details
|
|
||||||
associatedKey={associatedKey}
|
|
||||||
resourceName={resourceName}
|
|
||||||
onFinish={async () => {
|
|
||||||
await refresh();
|
|
||||||
resolve();
|
|
||||||
}}
|
|
||||||
onValueChange={() => {
|
|
||||||
closeWithConfirm && closeWithConfirm(true);
|
|
||||||
}}
|
|
||||||
onDraft={async () => {
|
|
||||||
await refresh();
|
|
||||||
resolve();
|
|
||||||
}}
|
|
||||||
onReset={resolve}
|
|
||||||
onDataChange={async () => {
|
|
||||||
await refresh();
|
|
||||||
}}
|
|
||||||
data={data}
|
|
||||||
resolve={resolve}
|
|
||||||
items={details}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onRangeChange={range => {
|
|
||||||
console.log({ range });
|
|
||||||
}}
|
|
||||||
views={['month', 'week', 'day']}
|
|
||||||
// step={120}
|
|
||||||
// showMultiDayTimes
|
|
||||||
// max={dates.add(dates.endOf(new Date(2015, 17, 1), 'day'), -1, 'hours')}
|
|
||||||
// defaultDate={new Date(2015, 3, 1)}
|
|
||||||
components={{
|
|
||||||
timeGutterHeader: () => <div>全天</div>,
|
|
||||||
}}
|
|
||||||
localizer={localizer}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user