diff --git a/.github/workflows/aliyun-container-registry.yml b/.github/workflows/aliyun-container-registry.yml index 74b1fac94..ccc128998 100644 --- a/.github/workflows/aliyun-container-registry.yml +++ b/.github/workflows/aliyun-container-registry.yml @@ -24,6 +24,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }} + submodules: true - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx @@ -51,7 +54,7 @@ jobs: uses: docker/build-push-action@v3 with: context: . - file: Dockerfile + file: Dockerfile.acr build-args: | VERDACCIO_URL=http://localhost:4873/ COMMIT_HASH=${GITHUB_SHA} diff --git a/Dockerfile.acr b/Dockerfile.acr new file mode 100644 index 000000000..1aa532e28 --- /dev/null +++ b/Dockerfile.acr @@ -0,0 +1,64 @@ +FROM node:16 as builder +ARG VERDACCIO_URL=http://host.docker.internal:10104/ +ARG COMIT_HASH + +RUN apt-get update && apt-get install -y jq +WORKDIR /tmp +COPY . /tmp +RUN npx npm-cli-adduser --username test --password test -e test@nocobase.com -r $VERDACCIO_URL +RUN cd /tmp && \ + NEWVERSION="$(cat lerna.json | jq '.version' | tr -d '"').$(date +%s)" \ + && tmp=$(mktemp) \ + && jq ".version = \"${NEWVERSION}\"" lerna.json > "$tmp" && mv "$tmp" lerna.json +RUN yarn install && yarn nocobase install && yarn pm add graph-collection-manager && yarn build + +RUN git checkout -b release \ + && yarn version:alpha -y + +RUN cd packages/pro-plugins && git checkout -b release \ + && git config user.email "test@mail.com" \ + && git config user.name "test" && git add . \ + && git commit -m "chore(versions): test publish packages xxx" + +RUN git config user.email "test@mail.com" \ + && git config user.name "test" && git add . \ + && git commit -m "chore(versions): test publish packages xxx" \ + && git submodule update --init --recursive -- packages/pro-plugins + +RUN yarn release:force --no-verify-access --no-git-reset --registry $VERDACCIO_URL + +RUN yarn config set registry $VERDACCIO_URL +WORKDIR /app +RUN cd /app \ + && yarn config set network-timeout 600000 -g \ + && yarn create nocobase-app my-nocobase-app -a \ + -e APP_ENV=production \ + -e PLUGIN_PACKAGE_PREFIX=@nocobase/plugin-,@nocobase/preset-,@nocobase/plugin-pro- \ + -e PRESET_NOCOBASE_PLUGINS=error-handler,collection-manager,ui-schema-storage,ui-routes-storage,file-manager,system-settings,verification,users,acl,china-region,workflow,client,export,import,audit-logs,graph-collection-manager \ + && cd /app/my-nocobase-app \ + && yarn install --production \ + && yarn add @nocobase/plugin-pro-graph-collection-manager -W + +RUN cd /app \ + && rm -rf my-nocobase-app/packages/app/client/src/.umi \ + && rm -rf nocobase.tar.gz \ + && find ./my-nocobase-app/node_modules/china-division/dist -name '*.csv' -delete \ + && find ./my-nocobase-app/node_modules/china-division/dist -name '*.sqlite' -delete \ + && tar -zcf ./nocobase.tar.gz -C /app/my-nocobase-app . + + +FROM node:16-stretch-slim +RUN apt-get update && apt-get install -y nginx + +RUN rm -rf /etc/nginx/sites-enabled/default +COPY ./docker/nocobase/nocobase.conf /etc/nginx/sites-enabled/nocobase.conf +COPY --from=builder /app/nocobase.tar.gz /app/nocobase.tar.gz + +WORKDIR /app/nocobase + +RUN mkdir -p /app/nocobase/storage/uploads/ && echo "$COMIT_HASH" >> /app/nocobase/storage/uploads/COMIT_HASH + +COPY ./docker/nocobase/docker-entrypoint.sh /app/ + +CMD ["/app/docker-entrypoint.sh"] + diff --git a/Dockerfile.official b/Dockerfile.official deleted file mode 100644 index a400b79c8..000000000 --- a/Dockerfile.official +++ /dev/null @@ -1,47 +0,0 @@ -FROM node:16 as builder -ARG VERDACCIO_URL=https://pkgs.nocobase.com/ - -RUN apt-get update && apt-get install -y jq -WORKDIR /tmp -COPY . /tmp -RUN npx npm-cli-adduser --username test --password test -e test@nocobase.com -r $VERDACCIO_URL -RUN cd /tmp && \ - NEWVERSION="$(cat lerna.json | jq '.version' | tr -d '"').$(date +%s)" \ - && tmp=$(mktemp) \ - && jq ".version = \"${NEWVERSION}\"" lerna.json > "$tmp" && mv "$tmp" lerna.json -RUN yarn install && yarn build - -RUN git checkout -b release \ - && yarn version:alpha -y \ - && git config user.email "test@mail.com" \ - && git config user.name "test" && git add . \ - && git commit -m "chore(versions): test publish packages xxx" \ - && yarn release:force --registry $VERDACCIO_URL - -RUN yarn config set registry $VERDACCIO_URL -WORKDIR /app -RUN cd /app \ - && yarn create nocobase-app my-nocobase-app -a -e APP_ENV=production \ - && cd /app/my-nocobase-app \ - && yarn install --production - -RUN cd /app \ - && rm -rf my-nocobase-app/packages/app/client/src/.umi \ - && rm -rf nocobase.tar.gz \ - && find ./my-nocobase-app/node_modules/china-division/dist -name '*.csv' -delete \ - && find ./my-nocobase-app/node_modules/china-division/dist -name '*.sqlite' -delete \ - && tar -zcf ./nocobase.tar.gz -C /app/my-nocobase-app . - - -FROM node:16-stretch-slim -RUN apt-get update && apt-get install -y nginx - -RUN rm -rf /etc/nginx/sites-enabled/default -COPY ./docker/nocobase/nocobase.conf /etc/nginx/sites-enabled/nocobase.conf -COPY --from=builder /app/nocobase.tar.gz /app/nocobase.tar.gz - -WORKDIR /app/nocobase - -COPY ./docker/nocobase/docker-entrypoint.sh /app/ - -CMD ["/app/docker-entrypoint.sh"] diff --git a/docs/zh-CN/welcome/release/v08-1-collection-templates.md b/docs/zh-CN/welcome/release/v08-1-collection-templates.md new file mode 100644 index 000000000..2fea83326 --- /dev/null +++ b/docs/zh-CN/welcome/release/v08-1-collection-templates.md @@ -0,0 +1,79 @@ +# v0.8.1: Collection 模板 + + + +## 为什么需要 Collection 模板? + +待补充 + +## 配置参数说明 + +```ts +interface ICollectionTemplate { + name: string; + title?: string; + /** 排序 */ + order?: number; + /** 默认配置 */ + default?: CollectionOptions; + /** UI 可配置的 CollectionOptions 参数(添加或编辑的 Collection 表单的字段) */ + configurableProperties?: Record; + /** 当前模板可用的字段类型 */ + availableFieldInterfaces?: AvailableFieldInterfacesInclude | AvailableFieldInterfacesExclude; +} + +interface AvailableFieldInterfacesInclude { + include?: any[]; +} + +interface AvailableFieldInterfacesExclude { + exclude?: any[]; +} + +interface CollectionOptions { + /** + * 自动生成 id + * @default true + * */ + autoGenId?: boolean; + /** 创建人 */ + createdBy?: boolean; + /** 最后更新人 */ + updatedBy?: boolean; + /** 创建日期 */ + createdAt?: boolean; + /** 更新日期 */ + updatedAt?: boolean; + /** 可排序 */ + sortable?: boolean; + /* 树结构 */ + tree?: string; + /* 日志 */ + logging?: boolean; + /** 继承 */ + inherits: string | string[]; + /* 字段列表 */ + fields?: FieldOptions[]; +} +``` + +## 示例 + +创建时都不需要 autoGenId,并且只提供 title 和 name 配置项 + +```ts +import { collectionConfigurableProperties } from '@nocobase/client'; + +{ + default: { + autoGenId: false, + fields: [], + }, + configurableProperties: { + ...collectionConfigurableProperties('name', 'title'), + }, +} +``` + +完整插件示例参考:[samples/custom-collection-template](https://github.com/nocobase/nocobase/tree/feat/collection-templates/packages/samples/custom-collection-template) + diff --git a/docs/zh-CN/welcome/release/v08-1-collection-templates/v08-1-collection-templates.jpg b/docs/zh-CN/welcome/release/v08-1-collection-templates/v08-1-collection-templates.jpg new file mode 100644 index 000000000..dd0a99af8 Binary files /dev/null and b/docs/zh-CN/welcome/release/v08-1-collection-templates/v08-1-collection-templates.jpg differ diff --git a/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx b/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx index f802b323e..a63782566 100644 --- a/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx +++ b/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx @@ -1,20 +1,24 @@ import { Spin } from 'antd'; import React, { useContext, useState } from 'react'; +import { keyBy } from 'lodash'; import { useAPIClient, useRequest } from '../api-client'; import { CollectionManagerSchemaComponentProvider } from './CollectionManagerSchemaComponentProvider'; import { CollectionManagerContext } from './context'; import * as defaultInterfaces from './interfaces'; import { CollectionManagerOptions } from './types'; +import { templateOptions } from '../collection-manager/Configuration/templates'; export const CollectionManagerProvider: React.FC = (props) => { - const { service, interfaces, collections = [], refreshCM } = props; + const { service, interfaces, collections = [], refreshCM, templates } = props; + const defaultTemplates = keyBy(templateOptions(), (item) => item.name); const ctx = useContext(CollectionManagerContext); return ( { ConfigurationTable, AddFieldAction, AddCollectionField, + AddCollection, + AddCollectionAction, + EditCollection, + EditCollectionAction, EditFieldAction, EditCollectionField, OverridingCollectionField, @@ -99,6 +107,8 @@ export const CollectionManagerShortcut2 = () => { EditFieldAction, OverridingFieldAction, ViewFieldAction, + AddCollectionAction, + EditCollectionAction, }} /> diff --git a/packages/core/client/src/collection-manager/Configuration/AddCollectionAction.tsx b/packages/core/client/src/collection-manager/Configuration/AddCollectionAction.tsx new file mode 100644 index 000000000..8824c23e8 --- /dev/null +++ b/packages/core/client/src/collection-manager/Configuration/AddCollectionAction.tsx @@ -0,0 +1,282 @@ +import { DownOutlined, PlusOutlined } from '@ant-design/icons'; +import { ArrayTable } from '@formily/antd'; +import { ISchema, useForm } from '@formily/react'; +import { uid } from '@formily/shared'; +import { Button, Dropdown, Menu } from 'antd'; +import { cloneDeep } from 'lodash'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRequest } from '../../api-client'; +import { RecordProvider, useRecord } from '../../record-provider'; +import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component'; +import { useCancelAction } from '../action-hooks'; +import { useCollectionManager } from '../hooks'; +import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider'; +import * as components from './components'; +import { templateOptions } from './templates'; + +const getSchema = (schema, record: any, compile): ISchema => { + if (!schema) { + return; + } + + const properties = cloneDeep(schema.configurableProperties) as any; + + if (schema.hasDefaultValue === true) { + properties['defaultValue'] = cloneDeep(schema.default.uiSchema); + properties['defaultValue']['title'] = compile('{{ t("Default value") }}'); + properties['defaultValue']['x-decorator'] = 'FormItem'; + } + const initialValue: any = { + name: `f_${uid()}`, + template: schema.name, + ...cloneDeep(schema.default), + }; + if (initialValue.reverseField) { + initialValue.reverseField.name = `f_${uid()}`; + } + return { + type: 'object', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Action.Drawer', + 'x-component-props': { + getContainer: '{{ getContainer }}', + }, + 'x-decorator': 'Form', + 'x-decorator-props': { + useValues(options) { + return useRequest( + () => + Promise.resolve({ + data: initialValue, + }), + options, + ); + }, + }, + title: '{{ t("Create collection") }}', + properties: { + summary: { + type: 'void', + 'x-component': 'FieldSummary', + 'x-component-props': { + schemaKey: schema.name, + }, + }, + // @ts-ignore + ...properties, + footer: { + type: 'void', + 'x-component': 'Action.Drawer.Footer', + properties: { + action1: { + title: '{{ t("Cancel") }}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCancelAction }}', + }, + }, + action2: { + title: '{{ t("Submit") }}', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: () => useCreateCollection(), + }, + }, + }, + }, + }, + }, + }, + }; +}; + +const useDefaultCollectionFields = (values) => { + let defaults = values.fields ? [...values.fields] : []; + const { autoGenId = true, createdAt = true, createdBy = true, updatedAt = true, updatedBy = true } = values; + if (autoGenId) { + const pk = values.fields.find((f) => f.primaryKey); + if (!pk) { + defaults.push({ + name: 'id', + type: 'bigInt', + autoIncrement: true, + primaryKey: true, + allowNull: false, + uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true }, + interface: 'id', + }); + } + } + if (createdAt) { + defaults.push({ + name: 'createdAt', + interface: 'createdAt', + type: 'date', + field: 'createdAt', + uiSchema: { + type: 'datetime', + title: '{{t("Created at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }); + } + if (createdBy) { + defaults.push({ + name: 'createdBy', + interface: 'createdBy', + type: 'belongsTo', + target: 'users', + foreignKey: 'createdById', + uiSchema: { + type: 'object', + title: '{{t("Created by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + }); + } + if (updatedAt) { + defaults.push({ + type: 'date', + field: 'updatedAt', + name: 'updatedAt', + interface: 'updatedAt', + uiSchema: { + type: 'string', + title: '{{t("Last updated at")}}', + 'x-component': 'DatePicker', + 'x-component-props': {}, + 'x-read-pretty': true, + }, + }); + } + if (updatedBy) { + defaults.push({ + type: 'belongsTo', + target: 'users', + foreignKey: 'updatedById', + name: 'updatedBy', + interface: 'updatedBy', + uiSchema: { + type: 'object', + title: '{{t("Last updated by")}}', + 'x-component': 'RecordPicker', + 'x-component-props': { + fieldNames: { + value: 'id', + label: 'nickname', + }, + }, + 'x-read-pretty': true, + }, + }); + } + // 其他 + return defaults; +}; + +const useCreateCollection = () => { + const form = useForm(); + const { refreshCM } = useCollectionManager(); + const ctx = useActionContext(); + const { refresh } = useResourceActionContext(); + const { resource } = useResourceContext(); + return { + async run() { + await form.submit(); + const values = cloneDeep(form.values); + const fields = useDefaultCollectionFields(values); + console.log(fields); + if (values.autoCreateReverseField) { + } else { + delete values.reverseField; + } + delete values.id; + delete values.autoCreateReverseField; + await resource.create({ + values: { + logging: true, + ...values, + fields, + }, + }); + ctx.setVisible(false); + await form.reset(); + refresh(); + await refreshCM(); + }, + }; +}; + +export const AddCollection = (props) => { + const record = useRecord(); + return ; +}; + +export const AddCollectionAction = (props) => { + const { scope, getContainer, item: record, children, trigger, align } = props; + const { getTemplate } = useCollectionManager(); + const [visible, setVisible] = useState(false); + const [schema, setSchema] = useState({}); + const compile = useCompile(); + const { t } = useTranslation(); + return ( + + + { + const schema = getSchema(getTemplate(info.key), record, compile); + setSchema(schema); + setVisible(true); + }} + > + {templateOptions().map((option) => { + return {compile(option.title)}; + })} + + } + > + {children || ( + + )} + + + + + ); +}; diff --git a/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx b/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx index f000448e8..c28790103 100644 --- a/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx +++ b/packages/core/client/src/collection-manager/Configuration/AddFieldAction.tsx @@ -9,12 +9,12 @@ import { useTranslation } from 'react-i18next'; import { useRequest } from '../../api-client'; import { RecordProvider, useRecord } from '../../record-provider'; import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component'; -import { useCancelAction, useCreateAction } from '../action-hooks'; +import { useCancelAction } from '../action-hooks'; import { useCollectionManager } from '../hooks'; import { IField } from '../interfaces/types'; import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider'; import * as components from './components'; -import { getOptions } from './interfaces'; +import { options } from './interfaces'; const getSchema = (schema: IField, record: any, compile) => { if (!schema) { @@ -114,7 +114,6 @@ export const useCollectionFieldFormValues = () => { const useCreateCollectionField = () => { const form = useForm(); - const { run } = useCreateAction(); const { refreshCM } = useCollectionManager(); const ctx = useActionContext(); const { refresh } = useResourceActionContext(); @@ -144,11 +143,44 @@ export const AddCollectionField = (props) => { export const AddFieldAction = (props) => { const { scope, getContainer, item: record, children, trigger, align } = props; - const { getInterface } = useCollectionManager(); + const { getInterface, getTemplate } = useCollectionManager(); const [visible, setVisible] = useState(false); const [schema, setSchema] = useState({}); const compile = useCompile(); const { t } = useTranslation(); + const getFieldOptions = () => { + const { availableFieldInterfaces } = getTemplate(record.template) || {}; + const { exclude, include } = availableFieldInterfaces || {}; + const optionArr = []; + options.forEach((v) => { + if (v.key === 'systemInfo') { + optionArr.push({ + ...v, + children: v.children.filter((v) => { + if (v.value === 'id') { + return typeof record['autoGenId'] === 'boolean' ? record['autoGenId'] : true; + } else { + return typeof record[v.value] === 'boolean' ? record[v.value] : true; + } + }), + }); + } else { + const children = v.children.filter((v) => { + if (include?.length) { + return include.includes(v.value); + } else if (exclude?.length) { + return !exclude.includes(v.value); + } + return true; + }); + optionArr.push({ + ...v, + children, + }); + } + }); + return optionArr; + }; return ( @@ -170,7 +202,7 @@ export const AddFieldAction = (props) => { } }} > - {getOptions().map((option) => { + {getFieldOptions().map((option) => { return ( option.children.length > 0 && ( diff --git a/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx b/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx index 28b4ca81d..31e4d4ced 100644 --- a/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx +++ b/packages/core/client/src/collection-manager/Configuration/ConfigurationTable.tsx @@ -1,13 +1,12 @@ import { useForm } from '@formily/react'; import { action } from '@formily/reactive'; import { uid } from '@formily/shared'; -import React, { useContext, useEffect, useRef, useState } from 'react'; +import React, { useContext, useRef, useState } from 'react'; import { CollectionFieldsTable } from '.'; -import { useRequest } from '../../api-client'; import { useCurrentAppInfo } from '../../appInfo'; import { useRecord } from '../../record-provider'; -import { SchemaComponent, SchemaComponentContext, useActionContext, useCompile } from '../../schema-component'; -import { useCancelAction, useUpdateCollectionActionAndRefreshCM } from '../action-hooks'; +import { SchemaComponent, SchemaComponentContext, useCompile } from '../../schema-component'; +import { useCancelAction } from '../action-hooks'; import { useCollectionManager } from '../hooks/useCollectionManager'; import { DataSourceContext } from '../sub-table'; import { AddSubFieldAction } from './AddSubFieldAction'; @@ -25,109 +24,6 @@ const useAsyncDataSource = (service: any) => (field: any) => { ); }; -const useCollectionValues = (options) => { - const { visible } = useActionContext(); - const result = useRequest( - () => - Promise.resolve({ - data: { - name: `t_${uid()}`, - createdBy: true, - updatedBy: true, - sortable: true, - logging: true, - fields: [ - { - name: 'id', - type: 'bigInt', - autoIncrement: true, - primaryKey: true, - allowNull: false, - uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true }, - interface: 'id', - }, - { - interface: 'createdAt', - type: 'date', - field: 'createdAt', - name: 'createdAt', - uiSchema: { - type: 'datetime', - title: '{{t("Created at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - interface: 'createdBy', - type: 'belongsTo', - target: 'users', - foreignKey: 'createdById', - name: 'createdBy', - uiSchema: { - type: 'object', - title: '{{t("Created by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - }, - { - type: 'date', - field: 'updatedAt', - name: 'updatedAt', - interface: 'updatedAt', - uiSchema: { - type: 'string', - title: '{{t("Last updated at")}}', - 'x-component': 'DatePicker', - 'x-component-props': {}, - 'x-read-pretty': true, - }, - }, - { - type: 'belongsTo', - target: 'users', - foreignKey: 'updatedById', - name: 'updatedBy', - interface: 'updatedBy', - uiSchema: { - type: 'object', - title: '{{t("Last updated by")}}', - 'x-component': 'RecordPicker', - 'x-component-props': { - fieldNames: { - value: 'id', - label: 'nickname', - }, - }, - 'x-read-pretty': true, - }, - }, - ], - }, - }), - { - ...options, - manual: true, - }, - ); - - useEffect(() => { - if (visible) { - result.run(); - } - }, [visible]); - - return result; -}; - const useSelectedRowKeys = () => { const [selectedRowKeys, setSelectedRowKeys] = useState([]); return [selectedRowKeys, setSelectedRowKeys]; @@ -205,13 +101,11 @@ export const ConfigurationTable = () => { useDestroySubField, useBulkDestroySubField, useSelectedRowKeys, - useCollectionValues, useAsyncDataSource, loadCollections, useCurrentFields, useNewId, useCancelAction, - useUpdateCollectionActionAndRefreshCM, enableInherits: database?.dialect === 'postgres', }} /> diff --git a/packages/core/client/src/collection-manager/Configuration/EditCollectionAction.tsx b/packages/core/client/src/collection-manager/Configuration/EditCollectionAction.tsx new file mode 100644 index 000000000..3799e1dee --- /dev/null +++ b/packages/core/client/src/collection-manager/Configuration/EditCollectionAction.tsx @@ -0,0 +1,161 @@ +import { ArrayTable } from '@formily/antd'; +import { ISchema, useForm } from '@formily/react'; +import { uid } from '@formily/shared'; +import { omit } from 'lodash'; +import cloneDeep from 'lodash/cloneDeep'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useRequest } from '../../api-client'; +import { RecordProvider, useRecord } from '../../record-provider'; +import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component'; +import { useCancelAction } from '../action-hooks'; +import { useCollectionManager } from '../hooks'; +import { IField } from '../interfaces/types'; +import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider'; +import * as components from './components'; + +const getSchema = (schema: IField, record: any, compile, getContainer): ISchema => { + if (!schema) { + return; + } + const properties = (cloneDeep(schema.configurableProperties) as any) || { + title: { + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + }, + name: { + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-disabled': true, + }, + }; + properties.name['x-disabled'] = true; + if (schema.hasDefaultValue === true) { + properties['defaultValue'] = cloneDeep(schema.default.uiSchema); + properties['defaultValue']['title'] = compile('{{ t("Default value") }}'); + properties['defaultValue']['x-decorator'] = 'FormItem'; + } + + return { + type: 'object', + properties: { + [uid()]: { + type: 'void', + 'x-component': 'Action.Drawer', + 'x-decorator': 'Form', + 'x-decorator-props': { + useValues: '{{ useValuesFromRecord }}', + }, + title: '{{ t("Edit collection") }}', + properties: { + ...properties, + footer: { + type: 'void', + 'x-component': 'Action.Drawer.Footer', + properties: { + action1: { + title: '{{ t("Cancel") }}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCancelAction }}', + }, + }, + action2: { + title: '{{ t("Submit") }}', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ useUpdateCollectionActionAndRefreshCM }}', + }, + }, + }, + }, + }, + }, + }, + }; +}; + +export const useValuesFromRecord = (options) => { + const record = useRecord(); + const result = useRequest(() => Promise.resolve({ data: { autoGenId: true, ...record } }), { + ...options, + manual: true, + }); + const ctx = useActionContext(); + useEffect(() => { + if (ctx.visible) { + result.run(); + } + }, [ctx.visible]); + return result; +}; + +export const useUpdateCollectionActionAndRefreshCM = (options) => { + const { refreshCM } = useCollectionManager(); + const form = useForm(); + const ctx = useActionContext(); + const { refresh } = useResourceActionContext(); + const { resource, targetKey } = useResourceContext(); + const { [targetKey]: filterByTk } = useRecord(); + return { + async run() { + await form.submit(); + await resource.update({ filterByTk, values: omit(form.values, ['fields']) }); + ctx.setVisible(false); + await form.reset(); + refresh(); + await refreshCM(); + }, + }; +}; + +export const EditCollection = (props) => { + const record = useRecord(); + return ; +}; + +export const EditCollectionAction = (props) => { + const { scope, getContainer, item: record, children } = props; + const { getTemplate } = useCollectionManager(); + const [visible, setVisible] = useState(false); + const [schema, setSchema] = useState({}); + const { t } = useTranslation(); + const compile = useCompile(); + + return ( + + + { + const templateConf = getTemplate(record.template); + const schema = getSchema( + { + ...templateConf, + }, + record, + compile, + getContainer, + ); + setSchema(schema); + setVisible(true); + }} + > + {children || t('Edit')} + + + + + ); +}; diff --git a/packages/core/client/src/collection-manager/Configuration/components/CollectionTemplate.tsx b/packages/core/client/src/collection-manager/Configuration/components/CollectionTemplate.tsx new file mode 100644 index 000000000..e4b618a9d --- /dev/null +++ b/packages/core/client/src/collection-manager/Configuration/components/CollectionTemplate.tsx @@ -0,0 +1,14 @@ +import { observer } from '@formily/react'; +import { Tag } from 'antd'; +import React from 'react'; +import { useCompile } from '../../../schema-component'; +import { useCollectionManager } from '../../hooks'; + +export const CollectionTemplate = observer((props: any) => { + const { value } = props; + const { getTemplate } = useCollectionManager(); + const compile = useCompile(); + const schema = getTemplate(value); + + return {compile(schema?.title||'{{t("General collection")}}')}; +}); diff --git a/packages/core/client/src/collection-manager/Configuration/index.tsx b/packages/core/client/src/collection-manager/Configuration/index.tsx index 7e994d938..5adfe6afa 100644 --- a/packages/core/client/src/collection-manager/Configuration/index.tsx +++ b/packages/core/client/src/collection-manager/Configuration/index.tsx @@ -1,15 +1,17 @@ import { registerValidateFormats } from '@formily/core'; -import { exp } from 'mathjs'; export * from './AddFieldAction'; export * from './ConfigurationTable'; export * from './EditFieldAction'; export * from './interfaces'; export * from './components'; +export * from './templates'; export * from './CollectionFieldsTable'; -export * from './schemas/collections' -export * from './OverridingCollectionField' -export * from './ViewInheritedField' +export * from './schemas/collections'; +export * from './OverridingCollectionField'; +export * from './ViewInheritedField'; +export * from './AddCollectionAction'; +export * from './EditCollectionAction'; registerValidateFormats({ uid: /^[A-Za-z0-9][A-Za-z0-9_-]*$/, diff --git a/packages/core/client/src/collection-manager/Configuration/interfaces.tsx b/packages/core/client/src/collection-manager/Configuration/interfaces.tsx index 4e6622baa..3e4ffb79b 100644 --- a/packages/core/client/src/collection-manager/Configuration/interfaces.tsx +++ b/packages/core/client/src/collection-manager/Configuration/interfaces.tsx @@ -35,6 +35,7 @@ export const getOptions = () => { return Object.keys(groupLabels).map((groupName) => { return { label: groupLabels[groupName], + key: groupName, children: Object.keys(fields[groupName] || {}) .map((type) => { const field = fields[groupName][type]; diff --git a/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts b/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts index 451010e81..da6ab39f0 100644 --- a/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts +++ b/packages/core/client/src/collection-manager/Configuration/schemas/collections.ts @@ -1,6 +1,7 @@ import { ISchema, Schema } from '@formily/react'; import { i18n } from '../../../i18n'; import { CollectionOptions } from '../../types'; +import { CollectionTemplate } from '../components/CollectionTemplate'; import { collectionFieldSchema } from './collectionFields'; const compile = (source) => { @@ -35,6 +36,16 @@ export const collection: CollectionOptions = { '{{t("Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.")}}', }, }, + { + type: 'string', + name: 'template', + interface: 'input', + uiSchema: { + title: '{{ t("Collection Template") }}', + type: 'string', + 'x-component': 'Input', + }, + }, { type: 'hasMany', name: 'fields', @@ -60,85 +71,6 @@ export const collection: CollectionOptions = { ], }; -export const createCollectionProperties = { - title: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - }, - name: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - 'x-validator': 'uid', - }, - inherits: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - 'x-visible': '{{ enableInherits}}', - 'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'], - }, - footer: { - type: 'void', - 'x-component': 'Action.Drawer.Footer', - properties: { - action1: { - title: '{{ t("Cancel") }}', - 'x-component': 'Action', - 'x-component-props': { - useAction: '{{ cm.useCancelAction }}', - }, - }, - action2: { - title: '{{ t("Submit") }}', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - useAction: '{{ cm.useCreateActionAndRefreshCM }}', - }, - }, - }, - }, -}; - -export const editCollectionProperties = { - title: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - }, - name: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - 'x-disabled': true, - }, - inherits: { - 'x-component': 'CollectionField', - 'x-decorator': 'FormItem', - 'x-disabled': true, - 'x-visible': '{{ enableInherits}}', - 'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'], - }, - footer: { - type: 'void', - 'x-component': 'Action.Drawer.Footer', - properties: { - action1: { - title: '{{ t("Cancel") }}', - 'x-component': 'Action', - 'x-component-props': { - useAction: '{{ useCancelAction }}', - }, - }, - action2: { - title: '{{ t("Submit") }}', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - useAction: '{{ useUpdateCollectionActionAndRefreshCM }}', - }, - }, - }, - }, -}; - export const collectionSchema: ISchema = { type: 'object', properties: { @@ -190,6 +122,7 @@ export const collectionSchema: ISchema = { title: '{{ t("Delete") }}', 'x-component': 'Action', 'x-component-props': { + icon: 'DeleteOutlined', useAction: '{{ cm.useBulkDestroyActionAndRefreshCM }}', confirm: { title: "{{t('Delete record')}}", @@ -200,22 +133,10 @@ export const collectionSchema: ISchema = { create: { type: 'void', title: '{{ t("Create collection") }}', - 'x-component': 'Action', + 'x-component': 'AddCollection', 'x-component-props': { type: 'primary', }, - properties: { - drawer: { - type: 'void', - title: '{{ t("Create collection") }}', - 'x-component': 'Action.Drawer', - 'x-decorator': 'Form', - 'x-decorator-props': { - useValues: '{{ useCollectionValues }}', - }, - properties: createCollectionProperties, - }, - }, }, }, }, @@ -255,6 +176,18 @@ export const collectionSchema: ISchema = { }, }, column3: { + type: 'void', + 'x-decorator': 'Table.Column.Decorator', + 'x-component': 'Table.Column', + title: '{{t("Collection template")}}', + properties: { + template: { + 'x-component': CollectionTemplate, + 'x-read-pretty': true, + }, + }, + }, + column4: { type: 'void', title: '{{ t("Actions") }}', 'x-component': 'Table.Column', @@ -294,22 +227,10 @@ export const collectionSchema: ISchema = { update: { type: 'void', title: '{{ t("Edit") }}', - 'x-component': 'Action.Link', + 'x-component': 'EditCollection', 'x-component-props': { type: 'primary', }, - properties: { - drawer: { - type: 'void', - 'x-component': 'Action.Drawer', - 'x-decorator': 'Form', - 'x-decorator-props': { - useValues: '{{ cm.useValuesFromRecord }}', - }, - title: '{{ t("Edit collection") }}', - properties: editCollectionProperties, - }, - }, }, delete: { type: 'void', diff --git a/packages/core/client/src/collection-manager/Configuration/templates.tsx b/packages/core/client/src/collection-manager/Configuration/templates.tsx new file mode 100644 index 000000000..7b6b95a62 --- /dev/null +++ b/packages/core/client/src/collection-manager/Configuration/templates.tsx @@ -0,0 +1,23 @@ +import { ISchema } from '@formily/react'; +import * as types from '../templates'; + +export const templates = new Map(); + +const collectionTemplates: any = {}; + +export function registerTemplate(key: string, schema: any) { + collectionTemplates[key] = schema; +} + +Object.keys(types).forEach((type) => { + const schema = types[type]; + registerTemplate(schema.name || 'others', { order: 0, ...schema }); +}); +export const templateOptions = () => + Object.keys(collectionTemplates) + .sort((a, b) => { + return collectionTemplates[a].order - collectionTemplates[b].order; + }) + .map((templace) => { + return collectionTemplates[templace]; + }); diff --git a/packages/core/client/src/collection-manager/action-hooks.ts b/packages/core/client/src/collection-manager/action-hooks.ts index ecfaef4ed..a9ca6c221 100644 --- a/packages/core/client/src/collection-manager/action-hooks.ts +++ b/packages/core/client/src/collection-manager/action-hooks.ts @@ -259,39 +259,6 @@ export const useBulkDestroyAction = () => { }; }; -export const useUpdateCollectionActionAndRefreshCM = (options) => { - const { refreshCM } = useCollectionManager(); - const form = useForm(); - const ctx = useActionContext(); - const { refresh } = useResourceActionContext(); - const { resource, targetKey } = useResourceContext(); - const { [targetKey]: filterByTk } = useRecord(); - return { - async run() { - await form.submit(); - await resource.update({ filterByTk, values: omit(form.values, ['fields']) }); - ctx.setVisible(false); - await form.reset(); - refresh(); - await refreshCM(); - }, - }; -}; - -export const useValuesFromRecord = (options) => { - const record = useRecord(); - const result = useRequest(() => Promise.resolve({ data: record }), { - ...options, - manual: true, - }); - const ctx = useActionContext(); - useEffect(() => { - if (ctx.visible) { - result.run(); - } - }, [ctx.visible]); - return result; -}; export const useValuesFromRA = (options) => { const ctx = useResourceActionContext(); diff --git a/packages/core/client/src/collection-manager/hooks/useCollectionManager.ts b/packages/core/client/src/collection-manager/hooks/useCollectionManager.ts index 823a1ef2a..694538c25 100644 --- a/packages/core/client/src/collection-manager/hooks/useCollectionManager.ts +++ b/packages/core/client/src/collection-manager/hooks/useCollectionManager.ts @@ -1,11 +1,11 @@ import { clone } from '@formily/shared'; +import { reduce, unionBy, uniq } from 'lodash'; import { useContext } from 'react'; -import { reduce, unionBy,uniq } from 'lodash'; import { CollectionManagerContext } from '../context'; import { CollectionFieldOptions } from '../types'; export const useCollectionManager = () => { - const { refreshCM, service, interfaces, collections } = useContext(CollectionManagerContext); + const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext); const getInheritedFields = (name) => { const inheritKeys = getInheritCollections(name); const inheritedFields = reduce( @@ -22,7 +22,7 @@ export const useCollectionManager = () => { const getCollectionFields = (name: string): CollectionFieldOptions[] => { const currentFields = collections?.find((collection) => collection.name === name)?.fields; const inheritedFields = getInheritedFields(name); - const totalFields = unionBy(currentFields?.concat(inheritedFields) || [], 'name').filter((v:any) => { + const totalFields = unionBy(currentFields?.concat(inheritedFields) || [], 'name').filter((v: any) => { return !v.isForeignKey; }); return totalFields; @@ -122,6 +122,9 @@ export const useCollectionManager = () => { getInterface(name: string) { return interfaces[name] ? clone(interfaces[name]) : null; }, + getTemplate(name: string = 'general') { + return templates[name] ? clone(templates[name] || templates['general']) : null; + }, getParentCollectionFields: (parentCollection, currentCollection) => { const currentFields = collections?.find((collection) => collection.name === currentCollection)?.fields; const parentFields = collections?.find((collection) => collection.name === parentCollection)?.fields; diff --git a/packages/core/client/src/collection-manager/index.tsx b/packages/core/client/src/collection-manager/index.tsx index 37ec7fd6c..f73433e2a 100644 --- a/packages/core/client/src/collection-manager/index.tsx +++ b/packages/core/client/src/collection-manager/index.tsx @@ -12,5 +12,7 @@ export * from './hooks'; export * as interfacesProperties from './interfaces/properties'; export * from './interfaces/types'; export * from './ResourceActionProvider'; +export { getConfigurableProperties } from './templates/properties'; +export * from './templates/types'; export * from './types'; diff --git a/packages/core/client/src/collection-manager/interfaces/properties/index.ts b/packages/core/client/src/collection-manager/interfaces/properties/index.ts index dbc2722d4..00a2ed39a 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/index.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/index.ts @@ -60,6 +60,7 @@ export const constraintsProps = { title: '{{t("ON DELETE")}}', required: true, default: 'SET NULL', + 'x-disabled': '{{ !createOnly }}', 'x-decorator': 'FormItem', 'x-component': 'Select', enum: [ diff --git a/packages/core/client/src/collection-manager/templates/calendar.tsx b/packages/core/client/src/collection-manager/templates/calendar.tsx new file mode 100644 index 000000000..8ae68baa4 --- /dev/null +++ b/packages/core/client/src/collection-manager/templates/calendar.tsx @@ -0,0 +1,37 @@ +import { getConfigurableProperties } from './properties'; +import { ICollectionTemplate } from './types'; + +export const calendar: ICollectionTemplate = { + name: 'calendar', + title: '{{t("Calendar collection")}}', + order: 2, + color: 'orange', + default: { + fields: [ + { + name: 'cron', + type: 'string', + uiSchema: { + type: 'string', + title: '{{t("Cron")}}', + 'x-component': 'Select', + enum: [ + { value: '0 0 * * *', label: '每天' }, + { value: '0 0 ? * 1', label: '每个星期一' }, + { value: '0 0 12 * * ?', label: '每天中午12点' }, + { value: '0 0 10,14,16 * * ?', label: '每天上午10点,下午2点,4点 ' }, + ], + }, + interface: 'select', + }, + { + name: 'exclude', + type: 'json', + }, + ], + }, + availableFieldInterfaces: { + include: [], + }, + configurableProperties: getConfigurableProperties('title', 'name', 'inherits'), +}; diff --git a/packages/core/client/src/collection-manager/templates/general.tsx b/packages/core/client/src/collection-manager/templates/general.tsx new file mode 100644 index 000000000..805028460 --- /dev/null +++ b/packages/core/client/src/collection-manager/templates/general.tsx @@ -0,0 +1,23 @@ +import { getConfigurableProperties } from './properties'; +import { ICollectionTemplate } from './types'; + +export const general: ICollectionTemplate = { + name: 'general', + title: '{{t("General collection")}}', + order: 1, + color: 'blue', + default: { + fields: [], + }, + configurableProperties: getConfigurableProperties( + 'title', + 'name', + 'inherits', + 'autoGenId', + 'createdAt', + 'updatedAt', + 'createdBy', + 'updatedBy', + 'sortable', + ), +}; diff --git a/packages/core/client/src/collection-manager/templates/index.tsx b/packages/core/client/src/collection-manager/templates/index.tsx new file mode 100644 index 000000000..09afc72d4 --- /dev/null +++ b/packages/core/client/src/collection-manager/templates/index.tsx @@ -0,0 +1,3 @@ +export * from './calendar'; +export * from './general'; + diff --git a/packages/core/client/src/collection-manager/templates/properties/index.ts b/packages/core/client/src/collection-manager/templates/properties/index.ts new file mode 100644 index 000000000..81b838a7d --- /dev/null +++ b/packages/core/client/src/collection-manager/templates/properties/index.ts @@ -0,0 +1,121 @@ +export const defaultConfigurableProperties = { + title: { + type: 'string', + title: '{{ t("Collection display name") }}', + required: true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + name: { + type: 'string', + title: '{{t("Collection name")}}', + required: true, + 'x-disabled': '{{ !createOnly }}', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + 'x-validator': 'uid', + description: + "{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}", + }, + inherits: { + title: '{{t("Inherits")}}', + type: 'hasMany', + name: 'inherits', + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + mode: 'multiple', + }, + 'x-disabled': '{{ !createOnly }}', + 'x-visible': '{{ enableInherits}}', + 'x-reactions': ['{{useAsyncDataSource(loadCollections)}}'], + }, + autoGenId: { + type: 'boolean', + 'x-content': '{{t("AutoGenId")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + 'x-reactions': [ + { + target: 'sortable', + when: '{{createOnly&&!$self.value}}', + fulfill: { + state: { + value: false, + }, + schema: { + 'x-disabled': true, + }, + }, + otherwise: { + schema: { + 'x-disabled': '{{!createOnly}}', + }, + }, + }, + ], + }, + createdBy: { + type: 'boolean', + 'x-content': '{{t("CreatedBy")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + }, + updatedBy: { + type: 'boolean', + 'x-content': '{{t("UpdatedBy")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + }, + createdAt: { + type: 'boolean', + 'x-content': '{{t("CreatedAt")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + }, + updatedAt: { + type: 'boolean', + 'x-content': '{{t("UpdatedAt")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + }, + sortable: { + type: 'boolean', + 'x-content': '{{t("Records can be sorted")}}', + default: true, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-disabled': '{{ !createOnly }}', + }, +}; + +export type DefaultConfigurableKeys = + | 'name' + | 'title' + | 'inherits' + | 'autoGenId' + | 'createdBy' + | 'updatedBy' + | 'createdAt' + | 'updatedAt' + | 'sortable'; + +export const getConfigurableProperties = (...keys: DefaultConfigurableKeys[]) => { + const props = {}; + for (const key of keys) { + if (defaultConfigurableProperties[key]) { + props[key] = defaultConfigurableProperties[key]; + } + } + return props; +}; diff --git a/packages/core/client/src/collection-manager/templates/treeCollection.tsx b/packages/core/client/src/collection-manager/templates/treeCollection.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/client/src/collection-manager/templates/types.ts b/packages/core/client/src/collection-manager/templates/types.ts new file mode 100644 index 000000000..b1a858fe3 --- /dev/null +++ b/packages/core/client/src/collection-manager/templates/types.ts @@ -0,0 +1,52 @@ +import { ISchema } from '@formily/react'; +import { FieldOptions } from '../../collection-manager/types'; + + + +export interface ICollectionTemplate { + name: string; + title?: string; + color?:string; + /** 排序 */ + order?: number; + /** 默认配置 */ + default?: CollectionOptions; + /** UI 可配置的 CollectionOptions 参数(添加或编辑的 Collection 表单的字段) */ + configurableProperties?: Record; + /** 当前模板可用的字段类型 */ + availableFieldInterfaces?: AvailableFieldInterfacesInclude | AvailableFieldInterfacesExclude; +} + +interface AvailableFieldInterfacesInclude { + include?: any[]; +} + +interface AvailableFieldInterfacesExclude { + exclude?: any[]; +} + +interface CollectionOptions { + /** + * 自动生成 id + * @default true + * */ + autoGenId?: boolean; + /** 创建人 */ + createdBy?: boolean; + /** 最后更新人 */ + updatedBy?: boolean; + /** 创建日期 */ + createdAt?: boolean; + /** 更新日期 */ + updatedAt?: boolean; + /** 可排序 */ + sortable?: boolean; + /* 树结构 */ + tree?: string; + /* 日志 */ + logging?: boolean; + /** 继承 */ + inherits?: string | string[]; + /* 字段列表 */ + fields?: FieldOptions[]; +} diff --git a/packages/core/client/src/collection-manager/types.ts b/packages/core/client/src/collection-manager/types.ts index 0925db7cf..5719f5660 100644 --- a/packages/core/client/src/collection-manager/types.ts +++ b/packages/core/client/src/collection-manager/types.ts @@ -4,6 +4,7 @@ export interface CollectionManagerOptions { service?: any; interfaces?: any; collections?: any[]; + templates?: any; refreshCM?: () => Promise; } diff --git a/packages/core/client/src/locale/en_US.ts b/packages/core/client/src/locale/en_US.ts index 7cb0482f1..916632e51 100644 --- a/packages/core/client/src/locale/en_US.ts +++ b/packages/core/client/src/locale/en_US.ts @@ -114,6 +114,15 @@ export default { "Collection display name": "Collection display name", "Collection name": "Collection name", "Inherits":"Inherits", + "AutoGenId":"Auto-generated ID field", + "CreatedBy":"Recording a row's created user", + "UpdatedBy":"Recording a row's last updated user", + "CreatedAt":"Recording a row's created time ", + "UpdatedAt":"Recording a row's last updated user", + "Records can be sorted": "Records can be sorted", + "Collection template":"Collection template", + "Calendar collection":"Calendar collection", + "General collection":"General collection", "Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.": "Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.", "Storage type": "Storage type", "Edit": "Edit", diff --git a/packages/core/client/src/locale/ja_JP.ts b/packages/core/client/src/locale/ja_JP.ts index 7604e0a55..4b9517ca9 100644 --- a/packages/core/client/src/locale/ja_JP.ts +++ b/packages/core/client/src/locale/ja_JP.ts @@ -107,6 +107,16 @@ export default { "Create collection": "コレクションの作成", "Collection display name": "コレクション名", "Collection name": "コレクション識別子", + "Inherits":"継承", + "AutoGenId":"IDフィールドの自動生成", + "CreatedBy":"レコード作成者", + "UpdatedBy":"レコード最終更新者", + "CreatedAt":"レコード作成時間", + "UpdatedAt":"レコード最終更新時間", + "Records can be sorted":"ソート可能", + "Collection template":"データテーブルテンプレート", + "Calendar collection":"カレンダデータテーブル", + "General collection":"一般データテーブル", "Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.": "ランダムに生成され、変更可能です。 アルファベット、数字、アンダースコアをサポートし、アルファベットから始まる必要があります。", "Storage type": "ストレージタイプ", "Edit": "編集", @@ -503,6 +513,5 @@ export default { "Format": "フォーマット", "Error message": "エラーメッセージ", "Record picker": "レコードピッカー", - "Sortable": "ソート可能", "Search and select collection": "Search and select collection", } diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts index 12aac6676..ce6798f4b 100644 --- a/packages/core/client/src/locale/zh_CN.ts +++ b/packages/core/client/src/locale/zh_CN.ts @@ -118,6 +118,15 @@ export default { "Collection display name": "数据表名称", "Collection name": "数据表标识", "Inherits":"继承", + "AutoGenId":"自动生成 ID 字段", + "CreatedBy":"记录创建人", + "UpdatedBy":"记录最后更新人", + "CreatedAt":"记录创建时间", + "UpdatedAt":"记录最后更新时间", + "Records can be sorted":"可以对行记录进行排序", + "Collection template":"数据表模板", + "Calendar collection":"日历数据表", + "General collection":"普通数据表", "Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.": "随机生成,可修改。支持英文、数字和下划线,必须以英文字母开头。", "Storage type": "存储类型", "Edit": "编辑", @@ -228,7 +237,6 @@ export default { "Editable": "可编辑", "Readonly": "只读(禁止编辑)", "Easy-reading": "只读(阅读模式)", - "Sortable": "可排序", "Add filter": "添加筛选条件", "Add filter group": "添加筛选分组", "Comparision": "值比较", diff --git a/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx b/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx index 705adac2c..4c0aef8f7 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/TableBlockDesigner.tsx @@ -37,7 +37,7 @@ export const TableBlockDesigner = () => { return ( - { @@ -51,7 +51,7 @@ export const TableBlockDesigner = () => { }, }); }} - /> + />} { const b = !value || selected.includes(item.name); if (item.inherit) { return false; + } else if (!item.fields.find((v) => v.primaryKey)) { + return false; } else { return b && !(item?.isThrough && item?.autoCreate); } diff --git a/packages/core/create-nocobase-app/src/generator.js b/packages/core/create-nocobase-app/src/generator.js index cb6fb67eb..adc4f3d97 100644 --- a/packages/core/create-nocobase-app/src/generator.js +++ b/packages/core/create-nocobase-app/src/generator.js @@ -105,6 +105,15 @@ class AppGenerator extends Generator { break; } + const keys = ['PLUGIN_PACKAGE_PREFIX', 'DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD', 'DB_STORAGE']; + + for (const key in env) { + if (keys.includes(key)) { + continue; + } + envs.push(`${key}=${env[key]}`); + } + return { ...this.context, dependencies: dependencies.join(`,\n `), diff --git a/packages/presets/nocobase/src/index.ts b/packages/presets/nocobase/src/index.ts index 8d63e6ade..a99fb9fd9 100644 --- a/packages/presets/nocobase/src/index.ts +++ b/packages/presets/nocobase/src/index.ts @@ -3,23 +3,25 @@ import path from 'path'; export class PresetNocoBase extends Plugin { async addBuiltInPlugins() { - const plugins = [ - 'error-handler', - 'collection-manager', - 'ui-schema-storage', - 'ui-routes-storage', - 'file-manager', - 'system-settings', - 'verification', - 'users', - 'acl', - 'china-region', - 'workflow', - 'client', - 'export', - 'import', - 'audit-logs', - ]; + const plugins = process.env.PRESET_NOCOBASE_PLUGINS + ? process.env.PRESET_NOCOBASE_PLUGINS.split(',') + : [ + 'error-handler', + 'collection-manager', + 'ui-schema-storage', + 'ui-routes-storage', + 'file-manager', + 'system-settings', + 'verification', + 'users', + 'acl', + 'china-region', + 'workflow', + 'client', + 'export', + 'import', + 'audit-logs', + ]; await this.app.pm.add(plugins, { enabled: true, builtIn: true, diff --git a/packages/pro-plugins b/packages/pro-plugins index 5a5fba41a..67f51c7fd 160000 --- a/packages/pro-plugins +++ b/packages/pro-plugins @@ -1 +1 @@ -Subproject commit 5a5fba41ae1cecaf3b073fcb610e9dc68dad0b85 +Subproject commit 67f51c7fdd132ab12821f31f737b6f0cfb70d212 diff --git a/packages/samples/custom-collection-template/README.md b/packages/samples/custom-collection-template/README.md new file mode 100644 index 000000000..3a67dc5a4 --- /dev/null +++ b/packages/samples/custom-collection-template/README.md @@ -0,0 +1,28 @@ +# custom-collection-template + +## Register + +```ts +yarn pm add sample-custom-collection-template +``` + +## Activate + +```bash +yarn pm enable sample-custom-collection-template +``` + +## Launch the app + +```bash +# for development +yarn dev + +# for production +yarn build +yarn start +``` + +## Demo + +[gif] diff --git a/packages/samples/custom-collection-template/client.d.ts b/packages/samples/custom-collection-template/client.d.ts new file mode 100755 index 000000000..765db9222 --- /dev/null +++ b/packages/samples/custom-collection-template/client.d.ts @@ -0,0 +1,4 @@ +// @ts-nocheck +export * from './lib/client'; +export { default } from './lib/client'; + diff --git a/packages/samples/custom-collection-template/client.js b/packages/samples/custom-collection-template/client.js new file mode 100755 index 000000000..238820257 --- /dev/null +++ b/packages/samples/custom-collection-template/client.js @@ -0,0 +1,30 @@ +"use strict"; + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +var _index = _interopRequireWildcard(require("./lib/client")); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var _exportNames = {}; +Object.defineProperty(exports, "default", { + enumerable: true, + get: function get() { + return _index.default; + } +}); + +Object.keys(_index).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; + if (key in exports && exports[key] === _index[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function get() { + return _index[key]; + } + }); +}); diff --git a/packages/samples/custom-collection-template/package.json b/packages/samples/custom-collection-template/package.json new file mode 100644 index 000000000..a98409c41 --- /dev/null +++ b/packages/samples/custom-collection-template/package.json @@ -0,0 +1,11 @@ +{ + "name": "@nocobase/plugin-sample-custom-collection-template", + "version": "0.8.0-alpha.13", + "main": "lib/server/index.js", + "devDependencies": { + "@nocobase/client": "0.8.0-alpha.13", + "@nocobase/server": "0.8.0-alpha.13", + "@nocobase/test": "0.8.0-alpha.13" + }, + "gitHead": "ce588eefb0bfc50f7d5bbee575e0b5e843bf6644" +} diff --git a/packages/samples/custom-collection-template/src/client/index.tsx b/packages/samples/custom-collection-template/src/client/index.tsx new file mode 100644 index 000000000..74834215a --- /dev/null +++ b/packages/samples/custom-collection-template/src/client/index.tsx @@ -0,0 +1,31 @@ +import { getConfigurableProperties, ICollectionTemplate, registerTemplate } from '@nocobase/client'; +import React from 'react'; + +const myCollectionTemplate: ICollectionTemplate = { + name: 'myCollection', + title: '{{t("Custom template")}}', + order: 6, + color: 'blue', + default: { + fields: [ + { + name: 'uuid', + type: 'string', + primaryKey: true, + allowNull: false, + uiSchema: { type: 'number', title: '{{t("UUID")}}', 'x-component': 'Input', 'x-read-pretty': true }, + interface: 'input', + }, + ], + }, + configurableProperties: getConfigurableProperties('name', 'title', 'inherits', 'createdAt', 'updatedAt'), + availableFieldInterfaces: { + exclude: ['linkTo', 'o2o'], + }, +}; + +registerTemplate('myCollection', myCollectionTemplate); + +export default React.memo((props) => { + return <>{props.children}; +}); diff --git a/packages/samples/custom-collection-template/src/index.ts b/packages/samples/custom-collection-template/src/index.ts new file mode 100644 index 000000000..7ddad5814 --- /dev/null +++ b/packages/samples/custom-collection-template/src/index.ts @@ -0,0 +1 @@ +export { default } from './server'; diff --git a/packages/samples/custom-collection-template/src/server/index.ts b/packages/samples/custom-collection-template/src/server/index.ts new file mode 100644 index 000000000..75c6b5349 --- /dev/null +++ b/packages/samples/custom-collection-template/src/server/index.ts @@ -0,0 +1,16 @@ +import { InstallOptions, Plugin } from '@nocobase/server'; + +export class CustomCollectionTemplatePlugin extends Plugin { + + afterAdd() {} + + beforeLoad() {} + + async load() {} + + async install(options: InstallOptions) { + // TODO + } +} + +export default CustomCollectionTemplatePlugin; diff --git a/yarn.lock b/yarn.lock index cdd33bb78..1f7c9cdbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -329,23 +329,6 @@ csstype "^3.0.8" tslib "^2.0.3" -"@antv/x6-react-shape@^1.6.2": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@antv/x6-react-shape/-/x6-react-shape-1.6.3.tgz#053660bd555c4356e9719134e3e2a857b51301e2" - integrity sha512-iCsRVkvKCVs7HUUblF/ALg5XxeeEPXDLHuYzRvbvNlCKKLbXLbaz6vJQSgTTrk2i4lrD+7xl4riA76FyWA3k/g== - -"@antv/x6@^1.34.2": - version "1.34.6" - resolved "https://registry.yarnpkg.com/@antv/x6/-/x6-1.34.6.tgz#b802b09b52c8620acf01734397714ea5e3f715b6" - integrity sha512-cl8ywN7CTNVT94/ZEbq2mnZQTQe6FUgIlGOAjNOPL8sljZTBrpE6OsuGw4BDA2/XllwELfUfvVpgBvbdwRKrxw== - dependencies: - csstype "^3.0.3" - jquery "^3.5.1" - jquery-mousewheel "^3.1.13" - lodash-es "^4.17.15" - mousetrap "^1.6.5" - utility-types "^3.10.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0": version "7.16.0" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -2969,15 +2952,6 @@ resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@dnd-kit/accessibility@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.0.tgz#b56e3750414fd907b7d6972b3116aa8f96d07fde" @@ -4383,153 +4357,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nocobase/acl@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/acl/-/acl-0.8.0-alpha.9.tgz#7ffab4a367befc74daa681b1d5c2c907126cb280" - integrity sha512-jEgpjO6gGi4GwSyWxlmcwd368/AQgmrdsODDTLhhrJJI9+ovj2mxO664lQgh3FkDGtmduKlBiZ+qcgK+/RWa1w== - dependencies: - "@nocobase/resourcer" "0.8.0-alpha.9" - json-templates "^4.2.0" - -"@nocobase/actions@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/actions/-/actions-0.8.0-alpha.9.tgz#3804bbf30f7c4e8aa205b9802ec4f56a58c18c72" - integrity sha512-EvdNjXa+TTMU2Xn+MnA7tSa5rooGFtzsvJsSYbkpODtsVQsmTDRIZYZsOYFk/XcezxjzTTsxxXMSA7sLq6wifQ== - dependencies: - "@nocobase/cache" "0.8.0-alpha.9" - "@nocobase/database" "0.8.0-alpha.9" - "@nocobase/resourcer" "0.8.0-alpha.9" - -"@nocobase/cache@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/cache/-/cache-0.8.0-alpha.9.tgz#99f7ca196dacd41b69ed7b7f179fcc1c4c5baa27" - integrity sha512-GCi7OE9nLrXKq/Og6SPtPcjtreUxxAL9x7CwmD2x3Zb7gNTc2GvQ9NWoAlO8KTLlWuzuNcJXegBhxaxV8Lt5ew== - dependencies: - cache-manager "^4.1.0" - -"@nocobase/client@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/client/-/client-0.8.0-alpha.9.tgz#03446b4c4160a7ebda9dfd8aa80e2203541eb72c" - integrity sha512-cvh01k8Ujhc9D7kDYQ7QKP5tnLF+wBOqixtisivti9aeklMBaOKj1oLmxKHZlB2IJzKpT+rUntpsxGxUS6co9A== - dependencies: - "@antv/g2plot" "^2.4.18" - "@dnd-kit/core" "^5.0.1" - "@dnd-kit/sortable" "^6.0.0" - "@emotion/css" "^11.7.1" - "@formily/antd" "2.0.20" - "@formily/core" "2.0.20" - "@formily/react" "2.0.20" - "@nocobase/sdk" "0.8.0-alpha.9" - "@nocobase/utils" "0.8.0-alpha.9" - ahooks "^3.0.5" - antd "~4.19.5" - axios "^0.26.1" - classnames "^2.3.1" - cron-parser "^4.6.0" - cronstrue "^2.11.0" - file-saver "^2.0.5" - i18next "^21.6.0" - json-templates "^4.2.0" - marked "^4.0.12" - mathjs "^10.6.0" - react-beautiful-dnd "^13.1.0" - react-big-calendar "^0.38.7" - react-contenteditable "^3.3.6" - react-drag-listview "^0.1.9" - react-helmet "^6.1.0" - react-hotkeys-hook "^3.4.7" - react-i18next "^11.15.1" - react-image-lightbox "^5.1.4" - react-js-cron "^1.4.0" - react-quill "^1.3.5" - react-router-dom "^5.2.0" - react-to-print "^2.14.7" - solarlunar-es "^1.0.9" - use-deep-compare-effect "^1.8.1" - -"@nocobase/database@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/database/-/database-0.8.0-alpha.9.tgz#9684b4ddcfc5396df2ef3d4ae9cd2ce59142df81" - integrity sha512-zkLTW2iFg1tlbSTlW40oEmM7O2GxHXmIzErnDR4it0GWoLqjDgzNHP5QjODOxiiB/+/unNFudfGTNCoXxu2ZvA== - dependencies: - "@nocobase/utils" "0.8.0-alpha.9" - async-mutex "^0.3.2" - cron-parser "4.4.0" - deepmerge "^4.2.2" - flat "^5.0.2" - glob "^7.1.6" - mathjs "^10.6.1" - moment "2.x" - semver "^7.3.7" - sequelize "^6.9.0" - umzug "^3.1.1" - -"@nocobase/resourcer@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/resourcer/-/resourcer-0.8.0-alpha.9.tgz#5415af5944b31293e5e2da73bf5c999866a90fab" - integrity sha512-6v9bfUex2RfT8C3xX+SUhj8/zECCY6pQA3ce8GV3xd9cu6ku8c7hIiNM3KwgPOD189aND3FxNwxqpECNaoWQ1A== - dependencies: - "@nocobase/utils" "0.8.0-alpha.9" - deepmerge "^4.2.2" - koa-compose "^4.1.0" - lodash "^4.17.21" - path-to-regexp "^6.1.0" - qs "^6.9.4" - -"@nocobase/sdk@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/sdk/-/sdk-0.8.0-alpha.9.tgz#5306e7bf4aabf00894a65036c584508d93bda379" - integrity sha512-v+GF4N5ldYwmoqQIukGWzz/eOm2TgxcUZq3uE+YeP/aiDa4XAfkLrpBNsvCS24k3s/U+84FYrvTWRRStJGSzBA== - dependencies: - axios "^0.26.1" - qs "^6.10.1" - -"@nocobase/server@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/server/-/server-0.8.0-alpha.9.tgz#bc1955e41b2ac9f6640fdf109516376604d4092c" - integrity sha512-/mHk2Uc8RS/3GcGvnMnFKlbf2aCufGuc+GOr5gEKuyWa50ibD4nZ7LKJx023uEO07rxWkFkhR6+ExuZ+BGRDGQ== - dependencies: - "@hapi/topo" "^6.0.0" - "@koa/cors" "^3.1.0" - "@koa/router" "^9.4.0" - "@nocobase/acl" "0.8.0-alpha.9" - "@nocobase/actions" "0.8.0-alpha.9" - "@nocobase/database" "0.8.0-alpha.9" - "@nocobase/resourcer" "0.8.0-alpha.9" - chalk "^4.1.1" - commander "^9.2.0" - find-package-json "^1.2.0" - i18next "^21.6.0" - koa "^2.13.4" - koa-bodyparser "^4.3.0" - koa-static "^5.0.0" - lodash "^4.17.21" - semver "^7.3.7" - xpipe "^1.0.5" - -"@nocobase/test@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/test/-/test-0.8.0-alpha.9.tgz#40aaed50af9b8fd23c0666156c806273c75b0a3f" - integrity sha512-bpzcogPKzfq8mjIsLDy0xlsd0dNAUgmju9kk+RPV5wNZfhvofaA7YduexawbL+dLJQC+iyjmhAhARxl8rVhKuA== - dependencies: - "@nocobase/server" "0.8.0-alpha.9" - "@types/supertest" "^2.0.11" - mockjs "^1.1.0" - mysql2 "^2.3.3" - pg "^8.7.3" - pg-hstore "^2.3.4" - sqlite3 "^5.0.8" - supertest "^6.1.6" - -"@nocobase/utils@0.8.0-alpha.9": - version "0.8.0-alpha.9" - resolved "https://registry.yarnpkg.com/@nocobase/utils/-/utils-0.8.0-alpha.9.tgz#fa7e203412e91ab7a6ebb12a6d53d83e09465591" - integrity sha512-oEqKs4+0sO4x0TFEtf+Tf1nIkb9q+9y9YmrqOn6MyMo/ejTSh6nlNt1rAYgs02gTP4uHkSOb2/4K4oDm0JXLdg== - dependencies: - "@hapi/topo" "^6.0.0" - deepmerge "^4.2.2" - flat-to-nested "^1.1.1" - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -6949,11 +6776,6 @@ async@^2.6.3, async@~2.6.1: dependencies: lodash "^4.17.14" -async@^3.2.3: - version "3.2.4" - resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -8334,7 +8156,7 @@ color-support@^1.1.2, color-support@^1.1.3: resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^3.0.0, color@^3.1.3: +color@^3.0.0: version "3.2.1" resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== @@ -8357,14 +8179,6 @@ colors@~1.2.1: resolved "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - columnify@^1.5.4: version "1.5.4" resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -9268,11 +9082,6 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== -csstype@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" - integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== - csstype@^3.0.8: version "3.1.0" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" @@ -9322,14 +9131,6 @@ d3-timer@^1.0.9: resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== -dagre@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" - integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== - dependencies: - graphlib "^2.1.8" - lodash "^4.17.15" - dargs@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -10049,11 +9850,6 @@ emojis-list@^3.0.0: resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - encodeurl@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -10942,7 +10738,7 @@ fclone@1.0.11, fclone@~1.0.11: resolved "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" integrity sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA= -fecha@^4.2.0, fecha@~4.2.0: +fecha@~4.2.0: version "4.2.3" resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== @@ -10973,13 +10769,6 @@ file-saver@^2.0.5: resolved "https://registry.npmmirror.com/file-saver/download/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38" integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== -file-stream-rotator@^0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz#007019e735b262bb6c6f0197e58e5c87cb96cec3" - integrity sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ== - dependencies: - moment "^2.29.1" - file-type@^3.3.0: version "3.9.0" resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" @@ -11158,11 +10947,6 @@ fmin@^0.0.2: tape "^4.5.1" uglify-js "^2.6.2" -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - follow-redirects@^1.14.0, follow-redirects@^1.14.8: version "1.14.9" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" @@ -11794,13 +11578,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= -graphlib@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" - growly@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -14197,16 +13974,6 @@ jmespath@0.15.0: resolved "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= -jquery-mousewheel@^3.1.13: - version "3.1.13" - resolved "https://registry.yarnpkg.com/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz#06f0335f16e353a695e7206bf50503cb523a6ee5" - integrity sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg== - -jquery@^3.5.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.1.tgz#fab0408f8b45fc19f956205773b62b292c147a16" - integrity sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw== - js-base64@^2.5.2: version "2.6.4" resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -14674,11 +14441,6 @@ koa@^2.13.4: type-is "^1.6.16" vary "^1.1.2" -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - latest-version@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" @@ -14919,7 +14681,7 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash-es@^4.17.11, lodash-es@^4.17.15: +lodash-es@^4.17.11: version "4.17.21" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== @@ -15086,17 +14848,6 @@ log-driver@^1.2.7: resolved "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== -logform@^2.3.2, logform@^2.4.0: - version "2.4.2" - resolved "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c" - integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw== - dependencies: - "@colors/colors" "1.5.0" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - long@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -15989,7 +15740,7 @@ moment-timezone@^0.5.31: dependencies: moment ">= 2.9.0" -moment@2.x, moment@^2.29.1: +moment@2.x: version "2.29.4" resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== @@ -16004,11 +15755,6 @@ moo@^0.5.0: resolved "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== -mousetrap@^1.6.5: - version "1.6.5" - resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9" - integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA== - mri@^1.1.5: version "1.2.0" resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" @@ -16689,11 +16435,6 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.0.1: - version "2.2.0" - resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" @@ -16814,13 +16555,6 @@ once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: dependencies: wrappy "1" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -19763,7 +19497,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -20644,11 +20378,6 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -safe-stable-stringify@^2.3.1: - version "2.4.1" - resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" - integrity sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA== - "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -21337,11 +21066,6 @@ stable@^0.1.8: resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - stack-utils@^1.0.1: version "1.0.5" resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" @@ -21981,11 +21705,6 @@ text-extensions@^1.0.0: resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -22243,11 +21962,6 @@ trim-newlines@^3.0.0: resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== - trough@^1.0.0: version "1.0.5" resolved "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" @@ -23072,11 +22786,6 @@ util@^0.11.0: dependencies: inherits "2.0.3" -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== - utility@0.1.11: version "0.1.11" resolved "https://registry.npmjs.org/utility/-/utility-0.1.11.tgz#fde60cf9b4e4751947a0cf5d104ce29367226715" @@ -23469,42 +23178,6 @@ window-size@0.1.0: resolved "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= -winston-daily-rotate-file@^4.7.1: - version "4.7.1" - resolved "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz#f60a643af87f8867f23170d8cd87dbe3603a625f" - integrity sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA== - dependencies: - file-stream-rotator "^0.6.1" - object-hash "^2.0.1" - triple-beam "^1.3.0" - winston-transport "^4.4.0" - -winston-transport@^4.4.0, winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - -winston@^3.8.2: - version "3.8.2" - resolved "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" - integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" - wkx@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c"