mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-09 15:39:24 +08:00
250 lines
7.7 KiB
TypeScript
250 lines
7.7 KiB
TypeScript
/**
|
|
* This file is part of the NocoBase (R) project.
|
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
* Authors: NocoBase Team.
|
|
*
|
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
*/
|
|
|
|
import { css, cx } from '@emotion/css';
|
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
|
import {
|
|
ActionContextProvider,
|
|
CollectionProvider_deprecated,
|
|
FormBlockContext,
|
|
NocoBaseRecursionField,
|
|
PopupSettingsProvider,
|
|
RecordProvider,
|
|
RefreshComponentProvider,
|
|
TabsContextProvider,
|
|
fetchTemplateData,
|
|
useACLActionParamsContext,
|
|
useAPIClient,
|
|
useActionContext,
|
|
useBlockRequestContext,
|
|
useCollectionManager_deprecated,
|
|
useCollectionParentRecordData,
|
|
useCollection_deprecated,
|
|
useDesignable,
|
|
useFormBlockContext,
|
|
useRecord,
|
|
useCollection,
|
|
useDataSourceHeaders,
|
|
useDataSourceKey,
|
|
Icon,
|
|
} from '@nocobase/client';
|
|
import { App, Button, Tooltip } from 'antd';
|
|
import _ from 'lodash';
|
|
import React, { useMemo, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
export const actionDesignerCss = css`
|
|
position: relative;
|
|
&:hover {
|
|
.general-schema-designer {
|
|
display: block;
|
|
}
|
|
}
|
|
.general-schema-designer {
|
|
position: absolute;
|
|
z-index: 999;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
display: none;
|
|
background: var(--colorBgSettingsHover);
|
|
border: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
pointer-events: none;
|
|
'&.nb-in-template': {
|
|
background: 'var(--colorTemplateBgSettingsHover)';
|
|
}
|
|
,
|
|
> .general-schema-designer-icons {
|
|
position: absolute;
|
|
right: 2px;
|
|
top: 2px;
|
|
line-height: 16px;
|
|
pointer-events: all;
|
|
.ant-space-item {
|
|
background-color: var(--colorSettings);
|
|
color: #fff;
|
|
line-height: 16px;
|
|
width: 16px;
|
|
padding-left: 1px;
|
|
align-self: stretch;
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
export const DuplicateAction = observer(
|
|
(props: any) => {
|
|
const { children, onlyIcon, icon, title } = props;
|
|
const { message } = App.useApp();
|
|
const field = useField();
|
|
const fieldSchema = useFieldSchema();
|
|
const api = useAPIClient();
|
|
const disabled: boolean = field.disabled || props.disabled;
|
|
const { designable } = useDesignable();
|
|
const [visible, setVisible] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const { service, __parent, block, resource } = useBlockRequestContext();
|
|
const { duplicateFields, duplicateMode = 'quickDulicate', duplicateCollection } = fieldSchema['x-component-props'];
|
|
const record = useRecord();
|
|
const parentRecordData: any = useCollectionParentRecordData();
|
|
const collection = useCollection();
|
|
const { id, __collection } = record;
|
|
const ctx = useActionContext();
|
|
const { name } = useCollection_deprecated();
|
|
const { getCollectionFields } = useCollectionManager_deprecated();
|
|
const { t } = useTranslation();
|
|
const collectionFields = getCollectionFields(__collection || name);
|
|
const formctx = useFormBlockContext();
|
|
const aclCtx = useACLActionParamsContext();
|
|
const dataSource = useDataSourceKey();
|
|
const headers = useDataSourceHeaders(dataSource);
|
|
const dataId = Array.isArray(collection.filterTargetKey)
|
|
? Object.assign(
|
|
{},
|
|
...collection.filterTargetKey.map((v) => {
|
|
return { [v]: record[v] };
|
|
}),
|
|
)
|
|
: record[collection.filterTargetKey] || id;
|
|
|
|
const template = {
|
|
key: 'duplicate',
|
|
dataId,
|
|
default: true,
|
|
fields:
|
|
duplicateFields?.filter((v) => {
|
|
return collectionFields.find((k) => v.includes(k.name));
|
|
}) || [],
|
|
collection: __collection || name,
|
|
};
|
|
const isLinkBtn = fieldSchema['x-component'] === 'Action.Link';
|
|
const handelQuickDuplicate = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const data = await fetchTemplateData(api, template, headers);
|
|
await resource['create']({
|
|
values: {
|
|
...data,
|
|
},
|
|
});
|
|
message.success(t('Saved successfully'));
|
|
if (block === 'form') {
|
|
__parent?.service?.refresh?.();
|
|
} else {
|
|
await service?.refresh?.();
|
|
}
|
|
setLoading(false);
|
|
} catch (error) {
|
|
setLoading(false);
|
|
console.error(error); // Handle or log the error appropriately
|
|
}
|
|
};
|
|
const handelDuplicate = () => {
|
|
if (!disabled && !loading && aclCtx) {
|
|
if (duplicateFields?.length > 0) {
|
|
if (duplicateMode === 'quickDulicate') {
|
|
handelQuickDuplicate();
|
|
} else {
|
|
setVisible(true);
|
|
}
|
|
} else {
|
|
message.error(t('Please configure the duplicate fields'));
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cx(actionDesignerCss, {
|
|
[css`
|
|
.general-schema-designer {
|
|
top: -10px;
|
|
bottom: -10px;
|
|
left: -10px;
|
|
right: -10px;
|
|
}
|
|
`]: isLinkBtn,
|
|
})}
|
|
>
|
|
<FormBlockContext.Provider
|
|
value={{
|
|
...formctx,
|
|
duplicateData: {
|
|
display: false,
|
|
enabled: true,
|
|
defaultTemplate: template,
|
|
},
|
|
}}
|
|
>
|
|
<div>
|
|
{isLinkBtn ? (
|
|
<a
|
|
className="nb-action-link"
|
|
role={props.role}
|
|
aria-label={props['aria-label']}
|
|
//@ts-ignore
|
|
disabled={disabled}
|
|
style={{
|
|
opacity: designable && field?.data?.hidden && 0.1,
|
|
cursor: loading ? 'not-allowed' : 'pointer',
|
|
position: 'relative',
|
|
}}
|
|
onClick={handelDuplicate}
|
|
>
|
|
<Tooltip title={title}>
|
|
<span style={{ marginRight: 3 }}>
|
|
{icon && typeof icon === 'string' ? <Icon type={icon} /> : icon}
|
|
</span>
|
|
</Tooltip>
|
|
{onlyIcon ? children[1] : loading ? t('Duplicating') : children || t('Duplicate')}
|
|
</a>
|
|
) : (
|
|
<Button
|
|
role={props.role}
|
|
aria-label={props['aria-label']}
|
|
disabled={disabled}
|
|
style={{
|
|
opacity: designable && field?.data?.hidden && 0.1,
|
|
}}
|
|
{...props}
|
|
onClick={handelDuplicate}
|
|
>
|
|
{loading ? t('Duplicating') : children || t('Duplicate')}
|
|
</Button>
|
|
)}
|
|
{/* Clear the context of Tabs to avoid affecting the Tabs of the upper-level popup */}
|
|
<TabsContextProvider>
|
|
<CollectionProvider_deprecated name={duplicateCollection || name}>
|
|
{/* 这里的 record 就是弹窗中创建表单的 sourceRecord */}
|
|
<RecordProvider record={{ ...parentRecordData, __collection: duplicateCollection || __collection }}>
|
|
<ActionContextProvider value={{ ...ctx, visible, setVisible }}>
|
|
<PopupSettingsProvider enableURL={false}>
|
|
<RefreshComponentProvider refresh={_.noop}>
|
|
<NocoBaseRecursionField schema={fieldSchema} basePath={field.address} onlyRenderProperties />
|
|
</RefreshComponentProvider>
|
|
</PopupSettingsProvider>
|
|
</ActionContextProvider>
|
|
</RecordProvider>
|
|
</CollectionProvider_deprecated>
|
|
</TabsContextProvider>
|
|
</div>
|
|
</FormBlockContext.Provider>
|
|
</div>
|
|
);
|
|
},
|
|
{
|
|
displayName: 'DuplicateAction',
|
|
},
|
|
);
|