mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
Merge branch 'next' into develop
This commit is contained in:
commit
102f916354
@ -11,10 +11,11 @@ import React, { FC } from 'react';
|
|||||||
import { MainComponent } from './MainComponent';
|
import { MainComponent } from './MainComponent';
|
||||||
|
|
||||||
const Loading: FC = () => <div>Loading...</div>;
|
const Loading: FC = () => <div>Loading...</div>;
|
||||||
const AppError: FC<{ error: Error }> = ({ error }) => {
|
const AppError: FC<{ error: Error & { title?: string } }> = ({ error }) => {
|
||||||
|
const title = error?.title || 'App Error';
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>App Error</div>
|
<div>{title}</div>
|
||||||
{error?.message}
|
{error?.message}
|
||||||
{process.env.__TEST__ && error?.stack}
|
{process.env.__TEST__ && error?.stack}
|
||||||
</div>
|
</div>
|
||||||
|
@ -129,12 +129,12 @@ export const enumType = [
|
|||||||
label: '{{t("is")}}',
|
label: '{{t("is")}}',
|
||||||
value: '$eq',
|
value: '$eq',
|
||||||
selected: true,
|
selected: true,
|
||||||
schema: { 'x-component': 'Select' },
|
schema: { 'x-component': 'Select', 'x-component-props': { mode: null } },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '{{t("is not")}}',
|
label: '{{t("is not")}}',
|
||||||
value: '$ne',
|
value: '$ne',
|
||||||
schema: { 'x-component': 'Select' },
|
schema: { 'x-component': 'Select', 'x-component-props': { mode: null } },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '{{t("is any of")}}',
|
label: '{{t("is any of")}}',
|
||||||
|
@ -96,7 +96,7 @@ export const FilterCollectionFieldInternalField: React.FC = (props: Props) => {
|
|||||||
const originalProps =
|
const originalProps =
|
||||||
compile({ ...(operator?.schema?.['x-component-props'] || {}), ...(uiSchema['x-component-props'] || {}) }) || {};
|
compile({ ...(operator?.schema?.['x-component-props'] || {}), ...(uiSchema['x-component-props'] || {}) }) || {};
|
||||||
|
|
||||||
field.componentProps = merge(originalProps, field.componentProps || {}, dynamicProps || {});
|
field.componentProps = merge(field.componentProps || {}, originalProps, dynamicProps || {});
|
||||||
}, [uiSchemaOrigin]);
|
}, [uiSchemaOrigin]);
|
||||||
|
|
||||||
if (!uiSchemaOrigin) return null;
|
if (!uiSchemaOrigin) return null;
|
||||||
|
@ -74,7 +74,7 @@ const useErrorProps = (app: Application, error: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const AppError: FC<{ error: Error; app: Application }> = observer(
|
const AppError: FC<{ error: Error & { title?: string }; app: Application }> = observer(
|
||||||
({ app, error }) => {
|
({ app, error }) => {
|
||||||
const props = getProps(app);
|
const props = getProps(app);
|
||||||
return (
|
return (
|
||||||
@ -87,7 +87,7 @@ const AppError: FC<{ error: Error; app: Application }> = observer(
|
|||||||
transform: translate(0, -50%);
|
transform: translate(0, -50%);
|
||||||
`}
|
`}
|
||||||
status="error"
|
status="error"
|
||||||
title={app.i18n.t('App error')}
|
title={error?.title || app.i18n.t('App error', { ns: 'client' })}
|
||||||
subTitle={app.i18n.t(error?.message)}
|
subTitle={app.i18n.t(error?.message)}
|
||||||
{...props}
|
{...props}
|
||||||
extra={[
|
extra={[
|
||||||
|
@ -14,6 +14,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
import { useAPIClient, useRequest } from '../../../api-client';
|
import { useAPIClient, useRequest } from '../../../api-client';
|
||||||
import { useCollectionManager } from '../../../data-source/collection';
|
import { useCollectionManager } from '../../../data-source/collection';
|
||||||
import { markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
|
import { markRecordAsNew } from '../../../data-source/collection-record/isNewRecord';
|
||||||
|
import { getDataSourceHeaders } from '../../../data-source/utils';
|
||||||
import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive';
|
import { useKeepAlive } from '../../../route-switch/antd/admin-layout/KeepAlive';
|
||||||
import { useSchemaComponentContext } from '../../hooks';
|
import { useSchemaComponentContext } from '../../hooks';
|
||||||
import { AssociationFieldContext } from './context';
|
import { AssociationFieldContext } from './context';
|
||||||
@ -67,9 +68,11 @@ export const AssociationFieldProvider = observer(
|
|||||||
if (_.isUndefined(ids) || _.isNil(ids) || _.isNaN(ids)) {
|
if (_.isUndefined(ids) || _.isNil(ids) || _.isNaN(ids)) {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.request({
|
return api.request({
|
||||||
resource: collectionField.target,
|
resource: collectionField.target,
|
||||||
action: Array.isArray(ids) ? 'list' : 'get',
|
action: Array.isArray(ids) ? 'list' : 'get',
|
||||||
|
headers: getDataSourceHeaders(cm?.dataSource?.key),
|
||||||
params: {
|
params: {
|
||||||
filter: {
|
filter: {
|
||||||
[targetKey]: ids,
|
[targetKey]: ids,
|
||||||
|
@ -65,7 +65,6 @@ export const DynamicComponent = (props: Props) => {
|
|||||||
...props.style,
|
...props.style,
|
||||||
},
|
},
|
||||||
utc: false,
|
utc: false,
|
||||||
underFilter: true,
|
|
||||||
}),
|
}),
|
||||||
name: 'value',
|
name: 'value',
|
||||||
'x-read-pretty': false,
|
'x-read-pretty': false,
|
||||||
|
@ -92,7 +92,6 @@ export const FormItem: any = withDynamicSchemaProps(
|
|||||||
[formItemLabelCss]: showTitle === false,
|
[formItemLabelCss]: showTitle === false,
|
||||||
});
|
});
|
||||||
}, [showTitle]);
|
}, [showTitle]);
|
||||||
console.log(className);
|
|
||||||
// 联动规则中的“隐藏保留值”的效果
|
// 联动规则中的“隐藏保留值”的效果
|
||||||
if (field.data?.hidden) {
|
if (field.data?.hidden) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -11,22 +11,40 @@ import { LoadingOutlined } from '@ant-design/icons';
|
|||||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||||
import { Input as AntdInput } from 'antd';
|
import { Input as AntdInput } from 'antd';
|
||||||
import { InputProps, TextAreaProps } from 'antd/es/input';
|
import { InputProps, TextAreaProps } from 'antd/es/input';
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { JSONTextAreaProps, Json } from './Json';
|
import { JSONTextAreaProps, Json } from './Json';
|
||||||
import { InputReadPrettyComposed, ReadPretty } from './ReadPretty';
|
import { InputReadPrettyComposed, ReadPretty } from './ReadPretty';
|
||||||
|
|
||||||
export { ReadPretty as InputReadPretty } from './ReadPretty';
|
export { ReadPretty as InputReadPretty } from './ReadPretty';
|
||||||
|
|
||||||
type ComposedInput = React.FC<InputProps> & {
|
type ComposedInput = React.FC<NocoBaseInputProps> & {
|
||||||
ReadPretty: InputReadPrettyComposed['Input'];
|
ReadPretty: InputReadPrettyComposed['Input'];
|
||||||
TextArea: React.FC<TextAreaProps> & { ReadPretty: InputReadPrettyComposed['TextArea'] };
|
TextArea: React.FC<TextAreaProps> & { ReadPretty: InputReadPrettyComposed['TextArea'] };
|
||||||
URL: React.FC<InputProps> & { ReadPretty: InputReadPrettyComposed['URL'] };
|
URL: React.FC<InputProps> & { ReadPretty: InputReadPrettyComposed['URL'] };
|
||||||
JSON: React.FC<JSONTextAreaProps> & { ReadPretty: InputReadPrettyComposed['JSON'] };
|
JSON: React.FC<JSONTextAreaProps> & { ReadPretty: InputReadPrettyComposed['JSON'] };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NocoBaseInputProps = InputProps & {
|
||||||
|
trim?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function InputInner(props: NocoBaseInputProps) {
|
||||||
|
const { onChange, trim, ...others } = props;
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(ev: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (trim) {
|
||||||
|
ev.target.value = ev.target.value.trim();
|
||||||
|
}
|
||||||
|
onChange?.(ev);
|
||||||
|
},
|
||||||
|
[onChange, trim],
|
||||||
|
);
|
||||||
|
return <AntdInput {...others} onChange={handleChange} />;
|
||||||
|
}
|
||||||
|
|
||||||
export const Input: ComposedInput = Object.assign(
|
export const Input: ComposedInput = Object.assign(
|
||||||
connect(
|
connect(
|
||||||
AntdInput,
|
InputInner,
|
||||||
mapProps((props, field) => {
|
mapProps((props, field) => {
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mockApp } from '@nocobase/client/demo-utils';
|
import { mockApp } from '@nocobase/client/demo-utils';
|
||||||
import { SchemaComponent, Plugin, ISchema } from '@nocobase/client';
|
import { SchemaComponent, Plugin, ISchema } from '@nocobase/client';
|
||||||
@ -15,15 +14,25 @@ const schema: ISchema = {
|
|||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
},
|
},
|
||||||
|
trim: {
|
||||||
|
type: 'string',
|
||||||
|
title: `Trim heading and tailing spaces`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
const Demo = () => {
|
const Demo = () => {
|
||||||
return <SchemaComponent schema={schema} />;
|
return <SchemaComponent schema={schema} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DemoPlugin extends Plugin {
|
class DemoPlugin extends Plugin {
|
||||||
async load() {
|
async load() {
|
||||||
this.app.router.add('root', { path: '/', Component: Demo })
|
this.app.router.add('root', { path: '/', Component: Demo });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { useForm } from '@formily/react';
|
import { useForm } from '@formily/react';
|
||||||
import { Space, theme } from 'antd';
|
import { Space, theme } from 'antd';
|
||||||
|
import type { CascaderProps, DefaultOptionType } from 'antd/lib/cascader';
|
||||||
import useInputStyle from 'antd/es/input/style';
|
import useInputStyle from 'antd/es/input/style';
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { renderToString } from 'react-dom/server';
|
import { renderToString } from 'react-dom/server';
|
||||||
@ -110,7 +111,7 @@ function renderHTML(exp: string, keyLabelMap, delimiters: [string, string] = ['{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOptionsValueLabelMap(options: any[], fieldNames = { value: 'value', label: 'label' }) {
|
function createOptionsValueLabelMap(options: any[], fieldNames: CascaderProps['fieldNames'] = defaultFieldNames) {
|
||||||
const map = new Map<string, string[]>();
|
const map = new Map<string, string[]>();
|
||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
map.set(option[fieldNames.value], [option[fieldNames.label]]);
|
map.set(option[fieldNames.value], [option[fieldNames.label]]);
|
||||||
@ -220,10 +221,24 @@ function useVariablesFromValue(value: string, delimiters: [string, string] = ['{
|
|||||||
}, [value, delimitersString]);
|
}, [value, delimitersString]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextArea(props) {
|
export type TextAreaProps = {
|
||||||
|
value?: string;
|
||||||
|
scope?: Partial<DefaultOptionType>[] | (() => Partial<DefaultOptionType>[]);
|
||||||
|
onChange?(value: string): void;
|
||||||
|
disabled?: boolean;
|
||||||
|
changeOnSelect?: CascaderProps['changeOnSelect'];
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
fieldNames?: CascaderProps['fieldNames'];
|
||||||
|
trim?: boolean;
|
||||||
|
delimiters?: [string, string];
|
||||||
|
addonBefore?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TextArea(props: TextAreaProps) {
|
||||||
const { wrapSSR, hashId, componentCls } = useStyles();
|
const { wrapSSR, hashId, componentCls } = useStyles();
|
||||||
const { scope, onChange, changeOnSelect, style, fieldNames, delimiters = ['{{', '}}'], addonBefore } = props;
|
const { scope, changeOnSelect, style, fieldNames, delimiters = ['{{', '}}'], addonBefore, trim = true } = props;
|
||||||
const value = typeof props.value === 'string' ? props.value : props.value == null ? '' : props.value.toString();
|
const value =
|
||||||
|
typeof props.value === 'string' ? props.value : props.value == null ? '' : (props.value as any).toString();
|
||||||
const variables = useVariablesFromValue(value, delimiters);
|
const variables = useVariablesFromValue(value, delimiters);
|
||||||
const inputRef = useRef<HTMLDivElement>(null);
|
const inputRef = useRef<HTMLDivElement>(null);
|
||||||
const [options, setOptions] = useState([]);
|
const [options, setOptions] = useState([]);
|
||||||
@ -241,6 +256,14 @@ export function TextArea(props) {
|
|||||||
const { token } = theme.useToken();
|
const { token } = theme.useToken();
|
||||||
const delimitersString = delimiters.join(' ');
|
const delimitersString = delimiters.join(' ');
|
||||||
|
|
||||||
|
const onChange = useCallback(
|
||||||
|
(target: HTMLDivElement) => {
|
||||||
|
const v = getValue(target, delimiters);
|
||||||
|
props.onChange?.(trim ? v.trim() : v);
|
||||||
|
},
|
||||||
|
[delimitersString, props.onChange, trim],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
preloadOptions(scope, variables)
|
preloadOptions(scope, variables)
|
||||||
.then((preloaded) => {
|
.then((preloaded) => {
|
||||||
@ -324,9 +347,9 @@ export function TextArea(props) {
|
|||||||
|
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
setRange(getCurrentRange(current));
|
setRange(getCurrentRange(current));
|
||||||
onChange(getValue(current, delimiters));
|
onChange(current);
|
||||||
},
|
},
|
||||||
[keyLabelMap, onChange, range, delimitersString],
|
[keyLabelMap, onChange, range],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onInput = useCallback(
|
const onInput = useCallback(
|
||||||
@ -336,9 +359,9 @@ export function TextArea(props) {
|
|||||||
}
|
}
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
setRange(getCurrentRange(currentTarget));
|
setRange(getCurrentRange(currentTarget));
|
||||||
onChange(getValue(currentTarget, delimiters));
|
onChange(currentTarget);
|
||||||
},
|
},
|
||||||
[ime, onChange, delimitersString],
|
[ime, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onBlur = useCallback(function ({ currentTarget }) {
|
const onBlur = useCallback(function ({ currentTarget }) {
|
||||||
@ -360,9 +383,9 @@ export function TextArea(props) {
|
|||||||
setIME(false);
|
setIME(false);
|
||||||
setChanged(true);
|
setChanged(true);
|
||||||
setRange(getCurrentRange(currentTarget));
|
setRange(getCurrentRange(currentTarget));
|
||||||
onChange(getValue(currentTarget, delimiters));
|
onChange(currentTarget);
|
||||||
},
|
},
|
||||||
[onChange, delimitersString],
|
[onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onPaste = useCallback(
|
const onPaste = useCallback(
|
||||||
@ -393,9 +416,9 @@ export function TextArea(props) {
|
|||||||
setChanged(true);
|
setChanged(true);
|
||||||
pasteHTML(ev.currentTarget, sanitizedHTML);
|
pasteHTML(ev.currentTarget, sanitizedHTML);
|
||||||
setRange(getCurrentRange(ev.currentTarget));
|
setRange(getCurrentRange(ev.currentTarget));
|
||||||
onChange(getValue(ev.currentTarget, delimiters));
|
onChange(ev.currentTarget);
|
||||||
},
|
},
|
||||||
[onChange, delimitersString],
|
[onChange],
|
||||||
);
|
);
|
||||||
const disabled = props.disabled || form.disabled;
|
const disabled = props.disabled || form.disabled;
|
||||||
return wrapSSR(
|
return wrapSSR(
|
||||||
|
@ -96,7 +96,6 @@ export const conditionAnalyses = async (
|
|||||||
) => {
|
) => {
|
||||||
const type = Object.keys(ruleGroup)[0] || '$and';
|
const type = Object.keys(ruleGroup)[0] || '$and';
|
||||||
const conditions = ruleGroup[type];
|
const conditions = ruleGroup[type];
|
||||||
|
|
||||||
let results = conditions.map(async (condition) => {
|
let results = conditions.map(async (condition) => {
|
||||||
if ('$and' in condition || '$or' in condition) {
|
if ('$and' in condition || '$or' in condition) {
|
||||||
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic);
|
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic);
|
||||||
@ -147,7 +146,10 @@ export const conditionAnalyses = async (
|
|||||||
if (type === '$and') {
|
if (type === '$and') {
|
||||||
return every(results, (v) => v);
|
return every(results, (v) => v);
|
||||||
} else {
|
} else {
|
||||||
return some(results, (v) => v);
|
if (results.length) {
|
||||||
|
return some(results, (v) => v);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -471,7 +471,6 @@ export const useFilterFormItemInitializerFields = (options?: any) => {
|
|||||||
'x-collection-field': `${name}.${field.name}`,
|
'x-collection-field': `${name}.${field.name}`,
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
utc: false,
|
utc: false,
|
||||||
underFilter: true,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (isAssocField(field)) {
|
if (isAssocField(field)) {
|
||||||
@ -486,7 +485,7 @@ export const useFilterFormItemInitializerFields = (options?: any) => {
|
|||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-use-decorator-props': 'useFormItemProps',
|
'x-use-decorator-props': 'useFormItemProps',
|
||||||
'x-collection-field': `${name}.${field.name}`,
|
'x-collection-field': `${name}.${field.name}`,
|
||||||
'x-component-props': { ...field.uiSchema?.['x-component-props'], utc: false, underFilter: true },
|
'x-component-props': { ...field.uiSchema?.['x-component-props'], utc: false },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const resultItem = {
|
const resultItem = {
|
||||||
@ -581,7 +580,7 @@ const associationFieldToMenu = (
|
|||||||
interface: field.interface,
|
interface: field.interface,
|
||||||
},
|
},
|
||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-component-props': { utc: false, underFilter: true },
|
'x-component-props': { utc: false },
|
||||||
'x-read-pretty': false,
|
'x-read-pretty': false,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-collection-field': `${collectionName}.${schemaName}`,
|
'x-collection-field': `${collectionName}.${schemaName}`,
|
||||||
@ -698,7 +697,7 @@ export const useFilterInheritsFormItemInitializerFields = (options?) => {
|
|||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-collection-field': `${name}.${field.name}`,
|
'x-collection-field': `${name}.${field.name}`,
|
||||||
'x-component-props': { utc: false, underFilter: true },
|
'x-component-props': { utc: false },
|
||||||
'x-read-pretty': field?.uiSchema?.['x-read-pretty'],
|
'x-read-pretty': field?.uiSchema?.['x-read-pretty'],
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const REGEX_OF_VARIABLE = /^\s*\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}\s*$/g;
|
export const REGEX_OF_VARIABLE = /^\s*\{\{\s*([\p{L}0-9_$-.]+?)\s*\}\}\s*$/u;
|
||||||
export const REGEX_OF_VARIABLE_IN_EXPRESSION = /\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}/g;
|
export const REGEX_OF_VARIABLE_IN_EXPRESSION = /\{\{\s*([a-zA-Z0-9_$-.]+?)\s*\}\}/g;
|
||||||
|
|
||||||
export const isVariable = (str: unknown) => {
|
export const isVariable = (str: unknown) => {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, RawAxiosRequestHeaders } from 'axios';
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, RawAxiosRequestHeaders } from 'axios';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
|
|
||||||
export interface ActionParams {
|
export interface ActionParams {
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||||
|
import duration from 'dayjs/plugin/duration';
|
||||||
import IsBetween from 'dayjs/plugin/isBetween';
|
import IsBetween from 'dayjs/plugin/isBetween';
|
||||||
import IsSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
import IsSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
||||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||||
@ -35,5 +36,6 @@ dayjs.extend(weekOfYear);
|
|||||||
dayjs.extend(weekYear);
|
dayjs.extend(weekYear);
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
dayjs.extend(advancedFormat);
|
dayjs.extend(advancedFormat);
|
||||||
|
dayjs.extend(duration);
|
||||||
|
|
||||||
export { dayjs };
|
export { dayjs };
|
||||||
|
@ -26,13 +26,16 @@ export class ErrorHandler {
|
|||||||
message += `: ${err.cause.message}`;
|
message += `: ${err.cause.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorData: { message: string; code: string; title?: string } = {
|
||||||
|
message,
|
||||||
|
code: err.code,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (err?.title) {
|
||||||
|
errorData.title = err.title;
|
||||||
|
}
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
errors: [
|
errors: [errorData],
|
||||||
{
|
|
||||||
message,
|
|
||||||
code: err.code,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,10 +104,6 @@ export const GanttBlockProvider = (props) => {
|
|||||||
const collection = cm.getCollection(props.collection, props.dataSource);
|
const collection = cm.getCollection(props.collection, props.dataSource);
|
||||||
const params = { filter: props.params?.filter, paginate: false, sort: [collection?.primaryKey || 'id'] };
|
const params = { filter: props.params?.filter, paginate: false, sort: [collection?.primaryKey || 'id'] };
|
||||||
|
|
||||||
if (collection?.tree) {
|
|
||||||
params['tree'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div aria-label="block-item-gantt" role="button">
|
<div aria-label="block-item-gantt" role="button">
|
||||||
<TableBlockProvider {...props} params={params}>
|
<TableBlockProvider {...props} params={params}>
|
||||||
|
@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
const MobilePicker = connect(
|
const MobilePicker = connect(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { value, onChange, disabled, options = [], mode } = props;
|
const { value, onChange, disabled, options = [], mode } = props;
|
||||||
|
console.log(props);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [selected, setSelected] = useState(value || []);
|
const [selected, setSelected] = useState(value || []);
|
||||||
@ -42,7 +43,7 @@ const MobilePicker = connect(
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
value={value}
|
||||||
dropdownStyle={{ display: 'none' }}
|
dropdownStyle={{ display: 'none' }}
|
||||||
multiple={mode === 'multiple'}
|
multiple={['multiple', 'tags'].includes(mode)}
|
||||||
onClear={() => {
|
onClear={() => {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
onChange(null);
|
onChange(null);
|
||||||
@ -77,10 +78,10 @@ const MobilePicker = connect(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CheckList
|
<CheckList
|
||||||
multiple={mode === 'multiple'}
|
multiple={['multiple', 'tags'].includes(mode)}
|
||||||
value={Array.isArray(selected) ? selected : [selected] || []}
|
value={Array.isArray(selected) ? selected : [selected] || []}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
if (mode === 'multiple') {
|
if (['multiple', 'tags'].includes(mode)) {
|
||||||
setSelected(val);
|
setSelected(val);
|
||||||
} else {
|
} else {
|
||||||
setSelected(val[0]);
|
setSelected(val[0]);
|
||||||
@ -96,7 +97,7 @@ const MobilePicker = connect(
|
|||||||
))}
|
))}
|
||||||
</CheckList>
|
</CheckList>
|
||||||
</div>
|
</div>
|
||||||
{mode === 'multiple' && (
|
{['multiple', 'tags'].includes(mode) && (
|
||||||
<Button block color="primary" onClick={handleConfirm} style={{ marginTop: '16px' }}>
|
<Button block color="primary" onClick={handleConfirm} style={{ marginTop: '16px' }}>
|
||||||
{t('Confirm')}
|
{t('Confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user