Merge branch 'next' into develop

This commit is contained in:
nocobase[bot] 2024-12-23 01:40:46 +00:00
commit 9f6688b436
6 changed files with 1014 additions and 1711 deletions

View File

@ -2,9 +2,7 @@
"version": "1.6.0-alpha.6",
"npmClient": "yarn",
"useWorkspaces": true,
"npmClientArgs": [
"--ignore-engines"
],
"npmClientArgs": ["--ignore-engines"],
"command": {
"version": {
"forcePublish": true,

View File

@ -854,6 +854,10 @@
"Failed to load plugin": "插件加载失败",
"Allow add new, update and delete actions": "允许增删改操作",
"Date display format": "日期显示格式",
"Date range limit": "日期限定范围",
"MinDate": "最小日期",
"MaxDate": "最大日期",
"Please select time or variable": "请选择时间或变量",
"Filter out a single piece or a group of records as a template": "筛选出一条或一组数据,作为模板",
"The title field is used to identify the template record": "用于识别模板数据",
"Template fields": "模板字段",

View File

@ -9,8 +9,9 @@
import { useFieldSchema } from '@formily/react';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { SchemaSettingsDateFormat } from '../../../../schema-settings/SchemaSettingsDateFormat';
import { SchemaSettingsDateRange } from '../../../../schema-settings/SchemaSettingsDateRange';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { ellipsisSettingsItem, enableLinkSettingsItem, openModeSettingsItem } from '../Input/inputComponentSettings';
export const datePickerComponentFieldSettings = new SchemaSettings({
@ -28,6 +29,24 @@ export const datePickerComponentFieldSettings = new SchemaSettings({
};
},
},
{
name: 'dateScopeSelect',
Component: SchemaSettingsDateRange as any,
useComponentProps() {
const schema = useFieldSchema();
const { fieldSchema: tableColumnSchema } = useColumnSchema();
const fieldSchema = tableColumnSchema || schema;
return {
fieldSchema,
};
},
useVisible() {
const { fieldSchema: columnSchema } = useColumnSchema();
const schema = useFieldSchema();
const fieldSchema = columnSchema || schema;
return !fieldSchema?.['x-read-pretty'];
},
},
ellipsisSettingsItem,
enableLinkSettingsItem,
openModeSettingsItem,

View File

@ -7,17 +7,19 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { connect, mapProps, mapReadPretty, useField, useFieldSchema } from '@formily/react';
import { connect, mapProps, mapReadPretty, useField, useFieldSchema, observer } from '@formily/react';
import { DatePicker as AntdDatePicker, DatePickerProps as AntdDatePickerProps, Space, Select } from 'antd';
import { RangePickerProps } from 'antd/es/date-picker';
import dayjs from 'dayjs';
import React, { useState } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { getPickerFormat } from '@nocobase/utils/client';
import { useTranslation } from 'react-i18next';
import { ReadPretty, ReadPrettyComposed } from './ReadPretty';
import { getDateRanges, mapDatePicker, mapRangePicker, inferPickerType } from './util';
import { useCompile } from '../../';
import { useVariables, useLocalVariables, isVariable } from '../../../variables';
import { autorun } from '@formily/reactive';
import { log10 } from 'mathjs';
interface IDatePickerProps {
utc?: boolean;
}
@ -52,9 +54,111 @@ const InternalRangePicker = connect(
export const DatePicker: ComposedDatePicker = (props: any) => {
const { utc = true } = useDatePickerContext();
const value = Array.isArray(props.value) ? props.value[0] : props.value;
const { parseVariable } = useVariables() || {};
const localVariables = useLocalVariables();
const [disabledDate, setDisabledDate] = useState(null);
const [disabledTime, setDisabledTime] = useState(null);
const disposeRef = useRef(null);
useEffect(() => {
if (disposeRef.current) {
disposeRef.current();
}
disposeRef.current = autorun(() => {
limitDate();
});
return () => {
disposeRef.current();
};
}, [props._maxDate, props._minDate, localVariables, parseVariable]);
const limitDate = async () => {
//dayjs()如果传入undefined可能会被解析成当前时间
let minDateTimePromise = props._minDate ? Promise.resolve(dayjs(props._minDate)) : Promise.resolve(null);
let maxDateTimePromise = props._maxDate ? Promise.resolve(dayjs(props._maxDate)) : Promise.resolve(null);
if (isVariable(props._maxDate)) {
maxDateTimePromise = parseVariable(props._maxDate, localVariables).then((result) => dayjs(result.value));
}
if (isVariable(props._minDate)) {
minDateTimePromise = parseVariable(props._minDate, localVariables).then((result) => dayjs(result.value));
}
const [minDateTime, maxDateTime] = await Promise.all([minDateTimePromise, maxDateTimePromise]);
const fullTimeArr = Array.from({ length: 60 }, (_, i) => i);
// 根据最小日期和最大日期限定日期时间
const disabledDate = (current) => {
if (!current || (!minDateTime && !maxDateTime)) {
return false;
}
// 确保 current 是一个 dayjs 对象
const currentDate = dayjs(current);
//在minDateTime或maxDateTime为null时 dayjs的比较函数会默认返回false 所以不做特殊判断
return currentDate.isBefore(minDateTime, 'minute') || currentDate.isAfter(maxDateTime, 'minute');
};
// 禁用时分秒
const disabledTime = (current) => {
if (!current || (!minDateTime && !maxDateTime)) {
return { disabledHours: () => [], disabledMinutes: () => [], disabledSeconds: () => [] };
}
const currentDate = dayjs(current);
const isCurrentMinDay = currentDate.isSame(minDateTime, 'day');
const isCurrentMaxDay = currentDate.isSame(maxDateTime, 'day');
const disabledHours = () => {
const hours = [];
if (isCurrentMinDay) {
hours.push(...Array.from({ length: minDateTime.hour() }, (_, i) => i));
}
if (isCurrentMaxDay) {
hours.push(...Array.from({ length: 24 - maxDateTime.hour() }, (_, i) => i + maxDateTime.hour() + 1));
}
return hours;
};
const getDisabledMinutes = (selectedHour: number) => {
if (isCurrentMinDay && selectedHour === minDateTime.hour()) {
return fullTimeArr.filter((i) => i < minDateTime.minute());
}
if (isCurrentMaxDay && selectedHour === maxDateTime.hour()) {
return fullTimeArr.filter((i) => i > maxDateTime.minute());
}
return [];
};
const getDisabledSeconds = (selectedHour: number, selectedMinute: number) => {
if (isCurrentMinDay && selectedHour === minDateTime.hour() && selectedMinute === minDateTime.minute()) {
return fullTimeArr.filter((i) => i < minDateTime.second());
}
if (isCurrentMaxDay && selectedHour === maxDateTime.hour() && selectedMinute === maxDateTime.minute()) {
return fullTimeArr.filter((i) => i > maxDateTime.second());
}
return [];
};
return {
disabledHours,
disabledMinutes: getDisabledMinutes,
disabledSeconds: getDisabledSeconds,
};
};
setDisabledDate(() => {
return disabledDate;
});
setDisabledTime(() => {
return disabledTime;
});
};
const newProps = {
utc,
...props,
disabledDate,
disabledTime,
showTime: props.showTime ? { defaultValue: dayjs('00:00:00', 'HH:mm:ss') } : false,
};
return <InternalDatePicker {...newProps} value={value} />;

View File

@ -0,0 +1,184 @@
/**
* 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 { ISchema, Schema, useField } from '@formily/react';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaComponent } from '../schema-component';
import { SchemaSettingsModalItem } from './SchemaSettings';
import {
useCollectionManager_deprecated,
useRecord,
useDesignable,
VariableInput,
useFormBlockContext,
FlagProvider,
useFlag,
} from '..';
import { isVariable } from '../variables/utils/isVariable';
export const SchemaSettingsDateRange = function DateRangeConfig(props: { fieldSchema: Schema }) {
const { fieldSchema } = props;
const field: any = useField();
const { dn } = useDesignable();
const { t } = useTranslation();
const { getCollectionJoinField } = useCollectionManager_deprecated();
const { isInSubForm, isInSubTable } = useFlag() || {};
const collectionField = getCollectionJoinField(fieldSchema?.['x-collection-field']) || {};
const showTime =
fieldSchema?.['x-component-props']?.showTime || collectionField?.uiSchema?.['x-component-props']?.showTime || false;
const dateFormat =
fieldSchema?.['x-component-props']?.dateFormat ||
collectionField?.uiSchema?.['x-component-props']?.dateFormat ||
'YYYY-MM-DD';
const timeFormat =
fieldSchema?.['x-component-props']?.timeFormat ||
collectionField?.uiSchema?.['x-component-props']?.timeFormat ||
'HH:mm:ss';
const picker =
fieldSchema?.['x-component-props']?.picker || collectionField?.uiSchema?.['x-component-props']?.picker || 'date';
const isReadPretty = fieldSchema['x-read-pretty'] || field.readOnly || field.readPretty;
const minDateDefaultValue = fieldSchema?.['x-component-props']?._minDate;
const maxDateDefaultValue = fieldSchema?.['x-component-props']?._maxDate;
const gmt = collectionField?.uiSchema?.['x-component-props'].gmt || false;
const utc = collectionField?.uiSchema?.['x-component-props'].utc || false;
const record = useRecord();
const { form } = useFormBlockContext();
const renderSchemaComponent = useCallback(
(props) => {
return (
<SchemaComponent
schema={{
'x-component': 'DatePicker',
'x-component-props': {
dateFormat,
timeFormat,
gmt,
utc,
picker,
showTime,
placeholder: '{{t("Please select time or variable")}}',
style: {
minWidth: 385,
},
},
name: 'value',
'x-read-pretty': false,
'x-validator': undefined,
'x-decorator': undefined,
}}
/>
);
},
[dateFormat, gmt, picker, showTime, timeFormat, utc],
);
const Component = useCallback(
(props) => {
return (
<VariableInput
{...props}
form={form}
record={record}
noDisabled={true}
renderSchemaComponent={renderSchemaComponent}
/>
);
},
[fieldSchema, form, record, renderSchemaComponent],
);
// 无论是日期还是变量的情况下当值为空的时候都转换成undefined
function getDateValue(dateObject) {
if (dateObject === null || dateObject === undefined) {
return undefined;
}
if (typeof dateObject === 'string') {
return dateObject;
}
if (typeof dateObject === 'object' && Object.prototype.hasOwnProperty.call(dateObject, 'value')) {
return getDateValue(dateObject.value);
}
return undefined;
}
return (
<SchemaSettingsModalItem
title={t('Date range limit')}
schema={
{
type: 'object',
'x-decorator': '',
properties: {
_minDate: {
type: 'string',
title: '{{t("MinDate")}}',
'x-decorator': 'FormItem',
'x-component': Component,
'x-component-props': {
name: '_minDate',
},
//日期和使用变量设置的默认值不同
default: isVariable(minDateDefaultValue) ? minDateDefaultValue : { value: minDateDefaultValue },
},
_maxDate: {
type: 'string',
title: '{{t("MaxDate")}}',
'x-decorator': 'FormItem',
'x-component': Component,
'x-component-props': {
name: '_maxDate',
},
default: isVariable(maxDateDefaultValue) ? maxDateDefaultValue : { value: maxDateDefaultValue },
},
},
} as ISchema
}
onSubmit={(data) => {
const schema: any = {
['x-uid']: fieldSchema['x-uid'],
};
schema['x-component-props'] = field.componentProps || {};
fieldSchema['x-component-props'] = {
...(field.componentProps || {}),
_maxDate: getDateValue(data?._maxDate),
_minDate: getDateValue(data?._minDate),
};
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps = fieldSchema['x-component-props'];
//子表格/表格区块
const parts = (field.path.entire as string).split('.');
parts.pop();
const modifiedString = parts.join('.');
field.query(`${modifiedString}.*[0:].${fieldSchema.name}`).forEach((f) => {
if (f.props.name === fieldSchema.name) {
f.setComponentProps({
_maxDate: getDateValue(data?._maxDate),
_minDate: getDateValue(data?._minDate),
});
}
});
dn.emit('patch', {
schema,
});
dn.refresh();
}}
// 这里是为了在新增表单中设置日期范围时设置的值能正常显示
ModalContextProvider={(props) => {
return (
<FlagProvider isInSubForm={isInSubForm} isInSubTable={isInSubTable} isInFormDataTemplate={true}>
{props.children}
</FlagProvider>
);
}}
/>
);
};

2400
yarn.lock

File diff suppressed because it is too large Load Diff