diff --git a/.github/workflows/get-plugins.yml b/.github/workflows/get-plugins.yml index ac9b196fd5..348544a180 100644 --- a/.github/workflows/get-plugins.yml +++ b/.github/workflows/get-plugins.yml @@ -38,13 +38,52 @@ jobs: owner: nocobase skip-token-revoke: true - id: get-plugins + name: Get plugins shell: bash run: | - echo "all-plugins=$(gh search repos "props.plugin-type:custom,rc,beta,alpha,unreleased" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" - echo "custom-plugins=$(gh search repos "props.plugin-type:custom" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" - echo "rc-plugins=$(gh search repos "props.plugin-type:rc" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" - echo "beta-plugins=$(gh search repos "props.plugin-type:beta,rc" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" - echo "alpha-plugins=$(gh search repos "props.plugin-type:alpha,beta,rc" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" - echo "unreleased-plugins=$(gh search repos "props.plugin-type:unreleased" --owner=nocobase --json name | jq -r 'map(.name) | tostring')" >> "$GITHUB_OUTPUT" + function retry() { + local i=0 + local plugins="[]" + until [ "$i" -ge 2 ] + do + plugins=$(gh search repos "props.plugin-type:$1" --owner=nocobase --json name | jq -r 'map(.name) | tostring') && [[ "$plugins" != "[]" ]] && break + i=$((i+1)) + sleep 10 + done + echo $plugins + } + allPlugins=$(retry custom,rc,beta,alpha,unreleased) + if [[ "$allPlugins" == "[]" ]]; then + echo "Get all plugins empty" + exit 1 + fi + customPlugins=$(retry custom) + if [[ "$customPlugins" == "[]" ]]; then + echo "Get custom plugins empty" + exit 1 + fi + rcPlugins=$(retry rc) + rcPlugins=$(retry custom) + if [[ "$rcPlugins" == "[]" ]]; then + echo "Get rc plugins empty" + exit 1 + fi + betaPlugins=$(retry beta,rc) + if [[ "$betaPlugins" == "[]" ]]; then + echo "Get beta plugins empty" + exit 1 + fi + alphaPlugins=$(retry alpha,beta,rc) + if [[ "$alphaPlugins" == "[]" ]]; then + echo "Get alpha plugins empty" + exit 1 + fi + unreleasedPlugins=$(retry unreleased) + echo "all-plugins=$allPlugins" >> "$GITHUB_OUTPUT" + echo "custom-plugins=$customPlugins" >> "$GITHUB_OUTPUT" + echo "rc-plugins=$rcPlugins" >> "$GITHUB_OUTPUT" + echo "beta-plugins=$betaPlugins" >> "$GITHUB_OUTPUT" + echo "alpha-plugins=$alphaPlugins" >> "$GITHUB_OUTPUT" + echo "unreleased-plugins=$unreleasedPlugins" >> "$GITHUB_OUTPUT" env: GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/packages/core/client/src/schema-component/antd/date-picker/util.ts b/packages/core/client/src/schema-component/antd/date-picker/util.ts index b34f17c4cb..f86e3bdf93 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/util.ts +++ b/packages/core/client/src/schema-component/antd/date-picker/util.ts @@ -81,8 +81,7 @@ const handleChangeOnFilter = (value, picker, showTime) => { } return value; }; - -const handleChangeOnForm = (value, dateOnly, utc, picker, showTime, gmt) => { +export const handleDateChangeOnForm = (value, dateOnly, utc, picker, showTime, gmt) => { const format = showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'; if (!value) { return value; @@ -120,7 +119,7 @@ export const mapDatePicker = function () { if (underFilter) { onChange(handleChangeOnFilter(value, picker, showTime)); } else { - onChange(handleChangeOnForm(value, dateOnly, utc, picker, showTime, gmt)); + onChange(handleDateChangeOnForm(value, dateOnly, utc, picker, showTime, gmt)); } } }, diff --git a/packages/core/server/src/plugin.ts b/packages/core/server/src/plugin.ts index a77a3c02c1..9fb5da40ec 100644 --- a/packages/core/server/src/plugin.ts +++ b/packages/core/server/src/plugin.ts @@ -173,6 +173,14 @@ export abstract class Plugin implements PluginInterface { }); } + private async getPluginBasePath() { + if (!this.options.packageName) { + this.app.log.trace(`plugin '${this.name}' is missing packageName`); + return; + } + return getPluginBasePath(this.options.packageName); + } + /** * @internal */ @@ -183,6 +191,7 @@ export abstract class Plugin implements PluginInterface { } const directory = resolve(basePath, 'server/collections'); if (await fsExists(directory)) { + this.app.log.trace(`load plugin collections [${this.name}]`); await this.db.import({ directory, from: this.options.packageName, diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx index 2329617f2e..a85ece304f 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx @@ -8,7 +8,7 @@ */ import { LeftOutlined, RightOutlined } from '@ant-design/icons'; -import { RecursionField, Schema, useFieldSchema } from '@formily/react'; +import { RecursionField, Schema, observer, useFieldSchema, useField } from '@formily/react'; import { PopupContextProvider, RecordProvider, @@ -21,13 +21,21 @@ import { useProps, useToken, withDynamicSchemaProps, - withSkeletonComponent, + useACLRoleContext, + useDesignable, + ActionContextProvider, + useActionContext, + CollectionProvider, + SchemaComponentOptions, + useFormBlockContext, + handleDateChangeOnForm, } from '@nocobase/client'; import type { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; -import { get } from 'lodash'; -import React, { useEffect, useMemo, useState } from 'react'; -import type { View } from 'react-big-calendar'; +import { get, cloneDeep } from 'lodash'; +import React, { useMemo, useState, useEffect, useCallback } from 'react'; +import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar'; +import * as dates from 'react-big-calendar/lib/utils/dates'; import { i18nt, useTranslation } from '../../locale'; import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer'; import Header from './components/Header'; @@ -37,7 +45,7 @@ import { useCalenderHeight } from './hook'; import useStyle from './style'; import type { ToolbarProps } from './types'; import { formatDate } from './utils'; - +import { addNew } from './schema'; interface Event { id: string; colorFieldValue: string; @@ -222,17 +230,25 @@ const useEvents = ( ]); }; -function findCreateSchema(schema): Schema { - return schema.reduceProperties((buf, current) => { - if (current['x-component'].endsWith('Action') && current['x-action'] === 'create') { - return current; - } - if (current['x-component'].endsWith('.ActionBar')) { - return findCreateSchema(current); - } - return buf; - }, null); -} +export const useInsertSchema = (component) => { + const fieldSchema = useFieldSchema(); + const { insertAfterBegin } = useDesignable(); + const insert = useCallback( + (ss) => { + const schema = fieldSchema.reduceProperties((buf, s) => { + if (s['x-component'] === 'AssociationField.' + component) { + return s; + } + return buf; + }, null); + if (!schema) { + insertAfterBegin(cloneDeep(ss)); + } + }, + [component], + ); + return insert; +}; export const Calendar: any = withDynamicSchemaProps( withSkeletonComponent( @@ -268,15 +284,18 @@ export const Calendar: any = withDynamicSchemaProps( const { wrapSSR, hashId, componentCls: containerClassName } = useStyle(); const parentRecordData = useCollectionParentRecordData(); const fieldSchema = useFieldSchema(); + const field = useField(); const { token } = useToken(); //nint deal with slot select to show create popup const { parseAction } = useACLRoleContext(); const collection = useCollection(); const canCreate = parseAction(`${collection.name}:create`); - const createActionSchema: Schema = useMemo(() => findCreateSchema(fieldSchema), [fieldSchema]); const startFieldName = fieldNames?.start?.[0]; const endFieldName = fieldNames?.end?.[0]; - + const insertAddNewer = useInsertSchema('AddNewer'); + const ctx = useActionContext(); + const [visibleAddNewer, setVisibleAddNewer] = useState(false); + const [currentSelectDate, setCurrentSelectDate] = useState(undefined); useEffect(() => { setView(props.defaultView); }, [props.defaultView]); @@ -327,7 +346,57 @@ export const Calendar: any = withDynamicSchemaProps( }; } }; + // 快速创建行程 + const useCreateFormBlockProps = () => { + const ctx = useFormBlockContext(); + let startDateValue = currentSelectDate.start; + let endDataValue = currentSelectDate.end; + const startCollectionField = collection.getField(startFieldName); + const endCollectionField = collection.getField(endFieldName); + useEffect(() => { + const form = ctx.form; + if (!form || ctx.service?.loading) { + return; + } + if (currentSelectDate) { + const startFieldProps = { + ...startCollectionField.uiSchema?.['x-component-props'], + ...ctx.form?.query(startFieldName).take()?.componentProps, + }; + const endFieldProps = { + ...endCollectionField.uiSchema?.['x-component-props'], + ...ctx.form?.query(endFieldName).take()?.componentProps, + }; + + startDateValue = handleDateChangeOnForm( + currentSelectDate.start, + startFieldProps.dateOnly, + startFieldProps.utc, + startFieldProps.picker, + startFieldProps.showTime, + startFieldProps.gtm, + ); + endDataValue = handleDateChangeOnForm( + currentSelectDate.end, + endFieldProps.dateOnly, + endFieldProps.utc, + endFieldProps.picker, + endFieldProps.showTime, + endFieldProps.gtm, + ); + if (!form.initialValues[startFieldName]) { + form.setInitialValuesIn([startFieldName], startDateValue); + } + if (!form.initialValues[endFieldName]) { + form.setInitialValuesIn([endFieldName], endDataValue); + } + } + }, [ctx.form, ctx.service?.data?.data, ctx.service?.loading]); + return { + form: ctx.form, + }; + }; const BigCalendar = reactBigCalendar?.BigCalendar; return wrapSSR( @@ -351,15 +420,10 @@ export const Calendar: any = withDynamicSchemaProps( onNavigate={setDate} onView={setView} onSelectSlot={(slotInfo) => { - //nint show create popup - if (canCreate && createActionSchema) { - const record = {}; - record[startFieldName] = slotInfo.start; - record[endFieldName] = slotInfo.end; - openPopup({ - recordData: record, - customActionSchema: createActionSchema, - }); + setCurrentSelectDate(slotInfo); + if (canCreate) { + insertAddNewer(addNew); + setVisibleAddNewer(true); } }} onDoubleClickEvent={() => { @@ -397,6 +461,20 @@ export const Calendar: any = withDynamicSchemaProps( localizer={localizer} /> + + + + { + return s['x-component'] === 'AssociationField.AddNewer'; + }} + /> + + + , ); }, diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/schema.ts b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/schema.ts new file mode 100644 index 0000000000..2518f2e8a5 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/schema.ts @@ -0,0 +1,47 @@ +/** + * 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. + */ + +const addNew = { + type: 'void', + 'x-component': 'AssociationField.AddNewer', + 'x-action': 'create', + title: '{{ t("Add record") }}', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + 'x-initializer-props': { + gridInitializer: 'popup:addNew:addBlock', + }, + properties: { + tab1: { + type: 'void', + title: '{{t("Add new")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:addNew:addBlock', + properties: {}, + }, + }, + }, + }, + }, + }, +}; +export { addNew };