diff --git a/docs/en/develop/3-10-FormItem-introduction.md b/docs/en/develop/3-10-FormItem-introduction.md new file mode 100644 index 00000000..228d982a --- /dev/null +++ b/docs/en/develop/3-10-FormItem-introduction.md @@ -0,0 +1,1047 @@ +English | [简体中文](/docs/zh/develop/3-10-FormItem-introduction.md) + +# Usage + +- Configuration of each form item in the form +- Generally only need to configure a little amount of parameters such as `type` +- `Form` component will verify the input value base on `formItem` configuration +- When verify faild, `Form` will not allowed to click `confirm` or `next` + +# How to use + +- Each form item contains universal configuration + - `name`, `key` of form item, required and unique. The value of the form is saved in `form.values[name]` after verification. + - `label`, the label on the left of form item. + - `required`, optional, the default is `false`, when `true` means the form item value must be input. + - `hidden`, whether to hidden the form item, default is `false`. + - `onChange`, set the handler to handle after the form item value change. + - `extra`, the information under form item. + - Take create network as an example `src/pages/network/containers/Network/actions/CreateNetwork.jsx` : + + ```javascript + { + name: 'mtu', + label: t('MTU'), + type: 'input-number', + min: 68, + max: 9000, + extra: t('Minimum value is 68 for IPv4, and 1280 for IPv6.'), + } + ``` + + ![extra](/docs/zh/develop/images/form/form-extra.png) + + - `tip`, the `?` icon at the right side of label, hover to see `tip`. + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx` : + + ```javascript + { + name: 'availableZone', + label: t('Available Zone'), + type: 'select', + placeholder: t('Please select'), + isWrappedValue: true, + required: true, + options: this.availableZones, + tip: t( + 'Availability zone refers to a physical area where power and network are independent of each other in the same area. In the same region, the availability zone and the availability zone can communicate with each other in the intranet, and the available zones can achieve fault isolation.' + ), + } + ``` + + ![tip](/docs/zh/develop/images/form/form-tip.png) + + - `validator`, validate whether the value of form item matchs requirements + - Return `Promise` + - Take create port as an example `src/pages/compute/containers/BareMetalNode/Detail/Port/actions/Create.jsx` : + + ```javascript + export const macAddressValidate = (rule, value) => { + if (isMacAddress(value.toUpperCase())) { + return Promise.resolve(true); + } + return Promise.reject(new Error(`${t('Invalid: ')}${macAddressMessage}`)); + }; + { + name: 'address', + label: t('MAC Address'), + required: true, + type: 'input', + validator: macAddressValidate, + } + ``` + + - `component`, directly use component to render instead of component configured in `type` + - Take resize instance as an example `src/pages/compute/containers/Instance/actions/Resize.jsx` : + - Direct display `FlavorSelectTable` component + + ```javascript + { + name: 'newFlavor', + label: t('Flavor'), + component: ( + + ), + required: true, + wrapperCol: { + xs: { + span: 24, + }, + sm: { + span: 18, + }, + }, + } + ``` + + - `labelCol`, adjust the layout of the form title, default use of the label layout defined under `form` + - Take manage quota as an example `src/pages/identity/containers/Project/actions/QuotaManager.jsx` : + + ```javascript + { + name: 'instances', + label: t('instance'), + type: 'input-number', + labelCol: { span: 12 }, + colNum: 2, + validator: this.checkMin, + } + ``` + + ![labelCol](/docs/zh/develop/images/form/label-col.png) + + - `wrapperCol`, adjust the layout of the right side of the form, default use of the layout defined under + - Take resize instance as an example `src/pages/compute/containers/Instance/actions/Resize.jsx` : + - Direct display `FlavorSelectTable` component + + ```javascript + { + name: 'newFlavor', + label: t('Flavor'), + component: ( + + ), + required: true, + wrapperCol: { + xs: { + span: 24, + }, + sm: { + span: 18, + }, + }, + } + ``` + + ![wrapperCol](/docs/zh/develop/images/form/wrapper-col.png) + + - `style`, define the style of the form + - Take create port as an example `src/pages/network/containers/VirtualAdapter/actions/Create.jsx` + + ```javascript + { + name: 'ipv6', + label: 'IPv6', + type: 'label', + style: { marginBottom: 24 }, + content: ( + + {t('The selected VPC/ subnet does not have IPv6 enabled.')}{' '} + {' '} + + ), + hidden: true, + } + ``` + + - `dependencies`, dependencies, array. after the value in the dependency changed, the verification of the current form is triggered. + - Take change password of instance as an example `src/pages/compute/containers/Instance/actions/ChangePassword.jsx` : + - `Confirm Password` verification, depending on the password input + + ```javascript + { + name: 'confirmPassword', + label: t('Confirm Password'), + type: 'input-password', + dependencies: ['password'], + required: true, + otherRule: getPasswordOtherRule('confirmPassword', 'instance'), + }, + ``` + + - `otherRule`, Additional verification rules + +- Based on its own `type`, each form has an independent configuration item, currently supported `type` are: + - `label` + - Used for show content + - `iconType` attribute, can show the resource corresponding icon + + ```javascript + const iconTypeMap = { + instance: , + router: , + externalNetwork: , + network: , + firewall: , + volume: , + gateway: , + user: , + snapshot: , + backup: , + keypair: , + image: ImageIcon, + aggregate: , + metadata: , + flavor: , + host: , + }; + ``` + + - Take attach volumn as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + { + name: 'instance', + label: t('Instance'), + type: 'label', + iconType: 'instance', + }, + ``` + + ![label](/docs/zh/develop/images/form/form-label.png) + + - `content` attribute, default is base on `name` to show, if has `content`, will show things in `content` + - `content` can be string / ReactNode + - Take modify qos as an example `src/pages/network/containers/VirtualAdapter/actions/ModifyQoS.jsx` : + + ```javascript + { + name: 'name', + label: t('Current QoS policy name'), + type: 'label', + content:
{qosPolicy.name || t('Not yet bound')}
, + hidden: !enableQosPolicy, + } + ``` + + - `input` + - input + - Take edit image as an example `src/pages/compute/containers/Image/actions/Edit.jsx` : + - input system version + + ```javascript + { + name: 'os_version', + label: t('OS Version'), + type: 'input', + required: true, + }, + ``` + + ![input](/docs/zh/develop/images/form/input.png) + + - `select` + - selector + - `options`, required, `option` array, each `option` has following attributes: + - `value`, value + - `label`, text to show + - Take select az when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx` : + + ```javascript + get availableZones() { + return (globalAvailabilityZoneStore.list.data || []) + .filter((it) => it.zoneState.available) + .map((it) => ({ + value: it.zoneName, + label: it.zoneName, + })); + } + + { + name: 'availableZone', + label: t('Available Zone'), + type: 'select', + placeholder: t('Please select'), + isWrappedValue: true, + required: true, + options: this.availableZones, + tip: t( + 'Availability zone refers to a physical area where power and network are independent of each other in the same area. In the same region, the availability zone and the availability zone can communicate with each other in the intranet, and the available zones can achieve fault isolation.' + ), + }, + ``` + + ![select](/docs/zh/develop/images/form/select.png) + + - `isWrappedValue`, indicates whether to include `option` information in the form item value + - Default `false`, value is the value of selected `option` + - If true, value is the `option` + + - `divider` + - Horizontal separator + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx` + + ```javascript + { + type: 'divider', + } + ``` + + ![divider](/docs/zh/develop/images/form/form-divider.png) + + - `radio` + - radio + - `options`, required, `option` array, each `option` has following attributes: + - `value`, value + - `label`, text to show + - Take choose login type when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/SystemStep/index.jsx` : + + ```javascript + get loginTypes() { + return [ + { + label: t('Keypair'), + value: 'keypair', + disabled: this.isWindowsImage, + }, + { + label: t('Password'), + value: 'password', + }, + ]; + } + + { + name: 'loginType', + label: t('Login Type'), + type: 'radio', + options: this.loginTypes, + isWrappedValue: true, + }, + ``` + + ![radio](/docs/zh/develop/images/form/radio.png) + + - `isWrappedValue`, indicates whether to include `option` information in the form item value + - Default `false`, value is the value of selected `option` + - If true, value is the `option` + + - `select-table` + - Table with selected actions + - `isMulti`, whether is multi, default is `false` + - `datas`, data source, using when front end paging + - `columns`, table columns configuration, the same as `BaseList` + - `filterParams`, search configuration + - `pageSize`, items number per page, default `5` + - `disabledFunc`, to configure which item can not be selected + - `selectedLabel`, the label at the bottom of the table, default is `selected` + - `header`, the content above the table + - `backendPageStore`, when backend paging, the data corresponding `store` + - `backendPageFunc`, when backend paging, the function to fetch data, default is `store.fetchListByPage`. + - `backendPageDataKey`, when backend paging, the key of data in `store`, default is `list`. + - `extraParams`, when backend paging, the extra params when sending request. + - `isSortByBack`, whether sort by backend, default is `false`. + - `defaultSortKey`, when using backend paging, default sort key. + - `defaultSortOrder`, when using backend paging, default sort order. + - `initValue`, initial value. + - `rowKey`, the `id` of each column. + - `onRow`, the handler to handle row click, deafult will select row when click. + - `tabs`, tab-type table + - `defaultTabValue`, when is tab-type tab, default `tab` + - `onTabChange`, when is tab-type tab, handler to handle tab change + - Take choose security group when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/NetworkStep/index.jsx` : + - The table has a tip on the left of label. + - Use backend paging & has extra params + - Is multi + - Has extra content on the top of table + - Need to override `onRow` to avoid click `see rules` button then selected + + ```javascript + { + name: 'securityGroup', + label: t('Security Group'), + type: 'select-table', + tip: t( + 'Each instance belongs to at least one security group, which needs to be specified when it is created. Instances in the same security group can communicate with each other on the network, and instances in different security groups are disconnected from the internal network by default.' + ), + backendPageStore: this.securityGroupStore, + extraParams: { project_id: this.currentProjectId }, + required: true, + isMulti: true, + header: ( +
+ {t( + 'The security group is similar to the firewall function and is used to set up network access control. ' + )} + {t(' You can go to the console to ')} + + {t('create a new security group')}>{' '} + + {t( + 'Note: The security group you use will act on all virtual adapters of the instance.' + )} +
+ ), + filterParams: securityGroupFilter, + columns: securityGroupColumns, + onRow: () => {}, + }, + ``` + + ![select-table](/docs/zh/develop/images/form/select-table.png) + + - Take create volumn as an example `src/pages/storage/containers/Volume/actions/Create/index.jsx` : + - This is a table with tab, default to show the first tab, when switching tab, data source will change + - Data is acquired by the front end paging, just directly configure the `datas` + - Not multi selected + - Configure selected label to `Image` + + ```javascript + { + name: 'image', + label: t('Operating System'), + type: 'select-table', + datas: this.images, + required: sourceTypesIsImage, + isMulti: false, + hidden: !sourceTypesIsImage, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + columns: getImageColumns(this), + tabs: this.systemTabs, + defaultTabValue: this.systemTabs[0].value, + selectedLabel: t('Image'), + onTabChange: this.onImageTabChange, + } + ``` + + ![select-table-tabs](/docs/zh/develop/images/form/select-table-tabs.png) + + - `input-number` + - Number input + - `min`, min number + - `max`, max number + - Take set MTU when create network as an example `src/pages/network/containers/Network/actions/CreateNetwork.jsx` : + - set min & max + + ```javascript + { + name: 'mtu', + label: t('MTU'), + type: 'input-number', + min: 68, + max: 9000, + extra: t('Minimum value is 68 for IPv4, and 1280 for IPv6.'), + }, + ``` + + ![input-number](/docs/zh/develop/images/form/input-number.png) + + - `input-int` + - Integer input + - `min`, min + - `max`, max + - Take set min disk when create image as an example `src/pages/compute/containers/Image/actions/Create.jsx` : + - set min & max + + ```javascript + { + name: 'min_disk', + label: t('Min System Disk(GB)'), + type: 'input-int', + min: 0, + max: 500, + } + ``` + + ![input-int](/docs/zh/develop/images/form/input-int.png) + + - `instance-volume` + - Insatnce volume configuration component + - `options`, volumn types options + - `minSize`, volumn size input min + - Take configure system disk when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx` : + + ```javascript + { + name: 'systemDisk', + label: t('System Disk'), + type: 'instance-volume', + options: this.volumeTypes, + required: !this.sourceTypeIsVolume, + hidden: this.sourceTypeIsVolume, + validator: this.checkSystemDisk, + minSize: this.getSystemDiskMinSize(), + extra: t('Disk size is limited by the min disk of flavor, image, etc.'), + onChange: this.onSystemDiskChange, + } + ``` + + ![instance-volume](/docs/zh/develop/images/form/instance-volume.png) + + - `input-password` + - password input + - Take set password when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/SystemStep/index.jsx` + - input password, confirm password, and verify password, ensure the consistency of the two input data + + ```javascript + { + name: 'password', + label: t('Login Password'), + type: 'input-password', + required: isPassword, + hidden: !isPassword, + otherRule: getPasswordOtherRule('password', 'instance'), + }, + { + name: 'confirmPassword', + label: t('Confirm Password'), + type: 'input-password', + required: isPassword, + hidden: !isPassword, + otherRule: getPasswordOtherRule('confirmPassword', 'instance'), + }, + ``` + + ![input-password](/docs/zh/develop/images/form/input-password.png) + + - `input-name` + - Name input box with format verification + - `placeholder`, placeholder for input + - `isFile`, verify name in file format + - `isKeypair`, verify name with key-pair support + - `isStack`, verify name with stack supported + - `isImage`, verify name with image suppport + - `isInstance`, verify name with instance suppport + - Take set name when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/SystemStep/index.jsx` : + + ```javascript + { + name: 'name', + label: t('Name'), + type: 'input-name', + placeholder: t('Please input name'), + required: true, + isInstance: true, + } + ``` + + ![input-name](/docs/zh/develop/images/form/input-name.png) + + - `port-range` + - Port input with Verification + - Take set source port when create security group rule as an example `src/pages/network/containers/SecurityGroup/Detail/Rule/actions/Create.jsx` : + + ```javascript + { + name: 'sourcePort', + label: t('Source Port/Port Range'), + type: 'port-range', + required: showSourcePort, + hidden: !showSourcePort, + } + ``` + + ![port-range](/docs/zh/develop/images/form/port-range.png) + + - `more` + - Hide / show more configuration items + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/SystemStep/index.jsx` : + + ```javascript + { + name: 'more', + label: t('Advanced Options'), + type: 'more', + } + ``` + + ![more](/docs/zh/develop/images/form/more.png) + + - `textarea` + - textarea + - Take set description when edit volumn as an example `src/pages/storage/containers/Volume/actions/Edit.jsx` : + + ```javascript + { + name: 'description', + label: t('Description'), + type: 'textarea', + } + ``` + + ![textarea](/docs/zh/develop/images/form/textarea.png) + + - `upload` + - file upload + - Take upload image as an example `src/pages/compute/containers/Image/actions/Create.jsx` : + + ```javascript + { + name: 'file', + label: t('File'), + type: 'upload', + required: true, + } + ``` + + ![upload](/docs/zh/develop/images/form/upload.png) + + - `add-select` + - Can added, delete an entry form item + - `minCount`, min entry count + - `maxCount`, max entry count + - `itemComponent`, click this component to add each entry + - `defaultItemValue`, the default value of new entry + - `addText`, the text on the right side of add item button component + - `addTextTips`, if has `maxCount`, the text will update with count + - Take set data disk when create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/BaseStep/index.jsx` : + - can set unlimit number of data disk + + ```javascript + { + name: 'dataDisk', + label: t('Data Disk'), + type: 'add-select', + options: this.volumeTypes, + defaultItemValue: this.defaultVolumeType, + itemComponent: InstanceVolume, + minCount: 0, + addTextTips: t('Data Disks'), + addText: t('Add Data Disks'), + extra: t( + 'Too many disks mounted on the instance will affect the read and write performance. It is recommended not to exceed 16 disks.' + ), + onChange: this.onDataDiskChange, + }, + ``` + + ![add-select](/docs/zh/develop/images/form/add-select.png) + + - `ip-input` + - IP input with verification + - `version`, ip version, default is `4`, also can be `6` + - Take attach interface to instance set ip as an example `src/pages/compute/containers/Instance/actions/AttachInterface.jsx` + + ```javascript + { + name: 'ip', + label: t('Given IP'), + type: 'ip-input', + required: ipType === 1, + hidden: ipType !== 1, + version, + // defaultIp, + validator: this.checkIP, + extra: t('Please make sure this IP address be available.'), + } + ``` + + ![ip-input](/docs/zh/develop/images/form/ip-input.png) + + - `member-allocator` + - Member allocator used in load balancer + - Take set member in lb as an example `src/pages/network/containers/LoadBalancers/StepCreateComponents/MemberStep/index.jsx` : + + ```javascript + { + name: 'extMembers', + type: 'member-allocator', + isLoading: this.store.list.isLoading, + ports: this.state.ports, + } + ``` + + ![member-allocator](/docs/zh/develop/images/form/member-allocator.png) + + - `descriptions` + - A form item to show types of information + - `title`, title of right content + - `onClick`, the jump button behind title + - `items`, configuration of each information to display, array. + - `label`, information display on the left side of the item + - `value`, information display on the right side of the item + - `span`, layout of label and value + - Take create instance confirm as an example `src/pages/compute/containers/Instance/actions/StepCreate/ConfirmStep/index.jsx` : + + ```javascript + { + name: 'confirm-config', + label: t('Config Overview'), + type: 'descriptions', + title: t('Base Config'), + onClick: () => { + this.goStep(0); + }, + items: [ + { + label: t('Start Source'), + value: context.source.label, + }, + { + label: t('System Disk'), + value: this.getSystemDisk(), + }, + { + label: t('Available Zone'), + value: context.availableZone.label, + }, + { + label: t('Start Source Name'), + value: this.getSourceValue(), + }, + { + label: t('Data Disk'), + value: this.getDataDisk(), + }, + { + label: t('Project'), + value: context.project, + }, + { + label: t('Flavor'), + value: this.getFlavor(), + }, + ], + } + ``` + + ![descriptions](/docs/zh/develop/images/form/descriptions.png) + + - `slider-input` + - Input + Slider linkage form item + - `min`, min value + - `max`, max value + - `description`, description under slider + - Take set size when create volumn as an example `src/pages/storage/containers/Volume/actions/Create/index.jsx` : + + ```javascript + { + name: 'size', + label: t('Capacity (GB)'), + type: 'slider-input', + max: this.maxSize, + min: minSize, + description: `${minSize}GB-${this.maxSize}GB`, + required: this.quotaIsLimit, + hidden: !this.quotaIsLimit, + onChange: this.onChangeSize, + }, + ``` + + ![slider-input](/docs/zh/develop/images/form/slider-input.png) + + - `title` + - Show title + - Take set params when create stack as an example `src/pages/heat/containers/Stack/actions/Create/Parameter.jsx` : + + ```javascript + { + label: t('Fill In The Parameters'), + type: 'title', + } + ``` + + ![title](/docs/zh/develop/images/form/title.png) + + - `switch` + - switch form item + - Take set port security when create port as an example `src/pages/network/containers/VirtualAdapter/actions/Create.jsx` : + + ```javascript + { + name: 'port_security_enabled', + label: t('Port Security'), + type: 'switch', + tip: t( + 'Disabling port security will turn off the security group policy protection and anti-spoofing protection on the port. General applicable scenarios: NFV or operation and maintenance Debug.' + ), + onChange: (e) => { + this.setState({ + port_security_enabled: e, + }); + }, + } + ``` + + ![switch](/docs/zh/develop/images/form/switch.png) + + - `check` + - checkbox + - `content`, words on the right side of the box + - Take whether to force shutodown instancen when resizing instance as an example `src/pages/compute/containers/Instance/actions/Resize.jsx` : + + ```javascript + { + name: 'option', + label: t('Forced Shutdown'), + type: 'check', + content: t('Agree to force shutdown'), + required: true, + }, + ``` + + ![check](/docs/zh/develop/images/form/check.png) + + - `transfer` + - transfer form item + - `leftTableColumns`, list configuration of left table + - `rightTableColumns`, List configuration of right table + - `dataSource`, data source for choose + - `showSearch`, whether to show search input + - `oriTargetKeys`, default selected + - `disabled`, whether to disable selecte datas in left table, default is `false` + - Take edit system role as an example `src/pages/identity/containers/User/actions/SystemRole.jsx` : + - Left is the project name list + - Right is the project name and role list of project + + ```javascript + { + name: 'select_project', + type: 'transfer', + label: t('Project'), + leftTableColumns: this.leftUserTable, + rightTableColumns: this.rightUserTable, + dataSource: this.projectList + ? this.projectList.filter((it) => it.domain_id === domainDefault) + : [], + disabled: false, + showSearch: true, + oriTargetKeys: projectRoles ? Object.keys(projectRoles) : [], + } + ``` + + ![transfer](/docs/zh/develop/images/form/transfer.png) + + - `check-group` + - checkbox group + - `options`, each checkbox's configuration + - `label`, label for checkbox + - `value`, value for checkbox + - Take edit metadata as an example `src/pages/configuration/containers/Metadata/actions/Edit.jsx` : + - Configure whether is `public` or `protected` + + ```javascript + { + name: 'options', + label: t('Options'), + type: 'check-group', + options: [ + { label: t('Public'), value: 'isPublic' }, + { label: t('Protected'), value: 'isProtected' }, + ], + } + ``` + + ![check-group](/docs/zh/develop/images/form/check-group.png) + + - `textarea-from-file` + - Textarea with read file feature + - After selected file, will read the contents of the file into the textarea + - Take set public-key information when create key-pare as an example `src/pages/compute/containers/Keypair/actions/Create.jsx` : + + ```javascript + { + name: 'public_key', + label: t('Public Key'), + type: 'textarea-from-file', + hidden: isCreate, + required: !isCreate, + } + ``` + + ![textarea-from-file](/docs/zh/develop/images/form/textarea-from-file.png) + + - `ip-distributer` + - IP distributer + - `subnets`, subnets can be selected + - can auto allocate ip, or manual input IP + - can add multi ip + - Take create port as an example `src/pages/network/containers/VirtualAdapter/actions/Create.jsx` : + + ```javascript + { + name: 'fixed_ips', + label: t('Owned Subnet'), + type: 'ip-distributer', + subnets: subnetDetails, + hidden: !network_id, + required: true, + } + ``` + + ![ip-distributer](/docs/zh/develop/images/form/ip-distributer.png) + + - `mac-address` + - Mac address input + - Support auto allocate, manual input + - Take set mac address when edit port as an example `src/pages/network/containers/VirtualAdapter/actions/Edit.jsx`: + + ```javascript + { + name: 'mac_address', + label: t('Mac Address'), + wrapperCol: { span: 16 }, + type: 'mac-address', + required: true, + } + ``` + + ![mac-address](/docs/zh/develop/images/form/mac-address.png) + + - `network-select-table` + - network selector + - Display current project network, shared networ, admin network(if is admin) in tabs. + - Take set network when create port as an example `src/pages/network/containers/VirtualAdapter/actions/Create.jsx` : + + ```javascript + { + name: 'network_id', + label: t('Owned Network'), + type: 'network-select-table', + onChange: this.handleOwnedNetworkChange, + required: true, + }, + ``` + + ![network-select-table](/docs/zh/develop/images/form/network-select-table.png) + + - `volume-select-table` + - volume selector + - Display volumes that can be used or is shared in tabs. + - `disabledFunc`, which volume can not be selected + - Take attach volumn as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + { + name: 'volume', + label: t('Volume'), + type: 'volume-select-table', + tip: multiTip, + isMulti: false, + required: true, + serverId: this.item.id, + disabledFunc: (record) => { + const diskFormat = _get( + record, + 'origin_data.volume_image_metadata.disk_format' + ); + return diskFormat === 'iso'; + }, + } + ``` + + ![volume-select-table](/docs/zh/develop/images/form/volume-select-table.png) + + - `tab-select-table` + - select table with tabs + - `isMulti`, whether is multi select + - Take choose qos when allocate fip as an example `src/pages/network/containers/FloatingIp/actions/Allocate.jsx` : + - There is current project qos & shared qos & all qos(if is admin) + + ```javascript + { + name: 'qos_policy_id', + label: t('QoS Policy'), + type: 'tab-select-table', + tabs: getQoSPolicyTabs.call(this), + isMulti: false, + tip: t('Choosing a QoS policy can limit bandwidth and DSCP'), + onChange: this.onQosChange, + } + ``` + + ![tab-select-table](/docs/zh/develop/images/form/tab-select-table.png) + + - `metadata-transfer` + - metadata transfer form item + - Take edit image metadata as an example `src/pages/compute/containers/Image/actions/ManageMetadata.jsx` : + + ```javascript + { + name: 'systems', + label: t('Metadata'), + type: 'metadata-transfer', + metadatas: this.metadatas, + validator: (rule, value) => { + if (this.hasNoValue(value)) { + return Promise.reject(t('Please input value')); + } + return Promise.resolve(); + }, + } + ``` + + ![metadata-transfer](/docs/zh/develop/images/form/metadata-transfer.png) + + - `aceEditor` + - aceEditor + - Take edit profile when create port as an example `src/pages/network/containers/VirtualAdapter/actions/Create.jsx` : + + ```javascript + { + name: 'bindingProfile', + label: t('Binding Profile'), + type: 'aceEditor', + hidden: !more, + mode: 'json', + wrapEnabled: true, + tabSize: 2, + width: '100%', + height: '200px', + setOptions: { + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + }, + validator: (item, value) => { + if (value !== undefined && value !== '') { + try { + JSON.parse(value); + return Promise.resolve(true); + } catch (e) { + return Promise.reject(new Error(t('Illegal JSON scheme'))); + } + } + return Promise.resolve(true); + }, + } + ``` + + ![aceEditor](/docs/zh/develop/images/form/ace-editor.png) + + - `input-json` + - input value in type of json + - Take edit params when create stack as an example `src/resources/stack.js` : + + ```javascript + export const getFormItemType = (type) => { + switch (type) { + case 'number': + return { + type: 'input-number', + }; + case 'json': + return { + type: 'input-json', + }; + case 'boolean': + return { + type: 'radio', + options: yesNoOptions, + }; + default: + return { + type: 'input', + }; + } + }; + ``` + + ![input-json](/docs/zh/develop/images/form/input-json.png) diff --git a/docs/en/develop/3-11-Action-introduction.md b/docs/en/develop/3-11-Action-introduction.md new file mode 100644 index 00000000..2a200e2a --- /dev/null +++ b/docs/en/develop/3-11-Action-introduction.md @@ -0,0 +1,141 @@ +English | [简体中文](/docs/zh/develop/3-11-Action-introduction.md) + +# Usage + +- Configure all the actions corresponding to resources + + ![Action](/docs/zh/develop/images/form/action.png) + +- After writing the corresponding configuration, the action button will be displayed in the corresponding position of resource list page. + +# Code location + +- `pages/xxxx/containers/XXXX/actions/index.jsx` + +# How to use + +- Return an object, includes `primaryAction`, `batchAction`, `rowAction` +- Take network as an example `src/pages/network/containers/Network/actions/index.jsx` : + - The primary action is `CreateNetwork` + - The batch action is `DeleteAction` + - The row actions are `Edit`, `CreateSubnet`, `DeleteAction` + + ```javascript + import CreateNetwork from './CreateNetwork'; + import CreateSubnet from './CreateSubnet'; + import DeleteAction from './Delete'; + import Edit from './Edit'; + + const actionConfigs = { + rowActions: { + firstAction: Edit, + moreActions: [ + { + action: CreateSubnet, + }, + { + action: DeleteAction, + }, + ], + }, + batchActions: [DeleteAction], + primaryActions: [CreateNetwork], + }; + + export default actionConfigs; + ``` + +- Configure the `actionConfigs` in the resource list page. + - Take network as an example `src/pages/network/containers/Network/ProjectNetwork.jsx` : + + ```javascript + import actionConfigs from './actions'; + get actionConfigs() { + return actionConfigs; + } + ``` + +## `primaryActions` button configuration + +- Return a list of components +- If there is no primary button, you can set it to `null` or` [] ` +- If it is not operational(For example, insufficient permissions), it will automatically hide + +## `batchActions` button configuration + +- Return a list of components +- If there is no batch button, you can set it to `null` or` [] ` +- If it is not operational(For example, insufficient permissions), it will automatically hide + +## `rowActions` button configuration + +- Return an object, includes the components that `firstAction`/`moreActions` correspond to. +- It can return `{}` +- `firstAction` is the first action button in row. + - If not operational, will be disabled. + - It can be a component. + - It can be `null`, + - Take `SystemInfo - network` as an example `src/pages/configuration/containers/SystemInfo/NeutronAgent/actions/index.jsx` : + + ```javascript + import Enable from './Enable'; + import Disable from './Disable'; + + const actionConfigs = { + rowActions: { + firstAction: null, + moreActions: [ + { + action: Enable, + }, + { + action: Disable, + }, + ], + }, + batchActions: [], + primaryActions: [], + }; + + export default actionConfigs; + ``` + +- `moreActions` + - The component under `more` action button + - Action list + - If the action is not operational, it will be hidden + - Supports two types of configurations, corresponding to different display schemes + - Each item is an object include `action` attribute + + ![volume-action](/docs/zh/develop/images/form/volume-action.png) + + - Each item is an object include `title`, `actions` attributes + + ![instance-action](/docs/zh/develop/images/form/instance-action.png) + + - Take instance actions as an example `src/pages/compute/containers/Instance/actions/index.jsx` : + + ```javascript + const statusActions = [ + StartAction, + StopAction, + LockAction, + UnlockAction, + RebootAction, + SoftRebootAction, + SuspendAction, + ResumeAction, + PauseAction, + UnpauseAction, + Shelve, + Unshelve, + ]; + const actionConfigs = { + rowActions: { + firstAction: Console, + moreActions: [ + { + title: t('Instance Status'), + actions: statusActions, + },...}} + ``` diff --git a/docs/en/develop/3-12-Menu-introduction.md b/docs/en/develop/3-12-Menu-introduction.md new file mode 100644 index 00000000..d8be5115 --- /dev/null +++ b/docs/en/develop/3-12-Menu-introduction.md @@ -0,0 +1,110 @@ +English | [简体中文](/docs/zh/develop/3-12-Menu-introduction.md) + +# Usage + +- Jump to the corresponding page after click +- Configuration of menu item on the left side of the console platform + + ![console-platform](/docs/zh/develop/images/menu/console-menu.png) + +- Configuration of menu item on the left side of the management platform + + ![management-platform](/docs/zh/develop/images/menu/admin-menu.png) + +- Support first-level menu with icon +- Support secondary menu expand +- Support menu item selected automatically switch after route change +- Support for automatic processing of breadcrumbs in the right side + +# Code location + +- Console platform menu configuration `src/layouts/menu.jsx` +- Management platform menu configuration `src/layouts/admin-menu.jsx` + +# How to use + +- The menu configuration in console and management platform are the same structure +- Return a `renderMenu` function which return a configuration list + +## first-level menu configuration + +- `path` + - URL +- `name` + - Name of route + - Name of menu item in menu list + - Name corresponding to the first level menu in breadcrumbs +- `key` + - ID of the route + - Unique +- `icon` + - the icon of menu + - When the menu is fully expanded, the icon and name are displayed. + - When the menu is folded, only the icon is displayed. +- `hasBreadcrumb` + - Whether to show breadcrumbs + - Default is `true` + - Take `home page` as an example: `hasBreadcrumb: false` +- `hasChildren` + - Whether the first-level menu contains a submenu + - Default is `true` + - First-level menu may not contain a submenu. For example `home page`. + + ```javascript + { + path: '/base/overview', + name: t('Home'), + key: '/home', + icon: , + hasBreadcrumb: false, + hasChildren: false, + } + ``` + + - First-level menu default contains submenu. For example `compute page`. + + ```javascript + { + path: '/compute', + name: t('Compute'), + key: '/compute', + icon: , + children: [...] + } + ``` + +## Submenu configuration + +- Submenu is configured in the `children` of first-level menu. +- Pages that do not need to be displayed in the menu, such as detail page, create page, are configured in the `children` of the submenu. +- Take flavor as an example + + ```javascript + { + path: '/compute/flavor', + name: t('Flavor'), + key: '/compute/flavor', + level: 1, + children: [ + { + path: /^\/compute\/flavor\/detail\/.[^/]+$/, + name: t('Flavor Detail'), + key: 'flavor-detail', + level: 2, + }, + ], + }, + ``` + +- `path` + - Route corresponding to the menu +- `name` + - name of menu + - Name of menu item in menu list + - Name in breadcrumbs +- `key` + - ID of menu + - Unique +- `level` + - submenu correspond to `level=1` + - `children` in submenu correspond to `level=2` diff --git a/docs/en/develop/3-13-Route-introduction.md b/docs/en/develop/3-13-Route-introduction.md new file mode 100644 index 00000000..a0448bd7 --- /dev/null +++ b/docs/en/develop/3-13-Route-introduction.md @@ -0,0 +1,72 @@ +English | [简体中文](/docs/zh/develop/3-13-Route-introduction.md) + +# Usage + +- Pages that need to display independently should configure route + - According to the needs of the product, the page under submenu needs to display in page, for example `compute - instance`. + - Resource list page + - For example, instance list page. + - Note that the relevant resource listings under the details page does not need to be configured + - Resource detail page + - For example, instance detail page. + - Form that need whole page to show + - For example, create instance page. + - Some menus have only first-level page, such as `Home`, you also need to configure routing + +# How to use + +## The submenu corresponds to the route + +- According to the requirements in the [catalog introduction](2-catalog-introduction.md), each first-level menu page has a separate folder under `pages`, the `containers` folder in it is used to place the submenu page code, `routes` folder is used to configure the route. +- Configuration is in `pages/xxxx/routes/index.js` +- The route configuration needs to follow a fixed format, see `src/pages/compute/routes/index.js` + - List + - Each child in the list, should follow: + - `path`, first-level menu corresponding name, for example nova compute use `compute`. + - `component`, layout components + - Pages about `auth`, for example `login`, use `src/layouts/User/index.jsx` + - Pages show after login, for example `instance`, use `src/layouts/Base/index.jsx` + - The layout automatically handles the display of the `menu item`, the right side of the content `header`, `breadcrumber`, etc. + - `routes`, The main content of the configuration is an array. + - Take compute route as an example `src/pages/compute/routes/index.js` : + + ```javascript + { path: `${PATH}/instance`, component: Instance, exact: true }, + ``` + + - `path`, Path corresponding to each full page, for example `compute/instance` + - `component`, the component corresponding to page, such as component under `containers` + +- For resource-type pages, generally configured + - List page, details page, complex creat page in console platform (simple creation generally uses modal) + - List page, detail page in management platform (with `-admin`/`_admin` in path) + - For detail page, we recommend using `id` + - Take instance as an example `src/pages/compute/routes/index.js` + + ```javascript + { path: `${PATH}/instance`, component: Instance, exact: true }, + { path: `${PATH}/instance-admin`, component: Instance, exact: true }, + { + path: `${PATH}/instance/detail/:id`, + component: InstanceDetail, + exact: true, + }, + { + path: `${PATH}/instance-admin/detail/:id`, + component: InstanceDetail, + exact: true, + }, + { path: `${PATH}/instance/create`, component: StepCreate, exact: true }, + ``` + +## The route corresponding to the first-level menu + +- First-level menu should add in `src/core/routes.js` +- Take compute as an example + + ```javascript + { + path: '/compute', + component: Compute, + }, + ``` diff --git a/docs/en/develop/3-14-I18n-introduction.md b/docs/en/develop/3-14-I18n-introduction.md new file mode 100644 index 00000000..297705ca --- /dev/null +++ b/docs/en/develop/3-14-I18n-introduction.md @@ -0,0 +1,42 @@ +English | [简体中文](/docs/zh/develop/3-14-I18n-introduction.md) + +# Usage + +- Framework supports internationalization, default support in English, Chinese + + ![i18n](/docs/zh/develop/images/i18n/i18n.png) + + ![english](/docs/zh/develop/images/i18n/english.png) + +# Code location + +- `src/locales/index.js` +- English: `src/locales/en.json` +- Chinese: `src/locales/zh.json` + +# How to use + +- The strings that need to be displayed internationally in the code are all in English, after using cli to complete string collection, generally, there is no need to update `en.json`, only need to modify the corresponding Chinese in `zh.json` to complete the internationalization operation +- Use function `t` to translate the string + - Take `instance` as an example, Corresponding international writing is `t('instance')` + - Note that English is case relevant + - Function `T` supports strings with parameters + - Params use `{}` to mark, for example : + + ```javascript + confirmContext = () => + t('Are you sure to { action }?', { + action: this.actionName || this.title, + }); + ``` + +- Collect + + ```shell + yarn run i18n + ``` + + - After colleced, `en.json` and `zh.json` will automatically update. + +- Update Chinese + - After colleced, just update directly in `zh.json`. diff --git a/docs/en/develop/3-7-ModalAction-introduction.md b/docs/en/develop/3-7-ModalAction-introduction.md new file mode 100644 index 00000000..41c3dc18 --- /dev/null +++ b/docs/en/develop/3-7-ModalAction-introduction.md @@ -0,0 +1,388 @@ +English | [简体中文](/docs/zh/develop/3-7-ModalAction-introduction.md) + +# Usage + +![Modal Form](/docs/zh/develop/images/form/modal.png) + +- After click the action button, the form modal will display. +- After click the `Confirm` button, the `loading` status will be displayed according to the status of request. +- After click the `Cancle` button, the modal form will disappear. +- If the request is sent successfully, a prompt message of successful action will be displayed in the upper right corner, and it will automatically disappear after a few seconds. +- If the request fails, an error message will be displayed in the upper right corner of the form page, which can only disappear after clicking the close button. +- Support batch action, after selecting multiple items in the table, you can click the action button above the table to perform batch action. + +# ModalAction code file + +- `src/containers/Action/ModalAction/index.jsx` + +# ModalAction attribute and function definitions introduction + +- Modal forms are all inherited from `ModalAction` component +- Code location: `pages/xxxx/containers/XXXX/actions/xxx.jsx` +- For the case where the form item is relatively less, the modal form is usually used +- Only need to override some functions and the development of page will be completed +- Attributes and functions are divided into the following four types: + - The attributes and functions that must be override, mainly include: + - Action ID + - Action title + - Action permissions + - Judgment whether to disable the action button + - Form Item config + - Function to send request + - The attributes and functions that override in need, mainly include: + - Form default value + - Form size + - Form's label & content layout + - Whether it is an asynchronous action + - Resource name + - Whether to display the resource name in the prompt of the request result + - Action button text + - The attributes and functions that do not need to be override, mainly include: + - Whether the current page is a admin page + - The basic functions in the parent class, mainly include: + - Render page + - Display of request status + - Display of request results +- See below for a more detailed and comprehensive introduction + +## The attributes and functions that must be override + +- `id` + - Static + - Resource action ID + - Need to be unique, only for all actions in the `actions` of the resource to be unique + - Must be override + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + static id = 'attach-volume'; + ``` + +- `title` + - Static + - Resource action title + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + static title = t('Attach Volume'); + ``` + +- `name` + - Resource action name + - Use the name in the prompt after the request + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + get name() { + return t('Attach volume'); + } + ``` + +- `policy` + - Static + - Action permission, if the permission verify failed, the action button will not be displayed on the resource list page + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + static policy = 'os_compute_api:os-volumes-attachments:create'; + ``` + +- `allowed` + - Static + - Determine whether the action button needs to be disabled + - Return `Promise` + - Button that no need to be disabled, write directly: + + ```javascript + static allowed() { + return Promise.resolve(true); + } + ``` + + - Param `item`, the item data in the resource list is generally used to determine the action of the item in the resource list. + - Param `containerProps`, parent container's (That is, the resource list page where the button is located) `props` attribute, generally used to determine the action of related resources under the details page. + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + - The admin page does not display the action button + - The button will show when the server is not: active / deleting / locked / ironic + + ```javascript + static allowed = (item, containerProps) => { + const { isAdminPage } = containerProps; + return Promise.resolve( + !isAdminPage && + isActive(item) && + isNotDeleting(item) && + isNotLocked(item) && + !isIronicInstance(item) + ); + }; + ``` + +- `formItems` + - The form item configuration list corresponding to the action + - The configuration of each form item can be referred to [3-10-FormItem introduction](3-10-FormItem-introduction.md) + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + - Form items includes: instance name label, volume selector + + ```javascript + get formItems() { + return [ + { + name: 'instance', + label: t('Instance'), + type: 'label', + iconType: 'instance', + }, + { + name: 'volume', + label: t('Volume'), + type: 'volume-select-table', + tip: multiTip, + isMulti: false, + required: true, + serverId: this.item.id, + disabledFunc: (record) => { + const diskFormat = _get( + record, + 'origin_data.volume_image_metadata.disk_format' + ); + return diskFormat === 'iso'; + }, + }, + ]; + } + ``` + +- `onSubmit` + - The request function of the action + - After the action request is successful, the modal will disappear, and a successful prompt will be displayed, and the prompt will disappear after a few seconds + - After the action fails, the modal will disappear and an error message will be displayed. You need to close the prompt manually before the prompt disappears. + - Return `Promise`. + - Return the function in the `store` that corresponding to the form. + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + onSubmit = (values) => { + const { volume } = values; + const { id } = this.item; + const volumeId = volume.selectedRowKeys[0]; + const body = { + volumeAttachment: { + volumeId, + }, + }; + return this.store.attachVolume({ id, body }); + }; + ``` + +## The attributes and functions that override in need + +- `init` + - Initial operation + - Defines `this.store` in it, `loading` status is based on `this.store.isSubmitting` + - Call the function to obtain other data required by the form in it + - Update attributes in `this.state` . + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + - Defines the corresponding `store` for the action + + ```javascript + init() { + this.store = globalServerStore; + } + ``` + +- `defaultValue` + - The initial value of the form + - Default value is `{}` + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx`为例 + - Set the initial value of the server name in the form + + ```javascript + get defaultValue() { + const { name } = this.item; + const value = { + instance: name, + }; + return value; + } + ``` + +- `nameForStateUpdate` + - Update key-value into `this.state` when form item value changed + - The key-value stored in `this.state` often affect the display of form items, generally need to be used with `get formItems` + - Such as expand and hide more configuration items + - Such as the `required` attribute change of some form items + - By default, the change of form item which `type` is `radio` or `more` will automaticly save to `this.state` + - Take attach interface to instance as an example `src/pages/compute/containers/Instance/actions/AttachInterface.jsx` : + - After selecte network in the form, the content of the subnet list will be updated + - However, after select the subnet in the form, the judgment of the input IP will be updated, etc. + + ```javascript + get nameForStateUpdate() { + return ['network', 'ipType', 'subnet']; + } + ``` + +- `instanceName` + - After the request is sent, the resource name in the prompt message + - Default is `this.values.name` + - Take edit fip as an example `src/pages/network/containers/FloatingIp/actions/Edit.jsx` : + - The prompt name is the address of the floating IP + + ```javascript + get instanceName() { + return this.item.floating_ip_address; + } + ``` + +- `isAsyncAction` + - Whether the current action is an asynchronous action + - Default is `false` + - If is asynchronous action, the prompt will be : `The xxx instruction has been issued, instance: xxx. \n You can wait for a few seconds to follow the changes of the list data or manually refresh the data to get the final display result.` + - If is synchronous action, the prompt will be : `xxx successfully, instance: xxx.` + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + + ```javascript + get isAsyncAction() { + return true; + } + ``` + +- `messageHasItemName` + - Whether to include the instance name in the prompt of the request result + - Default is `true` + - For some resources without `name` attribute, you can set the value `false` + - Take create snat as an example `src/pages/network/containers/Router/Snat/actions/Create.jsx` : + + ```javascript + get messageHasItemName() { + return false; + } + ``` + +- `buttonText` + - Static + - When the text on the action button is inconsistent with the title of the modal, this attribute needs to be override + - Take edit image as an example `src/pages/compute/containers/Image/actions/Edit.jsx` : + + ```javascript + static buttonText = t('Edit'); + ``` + +- `buttonType` + - Static + - The type of button, support `primary`, `danger` + - When the button is to emphasize the risk of action, the button or the text on the button is generally red, use `danger` + - As the example of disable cinder service`src/pages/configuration/containers/SystemInfo/CinderService/actions/Disable.jsx` : + + ```javascript + static buttonType = 'danger'; + ``` + +- `modalSize` + - Static + - Identifies the width of the modal: the value is`small`、`middle`、`large` + - The correspondence between value and width is : + - `small`: 520 + - `middle`: 720 + - `large`: 1200 + - Use with `getModalSize` + - Default is `small`, means the width of modal is 520px + + ```javascript + static get modalSize() { + return 'small'; + } + ``` + + - Take attach volume as an example `src/pages/compute/containers/Instance/actions/AttachVolume.jsx` : + - the size of modal is `large` + + ```javascript + static get modalSize() { + return 'large'; + } + + getModalSize() { + return 'large'; + } + ``` + +- `getModalSize` + - Identifies the width of the modal + - The value is`small`、`middle`、`large` + +- `labelCol` + - Configure the layout of the labels on the left side of the form + - Default is : + + ```javascript + get labelCol() { + const size = this.getModalSize(); + if (size === 'large') { + return { + xs: { span: 6 }, + sm: { span: 4 }, + }; + } + return { + xs: { span: 8 }, + sm: { span: 6 }, + }; + } + ``` + + - Take edit domain as an example `src/pages/identity/containers/Domain/actions/Edit.jsx` : + + ```javascript + get labelCol() { + return { + xs: { span: 6 }, + sm: { span: 5 }, + }; + } + ``` + +- `wrapperCol` + - Configure the layout of the content on the right side of the form + - Default is : + + ```javascript + get wrapperCol() { + return { + xs: { span: 16 }, + sm: { span: 16 }, + }; + } + ``` + + - Take manage metadata of flavor as an example `src/pages/compute/containers/Flavor/actions/ManageMetadata.jsx` : + + ```javascript + get wrapperCol() { + return { + xs: { span: 18 }, + sm: { span: 20 }, + }; + } + ``` + +## The attributes and functions that do not need to be override + +- `isAdminPage` + - Whether current page is a "management platform" page +- `successText` + - Successful prompt generated after the request +- `errorText` + - Error prompt generated after the request fails +- `containerProps` + - Get the `props` of the father component of the button +- `item` + - Get the data of the item that the action corresponding to +- `items` + - Get the data corresponding to the batch action + +## The basic functions in the parent class + +- `ModalAction` extends `BaseForm` +- Recommend to see `src/components/Form/index.jsx` diff --git a/docs/en/develop/3-8-ConfirmAction-introduction.md b/docs/en/develop/3-8-ConfirmAction-introduction.md new file mode 100644 index 00000000..9427275c --- /dev/null +++ b/docs/en/develop/3-8-ConfirmAction-introduction.md @@ -0,0 +1,259 @@ +English | [简体中文](/docs/zh/develop/3-8-ConfirmAction-introduction.md) + +# Usage + +![Confirm](/docs/zh/develop/images/form/confirm.png) + +- After click the action button, the confirm modal will display. +- After click the `Confirm` button, the `loading` status will be displayed according to the status of request. +- After click the `Cancle` button, the modal form will disappear. +- If the request is sent successfully, a prompt message of successful action will be displayed in the upper right corner, and it will automatically disappear after a few seconds. +- If the request fails, an error message will be displayed in the upper right corner of the form page, which can only disappear after clicking the close button. +- Support batch action, after selecting multiple items in the table, you can click the action button above the table to perform batch action. +- When using batch action, the resources that do not meet the action conditions among the resources selected in batch will be prompted. + +# ConfirmAction code file + +- `src/containers/Action/ConfirmAction/index.jsx` + +# ConfirmAction attribute and function definitions introduction + +- ConfirmAction are all inherited from `ModalAction` component +- Code location: `pages/xxxx/containers/XXXX/actions/xxx.jsx` +- For some action, it it only need to confirm again, user don't need to input more information. ConfirmAction can be used at this time, such as: shut down the instance. +- Only need to override some functions and the development of page will be completed +- Attributes and functions are divided into the following four types: + - The attributes and functions that must be override, mainly include: + - Action ID + - Action title + - Action permissions + - Judgment whether to disable the action button + - Function to send request + - The attributes and functions that override in need, mainly include: + - Resource name + - Whether to display the resource name in the prompt of the request result + - Whether it is an asynchronous action + - Action button text + - The attributes and functions that do not need to be override, mainly include: + - Whether the current page is a admin page + - The basic functions in the parent class, mainly include: + - Render page + - Display of request status + - Display of request results +- See below for a more detailed and comprehensive introduction + +## The attributes and functions that must be override + +- `id` + - Resource action ID + - Need to be unique, only for all actions in the `actions` of the resource to be unique + - Must be override + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get id() { + return 'stop'; + } + ``` + +- `title` + - Resource action title + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get title() { + return t('Stop Instance'); + } + ``` + +- `actionName` + - The action name + - Use the name in the prompt after the request + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get actionName() { + return t('stop instance'); + } + ``` + +- `policy` + - Action permission, if the permission verify failed, the action button will not be displayed on the resource list page. + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + policy = 'os_compute_api:servers:stop'; + ``` + +- `allowedCheckFunc` + - Determine whether the action button needs to be disabled + - Return `Boolean` + - Button that no need to be disabled, write directly: + + ```javascript + allowedCheckFunc = () => true; + ``` + + - Param `item`, the data corresponding to the action. + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + - The action button will be shown when the instance is running and is not lock in console or is under admin page. + + ```javascript + allowedCheckFunc = (item) => { + if (!item) { + return true; + } + return isNotLockedOrAdmin(item, this.isAdminPage) && this.isRunning(item); + }; + ``` + +- `onSubmit` + - The request function of the action + - After the action request is successful, the modal will disappear, and a successful prompt will be displayed, and the prompt will disappear after a few seconds + - After the action fails, the modal will disappear and an error message will be displayed. You need to close the prompt manually before the prompt disappears. + - Return `Promise`. + - Return the function in the `store` that corresponding to the form. + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + onSubmit = (item) => { + const { id } = item || this.item; + return globalServerStore.stop({ id }); + }; + ``` + +## The attributes and functions that override in need + +- `buttonText` + - When the text on the action button is inconsistent with the title of the modal, this attribute needs to be override + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + - The title of modal is `stop instance`, the text on button is `stop`. + + ```javascript + get buttonText() { + return t('Stop'); + } + ``` + +- `buttonType` + - The type of button, support `primary`, `danger`, `default` + - Default is `default` + - When the button is to emphasize the risk of action, the button or the text on the button is generally red, use `danger` + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get buttonType() { + return 'danger'; + } + ``` + +- `passiveAction` + - In batch action, if a resource does not meet the conditions, the prompt will be displayed before the request is sent. If the prompt needs to be in a passive voice, this attribute needs to be set. + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get passiveAction() { + return t('be stopped'); + } + ``` + +- `isAsyncAction` + - Whether the current action is an asynchronous action + - Default is `false` + - If is asynchronous action, the prompt will be : `The xxx instruction has been issued, instance: xxx. \n You can wait for a few seconds to follow the changes of the list data or manually refresh the data to get the final display result.` + - If is synchronous action, the prompt will be : `xxx successfully, instance: xxx.` + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + + ```javascript + get isAsyncAction() { + return true; + } + ``` + +- `messageHasItemName` + - Whether to include the instance name in the prompt of the request result + - Default is `true` + - For some resources without `name` attribute, you can set the value `false` + +- `performErrorMsg` + - In batch action, if a resource does not meet the conditions, a prompt will be displayed before the request is sent + - Default is `Unable to xxx, instance: xxx.` + - Take stop instance as an example `src/pages/compute/containers/Instance/actions/Stop.jsx` : + - If the instance selected is not running, it will prompt `Instance "{ name }" status is not in active or suspended, can not stop it.` + - If the instance selected is locked, it will prompt `Instance "{ name }" is locked, can not stop it.` + - Other case, will all prompt `You are not allowed to stop instance "{ name }".` + + ```javascript + performErrorMsg = (failedItems) => { + const instance = isArray(failedItems) ? failedItems[0] : failedItems; + let errorMsg = t('You are not allowed to stop instance "{ name }".', { + name: instance.name, + }); + if (!this.isRunning(instance)) { + errorMsg = t( + 'Instance "{ name }" status is not in active or suspended, can not stop it.', + { name: instance.name } + ); + } else if (!isNotLockedOrAdmin(instance, this.isAdminPage)) { + errorMsg = t('Instance "{ name }" is locked, can not stop it.', { + name: instance.name, + }); + } + return errorMsg; + }; + ``` + +- `getNameOne` + - The instance name in the prompt + - Default is : + + ```javascript + getNameOne = (data) => data.name;` + ``` + + - Param `data` is the resource that the action corresponding to + - Take release fip as an example `src/pages/network/containers/FloatingIp/actions/Release.jsx` : + + ```javascript + getNameOne = (data) => data.floating_ip_address; + ``` + +- `getName` + - It is not recommended to override this function + - It is recommended to override `getNameOne` + +- `confirmContext` + - The prompt in the confirm modal + - Default is `Are you sure to {action} (instance: {name})?` + - Take delete flavor as an example `src/pages/compute/containers/Flavor/actions/Delete.jsx` : + - Prompt `If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?` + + ```javascript + confirmContext = (data) => { + const name = this.getName(data); + return t( + "If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?", + { name } + ); + }; + ``` + +- `submitErrorMsg` + - Error message after action failed + - Generally do not need to override + - Default is `Unable to {action}, instance: {name}.` + +## The attributes and functions that do not need to be override + +- `isAdminPage` + - Whether current page is a "management platform" page +- `submitSuccessMsg` + - Successful prompt generated after the request +- `submitErrorMsgBatch` + - The error prompt after batch action request +- `perform` + - In batch action, determine whether the selected data is operable, and if it is not operable, give corresponding prompts + +## The basic functions in the parent class + +- Recommend to see `src/containers/Action/ConfirmAction/index.jsx` diff --git a/docs/en/develop/3-9-StepAction-introduction.md b/docs/en/develop/3-9-StepAction-introduction.md new file mode 100644 index 00000000..198f1540 --- /dev/null +++ b/docs/en/develop/3-9-StepAction-introduction.md @@ -0,0 +1,296 @@ +English | [简体中文](/docs/zh/develop/3-9-StepAction-introduction.md) + +# Usage + +![StepForm](/docs/zh/develop/images/form/step.png) + +- After click the action button, the step modal will display. +- Has it own route to visit +- Generally used to create resources, or form with lots of form items +- Support `Next Step`, `Previous Step` action button +- After click the `Cancle` button, will automatically jump to the corresponding resource list page +- If the request is sent successfully, a prompt message of successful action will be displayed in the upper right corner, and it will automatically disappear after a few seconds. + + ![FormOneStep](/docs/zh/develop/images/form/create-success.png) + +- If the request fails, an error message will be displayed in the upper right corner of the form page, which can only disappear after clicking the close button. + +# StepAction code file + +- `src/containers/Action/StepAction/index.jsx` + +# StepAction attribute and function definitions introduction + +- Step forms are all inherited from `StepAction` component +- Code location: `pages/xxxx/containers/XXXX/actions/xxx/index.jsx` +- Only need to override some functions and the development of page will be completed +- Need to write every step of the Form +- Attributes and functions are divided into the following four types: + - The attributes and functions that must be override, mainly include: + - Action ID + - Action title + - The page's location + - The corresponding resource page location + - Action permissions + - Judgment whether to disable the action button + - Form Item config + - Function to send request + - Configuration of each step + - The attributes and functions that override in need, mainly include: + - Whether there is a confirmation page + - Prompt after successful request + - Prompt after failed request + - Rendering of the data on the bottom left of the page + - The attributes and functions that do not need to be override, mainly include: + - Whether the current page is a admin page + - The basic functions in the parent class, mainly include: + - Render page + - Display of request status + - Display of request results +- See below for a more detailed and comprehensive introduction + +## The attributes and functions that must be override + +- `id` + - Static + - Resource action ID + - Need to be unique, only for all actions in the `actions` of the resource to be unique + - Must be override + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + + ```javascript + static id = 'instance-create'; + ``` + +- `title` + - Static + - Resource action title + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + + ```javascript + static title = t('Create Instance'); + ``` + +- `path` + - Thr corresponding route for resource action + - Static attribute or function + - When it is static function, here are the params + - Param `item`, the item data in the resource page + - Param `containerProps`, the `props` of the father component. (That is, the resource list page where the button is located) + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + - Click the `Create instance` button in the instance list page, the page will jump to `/compute/instance/create` + - Click the `Create instance` button in the instance group list page, the page will jump to `/compute/instance/create?servergroup=${detail.id}` + + ```javascript + static path = (_, containerProps) => { + const { detail, match } = containerProps || {}; + if (!detail || isEmpty(detail)) { + return '/compute/instance/create'; + } + if (match.path.indexOf('/compute/server') >= 0) { + return `/compute/instance/create?servergroup=${detail.id}`; + } + }; + ``` + + - Static attribute, Take create flavor as an example `src/pages/compute/containers/Flavor/actions/StepCreate/index.jsx` : + + ```javascript + static path = '/compute/flavor-admin/create'; + ``` + +- `policy` + - Static attribute + - The permission corresponding to the page, if the permission verification fails, the action button will not be displayed on the resource list page + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + + ```javascript + static policy = [ + 'os_compute_api:servers:create', + 'os_compute_api:os-availability-zone:list', + ]; + ``` + +- `allowed` + - Static + - Determine whether the action button needs to be disabled + - Return `Promise` + - Button that no need to be disabled, write directly: + + ```javascript + static allowed() { + return Promise.resolve(true); + } + ``` + +- `name` + - The name of the action + - Use the name in the prompt after the request + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + + ```javascript + get name() { + return t('Create instance'); + } + ``` + +- `listUrl` + - The resource list page corresponding to the action + - After the operation request is successful, it will automatically jump to the resource list page + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + - In the action column of the image list page, click Create a instance and successfully create, will return to the image list page + - In the action column of the volume list page, click Create a instance and successfully create, will return to the volume list page + - In the instance group detail page, click Create a instance and successfully create, will return to the instance group detail page + - In the instance list page, click Create a instance and successfully create, will return to the instance list page + + ```javascript + get listUrl() { + const { image, volume, servergroup } = this.locationParams; + if (image) { + return '/compute/image'; + } + if (volume) { + return '/storage/volume'; + } + if (servergroup) { + return `/compute/server-group/detail/${servergroup}`; + } + return '/compute/instance'; + } + ``` + +- `steps` + - Configuration of each step + - Each configuration item + - `title`, title of each step + - `component`, every step form the corresponding components, inherit from `BaseForm`(`src/components/Form`) + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + - Includes 4 steps: Base configuration, Network configuration, System configuration, Confirm configuration + + ```javascript + get steps() { + return [ + { + title: t('Base Config'), + component: BaseStep, + }, + { + title: t('Network Config'), + component: NetworkStep, + }, + { + title: t('System Config'), + component: SystemStep, + }, + { + title: t('Confirm Config'), + component: ConfirmStep, + }, + ]; + } + ``` + +- `onSubmit` + - The request function of the action + - After the action request is successful, will automatically jump to the resource list page + - After the action fails, will display error prompts in the form page + - Return `Promise`. + - Return the function in the `store` that corresponding to the form. + +## The attributes and functions that override in need + +- `init` + - Initial operation + - Defines `this.store` in it, `loading` status is based on `this.store.isSubmitting` + - Call the function to obtain other data required by the form in it + - Update attributes in `this.state` . + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + - Get quota information + + ```javascript + init() { + this.store = globalServerStore; + this.projectStore = globalProjectStore; + this.getQuota(); + } + ``` + +- `instanceName` + - After the request is sent, the resource name in the prompt message + - Default is `this.values.name` + - Take create instance as an example `src/pages/compute/containers/Instance/actions/StepCreate/index.jsx` : + - If is batch create instance, will display name in form of `${name}-${index + 1}` + + ```javascript + get instanceName() { + const { name, count = 1 } = this.values || {}; + if (count === 1) { + return this.unescape(name); + } + return this.unescape( + new Array(count) + .fill(count) + .map((_, index) => `${name}-${index + 1}`) + .join(', ') + ); + } + ``` + +- `renderFooterLeft` + - Rendering of internal left side of the bottom of the form + - Default return `null` + - src/pages/compute/containers/Instance/actions/StepCreate/index.jsx + - Show the number of batch create + - Based on the number of inputs and the remaining quota, determines if the current form is correct + + ```javascript + renderFooterLeft() { + const { data } = this.state; + const { count = 1, source: { value: sourceValue } = {} } = data; + const configs = { + min: 1, + max: sourceValue === 'bootableVolume' ? 1 : 100, + precision: 0, + onChange: this.onCountChange, + formatter: (value) => `$ ${value}`.replace(/\D/g, ''), + }; + return ( +
+ {t('Count')} + + {this.renderBadge()} +
+ ); + } + ``` + +- `successText` + - Successful prompt generated after the request + +- `errorText` + - Error prompt generated after the request fails + - Generally do not need replication + +- `renderFooterLeft` + - Rendering function on the left side of the table + +## The attributes and functions that do not need to be override + +- `values` + - After the form is verified successfully, use this value to update form value. + +- `isAdminPage` + - Whether current page is a "management platform" page + +- `getUrl` + - Generate function of page URL + - Such as: need to provide a ability of jump to the associated resource, use this function, you can jump to the corresponding address of the `console platform` in the `console platform`, and jump to the corresponding address of the `management platform` in the `management platform`. + +## The basic functions in the parent class + +- `StepAction` extends `StepForm` +- Recommend to see `src/components/StepForm/index.jsx` diff --git a/docs/zh/develop/3-10-FormItem-introduction.md b/docs/zh/develop/3-10-FormItem-introduction.md index 84a272f6..52fd2576 100644 --- a/docs/zh/develop/3-10-FormItem-introduction.md +++ b/docs/zh/develop/3-10-FormItem-introduction.md @@ -518,7 +518,6 @@ - `isImage`,以镜像支持的格式验证名称 - `isInstance`,以云主机支持的格式验证名称 - 以创建云主机输入名称`src/pages/compute/containers/Instance/actions/StepCreate/SystemStep/index.jsx`为例 - - 输入密码,确认密码,并要验证密码格式,以及两次输入数据的一致性 ```javascript { @@ -844,7 +843,6 @@ - 带有读取文件功能的多行文本输入框 - 选择文件后,会将文件的内容读取到文本输入框中 - 以创建密钥输入公钥信息`src/pages/compute/containers/Keypair/actions/Create.jsx`为例 - - 配置 公有、受保护的 属性 ```javascript { diff --git a/docs/zh/develop/3-11-Action-introduction.md b/docs/zh/develop/3-11-Action-introduction.md index ca8384bc..3b70977f 100644 --- a/docs/zh/develop/3-11-Action-introduction.md +++ b/docs/zh/develop/3-11-Action-introduction.md @@ -64,13 +64,12 @@ ## 批量操作按钮配置`batchActions` - 返回组件的列表 -- 如果没有主按钮,可以设置为`null`或`[]` +- 如果没有批量按钮,可以设置为`null`或`[]` - 如果不可操作(比如权限不够),将自动隐藏 ## 行操作按钮配置`rowActions` - 返回一个对象,内含`firstAction`, `moreActions`对应的组件 -- 批量操作按钮如果被禁用(比如) - 可以返回一个空对象`{}` - `firstAction`,行操作对应的第一个按钮 - 如果不可操作,按钮灰掉 @@ -101,7 +100,8 @@ export default actionConfigs; ``` -- `moreActions`,`更多`按钮下对应的操作组件 +- `moreActions` + - `更多`按钮下对应的操作组件 - 操作的数组 - 其内的操作如果不可用,将直接隐藏该操作按钮 - 支持两种格式的配置,对应了不同的展示方案 @@ -109,7 +109,7 @@ ![云硬盘操作](/docs/zh/develop/images/form/volume-action.png) - - 每个元数是个含有`title`、`actions`属性的对象 + - 每个元素是个含有`title`、`actions`属性的对象 ![云主机操作](/docs/zh/develop/images/form/instance-action.png) diff --git a/docs/zh/develop/3-12-Menu-introduction.md b/docs/zh/develop/3-12-Menu-introduction.md index c4713405..ff2fa6d8 100644 --- a/docs/zh/develop/3-12-Menu-introduction.md +++ b/docs/zh/develop/3-12-Menu-introduction.md @@ -7,7 +7,7 @@ ![控制台](/docs/zh/develop/images/menu/console-menu.png) -- 配置管理平台的左右菜单项 +- 配置管理平台的左侧菜单项 ![管理平台](/docs/zh/develop/images/menu/admin-menu.png) diff --git a/docs/zh/develop/3-7-ModalAction-introduction.md b/docs/zh/develop/3-7-ModalAction-introduction.md index fdb95c92..b46ced10 100644 --- a/docs/zh/develop/3-7-ModalAction-introduction.md +++ b/docs/zh/develop/3-7-ModalAction-introduction.md @@ -209,7 +209,7 @@ - `nameForStateUpdate` - 表单项内容变动时,更新到`this.state`中的表单键值对 - - 这些存储到`this.store`中的键值对往往会影响表单项的展示,需要配合`get formItems`中的代码使用 + - 这些存储到`this.state`中的键值对往往会影响表单项的展示,需要配合`get formItems`中的代码使用 - 如展开、隐藏更多配置项 - 如某些表单项必填性的变动 - 默认对`radio`, `more`类型的表单项的变动保存到`this.state`中 @@ -252,7 +252,7 @@ - 请求结果的提示语中,是否要包含实例名称 - 默认值为`true` - 有些资源,不存在名称,则可设置该值为`false` - - 以裸机节点创建端口``为例 + - 以创建 `SNAT` `src/pages/network/containers/Router/Snat/actions/Create.jsx` 为例 ```javascript get messageHasItemName() { @@ -309,8 +309,9 @@ ``` - `getModalSize` - - 配置表单左侧标题的布局 + - 标识弹出框的宽度 - 值为`small`、`middle`、`large` + - `labelCol` - 配置表单左侧标签的布局 - 默认值为 diff --git a/docs/zh/develop/3-8-ConfirmAction-introduction.md b/docs/zh/develop/3-8-ConfirmAction-introduction.md index 435992e9..60e64a11 100644 --- a/docs/zh/develop/3-8-ConfirmAction-introduction.md +++ b/docs/zh/develop/3-8-ConfirmAction-introduction.md @@ -16,9 +16,9 @@ - `src/containers/Action/ConfirmAction/index.jsx` -# ModalAction 属性与函数定义介绍 +# ConfirmAction 属性与函数定义介绍 -- 弹窗型表单都继承于 ModalAction 组件 +- 确认型表单都继承于 ModalAction 组件 - 代码位置:`pages/xxxx/containers/XXXX/actions/xxx.jsx` - 某些操作,只需要再次确认,无需用户输入更多内容即可,此时可使用该类型的组件,如:关闭云主机 - 只需要复写部分函数即可完成页面的开发 @@ -174,6 +174,7 @@ - 请求结果的提示语中,是否要包含实例名称 - 默认值为`true` - 有些资源,不存在名称,则可设置该值为`false` + - `performErrorMsg` - 批量操作时,如果某个资源不符合条件,会在发送请求前展示提示语 - 默认值为`无法xxx, 实例名称:xxxx。` @@ -220,6 +221,7 @@ - `getName` - 不建议复写该函数 - 建议复写`getNameOne` + - `confirmContext` - 确认弹窗中的提示语 - 默认为`确认{ action }(实例名称:{name})?` diff --git a/docs/zh/develop/3-9-StepAction-introduction.md b/docs/zh/develop/3-9-StepAction-introduction.md index 6589a865..213190d7 100644 --- a/docs/zh/develop/3-9-StepAction-introduction.md +++ b/docs/zh/develop/3-9-StepAction-introduction.md @@ -140,7 +140,6 @@ - 以创建云主机`src/pages/compute/containers/Instance/actions/StepCreate/index.jsx`为例 - 在镜像列表页的条目操作中,点击创建云主机并操作成功后,返回到镜像列表页 - 在云硬盘列表页的条目操作中,点击创建云主机并操作成功后,返回到云硬盘列表页 - - 在云硬盘列表页的条目操作中,点击创建云主机并操作成功后,返回到云硬盘列表页 - 在云主机组详情页,点击创建云主机并操作成功后,返回到云主机详情页中 - 在云主机列表页中,点击创建云主机并操作成功后,返回到云主机列表页