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