lots of updates
This commit is contained in:
parent
c23d2c5c3c
commit
5b014bc67e
@ -21,6 +21,7 @@ import * as uiSchema from './ui-schema';
|
|||||||
const Collection = database.getModel('collections');
|
const Collection = database.getModel('collections');
|
||||||
const collection = await Collection.create(config);
|
const collection = await Collection.create(config);
|
||||||
await collection.updateAssociations(config);
|
await collection.updateAssociations(config);
|
||||||
|
await collection.migrate();
|
||||||
|
|
||||||
const Route = database.getModel('routes');
|
const Route = database.getModel('routes');
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"beautiful-react-hooks": "^0.35.0",
|
"beautiful-react-hooks": "^0.35.0",
|
||||||
"constate": "^3.3.0",
|
"constate": "^3.3.0",
|
||||||
|
"flat": "^5.0.2",
|
||||||
"html-react-parser": "^1.2.7",
|
"html-react-parser": "^1.2.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"monaco-editor": "^0.25.2",
|
"monaco-editor": "^0.25.2",
|
||||||
|
@ -19,7 +19,7 @@ import { useMemo } from 'react';
|
|||||||
import { CodeOutlined } from '@ant-design/icons';
|
import { CodeOutlined } from '@ant-design/icons';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
|
|
||||||
import { ArrayCollapse, FormLayout, FormItem as FormilyFormItem } from '@formily/antd';
|
import { ArrayItems, ArrayCollapse, FormLayout, FormItem as FormilyFormItem } from '@formily/antd';
|
||||||
|
|
||||||
import { Space, Card, Modal, Spin } from 'antd';
|
import { Space, Card, Modal, Spin } from 'antd';
|
||||||
import { ArrayTable } from '../../schemas/array-table';
|
import { ArrayTable } from '../../schemas/array-table';
|
||||||
@ -74,6 +74,7 @@ export const SchemaField = createSchemaField({
|
|||||||
Page,
|
Page,
|
||||||
Chart,
|
Chart,
|
||||||
|
|
||||||
|
ArrayItems,
|
||||||
ArrayCollapse,
|
ArrayCollapse,
|
||||||
ArrayTable,
|
ArrayTable,
|
||||||
FormLayout,
|
FormLayout,
|
||||||
@ -365,6 +366,47 @@ export function useDesignable(path?: any) {
|
|||||||
refresh();
|
refresh();
|
||||||
return target;
|
return target;
|
||||||
},
|
},
|
||||||
|
moveToBefore(path1, path2) {
|
||||||
|
const source = findPropertyByPath(schema, path1);
|
||||||
|
const property = source.toJSON();
|
||||||
|
const target = findPropertyByPath(schema, path2);
|
||||||
|
if (!target) {
|
||||||
|
console.error('target schema does not exist.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!property.name) {
|
||||||
|
property.name = uid();
|
||||||
|
}
|
||||||
|
if (target['parentKey']) {
|
||||||
|
property['parentKey'] = target['parentKey'];
|
||||||
|
setKeys(property);
|
||||||
|
property['__insertBefore__'] = target['key'];
|
||||||
|
}
|
||||||
|
addPropertyBefore(target, property);
|
||||||
|
refresh();
|
||||||
|
return target.parent.properties[property.name];
|
||||||
|
},
|
||||||
|
moveToAfter(path1, path2) {
|
||||||
|
const source = findPropertyByPath(schema, path1);
|
||||||
|
const property = source.toJSON();
|
||||||
|
const target = findPropertyByPath(schema, path2);
|
||||||
|
if (!target) {
|
||||||
|
console.error('target schema does not exist.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!property.name) {
|
||||||
|
property.name = uid();
|
||||||
|
}
|
||||||
|
if (target['parentKey']) {
|
||||||
|
property['parentKey'] = target['parentKey'];
|
||||||
|
setKeys(property);
|
||||||
|
property['__insertAfter__'] = target['key'];
|
||||||
|
}
|
||||||
|
addPropertyAfter(target, property);
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
return target.parent.properties[property.name];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,31 +14,36 @@ export interface CollectionProviderProps {
|
|||||||
collectionName?: string;
|
collectionName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [CollectionProvider, useCollectionContext] = constate(
|
export function useCollection(props: CollectionProviderProps) {
|
||||||
(props: CollectionProviderProps) => {
|
const { collectionName } = props;
|
||||||
const { collectionName } = props;
|
const { collections = [], loading, refresh } = useCollectionsContext();
|
||||||
const { collections = [], loading, refresh } = useCollectionsContext();
|
let collection: any;
|
||||||
let collection: any;
|
let fields = [];
|
||||||
let fields = [];
|
if (collectionName) {
|
||||||
if (collectionName) {
|
collection = collections.find((item) => item.name === collectionName);
|
||||||
collection = collections.find((item) => item.name === collectionName);
|
}
|
||||||
}
|
if (collection) {
|
||||||
if (collection) {
|
fields = collection?.fields || [];
|
||||||
fields = collection?.fields || [];
|
}
|
||||||
}
|
let sortableField = collection.sortable;
|
||||||
return {
|
if (collection.sortable && typeof collection.sortable === 'object') {
|
||||||
collection,
|
sortableField = collection.sortable?.name;
|
||||||
fields,
|
}
|
||||||
loading,
|
return {
|
||||||
refresh,
|
sortableField,
|
||||||
getField(name: string) {
|
collection,
|
||||||
if (!name) {
|
fields,
|
||||||
return null;
|
loading,
|
||||||
}
|
refresh,
|
||||||
return fields.find((item) => item.name === name);
|
getField(name: string) {
|
||||||
},
|
if (!name) {
|
||||||
};
|
return null;
|
||||||
},
|
}
|
||||||
);
|
return fields.find((item) => item.name === name);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [CollectionProvider, useCollectionContext] = constate(useCollection);
|
||||||
|
|
||||||
export { CollectionProvider, useCollectionContext };
|
export { CollectionProvider, useCollectionContext };
|
||||||
|
@ -17,6 +17,8 @@ export interface SaveOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ListOptions {
|
export interface ListOptions {
|
||||||
|
defaultFilter?: any;
|
||||||
|
filter?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Resource {
|
export class Resource {
|
||||||
@ -31,9 +33,28 @@ export class Resource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list(options: ListOptions = {}) {
|
sort(options) {
|
||||||
const { resourceName } = this.options;
|
const { resourceName } = this.options;
|
||||||
return request(`${resourceName}:list`);
|
const { resourceKey, target, field = 'sort' } = options;
|
||||||
|
return request(`${resourceName}:sort/${resourceKey}`, {
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
target,
|
||||||
|
field,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
list(options: ListOptions = {}) {
|
||||||
|
const { defaultFilter, filter, ...others } = options;
|
||||||
|
const { resourceName } = this.options;
|
||||||
|
return request(`${resourceName}:list`, {
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
filter: decodeURIComponent(JSON.stringify({ and: [defaultFilter, filter].filter(Boolean) })),
|
||||||
|
...others,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get(options: GetOptions = {}) {
|
get(options: GetOptions = {}) {
|
||||||
|
@ -7,6 +7,7 @@ export const createdAt: ISchema = {
|
|||||||
group: 'systemInfo',
|
group: 'systemInfo',
|
||||||
order: 1,
|
order: 1,
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'date',
|
dataType: 'date',
|
||||||
field: 'created_at',
|
field: 'created_at',
|
||||||
|
@ -7,6 +7,7 @@ export const datetime: ISchema = {
|
|||||||
group: 'datetime',
|
group: 'datetime',
|
||||||
order: 1,
|
order: 1,
|
||||||
title: '日期',
|
title: '日期',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'date',
|
dataType: 'date',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const email: ISchema = {
|
|||||||
group: 'basic',
|
group: 'basic',
|
||||||
order: 4,
|
order: 4,
|
||||||
title: '电子邮箱',
|
title: '电子邮箱',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const number: ISchema = {
|
|||||||
group: 'basic',
|
group: 'basic',
|
||||||
order: 5,
|
order: 5,
|
||||||
title: '数字',
|
title: '数字',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'float',
|
dataType: 'float',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const percent: ISchema = {
|
|||||||
group: 'basic',
|
group: 'basic',
|
||||||
order: 6,
|
order: 6,
|
||||||
title: '百分比',
|
title: '百分比',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'float',
|
dataType: 'float',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const phone: ISchema = {
|
|||||||
group: 'basic',
|
group: 'basic',
|
||||||
order: 3,
|
order: 3,
|
||||||
title: '手机号码',
|
title: '手机号码',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const string: ISchema = {
|
|||||||
group: 'basic',
|
group: 'basic',
|
||||||
order: 1,
|
order: 1,
|
||||||
title: '单行文本',
|
title: '单行文本',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
interface: 'string',
|
interface: 'string',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
|
@ -7,6 +7,7 @@ export const time: ISchema = {
|
|||||||
group: 'datetime',
|
group: 'datetime',
|
||||||
order: 2,
|
order: 2,
|
||||||
title: '时间',
|
title: '时间',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'time',
|
dataType: 'time',
|
||||||
// name,
|
// name,
|
||||||
|
@ -7,6 +7,7 @@ export const updatedAt: ISchema = {
|
|||||||
group: 'systemInfo',
|
group: 'systemInfo',
|
||||||
order: 2,
|
order: 2,
|
||||||
title: '最后更新时间',
|
title: '最后更新时间',
|
||||||
|
sortable: true,
|
||||||
default: {
|
default: {
|
||||||
dataType: 'date',
|
dataType: 'date',
|
||||||
field: 'updated_at',
|
field: 'updated_at',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useContext, useMemo, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import { SchemaField } from '..';
|
import { SchemaField } from '..';
|
||||||
import { createForm, onFieldChange, onFieldReact } from '@formily/core';
|
import { createForm, onFieldChange, onFieldReact, onFormValuesChange } from '@formily/core';
|
||||||
import { FormProvider, FormConsumer, useFieldSchema, Schema, SchemaOptionsContext, ISchema } from '@formily/react';
|
import { FormProvider, FormConsumer, useFieldSchema, Schema, SchemaOptionsContext, ISchema, SchemaKey } from '@formily/react';
|
||||||
import { Field } from '@formily/core/esm/models/Field';
|
import { Field } from '@formily/core/esm/models/Field';
|
||||||
import { Form } from '@formily/core/esm/models/Form';
|
import { Form } from '@formily/core/esm/models/Form';
|
||||||
import {
|
import {
|
||||||
@ -14,26 +14,73 @@ import {
|
|||||||
} from '@formily/antd';
|
} from '@formily/antd';
|
||||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
|
||||||
function useFilterColumns(): Schema[] {
|
function useFilterColumns(): Map<SchemaKey, Schema> {
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const columns = schema.reduceProperties((columns, current) => {
|
const columns = schema.reduceProperties((columns, current) => {
|
||||||
if (current['x-component'] === 'Filter.Column') {
|
if (current['x-component'] === 'Filter.Column') {
|
||||||
return [...columns, current];
|
const fieldName = Object.keys(current.properties).shift();
|
||||||
|
columns.set(fieldName, current);
|
||||||
|
return columns;
|
||||||
}
|
}
|
||||||
return [...columns];
|
return columns;
|
||||||
}, []);
|
}, new Map<SchemaKey, Schema>());
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterItem = (props) => {
|
export const FilterItem = (props) => {
|
||||||
const { initialValues = {}, onRemove } = props;
|
const { value, initialValues = {}, onRemove, onChange } = props;
|
||||||
|
|
||||||
const options = useContext(SchemaOptionsContext);
|
const options = useContext(SchemaOptionsContext);
|
||||||
|
|
||||||
const { key } = initialValues;
|
|
||||||
const columns = useFilterColumns();
|
const columns = useFilterColumns();
|
||||||
console.log('useFilterColumns', columns);
|
console.log('FilterItem', value)
|
||||||
|
|
||||||
|
const toValues = (value) => {
|
||||||
|
if (!value) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (Object.keys(value).length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const fieldName = Object.keys(value).shift();
|
||||||
|
const nested = value[fieldName];
|
||||||
|
const column = columns.get(fieldName).toJSON();
|
||||||
|
if (!nested) {
|
||||||
|
return {
|
||||||
|
column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(nested).length === 0) {
|
||||||
|
return {
|
||||||
|
column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const operationValue = Object.keys(nested).shift();
|
||||||
|
console.log('toValues', {operationValue});
|
||||||
|
const operations = column?.['x-component-props']?.['operations']||[];
|
||||||
|
const operation = operations.find(operation => operation.value === operationValue);
|
||||||
|
console.log('toValues', {operation});
|
||||||
|
if (!operation) {
|
||||||
|
return {
|
||||||
|
column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation.noValue) {
|
||||||
|
return {
|
||||||
|
column,
|
||||||
|
operation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
column,
|
||||||
|
operation,
|
||||||
|
value: nested[operationValue],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = toValues(value);
|
||||||
|
|
||||||
|
// console.log('toValues', values, value);
|
||||||
|
|
||||||
const Remove = (props) => {
|
const Remove = (props) => {
|
||||||
return (
|
return (
|
||||||
@ -53,7 +100,7 @@ export const FilterItem = (props) => {
|
|||||||
const form = useMemo(
|
const form = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createForm({
|
createForm({
|
||||||
initialValues,
|
initialValues: values,
|
||||||
effects: (form) => {
|
effects: (form) => {
|
||||||
onFieldChange('column', (field: Field, form: Form) => {
|
onFieldChange('column', (field: Field, form: Form) => {
|
||||||
const column = (field.value || {}) as ISchema;
|
const column = (field.value || {}) as ISchema;
|
||||||
@ -76,12 +123,28 @@ export const FilterItem = (props) => {
|
|||||||
f.visible = !operation.noValue;
|
f.visible = !operation.noValue;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
onFormValuesChange((form) => {
|
||||||
|
const { column, operation, value } = form.values;
|
||||||
|
if (!operation?.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fieldName = Object.keys(column.properties).shift();
|
||||||
|
if (operation?.noValue) {
|
||||||
|
onChange({[fieldName]: {
|
||||||
|
[operation.value]: true,
|
||||||
|
}});
|
||||||
|
} else if (isValid(value)) {
|
||||||
|
onChange({[fieldName]: {
|
||||||
|
[operation.value]: value,
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
console.log('form.values', form.values);
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
<FormLayout layout={'inline'}>
|
<FormLayout layout={'inline'}>
|
||||||
@ -89,7 +152,7 @@ export const FilterItem = (props) => {
|
|||||||
<SchemaField.Void x-component="Space">
|
<SchemaField.Void x-component="Space">
|
||||||
<SchemaField.String
|
<SchemaField.String
|
||||||
name="column"
|
name="column"
|
||||||
x-decorator="FormItem"
|
x-decorator="FormilyFormItem"
|
||||||
x-decorator-props={{
|
x-decorator-props={{
|
||||||
asterisk: true,
|
asterisk: true,
|
||||||
feedbackLayout: 'none',
|
feedbackLayout: 'none',
|
||||||
@ -105,11 +168,11 @@ export const FilterItem = (props) => {
|
|||||||
value: 'name',
|
value: 'name',
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
enum={columns.map((column) => column.toJSON())}
|
enum={[...columns.values()].map((column) => column.toJSON())}
|
||||||
/>
|
/>
|
||||||
<SchemaField.String
|
<SchemaField.String
|
||||||
name="operation"
|
name="operation"
|
||||||
x-decorator="FormItem"
|
x-decorator="FormilyFormItem"
|
||||||
x-decorator-props={{
|
x-decorator-props={{
|
||||||
asterisk: true,
|
asterisk: true,
|
||||||
feedbackLayout: 'none',
|
feedbackLayout: 'none',
|
||||||
@ -124,7 +187,7 @@ export const FilterItem = (props) => {
|
|||||||
/>
|
/>
|
||||||
<SchemaField.String
|
<SchemaField.String
|
||||||
name="value"
|
name="value"
|
||||||
x-decorator="FormItem"
|
x-decorator="FormilyFormItem"
|
||||||
x-decorator-props={{
|
x-decorator-props={{
|
||||||
asterisk: true,
|
asterisk: true,
|
||||||
feedbackLayout: 'none',
|
feedbackLayout: 'none',
|
||||||
|
@ -38,6 +38,20 @@ const schema = {
|
|||||||
name: 'filter',
|
name: 'filter',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
'x-component': 'Filter',
|
'x-component': 'Filter',
|
||||||
|
default: {
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
field1: {
|
||||||
|
eq: 'aa',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field1: {
|
||||||
|
eq: 'bbb',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
column1: {
|
column1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -68,7 +82,7 @@ const schema = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
field1: {
|
field2: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
'x-component': 'InputNumber',
|
'x-component': 'InputNumber',
|
||||||
},
|
},
|
||||||
@ -79,7 +93,7 @@ const schema = {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<SchemaRenderer schema={schema} />
|
<SchemaRenderer debug={true} schema={schema} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
@ -5,12 +5,39 @@ import { useDynamicList } from 'ahooks';
|
|||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import { FilterItem } from './FilterItem';
|
import { FilterItem } from './FilterItem';
|
||||||
|
import cls from 'classnames';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
|
|
||||||
|
const toValue = (value) => {
|
||||||
|
if (!value) {
|
||||||
|
return {
|
||||||
|
logical: 'and',
|
||||||
|
list: [{}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (value.and) {
|
||||||
|
return {
|
||||||
|
logical: 'and',
|
||||||
|
list: value.and,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (value.or) {
|
||||||
|
return {
|
||||||
|
logical: 'and',
|
||||||
|
list: value.or,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
logical: 'and',
|
||||||
|
list: [{}],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function FilterGroup(props) {
|
export function FilterGroup(props) {
|
||||||
const { onRemove } = props;
|
const { bordered = true, onRemove, onChange } = props;
|
||||||
|
const value = toValue(props.value);
|
||||||
return (
|
return (
|
||||||
<div className={'nb-filter-group'}>
|
<div className={cls('nb-filter-group', { bordered })}>
|
||||||
{onRemove && (
|
{onRemove && (
|
||||||
<a className={'nb-filter-group-close'} onClick={() => onRemove()}>
|
<a className={'nb-filter-group-close'} onClick={() => onRemove()}>
|
||||||
<CloseCircleOutlined />
|
<CloseCircleOutlined />
|
||||||
@ -20,8 +47,13 @@ export function FilterGroup(props) {
|
|||||||
满足组内{' '}
|
满足组内{' '}
|
||||||
<Select
|
<Select
|
||||||
style={{ width: 80 }}
|
style={{ width: 80 }}
|
||||||
onChange={(value) => {}}
|
onChange={(logical) => {
|
||||||
defaultValue={'and'}
|
onChange &&
|
||||||
|
onChange({
|
||||||
|
[logical]: value.list,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
defaultValue={value.logical}
|
||||||
>
|
>
|
||||||
<Select.Option value={'and'}>全部</Select.Option>
|
<Select.Option value={'and'}>全部</Select.Option>
|
||||||
<Select.Option value={'or'}>任意</Select.Option>
|
<Select.Option value={'or'}>任意</Select.Option>
|
||||||
@ -29,9 +61,12 @@ export function FilterGroup(props) {
|
|||||||
条件
|
条件
|
||||||
</div>
|
</div>
|
||||||
<FilterList
|
<FilterList
|
||||||
initialValue={[{}]}
|
initialValue={value.list}
|
||||||
onChange={(list) => {
|
onChange={(list: any[]) => {
|
||||||
console.log({ list });
|
onChange &&
|
||||||
|
onChange({
|
||||||
|
[value.logical]: list.filter((item) => Object.keys(item).length),
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +75,9 @@ export function FilterGroup(props) {
|
|||||||
|
|
||||||
export function FilterList(props) {
|
export function FilterList(props) {
|
||||||
const { initialValue } = props;
|
const { initialValue } = props;
|
||||||
const { list, push, remove } = useDynamicList<any>(initialValue || []);
|
const { list, push, remove, replace } = useDynamicList<any>(
|
||||||
|
initialValue || [],
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.onChange && props.onChange(list);
|
props.onChange && props.onChange(list);
|
||||||
}, [list]);
|
}, [list]);
|
||||||
@ -48,18 +85,29 @@ export function FilterList(props) {
|
|||||||
<div className={'nb-filter-list'}>
|
<div className={'nb-filter-list'}>
|
||||||
<div>
|
<div>
|
||||||
{list.map((item, index) => {
|
{list.map((item, index) => {
|
||||||
if (item.type === 'group') {
|
if (item.and || item.or) {
|
||||||
return <FilterGroup key={index} onRemove={() => remove(index)} />;
|
return (
|
||||||
|
<FilterGroup
|
||||||
|
key={index}
|
||||||
|
value={item}
|
||||||
|
onChange={(value: any) => replace(index, value)}
|
||||||
|
onRemove={() => remove(index)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <FilterItem key={index} onRemove={() => remove(index)} />;
|
return (
|
||||||
|
<FilterItem
|
||||||
|
key={index}
|
||||||
|
value={item}
|
||||||
|
onChange={(value: any) => replace(index, value)}
|
||||||
|
onRemove={() => remove(index)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
push({
|
push({});
|
||||||
type: 'item',
|
|
||||||
key: new Date().toTimeString(),
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
添加条件
|
添加条件
|
||||||
@ -67,8 +115,7 @@ export function FilterList(props) {
|
|||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
push({
|
push({
|
||||||
type: 'group',
|
and: [{}],
|
||||||
key: new Date().toTimeString(),
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -80,9 +127,10 @@ export function FilterList(props) {
|
|||||||
|
|
||||||
export const Filter = connect(
|
export const Filter = connect(
|
||||||
(props) => {
|
(props) => {
|
||||||
|
// console.log('Filter.props', { props });
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FilterGroup />
|
<FilterGroup bordered={false} {...props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
.nb-filter-group {
|
.nb-filter-group {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
padding: 14px;
|
min-width: 400px;
|
||||||
border: 1px dashed rgb(222, 222, 222);
|
|
||||||
&-close {
|
&-close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
}
|
&.bordered {
|
||||||
|
border: 1px dashed rgb(222, 222, 222);
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nb-filter-list {
|
||||||
|
.ant-form-inline {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -123,7 +123,6 @@ export function SortableBodyRow(props: any) {
|
|||||||
<SortableContext
|
<SortableContext
|
||||||
strategy={horizontalListSortingStrategy}
|
strategy={horizontalListSortingStrategy}
|
||||||
items={React.Children.map(props.children, (child) => {
|
items={React.Children.map(props.children, (child) => {
|
||||||
console.log(child.key, 'child.key');
|
|
||||||
return `td${child.key}`;
|
return `td${child.key}`;
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@ -17,7 +17,7 @@ import { uid } from '@formily/shared';
|
|||||||
import useRequest from '@ahooksjs/use-request';
|
import useRequest from '@ahooksjs/use-request';
|
||||||
import { BaseResult } from '@ahooksjs/use-request/lib/types';
|
import { BaseResult } from '@ahooksjs/use-request/lib/types';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
import { MenuOutlined, DragOutlined, FilterOutlined } from '@ant-design/icons';
|
||||||
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
||||||
import {
|
import {
|
||||||
SortableContext,
|
SortableContext,
|
||||||
@ -42,6 +42,7 @@ import { Resource } from '../../resource';
|
|||||||
import {
|
import {
|
||||||
CollectionProvider,
|
CollectionProvider,
|
||||||
DisplayedMapProvider,
|
DisplayedMapProvider,
|
||||||
|
useCollection,
|
||||||
useCollectionContext,
|
useCollectionContext,
|
||||||
useDisplayedMapContext,
|
useDisplayedMapContext,
|
||||||
} from '../../constate';
|
} from '../../constate';
|
||||||
@ -59,7 +60,10 @@ import {
|
|||||||
SortableRowHandle,
|
SortableRowHandle,
|
||||||
} from './Sortable';
|
} from './Sortable';
|
||||||
import { DragHandle, Droppable, SortableItem } from '../../components/Sortable';
|
import { DragHandle, Droppable, SortableItem } from '../../components/Sortable';
|
||||||
import { VisibleContext } from '../../context';
|
import { isValid } from '@formily/shared';
|
||||||
|
import { FormButtonGroup, FormDialog, FormLayout, Submit } from '@formily/antd';
|
||||||
|
import flatten from 'flat';
|
||||||
|
import IconPicker from '../../components/icon-picker';
|
||||||
|
|
||||||
export interface ITableContext {
|
export interface ITableContext {
|
||||||
props: any;
|
props: any;
|
||||||
@ -79,12 +83,12 @@ export interface ITableRowContext {
|
|||||||
record: any;
|
record: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableConetxt = createContext<ITableContext>(null);
|
const TableContext = createContext<ITableContext>({} as any);
|
||||||
const TableRowContext = createContext<ITableRowContext>(null);
|
const TableRowContext = createContext<ITableRowContext>(null);
|
||||||
const CollectionFieldContext = createContext(null);
|
const CollectionFieldContext = createContext(null);
|
||||||
|
|
||||||
const useTable = () => {
|
const useTable = () => {
|
||||||
return useContext(TableConetxt);
|
return useContext(TableContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useTableRow = () => {
|
const useTableRow = () => {
|
||||||
@ -98,10 +102,15 @@ function useTableFilterAction() {
|
|||||||
refresh,
|
refresh,
|
||||||
props: { refreshRequestOnChange },
|
props: { refreshRequestOnChange },
|
||||||
} = useTable();
|
} = useTable();
|
||||||
|
const form = useForm();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
|
console.log('useTableFilterAction', form.values);
|
||||||
if (refreshRequestOnChange) {
|
if (refreshRequestOnChange) {
|
||||||
return service.refresh();
|
return service.run({
|
||||||
|
...service.params[0],
|
||||||
|
// filter,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -204,7 +213,7 @@ const useTableIndex = () => {
|
|||||||
const { pagination, props } = useTable();
|
const { pagination, props } = useTable();
|
||||||
const ctx = useContext(TableRowContext);
|
const ctx = useContext(TableRowContext);
|
||||||
if (pagination && !props.clientSidePagination) {
|
if (pagination && !props.clientSidePagination) {
|
||||||
const { pageSize, page } = pagination;
|
const { pageSize, page = 1 } = pagination;
|
||||||
return ctx.index + (page - 1) * pageSize;
|
return ctx.index + (page - 1) * pageSize;
|
||||||
}
|
}
|
||||||
return ctx.index;
|
return ctx.index;
|
||||||
@ -433,11 +442,13 @@ const useDataSource = () => {
|
|||||||
|
|
||||||
const TableMain = () => {
|
const TableMain = () => {
|
||||||
const {
|
const {
|
||||||
|
resource,
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
setSelectedRowKeys,
|
setSelectedRowKeys,
|
||||||
service,
|
service,
|
||||||
field,
|
field,
|
||||||
props: { rowKey, dragSort, showIndex },
|
props: { rowKey, dragSort, showIndex },
|
||||||
|
refresh,
|
||||||
} = useTable();
|
} = useTable();
|
||||||
const columns = useTableColumns();
|
const columns = useTableColumns();
|
||||||
const dataSource = useDataSource();
|
const dataSource = useDataSource();
|
||||||
@ -446,8 +457,29 @@ const TableMain = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={'nb-table'}>
|
<div className={'nb-table'}>
|
||||||
<DndContext
|
<DndContext
|
||||||
onDragEnd={(event) => {
|
onDragEnd={async (event) => {
|
||||||
console.log({ event });
|
const fromId = event.active?.id as any;
|
||||||
|
const toId = event.over?.id as any;
|
||||||
|
if (isValid(fromId) && isValid(toId)) {
|
||||||
|
const fromIndex = findIndex(
|
||||||
|
field.value,
|
||||||
|
(item) => item[rowKey] === fromId,
|
||||||
|
);
|
||||||
|
const toIndex = findIndex(
|
||||||
|
field.value,
|
||||||
|
(item) => item[rowKey] === toId,
|
||||||
|
);
|
||||||
|
console.log({ fromId, toId, fromIndex, toIndex });
|
||||||
|
field.move(fromIndex, toIndex);
|
||||||
|
refresh();
|
||||||
|
await resource.sort({
|
||||||
|
resourceKey: fromId,
|
||||||
|
target: {
|
||||||
|
[rowKey]: toId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await service.refresh();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{actionBars.map((actionBar) => (
|
{actionBars.map((actionBar) => (
|
||||||
@ -541,13 +573,35 @@ const TableMain = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const usePagination = (paginationProps?: any) => {
|
const usePagination = () => {
|
||||||
return useState(() => {
|
const field = useField<Formily.Core.Models.ArrayField>();
|
||||||
if (!paginationProps) {
|
const paginationProps = field.componentProps.pagination;
|
||||||
return false;
|
|
||||||
}
|
let pagination = paginationProps;
|
||||||
return { page: 1, pageSize: 10, ...paginationProps };
|
|
||||||
});
|
// const [pagination, setPagination] = useState(() => {
|
||||||
|
// if (!paginationProps) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// const { defaultPageSize = 10, ...others } = paginationProps;
|
||||||
|
// return { page: 1, pageSize: defaultPageSize, ...others };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (!paginationProps) {
|
||||||
|
// return setPagination(false);
|
||||||
|
// }
|
||||||
|
// const { defaultPageSize = 10, ...others } = paginationProps;
|
||||||
|
// setPagination({ page: 1, pageSize: defaultPageSize, ...others });
|
||||||
|
// }, [paginationProps]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
pagination,
|
||||||
|
(params) => {
|
||||||
|
const defaults = field.componentProps.pagination;
|
||||||
|
field.componentProps.pagination = { ...defaults, ...params };
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const TableProvider = (props: any) => {
|
const TableProvider = (props: any) => {
|
||||||
@ -559,10 +613,25 @@ const TableProvider = (props: any) => {
|
|||||||
} = props;
|
} = props;
|
||||||
const { schema } = useDesignable();
|
const { schema } = useDesignable();
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
const field = useField<Formily.Core.Models.ArrayField>();
|
||||||
const [pagination, setPagination] = usePagination(props.pagination);
|
const [pagination, setPagination] = usePagination();
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<any>([]);
|
||||||
const [, refresh] = useState(uid());
|
const [, refresh] = useState(uid());
|
||||||
const { resource } = useResource();
|
const { resource } = useResource();
|
||||||
|
const { sortableField } = useCollectionContext();
|
||||||
|
const dragSort = props.dragSort;
|
||||||
|
const getDefaultParams = () => {
|
||||||
|
const defaultParams = { ...pagination };
|
||||||
|
if (dragSort) {
|
||||||
|
defaultParams['sort'] = [sortableField || 'sort'];
|
||||||
|
} else {
|
||||||
|
defaultParams['sort'] = (props.defaultSort || []).join(',');
|
||||||
|
}
|
||||||
|
if (props.defaultFilter) {
|
||||||
|
defaultParams['defaultFilter'] = props.defaultFilter;
|
||||||
|
}
|
||||||
|
console.log({ defaultParams });
|
||||||
|
return defaultParams;
|
||||||
|
};
|
||||||
const service = useRequest(
|
const service = useRequest(
|
||||||
(params?: any) => {
|
(params?: any) => {
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
@ -582,12 +651,20 @@ const TableProvider = (props: any) => {
|
|||||||
onSuccess(data: any) {
|
onSuccess(data: any) {
|
||||||
field.setValue(data?.list || []);
|
field.setValue(data?.list || []);
|
||||||
},
|
},
|
||||||
defaultParams: [{ ...pagination }],
|
manual: true,
|
||||||
|
// defaultParams: [getDefaultParams()],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
console.log('refresh', { pagination });
|
useEffect(() => {
|
||||||
|
service.run(getDefaultParams());
|
||||||
|
}, [
|
||||||
|
pagination.pageSize,
|
||||||
|
props.dragSort,
|
||||||
|
props.defaultSort,
|
||||||
|
props.defaultFilter,
|
||||||
|
]);
|
||||||
return (
|
return (
|
||||||
<TableConetxt.Provider
|
<TableContext.Provider
|
||||||
value={{
|
value={{
|
||||||
resource,
|
resource,
|
||||||
refresh: () => {
|
refresh: () => {
|
||||||
@ -597,7 +674,7 @@ const TableProvider = (props: any) => {
|
|||||||
: service?.data?.total;
|
: service?.data?.total;
|
||||||
const maxPage = Math.ceil(total / pageSize);
|
const maxPage = Math.ceil(total / pageSize);
|
||||||
if (page > maxPage) {
|
if (page > maxPage) {
|
||||||
setPagination((prev) => ({ ...prev, page: maxPage }));
|
setPagination({ page: maxPage });
|
||||||
} else {
|
} else {
|
||||||
refresh(uid());
|
refresh(uid());
|
||||||
}
|
}
|
||||||
@ -613,7 +690,7 @@ const TableProvider = (props: any) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TableMain />
|
<TableMain />
|
||||||
</TableConetxt.Provider>
|
</TableContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -654,19 +731,18 @@ Table.Pagination = observer(() => {
|
|||||||
total={total}
|
total={total}
|
||||||
onChange={(current, pageSize) => {
|
onChange={(current, pageSize) => {
|
||||||
const page = pagination.pageSize !== pageSize ? 1 : current;
|
const page = pagination.pageSize !== pageSize ? 1 : current;
|
||||||
setPagination((prev) => ({
|
setPagination({
|
||||||
...prev,
|
|
||||||
page,
|
|
||||||
pageSize,
|
|
||||||
}));
|
|
||||||
if (clientSidePagination) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
service.run({
|
|
||||||
...service.params,
|
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
});
|
});
|
||||||
|
// if (clientSidePagination) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// service.run({
|
||||||
|
// ...service.params[0],
|
||||||
|
// page,
|
||||||
|
// pageSize,
|
||||||
|
// });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -1031,42 +1107,74 @@ Table.ActionBar = observer((props: any) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
|
||||||
|
const { fieldNames = [] } = options;
|
||||||
|
const properties = {};
|
||||||
|
fields.forEach((field, index) => {
|
||||||
|
if (fieldNames?.length && !fieldNames.includes(field.name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fieldOption = interfaces.get(field.interface);
|
||||||
|
if (!fieldOption.operations) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
properties[`column${index}`] = {
|
||||||
|
type: 'void',
|
||||||
|
title: field?.uiSchema?.title,
|
||||||
|
'x-component': 'Filter.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
operations: fieldOption.operations,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
[field.name]: {
|
||||||
|
...field.uiSchema,
|
||||||
|
'x-decorator': 'FormilyFormItem',
|
||||||
|
title: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldsToSortColumns = (fields: any[]) => {
|
||||||
|
const dataSource = [];
|
||||||
|
|
||||||
|
fields.forEach((field) => {
|
||||||
|
const fieldOption = interfaces.get(field.interface);
|
||||||
|
if (!fieldOption.sortable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dataSource.push({
|
||||||
|
value: field.name,
|
||||||
|
label: field?.uiSchema?.title,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return dataSource;
|
||||||
|
};
|
||||||
|
|
||||||
Table.Filter = observer((props: any) => {
|
Table.Filter = observer((props: any) => {
|
||||||
|
const { service } = useTable();
|
||||||
const { fieldNames = [] } = props;
|
const { fieldNames = [] } = props;
|
||||||
const { schema, DesignableBar } = useDesignable();
|
const { schema, DesignableBar } = useDesignable();
|
||||||
const form = useMemo(() => createForm(), []);
|
const form = useMemo(() => createForm(), []);
|
||||||
const { fields = [] } = useCollectionContext();
|
const { fields = [] } = useCollectionContext();
|
||||||
const fields2properties = (fields: any[]) => {
|
const [visible, setVisible] = useState(false);
|
||||||
const properties = {};
|
const obj = flatten(form.values.filter || {});
|
||||||
fields.forEach((field, index) => {
|
console.log('flatten', obj, Object.values(obj));
|
||||||
if (fieldNames?.length && !fieldNames.includes(field.name)) {
|
const count = Object.values(obj).filter((i) =>
|
||||||
return;
|
Array.isArray(i) ? i.length : i,
|
||||||
}
|
).length;
|
||||||
const fieldOption = interfaces.get(field.interface);
|
|
||||||
if (!fieldOption.operations) {
|
const icon = props.icon || 'FilterOutlined';
|
||||||
return;
|
|
||||||
}
|
|
||||||
properties[`column${index}`] = {
|
|
||||||
type: 'void',
|
|
||||||
title: field?.uiSchema?.title,
|
|
||||||
'x-component': 'Filter.Column',
|
|
||||||
'x-component-props': {
|
|
||||||
operations: fieldOption.operations,
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[field.name]: {
|
|
||||||
...field.uiSchema,
|
|
||||||
title: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return properties;
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
placement={'bottomLeft'}
|
placement={'bottomLeft'}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={setVisible}
|
||||||
content={
|
content={
|
||||||
<div>
|
<div>
|
||||||
<FormProvider form={form}>
|
<FormProvider form={form}>
|
||||||
@ -1077,17 +1185,32 @@ Table.Filter = observer((props: any) => {
|
|||||||
filter: {
|
filter: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
'x-component': 'Filter',
|
'x-component': 'Filter',
|
||||||
properties: fields2properties(fields),
|
properties: fieldsToFilterColumns(fields, { fieldNames }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<FormButtonGroup align={'right'}>
|
||||||
|
<Submit
|
||||||
|
onSubmit={() => {
|
||||||
|
const { filter } = form.values;
|
||||||
|
console.log('Table.Filter', form.values);
|
||||||
|
setVisible(false);
|
||||||
|
return service.run({
|
||||||
|
...service.params[0],
|
||||||
|
filter,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</Submit>
|
||||||
|
</FormButtonGroup>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button>
|
<Button icon={<IconPicker type={icon} />}>
|
||||||
{schema.title}
|
{count > 0 ? `${count} 个筛选项` : schema.title}
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</Button>
|
</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
@ -1099,7 +1222,11 @@ Table.Filter.DesignableBar = () => {
|
|||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const displayed = useDisplayedMapContext();
|
const displayed = useDisplayedMapContext();
|
||||||
const { fields } = useCollectionContext();
|
const { fields } = useCollectionContext();
|
||||||
|
const field = useField();
|
||||||
|
let fieldNames = field.componentProps.fieldNames || [];
|
||||||
|
if (fieldNames.length === 0) {
|
||||||
|
fieldNames = fields.map((field) => field.name);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -1119,22 +1246,72 @@ Table.Filter.DesignableBar = () => {
|
|||||||
<Menu>
|
<Menu>
|
||||||
<Menu.ItemGroup title={'筛选字段'}>
|
<Menu.ItemGroup title={'筛选字段'}>
|
||||||
{fields
|
{fields
|
||||||
.filter((field) => {
|
.filter((collectionField) => {
|
||||||
const option = interfaces.get(field.interface);
|
const option = interfaces.get(collectionField.interface);
|
||||||
return option.operations?.length;
|
return option.operations?.length;
|
||||||
})
|
})
|
||||||
.map((field) => (
|
.map((collectionField) => (
|
||||||
<SwitchMenuItem
|
<SwitchMenuItem
|
||||||
title={field?.uiSchema?.title}
|
title={collectionField?.uiSchema?.title}
|
||||||
checked={true}
|
checked={fieldNames.includes(collectionField.name)}
|
||||||
onChange={async (checked) => {}}
|
onChange={async (checked) => {
|
||||||
|
if (checked) {
|
||||||
|
fieldNames.push(collectionField.name);
|
||||||
|
} else {
|
||||||
|
const index = fieldNames.indexOf(
|
||||||
|
collectionField.name,
|
||||||
|
);
|
||||||
|
if (index > -1) {
|
||||||
|
fieldNames.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log({ fieldNames, field });
|
||||||
|
schema['x-component-props']['fieldNames'] = fieldNames;
|
||||||
|
field.componentProps.fieldNames = fieldNames;
|
||||||
|
updateSchema(schema);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Menu.ItemGroup>
|
</Menu.ItemGroup>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={(e) => {
|
onClick={async (e) => {
|
||||||
schema.title = uid();
|
const values = await FormDialog('修改名称和图标', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '按钮名称',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
title: '按钮图标',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
title: schema['title'],
|
||||||
|
icon: schema['x-component-props']?.['icon'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
schema['title'] = values.title;
|
||||||
|
schema['x-component-props']['icon'] = values.icon;
|
||||||
|
field.componentProps.icon = values.icon;
|
||||||
|
field.title = values.title;
|
||||||
|
updateSchema(schema);
|
||||||
refresh();
|
refresh();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1153,7 +1330,7 @@ Table.Filter.DesignableBar = () => {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除
|
移除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
@ -1262,6 +1439,7 @@ Table.Action.DesignableBar = () => {
|
|||||||
const isPopup = Object.keys(schema.properties || {}).length > 0;
|
const isPopup = Object.keys(schema.properties || {}).length > 0;
|
||||||
const inActionBar = schema.parent['x-component'] === 'Table.ActionBar';
|
const inActionBar = schema.parent['x-component'] === 'Table.ActionBar';
|
||||||
const displayed = useDisplayedMapContext();
|
const displayed = useDisplayedMapContext();
|
||||||
|
const field = useField();
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -1280,8 +1458,43 @@ Table.Action.DesignableBar = () => {
|
|||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={(e) => {
|
onClick={async (e) => {
|
||||||
schema.title = uid();
|
const values = await FormDialog('修改名称和图标', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '按钮名称',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
title: '按钮图标',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
title: schema['title'],
|
||||||
|
icon: schema['x-component-props']?.['icon'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
schema['title'] = values.title;
|
||||||
|
schema['x-component-props']['icon'] = values.icon;
|
||||||
|
field.componentProps.icon = values.icon;
|
||||||
|
field.title = values.title;
|
||||||
|
updateSchema(schema);
|
||||||
refresh();
|
refresh();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1293,11 +1506,19 @@ Table.Action.DesignableBar = () => {
|
|||||||
<Select
|
<Select
|
||||||
bordered={false}
|
bordered={false}
|
||||||
size={'small'}
|
size={'small'}
|
||||||
defaultValue={'modal'}
|
defaultValue={'Action.Modal'}
|
||||||
|
onChange={(value) => {
|
||||||
|
const s = Object.values(schema.properties).shift();
|
||||||
|
s['x-component'] = value;
|
||||||
|
refresh();
|
||||||
|
updateSchema(s);
|
||||||
|
// const f = field.query(getSchemaPath(s)).take()
|
||||||
|
// console.log('fffffff', { schema, f });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Select.Option value={'modal'}>对话框</Select.Option>
|
<Select.Option value={'Action.Modal'}>对话框</Select.Option>
|
||||||
<Select.Option value={'drawer'}>抽屉</Select.Option>
|
<Select.Option value={'Action.Drawer'}>抽屉</Select.Option>
|
||||||
<Select.Option value={'window'}>浏览器窗口</Select.Option>
|
<Select.Option value={'Action.Window'}>浏览器窗口</Select.Option>
|
||||||
</Select>{' '}
|
</Select>{' '}
|
||||||
内打开
|
内打开
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
@ -1321,7 +1542,7 @@ Table.Action.DesignableBar = () => {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除
|
移除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
@ -1389,7 +1610,8 @@ Table.Column.DesignableBar = () => {
|
|||||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const displayed = useDisplayedMapContext();
|
const displayed = useDisplayedMapContext();
|
||||||
const ctx = useContext(ColDraggableContext);
|
const collectionField = useContext(CollectionFieldContext);
|
||||||
|
console.log('displayed.map', displayed.map);
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -1409,7 +1631,38 @@ Table.Column.DesignableBar = () => {
|
|||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
const title = uid();
|
const values = await FormDialog('修改列标题', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
fieldName: {
|
||||||
|
type: 'string',
|
||||||
|
title: '原字段名称',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '自定义列标题',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
fieldName: collectionField?.uiSchema?.title,
|
||||||
|
title: schema['title'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const title = values.title || null;
|
||||||
field.title = title;
|
field.title = title;
|
||||||
schema.title = title;
|
schema.title = title;
|
||||||
refresh();
|
refresh();
|
||||||
@ -1420,8 +1673,9 @@ Table.Column.DesignableBar = () => {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
编辑列
|
修改列标题
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const s = remove();
|
const s = remove();
|
||||||
@ -1430,7 +1684,7 @@ Table.Column.DesignableBar = () => {
|
|||||||
await removeSchema(s);
|
await removeSchema(s);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除列
|
移除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
@ -1453,14 +1707,14 @@ Table.SortHandle = observer((props: any) => {
|
|||||||
|
|
||||||
Table.DesignableBar = observer((props) => {
|
Table.DesignableBar = observer((props) => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const { designable, schema, refresh, deepRemove } = useDesignable();
|
const { schema, refresh, deepRemove } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { dragRef } = useContext(DraggableBlockContext);
|
const { dragRef } = useContext(DraggableBlockContext);
|
||||||
if (!designable) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const defaultPageSize =
|
const defaultPageSize =
|
||||||
schema['x-component-props']?.['pagination']?.['defaultPageSize'] || 20;
|
field?.componentProps?.pagination?.defaultPageSize || 10;
|
||||||
|
const collectionName = field?.componentProps?.collectionName;
|
||||||
|
const { fields } = useCollection({ collectionName });
|
||||||
|
console.log({ collectionName });
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -1486,9 +1740,16 @@ Table.DesignableBar = observer((props) => {
|
|||||||
const bool = !field.componentProps.showIndex;
|
const bool = !field.componentProps.showIndex;
|
||||||
schema['x-component-props']['showIndex'] = bool;
|
schema['x-component-props']['showIndex'] = bool;
|
||||||
field.componentProps.showIndex = bool;
|
field.componentProps.showIndex = bool;
|
||||||
|
updateSchema(schema);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{field.componentProps.showIndex ? '隐藏序号' : '显示序号'}
|
<div className={'nb-space-between'}>
|
||||||
|
显示序号{' '}
|
||||||
|
<Switch
|
||||||
|
size={'small'}
|
||||||
|
checked={field.componentProps.showIndex}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={'dragSort'}
|
key={'dragSort'}
|
||||||
@ -1498,16 +1759,166 @@ Table.DesignableBar = observer((props) => {
|
|||||||
: 'sort';
|
: 'sort';
|
||||||
schema['x-component-props']['dragSort'] = dragSort;
|
schema['x-component-props']['dragSort'] = dragSort;
|
||||||
field.componentProps.dragSort = dragSort;
|
field.componentProps.dragSort = dragSort;
|
||||||
|
updateSchema(schema);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{field.componentProps.dragSort
|
<div className={'nb-space-between'}>
|
||||||
? '禁用拖拽排序'
|
启用拖拽排序{' '}
|
||||||
: '启用拖拽排序'}
|
<Switch
|
||||||
|
size={'small'}
|
||||||
|
checked={field.componentProps.dragSort}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{!field.componentProps.dragSort && (
|
{!field.componentProps.dragSort && (
|
||||||
<Menu.Item key={'defaultSort'}>默认排序</Menu.Item>
|
<Menu.Item
|
||||||
|
key={'defaultSort'}
|
||||||
|
onClick={async () => {
|
||||||
|
const defaultSort =
|
||||||
|
field.componentProps?.defaultSort?.map(
|
||||||
|
(item: string) => {
|
||||||
|
return item.startsWith('-')
|
||||||
|
? {
|
||||||
|
field: item.substring(1),
|
||||||
|
direction: 'desc',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
field: item,
|
||||||
|
direction: 'asc',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const values = await FormDialog('设置默认排序', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
defaultSort: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'ArrayItems',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
space: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component':
|
||||||
|
'ArrayItems.SortHandle',
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
enum: fieldsToSortColumns(fields),
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
width: 260,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enum: [
|
||||||
|
{ label: '正序', value: 'asc' },
|
||||||
|
{
|
||||||
|
label: '倒序',
|
||||||
|
value: 'desc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component':
|
||||||
|
'ArrayItems.Remove',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: '新增',
|
||||||
|
'x-component': 'ArrayItems.Addition',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
defaultSort,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const sort = values.defaultSort.map((item) => {
|
||||||
|
return item.direction === 'desc'
|
||||||
|
? `-${item.field}`
|
||||||
|
: item.field;
|
||||||
|
});
|
||||||
|
schema['x-component-props']['defaultSort'] = sort;
|
||||||
|
field.componentProps.defaultSort = sort;
|
||||||
|
await updateSchema(schema);
|
||||||
|
console.log('defaultSort', sort);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
设置默认排序
|
||||||
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
<Menu.Item key={'defaultFilter'}>筛选范围</Menu.Item>
|
<Menu.Item
|
||||||
|
key={'defaultFilter'}
|
||||||
|
onClick={async () => {
|
||||||
|
const { defaultFilter } = await FormDialog(
|
||||||
|
'设置筛选范围',
|
||||||
|
() => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
defaultFilter: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'Filter',
|
||||||
|
properties: fieldsToFilterColumns(fields),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).open({
|
||||||
|
initialValues: {
|
||||||
|
defaultFilter:
|
||||||
|
field?.componentProps?.defaultFilter || {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
schema['x-component-props']['defaultFilter'] =
|
||||||
|
defaultFilter;
|
||||||
|
field.componentProps.defaultFilter = defaultFilter;
|
||||||
|
await updateSchema(schema);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
设置筛选范围
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Item key={'defaultPageSize'}>
|
<Menu.Item key={'defaultPageSize'}>
|
||||||
每页默认显示{' '}
|
每页默认显示{' '}
|
||||||
<Select
|
<Select
|
||||||
@ -1516,11 +1927,16 @@ Table.DesignableBar = observer((props) => {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
const componentProps = schema['x-component-props'] || {};
|
const componentProps = schema['x-component-props'] || {};
|
||||||
set(componentProps, 'pagination.defaultPageSize', value);
|
set(componentProps, 'pagination.defaultPageSize', value);
|
||||||
|
set(componentProps, 'pagination.pageSize', value);
|
||||||
schema['x-component-props'] = componentProps;
|
schema['x-component-props'] = componentProps;
|
||||||
|
field.componentProps.pagination.pageSize = value;
|
||||||
|
field.componentProps.pagination.defaultPageSize = value;
|
||||||
refresh();
|
refresh();
|
||||||
|
updateSchema(schema);
|
||||||
}}
|
}}
|
||||||
defaultValue={defaultPageSize}
|
defaultValue={defaultPageSize}
|
||||||
>
|
>
|
||||||
|
<Select.Option value={10}>10</Select.Option>
|
||||||
<Select.Option value={20}>20</Select.Option>
|
<Select.Option value={20}>20</Select.Option>
|
||||||
<Select.Option value={50}>50</Select.Option>
|
<Select.Option value={50}>50</Select.Option>
|
||||||
<Select.Option value={100}>100</Select.Option>
|
<Select.Option value={100}>100</Select.Option>
|
||||||
@ -1539,7 +1955,7 @@ Table.DesignableBar = observer((props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除当前区块
|
移除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
|
@ -224,4 +224,10 @@ td.nb-table-operation {
|
|||||||
.action-bar-align-left.isOver {
|
.action-bar-align-left.isOver {
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
background-color: #e6f7ff;
|
background-color: #e6f7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nb-space-between {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { observer, connect, useField, RecursionField } from '@formily/react';
|
import { observer, connect, useField, RecursionField } from '@formily/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button, Tabs as AntdTabs, Dropdown, Menu, Switch } from 'antd';
|
import { Button, Tabs as AntdTabs, Dropdown, Menu, Switch } from 'antd';
|
||||||
import { findPropertyByPath, getSchemaPath, useDesignable } from '../../components/schema-renderer';
|
import {
|
||||||
|
findPropertyByPath,
|
||||||
|
getSchemaPath,
|
||||||
|
SchemaField,
|
||||||
|
useDesignable,
|
||||||
|
} from '../../components/schema-renderer';
|
||||||
import { Schema, SchemaKey } from '@formily/react';
|
import { Schema, SchemaKey } from '@formily/react';
|
||||||
import { PlusOutlined, MenuOutlined } from '@ant-design/icons';
|
import { PlusOutlined, MenuOutlined } from '@ant-design/icons';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@ -11,6 +16,8 @@ import './style.less';
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { DragHandle, SortableItem } from '../../components/Sortable';
|
import { DragHandle, SortableItem } from '../../components/Sortable';
|
||||||
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
||||||
|
import { FormDialog, FormLayout } from '@formily/antd';
|
||||||
|
import IconPicker from '../../components/icon-picker';
|
||||||
|
|
||||||
const useTabs = ({ singleton }) => {
|
const useTabs = ({ singleton }) => {
|
||||||
const tabsField = useField();
|
const tabsField = useField();
|
||||||
@ -38,7 +45,8 @@ const useTabs = ({ singleton }) => {
|
|||||||
|
|
||||||
export const Tabs: any = observer((props: any) => {
|
export const Tabs: any = observer((props: any) => {
|
||||||
const { singleton, ...others } = props;
|
const { singleton, ...others } = props;
|
||||||
const { schema, DesignableBar, appendChild, root, remove, insertAfter } = useDesignable();
|
const { schema, DesignableBar, appendChild, root, remove, insertAfter } =
|
||||||
|
useDesignable();
|
||||||
const tabs = useTabs({ singleton });
|
const tabs = useTabs({ singleton });
|
||||||
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
||||||
|
|
||||||
@ -93,14 +101,45 @@ export const Tabs: any = observer((props: any) => {
|
|||||||
type={'dashed'}
|
type={'dashed'}
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
const values = await FormDialog('新增标签页', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '标签名称',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
title: '标签图标',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
title: schema['title'],
|
||||||
|
icon: schema['x-component-props']?.['icon'],
|
||||||
|
},
|
||||||
|
});
|
||||||
const data = appendChild({
|
const data = appendChild({
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: uid(),
|
name: uid(),
|
||||||
title: uid(),
|
title: values.title,
|
||||||
'x-component': 'Tabs.TabPane',
|
'x-component': 'Tabs.TabPane',
|
||||||
'x-designable-bar': 'Tabs.TabPane.DesignableBar',
|
'x-designable-bar': 'Tabs.TabPane.DesignableBar',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
tab: uid(),
|
icon: values.icon,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
@ -151,6 +190,7 @@ Tabs.TabPane = observer((props: any) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={'nb-tab-pane'}>
|
<div className={'nb-tab-pane'}>
|
||||||
|
<IconPicker type={props.icon} />
|
||||||
{schema.title} <DesignableBar />
|
{schema.title} <DesignableBar />
|
||||||
</div>
|
</div>
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
@ -185,10 +225,14 @@ Tabs.DesignableBar = () => {
|
|||||||
schema['x-component-props'] || {};
|
schema['x-component-props'] || {};
|
||||||
schema['x-component-props'].singleton = singleton;
|
schema['x-component-props'].singleton = singleton;
|
||||||
field.componentProps.singleton = singleton;
|
field.componentProps.singleton = singleton;
|
||||||
|
updateSchema(schema);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
禁用标签页 <span style={{ marginRight: 24 }}></span>{' '}
|
禁用标签页 <span style={{ marginRight: 24 }}></span>{' '}
|
||||||
<Switch size={'small'} checked={!!field.componentProps.singleton}/>
|
<Switch
|
||||||
|
size={'small'}
|
||||||
|
checked={!!field.componentProps.singleton}
|
||||||
|
/>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
@ -203,7 +247,7 @@ Tabs.DesignableBar = () => {
|
|||||||
Tabs.TabPane.DesignableBar = () => {
|
Tabs.TabPane.DesignableBar = () => {
|
||||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
const field = useField();
|
||||||
return (
|
return (
|
||||||
<div className={cls('designable-bar', { active: visible })}>
|
<div className={cls('designable-bar', { active: visible })}>
|
||||||
<span
|
<span
|
||||||
@ -222,12 +266,45 @@ Tabs.TabPane.DesignableBar = () => {
|
|||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
schema.title = uid();
|
const values = await FormDialog('修改名称和图标', () => {
|
||||||
|
return (
|
||||||
|
<FormLayout layout={'vertical'}>
|
||||||
|
<SchemaField
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
title: '标签名称',
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
title: '标签图标',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}).open({
|
||||||
|
initialValues: {
|
||||||
|
title: schema['title'],
|
||||||
|
icon: schema['x-component-props']?.['icon'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
field.componentProps.icon = values.icon;
|
||||||
|
schema.title = values.title;
|
||||||
schema['x-component-props'] =
|
schema['x-component-props'] =
|
||||||
schema['x-component-props'] || {};
|
schema['x-component-props'] || {};
|
||||||
schema['x-component-props']['tab'] = uid();
|
schema['x-component-props']['icon'] = values.icon;
|
||||||
refresh();
|
refresh();
|
||||||
|
updateSchema(schema);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
修改名称和图标
|
修改名称和图标
|
||||||
@ -240,7 +317,7 @@ Tabs.TabPane.DesignableBar = () => {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除
|
移除
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ export class Table {
|
|||||||
model,
|
model,
|
||||||
fields = [],
|
fields = [],
|
||||||
indexes = [],
|
indexes = [],
|
||||||
|
sortable,
|
||||||
} = options;
|
} = options;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
@ -155,9 +156,28 @@ export class Table {
|
|||||||
this.addIndexes(indexes, 'modelOnly');
|
this.addIndexes(indexes, 'modelOnly');
|
||||||
// this.modelInit('modelOnly');
|
// this.modelInit('modelOnly');
|
||||||
this.setFields(fields);
|
this.setFields(fields);
|
||||||
|
this.initSortable();
|
||||||
database.runHooks('afterTableInit', this);
|
database.runHooks('afterTableInit', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public initSortable() {
|
||||||
|
const { sortable } = this.options;
|
||||||
|
if (!sortable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof sortable === 'string') {
|
||||||
|
this.addField({
|
||||||
|
type: 'sort',
|
||||||
|
name: sortable,
|
||||||
|
});
|
||||||
|
} else if (typeof sortable === 'object') {
|
||||||
|
this.addField({
|
||||||
|
...sortable,
|
||||||
|
type: 'sort',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public modelInit(reinitialize: Reinitialize = false) {
|
public modelInit(reinitialize: Reinitialize = false) {
|
||||||
if (reinitialize || !this.Model) {
|
if (reinitialize || !this.Model) {
|
||||||
this.Model = this.defaultModel || class extends Model { };
|
this.Model = this.defaultModel || class extends Model { };
|
||||||
@ -389,6 +409,7 @@ export class Table {
|
|||||||
for (const key in fields) {
|
for (const key in fields) {
|
||||||
this.addField(fields[key], false);
|
this.addField(fields[key], false);
|
||||||
}
|
}
|
||||||
|
this.initSortable();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.addIndexes(indexes, false);
|
this.addIndexes(indexes, false);
|
||||||
this.modelInit(true);
|
this.modelInit(true);
|
||||||
|
@ -4,11 +4,12 @@ export default {
|
|||||||
name: 'collections',
|
name: 'collections',
|
||||||
title: '数据表配置',
|
title: '数据表配置',
|
||||||
model: 'Collection',
|
model: 'Collection',
|
||||||
|
sortable: 'sort',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
// {
|
||||||
type: 'sort',
|
// type: 'sort',
|
||||||
name: 'sort',
|
// name: 'sort',
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
type: 'uid',
|
type: 'uid',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@ -24,6 +25,11 @@ export default {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'privilege',
|
name: 'privilege',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'json',
|
||||||
|
name: 'sortable',
|
||||||
|
defaultValue: 'sort',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'json',
|
type: 'json',
|
||||||
name: 'options',
|
name: 'options',
|
||||||
|
@ -5,12 +5,17 @@ export default {
|
|||||||
name: 'fields',
|
name: 'fields',
|
||||||
title: '字段配置',
|
title: '字段配置',
|
||||||
model: 'Field',
|
model: 'Field',
|
||||||
|
sortable: {
|
||||||
|
type: 'sort',
|
||||||
|
name: 'sort',
|
||||||
|
scope: ['parentKey'],
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
// {
|
||||||
type: 'sort',
|
// type: 'sort',
|
||||||
name: 'sort',
|
// name: 'sort',
|
||||||
scope: ['parentKey'],
|
// scope: ['parentKey'],
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
type: 'uid',
|
type: 'uid',
|
||||||
name: 'key',
|
name: 'key',
|
||||||
|
@ -4,12 +4,17 @@ export default {
|
|||||||
name: 'routes',
|
name: 'routes',
|
||||||
title: '路由表',
|
title: '路由表',
|
||||||
model: 'Route',
|
model: 'Route',
|
||||||
|
sortable: {
|
||||||
|
type: 'sort',
|
||||||
|
name: 'sort',
|
||||||
|
scope: ['parentKey'],
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
// {
|
||||||
type: 'sort',
|
// type: 'sort',
|
||||||
name: 'sort',
|
// name: 'sort',
|
||||||
scope: ['parentKey'],
|
// scope: ['parentKey'],
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
type: 'uid',
|
type: 'uid',
|
||||||
name: 'key',
|
name: 'key',
|
||||||
|
@ -4,17 +4,22 @@ export default {
|
|||||||
name: 'ui_schemas',
|
name: 'ui_schemas',
|
||||||
title: '字段配置',
|
title: '字段配置',
|
||||||
model: 'UISchema',
|
model: 'UISchema',
|
||||||
|
sortable: {
|
||||||
|
type: 'sort',
|
||||||
|
name: 'sort',
|
||||||
|
scope: ['parentKey'],
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: 'uid',
|
type: 'uid',
|
||||||
name: 'key',
|
name: 'key',
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
type: 'sort',
|
// type: 'sort',
|
||||||
name: 'sort',
|
// name: 'sort',
|
||||||
scope: ['parentKey'],
|
// scope: ['parentKey'],
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
@ -8710,6 +8710,11 @@ flat-to-nested@^1.1.1:
|
|||||||
resolved "https://registry.npmjs.org/flat-to-nested/-/flat-to-nested-1.1.1.tgz#ec183cd9a72f6bfbf8ca21acb0fc8235e509f581"
|
resolved "https://registry.npmjs.org/flat-to-nested/-/flat-to-nested-1.1.1.tgz#ec183cd9a72f6bfbf8ca21acb0fc8235e509f581"
|
||||||
integrity sha512-Sym5oik6BO9JnsDEjv9Q9hPTCexG2ttk0UiM2mgLEiCiiUOQr8acBd33r8ixnoSGR0HAxPoP8WtLAL5oV46IhQ==
|
integrity sha512-Sym5oik6BO9JnsDEjv9Q9hPTCexG2ttk0UiM2mgLEiCiiUOQr8acBd33r8ixnoSGR0HAxPoP8WtLAL5oV46IhQ==
|
||||||
|
|
||||||
|
flat@^5.0.2:
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||||
|
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||||
|
|
||||||
flatted@^3.1.0:
|
flatted@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
|
resolved "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
|
||||||
|
Loading…
Reference in New Issue
Block a user