mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-08 06:59:26 +08:00
307 lines
8.7 KiB
TypeScript
307 lines
8.7 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import { Field } from '@formily/core';
|
|
import { RecursionField, useField, useFieldSchema } from '@formily/react';
|
|
import { useRequest } from 'ahooks';
|
|
import { Col, Row } from 'antd';
|
|
import template from 'lodash/template';
|
|
import React, { createContext, useContext } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import {
|
|
ACLCollectionProvider,
|
|
TableFieldResource,
|
|
useActionContext,
|
|
useAPIClient,
|
|
useDesignable,
|
|
useRecord,
|
|
WithoutTableFieldResource,
|
|
} from '../';
|
|
import { CollectionProvider, useCollection, useCollectionManager } from '../collection-manager';
|
|
import { useRecordIndex } from '../record-provider';
|
|
import { SharedFilterProvider } from './SharedFilterProvider';
|
|
|
|
export const BlockResourceContext = createContext(null);
|
|
export const BlockAssociationContext = createContext(null);
|
|
export const BlockRequestContext = createContext<any>(null);
|
|
|
|
export const useBlockResource = () => {
|
|
return useContext(BlockResourceContext);
|
|
};
|
|
|
|
interface UseResourceProps {
|
|
resource: any;
|
|
association?: any;
|
|
useSourceId?: any;
|
|
block?: any;
|
|
}
|
|
|
|
const useAssociation = (props) => {
|
|
const { association } = props;
|
|
const { getCollectionField } = useCollectionManager();
|
|
if (typeof association === 'string') {
|
|
return getCollectionField(association);
|
|
} else if (association?.collectionName && association?.name) {
|
|
return getCollectionField(`${association?.collectionName}.${association?.name}`);
|
|
}
|
|
};
|
|
|
|
const useResource = (props: UseResourceProps) => {
|
|
const { block, resource, useSourceId } = props;
|
|
const record = useRecord();
|
|
const api = useAPIClient();
|
|
const association = useAssociation(props);
|
|
const sourceId = useSourceId?.();
|
|
|
|
const field = useField<Field>();
|
|
if (block === 'TableField') {
|
|
const options = {
|
|
field,
|
|
api,
|
|
resource,
|
|
sourceId: sourceId || record[association?.sourceKey || 'id'],
|
|
};
|
|
return new TableFieldResource(options);
|
|
}
|
|
const withoutTableFieldResource = useContext(WithoutTableFieldResource);
|
|
const __parent = useContext(BlockRequestContext);
|
|
if (
|
|
!withoutTableFieldResource &&
|
|
__parent?.block === 'TableField' &&
|
|
__parent?.resource instanceof TableFieldResource
|
|
) {
|
|
return __parent.resource;
|
|
}
|
|
if (!association) {
|
|
return api.resource(resource);
|
|
}
|
|
if (sourceId) {
|
|
return api.resource(resource, sourceId);
|
|
}
|
|
|
|
return api.resource(resource, record[association?.sourceKey || 'id']);
|
|
};
|
|
|
|
const useActionParams = (props) => {
|
|
const { useParams } = props;
|
|
const params = useParams?.() || {};
|
|
return { ...props.params, ...params };
|
|
};
|
|
|
|
export const useResourceAction = (props, opts = {}) => {
|
|
/**
|
|
* fieldName: 来自 TableFieldProvider
|
|
*/
|
|
const { resource, action, fieldName: tableFieldName } = props;
|
|
const { fields } = useCollection();
|
|
const appends = fields?.filter((field) => field.target && field.interface !== 'snapshot').map((field) => field.name);
|
|
const params = useActionParams(props);
|
|
const api = useAPIClient();
|
|
const fieldSchema = useFieldSchema();
|
|
const { snapshot } = useActionContext();
|
|
const record = useRecord();
|
|
|
|
if (!Object.keys(params).includes('appends') && appends?.length) {
|
|
params['appends'] = appends;
|
|
}
|
|
const result = useRequest(
|
|
snapshot
|
|
? async () => ({
|
|
data: record[tableFieldName] ?? [],
|
|
})
|
|
: (opts) => {
|
|
if (!action) {
|
|
return Promise.resolve({});
|
|
}
|
|
const actionParams = { ...opts };
|
|
if (params.appends) {
|
|
actionParams.appends = params.appends;
|
|
}
|
|
return resource[action](actionParams).then((res) => res.data);
|
|
},
|
|
{
|
|
...opts,
|
|
onSuccess(data, params) {
|
|
opts?.['onSuccess']?.(data, params);
|
|
if (fieldSchema['x-uid']) {
|
|
api.services[fieldSchema['x-uid']] = result;
|
|
}
|
|
},
|
|
defaultParams: [params],
|
|
refreshDeps: [JSON.stringify(params.appends)],
|
|
},
|
|
);
|
|
return result;
|
|
};
|
|
|
|
export const MaybeCollectionProvider = (props) => {
|
|
const { collection } = props;
|
|
return collection ? (
|
|
<CollectionProvider collection={collection}>
|
|
<ACLCollectionProvider>{props.children}</ACLCollectionProvider>
|
|
</CollectionProvider>
|
|
) : (
|
|
<>{props.children}</>
|
|
);
|
|
};
|
|
|
|
const BlockRequestProvider = (props) => {
|
|
const field = useField();
|
|
const resource = useBlockResource();
|
|
const service = useResourceAction(
|
|
{ ...props, resource },
|
|
{
|
|
...props.requestOptions,
|
|
},
|
|
);
|
|
const __parent = useContext(BlockRequestContext);
|
|
return (
|
|
<BlockRequestContext.Provider value={{ block: props.block, props, field, service, resource, __parent }}>
|
|
{props.children}
|
|
</BlockRequestContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useBlockRequestContext = () => {
|
|
return useContext(BlockRequestContext);
|
|
};
|
|
|
|
export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
|
|
const fieldSchema = useFieldSchema();
|
|
const { findComponent } = useDesignable();
|
|
const field = useField();
|
|
const Component = findComponent(field.component?.[0]) || React.Fragment;
|
|
const associationFilterSchema = fieldSchema.reduceProperties((buf, s) => {
|
|
if (s['x-component'] === 'AssociationFilter') {
|
|
return s;
|
|
}
|
|
return buf;
|
|
}, null);
|
|
|
|
if (associationFilterSchema) {
|
|
return (
|
|
<Component {...field.componentProps}>
|
|
<Row
|
|
className={css`
|
|
height: 100%;
|
|
`}
|
|
gutter={16}
|
|
wrap={false}
|
|
>
|
|
<Col
|
|
className={css`
|
|
width: 200px;
|
|
flex: 0 0 auto;
|
|
`}
|
|
>
|
|
<RecursionField
|
|
schema={fieldSchema}
|
|
onlyRenderProperties
|
|
filterProperties={(s) => s['x-component'] === 'AssociationFilter'}
|
|
/>
|
|
</Col>
|
|
<Col
|
|
className={css`
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
`}
|
|
>
|
|
<div
|
|
className={css`
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
`}
|
|
>
|
|
<RecursionField
|
|
schema={fieldSchema}
|
|
onlyRenderProperties
|
|
filterProperties={(s) => s['x-component'] !== 'AssociationFilter'}
|
|
/>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</Component>
|
|
);
|
|
}
|
|
return props.children;
|
|
};
|
|
|
|
export const BlockProvider = (props) => {
|
|
const { collection, association } = props;
|
|
const resource = useResource(props);
|
|
return (
|
|
<MaybeCollectionProvider collection={collection}>
|
|
<BlockAssociationContext.Provider value={association}>
|
|
<BlockResourceContext.Provider value={resource}>
|
|
<BlockRequestProvider {...props}>
|
|
<SharedFilterProvider {...props} />
|
|
</BlockRequestProvider>
|
|
</BlockResourceContext.Provider>
|
|
</BlockAssociationContext.Provider>
|
|
</MaybeCollectionProvider>
|
|
);
|
|
};
|
|
|
|
export const useBlockAssociationContext = () => {
|
|
return useContext(BlockAssociationContext);
|
|
};
|
|
|
|
export const useFilterByTk = () => {
|
|
const { resource, __parent } = useContext(BlockRequestContext);
|
|
const recordIndex = useRecordIndex();
|
|
const record = useRecord();
|
|
const collection = useCollection();
|
|
const { getCollectionField } = useCollectionManager();
|
|
const assoc = useContext(BlockAssociationContext);
|
|
const withoutTableFieldResource = useContext(WithoutTableFieldResource);
|
|
if (!withoutTableFieldResource) {
|
|
if (resource instanceof TableFieldResource || __parent?.block === 'TableField') {
|
|
return recordIndex;
|
|
}
|
|
}
|
|
|
|
if (assoc) {
|
|
const association = getCollectionField(assoc);
|
|
return record?.[association.targetKey || 'id'];
|
|
}
|
|
return record?.[collection.filterTargetKey || 'id'];
|
|
};
|
|
|
|
export const useSourceIdFromRecord = () => {
|
|
const record = useRecord();
|
|
const { getCollectionField } = useCollectionManager();
|
|
const assoc = useContext(BlockAssociationContext);
|
|
if (assoc) {
|
|
const association = getCollectionField(assoc);
|
|
return record?.[association.sourceKey || 'id'];
|
|
}
|
|
};
|
|
|
|
export const useSourceIdFromParentRecord = () => {
|
|
const record = useRecord();
|
|
const { getCollectionField } = useCollectionManager();
|
|
const assoc = useContext(BlockAssociationContext);
|
|
if (assoc) {
|
|
const association = getCollectionField(assoc);
|
|
return record?.__parent?.[association.sourceKey || 'id'];
|
|
}
|
|
};
|
|
|
|
export const useParamsFromRecord = () => {
|
|
const filterByTk = useFilterByTk();
|
|
return {
|
|
filterByTk: filterByTk,
|
|
};
|
|
};
|
|
|
|
export const RecordLink = (props) => {
|
|
const field = useField();
|
|
const record = useRecord();
|
|
const { title, to, ...others } = props;
|
|
const compiled = template(to || '');
|
|
return (
|
|
<Link {...others} to={compiled({ record: record || {} })}>
|
|
{field.title}
|
|
</Link>
|
|
);
|
|
};
|