feat: 添加mobile的级联组件 (#1221)
Reviewed-on: daoyoucloud/tachybase#1221 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
		
							parent
							
								
									f651f460d2
								
							
						
					
					
						commit
						05cc91c6fc
					
				@ -9,6 +9,7 @@ import {
 | 
			
		||||
  ImageSearchItemView,
 | 
			
		||||
  ImageSearchProvider,
 | 
			
		||||
  ImageSearchView,
 | 
			
		||||
  MCascader,
 | 
			
		||||
  MCheckbox,
 | 
			
		||||
  MContainer,
 | 
			
		||||
  MDatePicker,
 | 
			
		||||
@ -87,6 +88,7 @@ export const MobileCore: React.FC<PropsWithChildren> = (props) => {
 | 
			
		||||
        MDatePicker,
 | 
			
		||||
        MRadio,
 | 
			
		||||
        MImageUploader,
 | 
			
		||||
        MCascader,
 | 
			
		||||
        CollectionField: CollectionField,
 | 
			
		||||
        MSelect,
 | 
			
		||||
      }}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ const customComponent = {
 | 
			
		||||
  'Upload.Attachment': 'MImageUploader',
 | 
			
		||||
  DatePicker: 'MDatePicker',
 | 
			
		||||
  Select: 'MSelect',
 | 
			
		||||
  Cascader: 'MCascader',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const canMobileField = (componentName: string) => customComponent[componentName];
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,282 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  BlockItem,
 | 
			
		||||
  CollectionProvider,
 | 
			
		||||
  useCollectionManager,
 | 
			
		||||
  useFieldServiceFilter,
 | 
			
		||||
  useRequest,
 | 
			
		||||
} from '@tachybase/client';
 | 
			
		||||
import { observer, useFieldSchema, useForm } from '@tachybase/schema';
 | 
			
		||||
import { isArray } from '@tachybase/utils/client';
 | 
			
		||||
 | 
			
		||||
import { Button, CascaderView, Divider, Popup, SearchBar, Space } from 'antd-mobile';
 | 
			
		||||
import { CloseOutline } from 'antd-mobile-icons';
 | 
			
		||||
 | 
			
		||||
import { useTranslation } from '../../../../../locale';
 | 
			
		||||
import { MobileProvider } from '../../../provider';
 | 
			
		||||
import { MInput } from '../Input';
 | 
			
		||||
import { useStyles } from './style';
 | 
			
		||||
 | 
			
		||||
export const AntdCascader = observer((props) => {
 | 
			
		||||
  const { service, fieldNames, disabled, multiple, onChange, value, formCascaderValues, index } = props as any;
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const cm = useCollectionManager();
 | 
			
		||||
  const { styles } = useStyles();
 | 
			
		||||
  const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
 | 
			
		||||
  const changOnSelect = fieldSchema['x-component-props']?.changOnSelect || false;
 | 
			
		||||
  const collection = cm.getCollection(fieldSchema['x-collection-field']);
 | 
			
		||||
  const isChinaRegion = collectionField.interface === 'chinaRegion';
 | 
			
		||||
  const [options, setOptions] = useState([]);
 | 
			
		||||
  const [popupVisible, setPopupVisible] = useState(false);
 | 
			
		||||
  const [fieldServiceFilter] = useFieldServiceFilter(service?.params);
 | 
			
		||||
  const [filter, setFilter] = useState(fieldServiceFilter);
 | 
			
		||||
  const [searchValue, setSearchValue] = useState('');
 | 
			
		||||
  const [cascaderValue, setCascaderValue] = useState(value);
 | 
			
		||||
  const [valueText, setValueText] = useState('');
 | 
			
		||||
  const [flatOption, setFlatOption] = useState([]);
 | 
			
		||||
  const { run } = useRequest(
 | 
			
		||||
    {
 | 
			
		||||
      resource: collection?.name,
 | 
			
		||||
      action: 'list',
 | 
			
		||||
      params: {
 | 
			
		||||
        filter,
 | 
			
		||||
        tree: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      manual: true,
 | 
			
		||||
      onSuccess({ data }) {
 | 
			
		||||
        if (data) {
 | 
			
		||||
          setOptions(cascadeOption(data));
 | 
			
		||||
          const valueOption = flatOptions(data);
 | 
			
		||||
          setFlatOption(valueOption);
 | 
			
		||||
          if (value) {
 | 
			
		||||
            setValueText(getValueText(valueOption, value, '', fieldNames['label']));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const cascadeOption = (option) => {
 | 
			
		||||
    option.forEach((item) => {
 | 
			
		||||
      item['value'] = item[fieldNames.value];
 | 
			
		||||
      item['label'] = item[fieldNames.label];
 | 
			
		||||
      if (item.children) {
 | 
			
		||||
        item.children = cascadeOption(item.children);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return option;
 | 
			
		||||
  };
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!isChinaRegion && collection?.name) {
 | 
			
		||||
      checkedPopup();
 | 
			
		||||
    }
 | 
			
		||||
  }, [filter]);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (options.length) {
 | 
			
		||||
      cascadeOption(options);
 | 
			
		||||
    }
 | 
			
		||||
  }, [fieldSchema['x-component-props']?.['fieldNames']]);
 | 
			
		||||
 | 
			
		||||
  const handleSelect = async (option) => {
 | 
			
		||||
    if (option) {
 | 
			
		||||
      setCascaderValue(option[option.length - 1]);
 | 
			
		||||
    } else {
 | 
			
		||||
      setCascaderValue({});
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const checkedPopup = () => {
 | 
			
		||||
    if (collection && collection.name) {
 | 
			
		||||
      run();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <CollectionProvider name={collectionField.collectionName}>
 | 
			
		||||
      <div
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          if (disabled) return;
 | 
			
		||||
          setPopupVisible(true);
 | 
			
		||||
          checkedPopup();
 | 
			
		||||
        }}
 | 
			
		||||
        style={{ display: 'flex', alignItems: 'center' }}
 | 
			
		||||
      >
 | 
			
		||||
        {value && Object.keys(value).length ? (
 | 
			
		||||
          <MInput style={{ '--color': '#1e8bf1' }} value={valueText} disabled={disabled} clearable={false} />
 | 
			
		||||
        ) : (
 | 
			
		||||
          <MInput value={valueText ? '' : '请选择'} style={{ '--color': '#c5c5c5' }} />
 | 
			
		||||
        )}
 | 
			
		||||
        {(value && Object.keys(value).length) || (formCascaderValues && index !== 0) ? (
 | 
			
		||||
          <CloseOutline
 | 
			
		||||
            style={{ margin: '5px 10px 5px 20px' }}
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              if (index) {
 | 
			
		||||
                const formValue = [...formCascaderValues];
 | 
			
		||||
                if (formCascaderValues.length === 1 && index === 0) {
 | 
			
		||||
                  formValue[index] = {};
 | 
			
		||||
                } else {
 | 
			
		||||
                  formValue.splice(index, 1);
 | 
			
		||||
                }
 | 
			
		||||
                onChange(formValue);
 | 
			
		||||
              } else {
 | 
			
		||||
                onChange({});
 | 
			
		||||
              }
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        ) : null}
 | 
			
		||||
      </div>
 | 
			
		||||
      <Popup
 | 
			
		||||
        visible={popupVisible}
 | 
			
		||||
        className={`${styles['PopupStyle']}`}
 | 
			
		||||
        onMaskClick={() => {
 | 
			
		||||
          setPopupVisible(false);
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <MobileProvider>
 | 
			
		||||
          <SearchBar
 | 
			
		||||
            placeholder="请输入内容"
 | 
			
		||||
            value={searchValue}
 | 
			
		||||
            onChange={(value) => {
 | 
			
		||||
              const paramsFilter = { ...filter };
 | 
			
		||||
              paramsFilter[fieldNames['label']] = { $includes: value };
 | 
			
		||||
              setFilter(paramsFilter);
 | 
			
		||||
              setSearchValue(value);
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
          <Divider />
 | 
			
		||||
          <CascaderView options={options} onChange={(value, extend) => handleSelect(extend['items'])} />
 | 
			
		||||
          <Space justify="evenly" align="center">
 | 
			
		||||
            <Button
 | 
			
		||||
              color="primary"
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                setPopupVisible(false);
 | 
			
		||||
                const paramsFilter = { ...filter };
 | 
			
		||||
                if (paramsFilter[fieldNames['label']]) delete paramsFilter[fieldNames['label']];
 | 
			
		||||
                setSearchValue('');
 | 
			
		||||
                setFilter(paramsFilter);
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              取消
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
              color="primary"
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                const changevalue = ['m2m', 'o2m'].includes(collectionField.interface)
 | 
			
		||||
                  ? getChangValue(formCascaderValues, cascaderValue, index)
 | 
			
		||||
                  : cascaderValue;
 | 
			
		||||
                onChange(changevalue);
 | 
			
		||||
                setValueText(getValueText(flatOption, cascaderValue, '', fieldNames['label']));
 | 
			
		||||
                setPopupVisible(false);
 | 
			
		||||
                const paramsFilter = { ...filter };
 | 
			
		||||
                if (paramsFilter[fieldNames['label']]) delete paramsFilter[fieldNames['label']];
 | 
			
		||||
                setSearchValue('');
 | 
			
		||||
                setFilter(paramsFilter);
 | 
			
		||||
              }}
 | 
			
		||||
              disabled={
 | 
			
		||||
                !changOnSelect
 | 
			
		||||
                  ? cascaderValue && cascaderValue !== '{}' && !cascaderValue?.children
 | 
			
		||||
                    ? false
 | 
			
		||||
                    : true
 | 
			
		||||
                  : cascaderValue && cascaderValue !== '{}'
 | 
			
		||||
                    ? false
 | 
			
		||||
                    : true
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              确定
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Space>
 | 
			
		||||
        </MobileProvider>
 | 
			
		||||
      </Popup>
 | 
			
		||||
    </CollectionProvider>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const getValueText = (option, item, value, label) => {
 | 
			
		||||
  const text = `${item?.[label] || ''}${value}`;
 | 
			
		||||
  if (item?.parentId) {
 | 
			
		||||
    const filterItem = option.find((optionsItem) => optionsItem.id === item.parentId);
 | 
			
		||||
    const itemText = `/${text}`;
 | 
			
		||||
    return getValueText(option, filterItem, itemText, label);
 | 
			
		||||
  }
 | 
			
		||||
  return text;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const flatOptions = (options) => {
 | 
			
		||||
  const flatChildren = (item, option) => {
 | 
			
		||||
    option.push(item);
 | 
			
		||||
    if (item?.children) {
 | 
			
		||||
      item.children.forEach((childrenItem) => {
 | 
			
		||||
        return flatChildren(childrenItem, option);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    return option;
 | 
			
		||||
  };
 | 
			
		||||
  const optionflat = [];
 | 
			
		||||
  options.forEach((value) => {
 | 
			
		||||
    const flatValue = flatChildren(value, []);
 | 
			
		||||
    optionflat.push(...flatValue);
 | 
			
		||||
  });
 | 
			
		||||
  return optionflat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getChangValue = (formCascaderValues, cascaderValue, index) => {
 | 
			
		||||
  const value = [];
 | 
			
		||||
  if (formCascaderValues && (index || index === 0)) {
 | 
			
		||||
    formCascaderValues[index] = cascaderValue;
 | 
			
		||||
    value.push(...formCascaderValues);
 | 
			
		||||
  } else {
 | 
			
		||||
    value.push(cascaderValue);
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const InternalCascader = observer((props) => {
 | 
			
		||||
  const { value, onChange } = props as any;
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const cm = useCollectionManager();
 | 
			
		||||
  const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {isArray(value) ? (
 | 
			
		||||
        <>
 | 
			
		||||
          {value.map((item, index) => {
 | 
			
		||||
            const filterProps = {
 | 
			
		||||
              ...props,
 | 
			
		||||
              value: item,
 | 
			
		||||
              formCascaderValues: value || [],
 | 
			
		||||
              index,
 | 
			
		||||
            };
 | 
			
		||||
            return <AntdCascader {...filterProps} key={index} />;
 | 
			
		||||
          })}
 | 
			
		||||
        </>
 | 
			
		||||
      ) : (
 | 
			
		||||
        <AntdCascader {...props} />
 | 
			
		||||
      )}
 | 
			
		||||
      {['m2m', 'o2m'].includes(collectionField.interface) ? (
 | 
			
		||||
        <>
 | 
			
		||||
          <Button
 | 
			
		||||
            style={{
 | 
			
		||||
              textAlign: 'center',
 | 
			
		||||
              width: '100%',
 | 
			
		||||
              border: '1px dashed #c5c5c5',
 | 
			
		||||
              color: '#c5c5c5',
 | 
			
		||||
              fontSize: '12px',
 | 
			
		||||
              height: '5vh',
 | 
			
		||||
              lineHeight: '2vh',
 | 
			
		||||
            }}
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              const pushValue = value ? [...value, {}] : [{}, {}];
 | 
			
		||||
              onChange(pushValue);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            + {t('Add new')}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </>
 | 
			
		||||
      ) : null}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@tachybase/schema';
 | 
			
		||||
import { isArray } from '@tachybase/utils/client';
 | 
			
		||||
 | 
			
		||||
import { InternalCascader } from './AntdCadcader';
 | 
			
		||||
import { ReadPretty } from './ReadPretty';
 | 
			
		||||
 | 
			
		||||
export const MCascader = connect(
 | 
			
		||||
  InternalCascader,
 | 
			
		||||
  mapProps((props) => {
 | 
			
		||||
    return { ...props };
 | 
			
		||||
  }),
 | 
			
		||||
  mapReadPretty(ReadPretty),
 | 
			
		||||
);
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { ArrayField, toArr, useField } from '@tachybase/schema';
 | 
			
		||||
 | 
			
		||||
import { defaultFieldNames } from './defaultFieldNames';
 | 
			
		||||
 | 
			
		||||
export const ReadPretty: React.FC<unknown> = (props: any) => {
 | 
			
		||||
  const { fieldNames = defaultFieldNames } = props;
 | 
			
		||||
  const values = toArr(props.value);
 | 
			
		||||
  const len = values.length;
 | 
			
		||||
  const field = useField<ArrayField>();
 | 
			
		||||
  let dataSource = field.dataSource;
 | 
			
		||||
  const data = [];
 | 
			
		||||
  for (const item of values) {
 | 
			
		||||
    if (typeof item === 'object') {
 | 
			
		||||
      data.push(item);
 | 
			
		||||
    } else {
 | 
			
		||||
      const curr = dataSource?.find((v) => v[fieldNames.value] === item);
 | 
			
		||||
      dataSource = curr?.[fieldNames.children] || [];
 | 
			
		||||
      data.push(curr || { label: item, value: item });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      {data.map((item, index) => {
 | 
			
		||||
        return (
 | 
			
		||||
          <span key={index}>
 | 
			
		||||
            {typeof item === 'object' ? item[fieldNames.label] : item}
 | 
			
		||||
            {len > index + 1 && ' / '}
 | 
			
		||||
          </span>
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
export const defaultFieldNames = {
 | 
			
		||||
  label: 'label',
 | 
			
		||||
  value: 'value',
 | 
			
		||||
  children: 'children',
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
export * from './Cadcader';
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
import { createStyles } from '@tachybase/client';
 | 
			
		||||
 | 
			
		||||
export const useStyles = createStyles(({ css }) => ({
 | 
			
		||||
  PopupStyle: css`
 | 
			
		||||
    .adm-popup-body {
 | 
			
		||||
      padding: 10px;
 | 
			
		||||
      .adm-space {
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        margin: 10px 0 10px 0;
 | 
			
		||||
        color: #4592ff;
 | 
			
		||||
        button {
 | 
			
		||||
          width: 23vw;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      .adm-divider {
 | 
			
		||||
        margin: 0;
 | 
			
		||||
      }
 | 
			
		||||
      .adm-list-body {
 | 
			
		||||
        height: 30vh;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        text-align: center;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  `,
 | 
			
		||||
}));
 | 
			
		||||
@ -226,7 +226,6 @@ export const AntdSelect = observer((props) => {
 | 
			
		||||
                  if (paramsFilter[fieldNamesLabel]) delete paramsFilter[fieldNamesLabel];
 | 
			
		||||
                  setSearchValue('');
 | 
			
		||||
                  setFilter(paramsFilter);
 | 
			
		||||
                  setSearchValue('');
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                确定
 | 
			
		||||
 | 
			
		||||
@ -1,131 +0,0 @@
 | 
			
		||||
export default {
 | 
			
		||||
  Nester: {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    'x-component': 'AssociationField.Nester',
 | 
			
		||||
    properties: {
 | 
			
		||||
      grid: {
 | 
			
		||||
        type: 'void',
 | 
			
		||||
        'x-component': 'Grid',
 | 
			
		||||
        'x-initializer': 'form:configureFields',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  AddNewer: {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    'x-component': 'AssociationField.AddNewer',
 | 
			
		||||
    'x-action': 'create',
 | 
			
		||||
    title: '{{ t("Add record") }}',
 | 
			
		||||
    'x-component-props': {
 | 
			
		||||
      className: 'nb-action-popup',
 | 
			
		||||
    },
 | 
			
		||||
    properties: {
 | 
			
		||||
      tabs: {
 | 
			
		||||
        type: 'void',
 | 
			
		||||
        'x-component': 'Tabs',
 | 
			
		||||
        'x-component-props': {},
 | 
			
		||||
        'x-initializer': 'TabPaneInitializersForCreateFormBlock',
 | 
			
		||||
        properties: {
 | 
			
		||||
          tab1: {
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            title: '{{t("Add new")}}',
 | 
			
		||||
            'x-component': 'Tabs.TabPane',
 | 
			
		||||
            'x-designer': 'Tabs.Designer',
 | 
			
		||||
            'x-component-props': {},
 | 
			
		||||
            properties: {
 | 
			
		||||
              grid: {
 | 
			
		||||
                type: 'void',
 | 
			
		||||
                'x-component': 'Grid',
 | 
			
		||||
                'x-initializer': 'popup:addNew:addBlock',
 | 
			
		||||
                properties: {},
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  Selector: {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    'x-component': 'AssociationField.Selector',
 | 
			
		||||
    title: '{{ t("Select record") }}',
 | 
			
		||||
    'x-component-props': {
 | 
			
		||||
      className: 'nb-record-picker-selector',
 | 
			
		||||
    },
 | 
			
		||||
    properties: {
 | 
			
		||||
      grid: {
 | 
			
		||||
        type: 'void',
 | 
			
		||||
        'x-component': 'Grid',
 | 
			
		||||
        'x-initializer': 'popup:tableSelector:addBlock',
 | 
			
		||||
        properties: {},
 | 
			
		||||
      },
 | 
			
		||||
      footer: {
 | 
			
		||||
        'x-component': 'Action.Container.Footer',
 | 
			
		||||
        'x-component-props': {},
 | 
			
		||||
        properties: {
 | 
			
		||||
          actions: {
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            'x-component': 'ActionBar',
 | 
			
		||||
            'x-component-props': {},
 | 
			
		||||
            properties: {
 | 
			
		||||
              submit: {
 | 
			
		||||
                title: '{{ t("Submit") }}',
 | 
			
		||||
                'x-action': 'submit',
 | 
			
		||||
                'x-component': 'Action',
 | 
			
		||||
                'x-use-component-props': 'usePickActionProps',
 | 
			
		||||
                // 'x-designer': 'Action.Designer',
 | 
			
		||||
                'x-toolbar': 'ActionSchemaToolbar',
 | 
			
		||||
                'x-settings': 'actionSettings:submit',
 | 
			
		||||
                'x-component-props': {
 | 
			
		||||
                  type: 'primary',
 | 
			
		||||
                  htmlType: 'submit',
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  Viewer: {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    title: '{{ t("View record") }}',
 | 
			
		||||
    'x-component': 'AssociationField.Viewer',
 | 
			
		||||
    'x-component-props': {
 | 
			
		||||
      className: 'nb-action-popup',
 | 
			
		||||
    },
 | 
			
		||||
    properties: {
 | 
			
		||||
      tabs: {
 | 
			
		||||
        type: 'void',
 | 
			
		||||
        'x-component': 'Tabs',
 | 
			
		||||
        'x-component-props': {},
 | 
			
		||||
        'x-initializer': 'TabPaneInitializers',
 | 
			
		||||
        properties: {
 | 
			
		||||
          tab1: {
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            title: '{{t("Details")}}',
 | 
			
		||||
            'x-component': 'Tabs.TabPane',
 | 
			
		||||
            'x-designer': 'Tabs.Designer',
 | 
			
		||||
            'x-component-props': {},
 | 
			
		||||
            properties: {
 | 
			
		||||
              grid: {
 | 
			
		||||
                type: 'void',
 | 
			
		||||
                'x-component': 'Grid',
 | 
			
		||||
                'x-initializer': 'popup:common:addBlock',
 | 
			
		||||
                properties: {},
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  SubTable: {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    'x-component': 'AssociationField.SubTable',
 | 
			
		||||
    'x-initializer': 'table:configureColumns',
 | 
			
		||||
    'x-initializer-props': {
 | 
			
		||||
      action: false,
 | 
			
		||||
    },
 | 
			
		||||
    properties: {},
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@ -4,3 +4,4 @@ export * from './DatePicker';
 | 
			
		||||
export * from './ImageUploader';
 | 
			
		||||
export * from './Radio';
 | 
			
		||||
export * from './Select';
 | 
			
		||||
export * from './Cascader';
 | 
			
		||||
 | 
			
		||||
@ -45,5 +45,6 @@
 | 
			
		||||
  "Add block": "Add block",
 | 
			
		||||
  "all": "all",
 | 
			
		||||
  "AllProducts": "AllProducts",
 | 
			
		||||
  "sort":"sort"
 | 
			
		||||
  "sort":"sort",
 | 
			
		||||
  "Add new":"Add new"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,5 +48,6 @@
 | 
			
		||||
  "sort": "排序",
 | 
			
		||||
  "Swiper":"轮播图",
 | 
			
		||||
  "TabSearch":"表格搜索",
 | 
			
		||||
  "ImageSearch":"图片搜索"
 | 
			
		||||
  "ImageSearch":"图片搜索",
 | 
			
		||||
  "Add new":"添加"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user