feat: file storages (#314)
* feat: file storages * feat: storageOptions * feat: radio field * fix: documentRoot
This commit is contained in:
parent
fefa1fc0c9
commit
2fd27ea9f3
@ -14,6 +14,7 @@ import {
|
|||||||
compose,
|
compose,
|
||||||
DesignableSwitch,
|
DesignableSwitch,
|
||||||
DocumentTitleProvider,
|
DocumentTitleProvider,
|
||||||
|
FileStorageShortcut,
|
||||||
i18n,
|
i18n,
|
||||||
MenuItemInitializers,
|
MenuItemInitializers,
|
||||||
PluginManagerProvider,
|
PluginManagerProvider,
|
||||||
@ -85,6 +86,7 @@ const providers = [
|
|||||||
WorkflowShortcut,
|
WorkflowShortcut,
|
||||||
SystemSettingsShortcut,
|
SystemSettingsShortcut,
|
||||||
SchemaTemplateShortcut,
|
SchemaTemplateShortcut,
|
||||||
|
FileStorageShortcut,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import { SettingOutlined } from '@ant-design/icons';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PluginManager } from '..';
|
||||||
|
import { ActionContext, SchemaComponent } from '../schema-component';
|
||||||
|
import { storageSchema } from './schemas/storage';
|
||||||
|
import { StorageOptions } from './StorageOptions';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("File storages")}}',
|
||||||
|
properties: {
|
||||||
|
storageSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FileStorageShortcut = () => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||||
|
<PluginManager.Toolbar.Item
|
||||||
|
eventKey={'FileStorage'}
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
icon={<SettingOutlined />}
|
||||||
|
title={t('File storages')}
|
||||||
|
/>
|
||||||
|
<SchemaComponent components={{ StorageOptions }} schema={schema} />
|
||||||
|
</ActionContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
98
packages/core/client/src/file-manager/StorageOptions.tsx
Normal file
98
packages/core/client/src/file-manager/StorageOptions.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { FormLayout } from '@formily/antd';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { observer, RecursionField, Schema, useField, useForm } from '@formily/react';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
local: {
|
||||||
|
properties: {
|
||||||
|
documentRoot: {
|
||||||
|
title: '{{t("Destination")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'ali-oss': {
|
||||||
|
properties: {
|
||||||
|
region: {
|
||||||
|
title: '{{t("Region")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
accessKeyId: {
|
||||||
|
title: '{{t("AccessKey ID")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
accessKeySecret: {
|
||||||
|
title: '{{t("AccessKey Secret")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
bucket: {
|
||||||
|
title: '{{t("Bucket")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
properties: {
|
||||||
|
region: {
|
||||||
|
title: '{{t("Region")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
accessKeyId: {
|
||||||
|
title: '{{t("AccessKey ID")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
secretAccessKey: {
|
||||||
|
title: '{{t("AccessKey Secret")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
bucket: {
|
||||||
|
title: '{{t("Bucket")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StorageOptions = observer((props) => {
|
||||||
|
const form = useForm();
|
||||||
|
const field = useField<Field>();
|
||||||
|
const [s, setSchema] = useState(new Schema({}));
|
||||||
|
useEffect(() => {
|
||||||
|
form.clearFormGraph('options.*');
|
||||||
|
setSchema(new Schema(schema[form.values.type] || {}));
|
||||||
|
}, [form.values.type]);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<RecursionField key={form.values.type || 'local'} basePath={field.address} onlyRenderProperties schema={s} />
|
||||||
|
</FormLayout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
1
packages/core/client/src/file-manager/index.ts
Normal file
1
packages/core/client/src/file-manager/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './FileStorageShortcut';
|
350
packages/core/client/src/file-manager/schemas/storage.ts
Normal file
350
packages/core/client/src/file-manager/schemas/storage.ts
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { useRequest } from '../../api-client';
|
||||||
|
import { useActionContext } from '../../schema-component';
|
||||||
|
|
||||||
|
const collection = {
|
||||||
|
name: 'storages',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'title',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Storage display name")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Storage name")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'type',
|
||||||
|
interface: 'select',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Storage type")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Select',
|
||||||
|
required: true,
|
||||||
|
enum: [
|
||||||
|
{ label: '{{t("Local storage")}}', value: 'local' },
|
||||||
|
{ label: '{{t("Aliyun OSS")}}', value: 'ali-oss' },
|
||||||
|
{ label: '{{t("Amazon S3")}}', value: 's3' },
|
||||||
|
],
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'baseUrl',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Storage base URL")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
name: 'default',
|
||||||
|
interface: 'boolean',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Default storage")}}',
|
||||||
|
type: 'boolean',
|
||||||
|
'x-component': 'Checkbox',
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const storageSchema: ISchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
block1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'ResourceActionProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection,
|
||||||
|
resourceName: 'storages',
|
||||||
|
request: {
|
||||||
|
resource: 'storages',
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 50,
|
||||||
|
sort: ['id'],
|
||||||
|
appends: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-component': 'CollectionProvider',
|
||||||
|
'x-component-props': {
|
||||||
|
collection,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
delete: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Delete") }}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useBulkDestroyAction }}',
|
||||||
|
confirm: {
|
||||||
|
title: "{{t('Delete storage')}}",
|
||||||
|
content: "{{t('Are you sure you want to delete it?')}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Add storage")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-decorator-props': {
|
||||||
|
useValues(options) {
|
||||||
|
const ctx = useActionContext();
|
||||||
|
return useRequest(
|
||||||
|
() =>
|
||||||
|
Promise.resolve({
|
||||||
|
data: {
|
||||||
|
name: `s_${uid()}`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{ ...options, refreshDeps: [ctx.visible] },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: '{{t("Add storage")}}',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
description:
|
||||||
|
'{{t("Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.")}}',
|
||||||
|
},
|
||||||
|
baseUrl: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'StorageOptions',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
title: '',
|
||||||
|
'x-content': '{{t("Default storage")}}',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer.Footer',
|
||||||
|
properties: {
|
||||||
|
cancel: {
|
||||||
|
title: '{{t("Cancel")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
title: '{{t("Submit")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction: '{{ cm.useCreateAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
type: 'void',
|
||||||
|
'x-uid': 'input',
|
||||||
|
'x-component': 'Table.Void',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
useDataSource: '{{ cm.useDataSourceFromRAC }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'number',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column2: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column3: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'Table.Column.Decorator',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
default: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
column4: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Actions")}}',
|
||||||
|
'x-component': 'Table.Column',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
update: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Edit")}}',
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'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 storage")}}',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-disabled': true,
|
||||||
|
},
|
||||||
|
baseUrl: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-disabled': true,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'StorageOptions',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
title: '',
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-content': '{{t("Default storage")}}',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer.Footer',
|
||||||
|
properties: {
|
||||||
|
cancel: {
|
||||||
|
title: '{{t("Cancel")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
title: '{{t("Submit")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction: '{{ cm.useUpdateAction }}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Delete") }}',
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'x-component-props': {
|
||||||
|
confirm: {
|
||||||
|
title: "{{t('Delete role')}}",
|
||||||
|
content: "{{t('Are you sure you want to delete it?')}}",
|
||||||
|
},
|
||||||
|
useAction: '{{cm.useDestroyAction}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -11,6 +11,7 @@ export * from './board';
|
|||||||
export * from './china-region';
|
export * from './china-region';
|
||||||
export * from './collection-manager';
|
export * from './collection-manager';
|
||||||
export * from './document-title';
|
export * from './document-title';
|
||||||
|
export * from './file-manager';
|
||||||
export * from './i18n';
|
export * from './i18n';
|
||||||
export * from './icon';
|
export * from './icon';
|
||||||
export * from './plugin-manager';
|
export * from './plugin-manager';
|
||||||
|
@ -382,4 +382,12 @@ export default {
|
|||||||
'Action permission': '操作权限',
|
'Action permission': '操作权限',
|
||||||
'Field permission': '字段权限',
|
'Field permission': '字段权限',
|
||||||
'Scope name': '数据范围名称',
|
'Scope name': '数据范围名称',
|
||||||
|
|
||||||
|
'File storages': '文件存储',
|
||||||
|
'Storage display name': '文件存储名称',
|
||||||
|
'Storage name': '文件存储标识',
|
||||||
|
'Default storage': '默认存储',
|
||||||
|
'Add storage': '添加文件存储',
|
||||||
|
'Edit storage': '编辑文件存储',
|
||||||
|
'Storage base URL': 'Base URL',
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ const InternalAdminLayout = (props: any) => {
|
|||||||
{ component: 'WorkflowShortcut', pin: true },
|
{ component: 'WorkflowShortcut', pin: true },
|
||||||
{ component: 'SchemaTemplateShortcut', pin: true },
|
{ component: 'SchemaTemplateShortcut', pin: true },
|
||||||
{ component: 'SystemSettingsShortcut' },
|
{ component: 'SystemSettingsShortcut' },
|
||||||
|
{ component: 'FileStorageShortcut' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</ACLAllowConfigure>
|
</ACLAllowConfigure>
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
RealFieldOptions
|
RealFieldOptions
|
||||||
} from './number-field';
|
} from './number-field';
|
||||||
import { PasswordFieldOptions } from './password-field';
|
import { PasswordFieldOptions } from './password-field';
|
||||||
|
import { RadioFieldOptions } from './radio-field';
|
||||||
import { SortFieldOptions } from './sort-field';
|
import { SortFieldOptions } from './sort-field';
|
||||||
import { StringFieldOptions } from './string-field';
|
import { StringFieldOptions } from './string-field';
|
||||||
import { TextFieldOptions } from './text-field';
|
import { TextFieldOptions } from './text-field';
|
||||||
@ -35,6 +36,7 @@ export * from './has-one-field';
|
|||||||
export * from './json-field';
|
export * from './json-field';
|
||||||
export * from './number-field';
|
export * from './number-field';
|
||||||
export * from './password-field';
|
export * from './password-field';
|
||||||
|
export * from './radio-field';
|
||||||
export * from './relation-field';
|
export * from './relation-field';
|
||||||
export * from './sort-field';
|
export * from './sort-field';
|
||||||
export * from './string-field';
|
export * from './string-field';
|
||||||
@ -54,6 +56,7 @@ export type FieldOptions =
|
|||||||
| JsonFieldOptions
|
| JsonFieldOptions
|
||||||
| JsonbFieldOptions
|
| JsonbFieldOptions
|
||||||
| BooleanFieldOptions
|
| BooleanFieldOptions
|
||||||
|
| RadioFieldOptions
|
||||||
| SortFieldOptions
|
| SortFieldOptions
|
||||||
| TextFieldOptions
|
| TextFieldOptions
|
||||||
| VirtualFieldOptions
|
| VirtualFieldOptions
|
||||||
|
50
packages/core/database/src/fields/radio-field.ts
Normal file
50
packages/core/database/src/fields/radio-field.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
import { BaseColumnFieldOptions, Field } from './field';
|
||||||
|
|
||||||
|
export interface RadioFieldOptions extends BaseColumnFieldOptions {
|
||||||
|
type: 'radio';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂时只支持全局,不支持批量
|
||||||
|
*/
|
||||||
|
export class RadioField extends Field {
|
||||||
|
get dataType() {
|
||||||
|
return DataTypes.BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const { name } = this.options;
|
||||||
|
this.listener = async (model, { transaction }) => {
|
||||||
|
if (!model.changed(name as any)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = model.get(name) as boolean;
|
||||||
|
if (value) {
|
||||||
|
const M = this.collection.model;
|
||||||
|
await M.update(
|
||||||
|
{ [name]: false },
|
||||||
|
{
|
||||||
|
where: {
|
||||||
|
[name]: true,
|
||||||
|
},
|
||||||
|
transaction,
|
||||||
|
hooks: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bind() {
|
||||||
|
super.bind();
|
||||||
|
this.on('beforeCreate', this.listener.bind(this));
|
||||||
|
this.on('beforeUpdate', this.listener.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
unbind() {
|
||||||
|
super.unbind();
|
||||||
|
this.off('beforeCreate', this.listener.bind(this));
|
||||||
|
this.off('beforeUpdate', this.listener.bind(this));
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,7 @@ export default {
|
|||||||
// TODO(feature): 需要使用一个实现了可设置默认值的字段
|
// TODO(feature): 需要使用一个实现了可设置默认值的字段
|
||||||
{
|
{
|
||||||
comment: '默认引擎',
|
comment: '默认引擎',
|
||||||
type: 'boolean',
|
type: 'radio',
|
||||||
name: 'default',
|
name: 'default',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
|
@ -82,11 +82,12 @@ async function middleware(app: Application, options?) {
|
|||||||
|
|
||||||
// 以下情况才认为当前进程所应该提供静态服务
|
// 以下情况才认为当前进程所应该提供静态服务
|
||||||
// 否则都忽略,交给其他 server 来提供(如 nginx/cdn 等)
|
// 否则都忽略,交给其他 server 来提供(如 nginx/cdn 等)
|
||||||
if (url.origin && url.origin !== LOCALHOST) {
|
if (url.origin && storage?.options?.serve === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const basePath = url.pathname.startsWith('/') ? url.pathname : `/${url.pathname}`;
|
const basePath = url.pathname.startsWith('/') ? url.pathname : `/${url.pathname}`;
|
||||||
|
|
||||||
if (!match(basePath, ctx.path)) {
|
if (!match(basePath, ctx.path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user