mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
Merge branch 'next' into develop
This commit is contained in:
commit
9f6688b436
@ -2,9 +2,7 @@
|
|||||||
"version": "1.6.0-alpha.6",
|
"version": "1.6.0-alpha.6",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"npmClientArgs": [
|
"npmClientArgs": ["--ignore-engines"],
|
||||||
"--ignore-engines"
|
|
||||||
],
|
|
||||||
"command": {
|
"command": {
|
||||||
"version": {
|
"version": {
|
||||||
"forcePublish": true,
|
"forcePublish": true,
|
||||||
|
@ -854,6 +854,10 @@
|
|||||||
"Failed to load plugin": "插件加载失败",
|
"Failed to load plugin": "插件加载失败",
|
||||||
"Allow add new, update and delete actions": "允许增删改操作",
|
"Allow add new, update and delete actions": "允许增删改操作",
|
||||||
"Date display format": "日期显示格式",
|
"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": "筛选出一条或一组数据,作为模板",
|
"Filter out a single piece or a group of records as a template": "筛选出一条或一组数据,作为模板",
|
||||||
"The title field is used to identify the template record": "用于识别模板数据",
|
"The title field is used to identify the template record": "用于识别模板数据",
|
||||||
"Template fields": "模板字段",
|
"Template fields": "模板字段",
|
||||||
|
@ -9,8 +9,9 @@
|
|||||||
|
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
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 { 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';
|
import { ellipsisSettingsItem, enableLinkSettingsItem, openModeSettingsItem } from '../Input/inputComponentSettings';
|
||||||
|
|
||||||
export const datePickerComponentFieldSettings = new SchemaSettings({
|
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,
|
ellipsisSettingsItem,
|
||||||
enableLinkSettingsItem,
|
enableLinkSettingsItem,
|
||||||
openModeSettingsItem,
|
openModeSettingsItem,
|
||||||
|
@ -7,17 +7,19 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* 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 { DatePicker as AntdDatePicker, DatePickerProps as AntdDatePickerProps, Space, Select } from 'antd';
|
||||||
import { RangePickerProps } from 'antd/es/date-picker';
|
import { RangePickerProps } from 'antd/es/date-picker';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { getPickerFormat } from '@nocobase/utils/client';
|
import { getPickerFormat } from '@nocobase/utils/client';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ReadPretty, ReadPrettyComposed } from './ReadPretty';
|
import { ReadPretty, ReadPrettyComposed } from './ReadPretty';
|
||||||
import { getDateRanges, mapDatePicker, mapRangePicker, inferPickerType } from './util';
|
import { getDateRanges, mapDatePicker, mapRangePicker, inferPickerType } from './util';
|
||||||
import { useCompile } from '../../';
|
import { useCompile } from '../../';
|
||||||
|
import { useVariables, useLocalVariables, isVariable } from '../../../variables';
|
||||||
|
import { autorun } from '@formily/reactive';
|
||||||
|
import { log10 } from 'mathjs';
|
||||||
interface IDatePickerProps {
|
interface IDatePickerProps {
|
||||||
utc?: boolean;
|
utc?: boolean;
|
||||||
}
|
}
|
||||||
@ -52,9 +54,111 @@ const InternalRangePicker = connect(
|
|||||||
export const DatePicker: ComposedDatePicker = (props: any) => {
|
export const DatePicker: ComposedDatePicker = (props: any) => {
|
||||||
const { utc = true } = useDatePickerContext();
|
const { utc = true } = useDatePickerContext();
|
||||||
const value = Array.isArray(props.value) ? props.value[0] : props.value;
|
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 = {
|
const newProps = {
|
||||||
utc,
|
utc,
|
||||||
...props,
|
...props,
|
||||||
|
disabledDate,
|
||||||
|
disabledTime,
|
||||||
showTime: props.showTime ? { defaultValue: dayjs('00:00:00', 'HH:mm:ss') } : false,
|
showTime: props.showTime ? { defaultValue: dayjs('00:00:00', 'HH:mm:ss') } : false,
|
||||||
};
|
};
|
||||||
return <InternalDatePicker {...newProps} value={value} />;
|
return <InternalDatePicker {...newProps} value={value} />;
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user