feat: drawer close with confirm

This commit is contained in:
chenos 2021-03-24 10:28:01 +08:00
parent 388dbc5ef4
commit bb5aa17106
7 changed files with 126 additions and 53 deletions

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Space, Button, Popconfirm, Popover } from 'antd';
import { FilterOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import { FilterOutlined, PlusOutlined, SelectOutlined, DeleteOutlined } from '@ant-design/icons';
import Drawer from '@/components/pages/AdminLoader/Drawer';
import View from '@/components/pages/AdminLoader/View';
import get from 'lodash/get';
@ -8,7 +8,7 @@ import set from 'lodash/set';
export function Create(props) {
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
const { title, viewName } = schema;
const { title, viewName, transform } = schema;
return (
<>
<Button
@ -16,19 +16,32 @@ export function Create(props) {
onClick={() => {
Drawer.open({
title: title,
content: ({resolve}) => (
content: ({resolve, closeWithConfirm}) => (
<div>
<View
{...restProps}
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
associatedKey={associatedKey}
viewName={viewName}
onReset={resolve}
onDraft={async (values) => {
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 (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);
@ -54,7 +67,7 @@ export function Update(props) {
onClick={() => {
Drawer.open({
title: title,
content: ({resolve}) => (
content: ({resolve, closeWithConfirm}) => (
<div>
<View
{...restProps}
@ -66,6 +79,9 @@ export function Update(props) {
await resolve();
onFinish && await onFinish(values);
}}
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
onFinish={async (values) => {
await resolve();
onFinish && await onFinish(values);
@ -85,7 +101,7 @@ export function Update(props) {
export function Add(props) {
const { size, onFinish, schema = {}, associatedKey, ...restProps } = props;
console.log({associatedKey}, 'add');
const { filter, title, viewName, transform } = schema;
const { filter, title, viewName, transform, componentProps = {} } = schema;
return (
<>
<Button
@ -132,8 +148,8 @@ export function Add(props) {
},
});
}}
icon={<PlusOutlined />}
type={'primary'}
icon={<SelectOutlined />}
{...componentProps}
>{ title }</Button>
</>
)

View File

@ -3,7 +3,7 @@ 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 { Drawer } from 'antd'
import { Modal, Drawer } from 'antd'
import { DrawerProps } from 'antd/lib/drawer'
import { useContext } from 'react'
import { ConfigProvider } from 'antd'
@ -77,7 +77,7 @@ export function FormDrawer(title: any, content: any): IFormDrawer {
width: '75%',
...props,
onClose: (e: any) => {
props?.onClose?.(e)
props?.onClose?.(e);
formDrawer.close()
},
afterVisibleChange: (visible: boolean) => {
@ -88,25 +88,13 @@ export function FormDrawer(title: any, content: any): IFormDrawer {
env.root = undefined
},
}
const render = (visible = true, resolve?: () => any, reject?: () => any) => {
ReactDOM.render(
<ConfigProvider locale={zhCN}>
<Drawer {...drawer} className={'nb-drawer'} visible={visible}>
{createElement(content, {
resolve,
reject,
})}
</Drawer>
</ConfigProvider>,
env.root
)
}
document.body.appendChild(env.root)
const formDrawer = {
open: (props: any) => {
render(
false,
() => {
formDrawer.closeWithConfirm = false;
formDrawer.close()
},
() => {
@ -117,6 +105,7 @@ export function FormDrawer(title: any, content: any): IFormDrawer {
render(
true,
() => {
formDrawer.closeWithConfirm = false;
formDrawer.close()
},
() => {
@ -127,14 +116,53 @@ export function FormDrawer(title: any, content: any): IFormDrawer {
},
close: () => {
if (!env.root) return
const els = document.querySelectorAll('.env-root-push');
if (els.length) {
const last = els[els.length-1];
last.className = 'env-root';
if (formDrawer.closeWithConfirm) {
Modal.confirm({
title: '表单内容发生变化,确定不保存吗?',
okText: '确定',
cancelText: '取消',
onOk() {
formDrawer.closeWithConfirm = false;
const els = document.querySelectorAll('.env-root-push');
if (els.length) {
const last = els[els.length-1];
last.className = 'env-root';
}
render(false)
},
});
} else {
const els = document.querySelectorAll('.env-root-push');
if (els.length) {
const last = els[els.length-1];
last.className = 'env-root';
}
render(false)
}
render(false)
},
closeWithConfirm: false,
}
const closeWithConfirm = (bool) => {
formDrawer.closeWithConfirm = bool;
}
const render = (visible = true, resolve?: () => any, reject?: () => any) => {
ReactDOM.render(
<ConfigProvider locale={zhCN}>
<Drawer {...drawer} className={'nb-drawer'} visible={visible}>
{createElement(content, {
resolve,
reject,
closeWithConfirm,
})}
</Drawer>
</ConfigProvider>,
env.root
)
}
document.body.appendChild(env.root)
return formDrawer
}

View File

@ -23,7 +23,8 @@ import cloneDeep from 'lodash/cloneDeep'
import { Spin } from '@nocobase/client';
import { markdown } from '@/components/views/Field';
export function fields2properties(fields = []) {
export function fields2properties(fields = [], options: any = {}) {
const { mode } = options;
const properties = {};
fields.forEach(field => {
const data = {
@ -31,6 +32,9 @@ export function fields2properties(fields = []) {
title: field.title,
required: field.required,
};
if (field.createOnly && mode !== 'create') {
set(data, 'x-component-props.disabled', true);
}
const linkages = field.linkages;
delete field.linkages;
set(data, 'x-component-props.schema', cloneDeep(field));
@ -80,13 +84,13 @@ export function fields2properties(fields = []) {
data.description = <div className={'markdown-content'} dangerouslySetInnerHTML={{__html: markdown(field.tooltip)}}></div>;
}
});
console.log({properties});
console.log({properties, options});
return properties;
}
const actions = createFormActions();
export function Form(props: any) {
const { onReset, __parent, noRequest = false, onFinish, onDraft, resolve, data: record = {}, associatedKey, schema = {} } = props;
const { onValueChange, onReset, __parent, noRequest = false, onFinish, onDraft, resolve, data: record = {}, associatedKey, schema = {} } = props;
console.log({ noRequest, record, associatedKey, __parent });
const { statusable, resourceName, rowKey = 'id', fields = [], appends = [], associationField = {} } = schema;
@ -125,11 +129,15 @@ export function Form(props: any) {
// actions={actions}
schema={{
type: 'object',
properties: fields2properties(fields),
properties: fields2properties(fields, { mode: !!Object.keys(data).length ? null : 'create' }),
}}
onReset={async () => {
onReset && await onReset();
}}
onChange={(values) => {
console.log('onValueChange')
onValueChange && onValueChange(values)
}}
onSubmit={async (values) => {
console.log({status});
if (!noRequest) {

View File

@ -5,7 +5,7 @@ import { Spin } from '@nocobase/client';
import { useRequest, useLocation } from 'umi';
import api from '@/api-client';
import { Actions } from '../Actions';
import { Table as AntdTable, Card, Pagination, Button, Tabs, Descriptions, Tooltip } from 'antd';
import { Modal, Table as AntdTable, Card, Pagination, Button, Tabs, Descriptions, Tooltip } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import { components, fields2columns } from '@/components/views/SortableTable';
import ReactDragListView from 'react-drag-listview';
@ -19,7 +19,7 @@ import { Form } from './Form';
import { View } from './';
export function Details(props) {
const { onReset, __parent, noRequest, associatedKey, resourceName, onFinish, onDataChange, data, items = [], resolve } = props;
const { onValueChange, onReset, __parent, noRequest, associatedKey, resourceName, onFinish, onDataChange, data, items = [], resolve } = props;
if (!items || items.length === 0) {
return null;
}
@ -46,6 +46,7 @@ export function Details(props) {
}
return (
<View
onValueChange={onValueChange}
onReset={onReset}
__parent={__parent}
noRequest={noRequest}
@ -127,7 +128,6 @@ export function SubTable(props: any) {
}
const { type } = associationField;
const { data = [], loading, mutate, refresh, run, params } = useRequest((params = {}, ...args) => {
return !associatedKey || type === 'virtual' || type === 'json' ? Promise.resolve({
data: (props.data||[]).map(item => {
@ -231,7 +231,7 @@ export function SubTable(props: any) {
rowKey={rowKey}
dataSource={dataSource}
size={size}
columns={fields2columns(cloneFields)}
columns={fields2columns(cloneFields, { mutate, dataSource, onChange })}
pagination={false}
onChange={(pagination, filters, sorter, extra) => {
@ -253,7 +253,7 @@ export function SubTable(props: any) {
bodyStyle: {
// padding: 0,
},
content: ({resolve}) => (
content: ({resolve, closeWithConfirm}) => (
<div>
<Details
// __parent={__parent}
@ -272,6 +272,9 @@ export function SubTable(props: any) {
onReset={resolve}
onDataChange={() => {
}}
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
noRequest={true}
data={data}

View File

@ -5,7 +5,7 @@ import { Spin } from '@nocobase/client';
import { useRequest, useHistory } from 'umi';
import api from '@/api-client';
import { Actions } from '../Actions';
import { PageHeader, Table as AntdTable, Card, Pagination, Button, Tabs, Descriptions, Tooltip } from 'antd';
import { Modal, PageHeader, Table as AntdTable, Card, Pagination, Button, Tabs, Descriptions, Tooltip } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import { components, fields2columns } from '@/components/views/SortableTable';
import ReactDragListView from 'react-drag-listview';
@ -21,7 +21,7 @@ import pathToRegexp from 'path-to-regexp'
export const icon = <LoadingOutlined style={{ fontSize: 36 }} spin />;
export function Details(props) {
const { __parent, associatedKey, resourceName, onFinish, onReset, onDataChange, data, items = [], resolve } = props;
const { __parent, associatedKey, resourceName, onFinish, onReset, onDataChange, data, items = [], resolve, onValueChange } = props;
if (!items || items.length === 0) {
return null;
}
@ -47,7 +47,10 @@ export function Details(props) {
viewName = `${resourceName}.${view.name}`;
}
return (
<View __parent={__parent} associatedKey={associatedKey} onReset={onReset} onFinish={onFinish} onDataChange={onDataChange} data={data} viewName={viewName}/>
<View
onValueChange={onValueChange}
__parent={__parent}
associatedKey={associatedKey} onReset={onReset} onFinish={onFinish} onDataChange={onDataChange} data={data} viewName={viewName}/>
);
})}
</div>
@ -403,7 +406,7 @@ export function Table(props: any) {
bodyStyle: {
// padding: 0,
},
content: ({resolve}) => (
content: ({resolve, closeWithConfirm}) => (
<div>
<Details
__parent={__parent}
@ -414,6 +417,9 @@ export function Table(props: any) {
resolve();
await reloadMenu();
}}
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
onDraft={async () => {
await refresh();
resolve();

View File

@ -12,6 +12,7 @@ import './style.less';
import { getImageByUrl, testUrl } from '@/components/form.fields';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
import marked from 'marked';
import set from 'lodash/set';
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css';
import api from '@/api-client';
@ -231,22 +232,22 @@ export function SubTableField(props: any) {
}
export function LinkToField(props: any) {
const { data, schema, value } = props;
const { ctx, data, schema, value } = props;
if (!value) {
return null;
}
console.log({props});
// console.log({props});
const values = Array.isArray(value) ? value : [value];
const isArr = Array.isArray(value);
return (
<div className={'link-to-field'}>
{values.map(item => <LinkToFieldLink parent={data} data={item} schema={schema}/>)}
{values.map((item, itemIndex) => <LinkToFieldLink isArr={isArr} itemIndex={itemIndex} ctx={ctx} parent={data} data={item} schema={schema}/>)}
</div>
);
}
export function LinkToFieldLink(props) {
const { parent, schema, schema: { title, labelField, viewName, name, target, collection_name } } = props;
const [visible, setVisible] = useState(false);
const { isArr, itemIndex, ctx, parent, schema, schema: { title, labelField, viewName, name, target, collection_name } } = props;
const [data, setData] = useState(props.data||{});
return (
<span className={'link-to-field-tag'}>
@ -255,14 +256,25 @@ export function LinkToFieldLink(props) {
// setVisible(true);
Drawer.open({
title: data[labelField],
content: ({resolve}) => {
console.log({parent, data, props, schema});
content: ({resolve, closeWithConfirm}) => {
// console.log({ctx, parent, data, props, schema});
const { index, mutate, dataSource, onChange } = ctx;
return (
<div>
<View onFinish={(values) => {
<View
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
noRequest={!!onChange} onFinish={(values) => {
let items = [...dataSource];
const parentData = {...parent};
set(parentData, isArr ? [name, itemIndex] : [name], values);
items[index] = parentData;
setData(values);
mutate(items);
onChange(items);
resolve();
console.log({data, values});
// console.log({values, parentData, data, items});
}} associatedKey={parent.id} data={data} viewName={viewName || `${collection_name}.${name}.descriptions`}/>
</div>
);

View File

@ -66,7 +66,7 @@ export function fields2columns(fields = [], ctx: any = {}) {
if (!field.dataIndex) {
field.dataIndex = field.name.split('.');
}
field.render = (value, record) => field.interface === 'sort' ? <DragHandle/> : <Field data={record} viewType={'table'} schema={field} value={value}/>;
field.render = (value, record, index) => field.interface === 'sort' ? <DragHandle/> : <Field ctx={{...ctx, index}} data={record} viewType={'table'} schema={field} value={value}/>;
field.className = `${field.className||''} noco-field-${field.interface}`;
if (field.editable && field.interface === 'boolean') {
field.title = (