Showing
7 changed files
with
519 additions
and
0 deletions
| @@ -6,6 +6,8 @@ export * from './qx-tags-input'; | @@ -6,6 +6,8 @@ export * from './qx-tags-input'; | ||
| 6 | export * from './qx-user-selector'; | 6 | export * from './qx-user-selector'; |
| 7 | export * from './qx-org-selector'; | 7 | export * from './qx-org-selector'; |
| 8 | export * from './qx-form-select'; | 8 | export * from './qx-form-select'; |
| 9 | +export * from './qx-pos-selector'; | ||
| 10 | +export * from './qx-role-selector'; | ||
| 9 | export * from './qx-app-selector'; | 11 | export * from './qx-app-selector'; |
| 10 | export * from './utils'; | 12 | export * from './utils'; |
| 11 | export * from './qx-field'; | 13 | export * from './qx-field'; |
src/qx-role-selector/index.ts
0 → 100644
| 1 | +import QxRoleSelectorInput from './src/input'; | ||
| 2 | +import QxRoleSelectorDialog from './src/dialog'; | ||
| 3 | +import React from 'react'; | ||
| 4 | + | ||
| 5 | +interface QxRoleSelectorType extends React.FC { | ||
| 6 | + Dialog: typeof QxRoleSelectorDialog; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export const QxRoleSelector = QxRoleSelectorInput as QxRoleSelectorType; | ||
| 10 | + | ||
| 11 | +QxRoleSelector.Dialog = QxRoleSelectorDialog; |
src/qx-role-selector/src/core.tsx
0 → 100644
| 1 | +import * as React from 'react'; | ||
| 2 | +import { useCallback, useEffect, useImperativeHandle, useState } from 'react'; | ||
| 3 | +import { Checkbox, Empty, Input, Radio, Spin } from 'antd'; | ||
| 4 | +import Menu from 'antd/es/menu'; | ||
| 5 | +import { SearchOutlined } from '@ant-design/icons'; | ||
| 6 | +import { getRoles, getRolesByAppCode } from './service'; | ||
| 7 | +import { cloneDeep } from "lodash-es"; | ||
| 8 | +// import _ from 'lodash'; | ||
| 9 | + | ||
| 10 | +type PosCoreProps = { | ||
| 11 | + appId?: string; | ||
| 12 | + cRef?: any; | ||
| 13 | + multiple?: boolean; | ||
| 14 | + placeholder?: string; | ||
| 15 | + params?: any; | ||
| 16 | + onSelect?: (selectedKeys: string[], selectedData: RoleModel[]) => void; | ||
| 17 | + request: any; | ||
| 18 | + isRole?: boolean; // 是否为设计时角色权限 | ||
| 19 | + appCode?: string; | ||
| 20 | +}; | ||
| 21 | + | ||
| 22 | +export interface RoleModel { | ||
| 23 | + id: string; | ||
| 24 | + name: string; | ||
| 25 | + appId?: string; | ||
| 26 | + code: string; | ||
| 27 | + visible?: boolean; | ||
| 28 | + disabled?: boolean; | ||
| 29 | + child: RoleModel[]; | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +const RoleSelCore: React.FC<PosCoreProps> = ({ | ||
| 33 | + appId, | ||
| 34 | + cRef, | ||
| 35 | + multiple, | ||
| 36 | + placeholder, | ||
| 37 | + onSelect, | ||
| 38 | + isRole, | ||
| 39 | + appCode, | ||
| 40 | + ...props | ||
| 41 | +}) => { | ||
| 42 | + const [loading, setLoading] = useState<boolean>(true); | ||
| 43 | + | ||
| 44 | + const [data, setData] = useState<RoleModel[]>([]); | ||
| 45 | + | ||
| 46 | + const [selectedData, setSelectedData] = useState<RoleModel[]>([]); | ||
| 47 | + const [selectedKeys, setSelectedKeys] = useState<string[]>(); | ||
| 48 | + const [expandedKeys, setExpandedKeys] = useState<string[]>([]); | ||
| 49 | + const [keywords, setKeywords] = useState<string>(''); | ||
| 50 | + | ||
| 51 | + const requestData = useCallback(() => { | ||
| 52 | + setLoading(true); | ||
| 53 | + let _getRole = getRoles; | ||
| 54 | + if (isRole) { | ||
| 55 | + _getRole = getRolesByAppCode; | ||
| 56 | + } | ||
| 57 | + // todo 当前为指定appCode,待接口修改为appId传入(不用appId了) | ||
| 58 | + _getRole(props.request, appCode || '').then((res) => { | ||
| 59 | + const roles: RoleModel[] = res.child || []; | ||
| 60 | + if (roles) { | ||
| 61 | + const _expandedKeys: string[] = []; | ||
| 62 | + roles.forEach((item) => { | ||
| 63 | + _expandedKeys.push(item.id); | ||
| 64 | + }); | ||
| 65 | + setExpandedKeys(_expandedKeys); | ||
| 66 | + setData(roles); | ||
| 67 | + setLoading(false); | ||
| 68 | + } | ||
| 69 | + }); | ||
| 70 | + }, []); | ||
| 71 | + | ||
| 72 | + useEffect(() => { | ||
| 73 | + requestData(); | ||
| 74 | + }, [requestData]); | ||
| 75 | + | ||
| 76 | + useEffect(() => { | ||
| 77 | + const isOnSelect = onSelect ? onSelect : () => { }; | ||
| 78 | + if (selectedKeys) { | ||
| 79 | + isOnSelect(selectedKeys, selectedData); | ||
| 80 | + } | ||
| 81 | + }, [onSelect, selectedKeys, selectedData]); | ||
| 82 | + | ||
| 83 | + const handleSelect = (selectData: { selectedKeys: string[] }) => { | ||
| 84 | + //单选走这里 | ||
| 85 | + if (!multiple) { | ||
| 86 | + setSelectedKeys(selectData.selectedKeys); | ||
| 87 | + } | ||
| 88 | + }; | ||
| 89 | + useImperativeHandle(cRef, () => ({ | ||
| 90 | + // 暴露给父组件 | ||
| 91 | + remove: (index: number) => { | ||
| 92 | + let _selectedKeys: string[] = []; | ||
| 93 | + let _selectedData: RoleModel[] = []; | ||
| 94 | + if (selectedKeys && selectedKeys.length > 0) { | ||
| 95 | + _selectedKeys = [...selectedKeys]; | ||
| 96 | + _selectedData = [...selectedData]; | ||
| 97 | + _selectedKeys.splice(index, 1); | ||
| 98 | + _selectedData.splice(index, 1); | ||
| 99 | + setSelectedData(_selectedData); | ||
| 100 | + setSelectedKeys(_selectedKeys); | ||
| 101 | + } | ||
| 102 | + }, | ||
| 103 | + emptySelect: () => { | ||
| 104 | + setSelectedData([]); | ||
| 105 | + setSelectedKeys([]); | ||
| 106 | + }, | ||
| 107 | + })); | ||
| 108 | + const handleMultiSelect = (checked: boolean, item: RoleModel) => { | ||
| 109 | + let _selectedKeys: string[] = []; | ||
| 110 | + let _selectedData: RoleModel[] = []; | ||
| 111 | + if (selectedKeys) { | ||
| 112 | + _selectedKeys = [...selectedKeys]; | ||
| 113 | + _selectedData = [...selectedData]; | ||
| 114 | + } | ||
| 115 | + // console.log(checked, item.id, _selectedKeys); | ||
| 116 | + | ||
| 117 | + if (checked) { | ||
| 118 | + _selectedKeys.push(item.id); | ||
| 119 | + _selectedData.push(item); | ||
| 120 | + } else { | ||
| 121 | + const index = _selectedKeys.indexOf(item.id); | ||
| 122 | + if (index > -1) { | ||
| 123 | + _selectedKeys.splice(index, 1); | ||
| 124 | + _selectedData.splice(index, 1); | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + setSelectedData(_selectedData); | ||
| 129 | + setSelectedKeys(_selectedKeys); | ||
| 130 | + }; | ||
| 131 | + | ||
| 132 | + //多选走这里 | ||
| 133 | + const filter = (word: string) => { | ||
| 134 | + setKeywords(word); | ||
| 135 | + const traverse = function (node: any) { | ||
| 136 | + const childNodes = node.child || []; | ||
| 137 | + | ||
| 138 | + childNodes.forEach((child) => { | ||
| 139 | + child.visible = child.name.indexOf(word) > -1; | ||
| 140 | + | ||
| 141 | + traverse(child); | ||
| 142 | + }); | ||
| 143 | + | ||
| 144 | + if (!node.visible && childNodes.length) { | ||
| 145 | + node.visible = childNodes.some((child) => child.visible); | ||
| 146 | + } | ||
| 147 | + }; | ||
| 148 | + | ||
| 149 | + if (data) { | ||
| 150 | + const _data = cloneDeep(data); | ||
| 151 | + _data.forEach((item) => { | ||
| 152 | + traverse(item); | ||
| 153 | + }); | ||
| 154 | + setData(_data); | ||
| 155 | + } | ||
| 156 | + }; | ||
| 157 | + | ||
| 158 | + const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
| 159 | + e.stopPropagation(); | ||
| 160 | + // @ts-ignore | ||
| 161 | + filter(e.target.value.trim()); | ||
| 162 | + }; | ||
| 163 | + const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| 164 | + // @ts-ignore | ||
| 165 | + if (e.type === 'click' && e.target.value === '' && data) { | ||
| 166 | + //如果是清空 | ||
| 167 | + filter(''); | ||
| 168 | + } | ||
| 169 | + }; | ||
| 170 | + | ||
| 171 | + const renderText = (text: string) => { | ||
| 172 | + let title = <> {text}</>; | ||
| 173 | + if (keywords) { | ||
| 174 | + const index = text.indexOf(keywords); | ||
| 175 | + if (index > -1) { | ||
| 176 | + title = ( | ||
| 177 | + <> | ||
| 178 | + {text.substr(0, index)} | ||
| 179 | + <span className={'qx-keywords-highlight'}>{keywords}</span> | ||
| 180 | + {text.substr(index + keywords.length)} | ||
| 181 | + </> | ||
| 182 | + ); | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + return title; | ||
| 186 | + }; | ||
| 187 | + | ||
| 188 | + return ( | ||
| 189 | + <div className={'qx-search-menus__wrap'}> | ||
| 190 | + <Input | ||
| 191 | + // style={{ display: 'none' }} | ||
| 192 | + className={'qx-selector-sub-search'} | ||
| 193 | + placeholder={placeholder || '请输入角色名称,按回车键搜索'} | ||
| 194 | + allowClear | ||
| 195 | + prefix={<SearchOutlined />} | ||
| 196 | + onChange={(e) => { | ||
| 197 | + handleChange(e); | ||
| 198 | + }} | ||
| 199 | + onPressEnter={(e) => { | ||
| 200 | + handleSearch(e); | ||
| 201 | + }} | ||
| 202 | + /> | ||
| 203 | + <div className="qx-search-menus"> | ||
| 204 | + {expandedKeys.length ? ( | ||
| 205 | + <Menu | ||
| 206 | + mode={'inline'} | ||
| 207 | + onSelect={handleSelect} | ||
| 208 | + selectedKeys={multiple ? [] : selectedKeys} | ||
| 209 | + multiple={!!multiple} | ||
| 210 | + defaultOpenKeys={expandedKeys} | ||
| 211 | + > | ||
| 212 | + {data.map((item) => { | ||
| 213 | + if (typeof item.visible === 'boolean' && !item.visible) { | ||
| 214 | + return null; | ||
| 215 | + } | ||
| 216 | + if (item.child) { | ||
| 217 | + return ( | ||
| 218 | + <Menu.SubMenu key={item.id} title={item.name}> | ||
| 219 | + {item.child.map((child) => { | ||
| 220 | + return typeof child.visible === 'boolean' && !child.visible ? null : ( | ||
| 221 | + <Menu.Item key={child.id} disabled={child.disabled}> | ||
| 222 | + {multiple ? ( | ||
| 223 | + <Checkbox | ||
| 224 | + checked={selectedKeys && selectedKeys.indexOf(child.id) > -1} | ||
| 225 | + disabled={child.disabled} | ||
| 226 | + onChange={(e) => { | ||
| 227 | + handleMultiSelect(e.target.checked, child); | ||
| 228 | + }} | ||
| 229 | + > | ||
| 230 | + {renderText(child.name)} | ||
| 231 | + </Checkbox> | ||
| 232 | + ) : isRole ? ( | ||
| 233 | + <Radio | ||
| 234 | + checked={ | ||
| 235 | + selectedKeys?.length && selectedKeys[0].indexOf(child.id) > -1 | ||
| 236 | + } | ||
| 237 | + disabled={child.disabled} | ||
| 238 | + > | ||
| 239 | + {renderText(child.name)} | ||
| 240 | + </Radio> | ||
| 241 | + ) : ( | ||
| 242 | + renderText(child.name) | ||
| 243 | + )} | ||
| 244 | + </Menu.Item> | ||
| 245 | + ); | ||
| 246 | + })} | ||
| 247 | + </Menu.SubMenu> | ||
| 248 | + ); | ||
| 249 | + } | ||
| 250 | + return null; | ||
| 251 | + })} | ||
| 252 | + </Menu> | ||
| 253 | + ) : null} | ||
| 254 | + | ||
| 255 | + <Spin | ||
| 256 | + spinning={loading} | ||
| 257 | + style={{ width: '100%', marginTop: '40px' }} | ||
| 258 | + // indicator={<LoadingOutlined style={{ fontSize: 24, marginTop: '40px' }} spin />} | ||
| 259 | + /> | ||
| 260 | + {!loading && data.length === 0 ? <Empty style={{ paddingTop: '30px' }} /> : null} | ||
| 261 | + </div> | ||
| 262 | + </div> | ||
| 263 | + ); | ||
| 264 | +}; | ||
| 265 | + | ||
| 266 | +export default RoleSelCore; |
src/qx-role-selector/src/dialog.tsx
0 → 100644
| 1 | +import * as React from 'react'; | ||
| 2 | +import { useRef, useState } from 'react'; | ||
| 3 | +import { Modal, Tag } from 'antd'; | ||
| 4 | +import './style.less'; | ||
| 5 | +import type { RoleModel } from './core'; | ||
| 6 | +import RoleSelCore from './core'; | ||
| 7 | +// import { RequestMethod } from 'umi-request'; | ||
| 8 | + | ||
| 9 | +type RoleSelectorDialogProps = { | ||
| 10 | + appId?: string; | ||
| 11 | + title?: string; | ||
| 12 | + visible: boolean; | ||
| 13 | + onCancel: () => void; | ||
| 14 | + data?: []; | ||
| 15 | + multiple?: boolean; | ||
| 16 | + onOk: (selectedKeys: string[], selectedData: RoleModel[]) => void; | ||
| 17 | + request: AnalyserOptions; | ||
| 18 | + isRole?: boolean; // 是否为设计时角色权限 | ||
| 19 | + appCode?: string; | ||
| 20 | +}; | ||
| 21 | + | ||
| 22 | +const RoleSelectorDialog: React.FC<RoleSelectorDialogProps> = (props) => { | ||
| 23 | + const [selectedData, setSelectedData] = useState<RoleModel[]>([]); | ||
| 24 | + const [selectedKeys, setSelectedKeys] = useState<string[]>([]); | ||
| 25 | + const coreRef = useRef({ | ||
| 26 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| 27 | + remove: (index: number) => { | ||
| 28 | + // console.log(index) | ||
| 29 | + }, | ||
| 30 | + emptySelect: () => {}, | ||
| 31 | + }); | ||
| 32 | + | ||
| 33 | + const handleOk = () => { | ||
| 34 | + props.onOk(selectedKeys, selectedData); | ||
| 35 | + coreRef.current.emptySelect(); | ||
| 36 | + setSelectedKeys([]); | ||
| 37 | + setSelectedData([]); | ||
| 38 | + }; | ||
| 39 | + const handleCancel = () => { | ||
| 40 | + props.onCancel(); | ||
| 41 | + coreRef.current.emptySelect(); | ||
| 42 | + setSelectedKeys([]); | ||
| 43 | + setSelectedData([]); | ||
| 44 | + }; | ||
| 45 | + | ||
| 46 | + const handleSelectPos = (keys: string[], datas: RoleModel[]) => { | ||
| 47 | + setSelectedKeys(keys); | ||
| 48 | + setSelectedData(datas); | ||
| 49 | + }; | ||
| 50 | + | ||
| 51 | + const handleRemove = (index: number) => { | ||
| 52 | + coreRef.current.remove(index); | ||
| 53 | + }; | ||
| 54 | + | ||
| 55 | + return ( | ||
| 56 | + <Modal | ||
| 57 | + title={props.title || '选择角色'} | ||
| 58 | + width={560} | ||
| 59 | + visible={props.visible} | ||
| 60 | + className={'qx-role-selector__dialog'} | ||
| 61 | + onOk={handleOk} | ||
| 62 | + onCancel={handleCancel} | ||
| 63 | + > | ||
| 64 | + {props.multiple ? ( | ||
| 65 | + <div className={'qx-role-selected__temp'}> | ||
| 66 | + {(selectedData || []).map((item: RoleModel, index: number) => ( | ||
| 67 | + <Tag closable color={'blue'} key={item.id} onClose={() => handleRemove(index)}> | ||
| 68 | + {item.name} | ||
| 69 | + </Tag> | ||
| 70 | + ))} | ||
| 71 | + </div> | ||
| 72 | + ) : null} | ||
| 73 | + <div className={'qx-role-selector__content'}> | ||
| 74 | + {props.visible ? ( | ||
| 75 | + <RoleSelCore | ||
| 76 | + request={props.request} | ||
| 77 | + cRef={coreRef} | ||
| 78 | + // multiple | ||
| 79 | + multiple={!props?.isRole} | ||
| 80 | + onSelect={handleSelectPos} | ||
| 81 | + appId={props.appId} | ||
| 82 | + isRole={props?.isRole} | ||
| 83 | + appCode={props?.appCode} | ||
| 84 | + /> | ||
| 85 | + ) : null} | ||
| 86 | + </div> | ||
| 87 | + </Modal> | ||
| 88 | + ); | ||
| 89 | +}; | ||
| 90 | + | ||
| 91 | +export default RoleSelectorDialog; |
src/qx-role-selector/src/input.tsx
0 → 100644
| 1 | +import React, { useEffect, useState } from 'react'; | ||
| 2 | +import { Checkbox, Input, Tag } from 'antd'; | ||
| 3 | +import { ApartmentOutlined } from '@ant-design/icons'; | ||
| 4 | +import PosSelectorDialog from './dialog'; | ||
| 5 | +// import { RequestMethod } from 'umi-request'; | ||
| 6 | + | ||
| 7 | +export type QxRoleSelectorProps = { | ||
| 8 | + onChange?: (data: any) => void; | ||
| 9 | + defaultValue?: any; | ||
| 10 | + disabled?: boolean; | ||
| 11 | + multiple?: boolean; | ||
| 12 | + readOnly?: boolean; | ||
| 13 | + name?: string; | ||
| 14 | + data?: []; | ||
| 15 | + request: any; | ||
| 16 | +}; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * 表单设计器(XRender) | ||
| 20 | + * @constructor | ||
| 21 | + */ | ||
| 22 | +const QxRoleSelector: React.FC<QxRoleSelectorProps> = (props) => { | ||
| 23 | + const [selectOrgs, setSelectOrgs] = useState([]); | ||
| 24 | + const [visible, setVisible] = useState(false); | ||
| 25 | + const [value, setValue] = useState<string | string[]>(); | ||
| 26 | + | ||
| 27 | + useEffect(() => { | ||
| 28 | + setValue(props.defaultValue); | ||
| 29 | + }, []); | ||
| 30 | + | ||
| 31 | + // getUserList() | ||
| 32 | + const handleOk = (keys: [], data: []) => { | ||
| 33 | + let _value: string[] | string = keys; | ||
| 34 | + if (!props.multiple && keys && keys.length > 0) { | ||
| 35 | + // @ts-ignore | ||
| 36 | + _value = keys[0]; | ||
| 37 | + } | ||
| 38 | + setValue(_value); | ||
| 39 | + setSelectOrgs(data); | ||
| 40 | + setVisible(false); | ||
| 41 | + if (props.onChange) { | ||
| 42 | + props.onChange(_value); | ||
| 43 | + } | ||
| 44 | + }; | ||
| 45 | + | ||
| 46 | + const handleCancel = () => { | ||
| 47 | + setVisible(false); | ||
| 48 | + }; | ||
| 49 | + const handleRemove = (index: number) => { | ||
| 50 | + let _value: string | string[] = ''; | ||
| 51 | + let _selected: string[] = []; | ||
| 52 | + if (props.multiple) { | ||
| 53 | + _value = [...value]; | ||
| 54 | + _selected = [...selectOrgs]; | ||
| 55 | + _value.splice(index, 1); | ||
| 56 | + _selected.splice(index, 1); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + setValue(_value); | ||
| 60 | + setSelectOrgs(_selected); | ||
| 61 | + | ||
| 62 | + if (props.onChange) { | ||
| 63 | + props.onChange(_value); | ||
| 64 | + } | ||
| 65 | + }; | ||
| 66 | + | ||
| 67 | + return ( | ||
| 68 | + <> | ||
| 69 | + {props.name ? ( | ||
| 70 | + props.multiple && typeof value !== 'string' ? ( | ||
| 71 | + <Checkbox.Group name={props.name} value={value} style={{ display: 'none' }} /> | ||
| 72 | + ) : ( | ||
| 73 | + <Input style={{ display: 'none' }} value={value} /> | ||
| 74 | + ) | ||
| 75 | + ) : null} | ||
| 76 | + <div | ||
| 77 | + className={'qx-user-selector ant-input'} | ||
| 78 | + style={{ minHeight: '33px' }} | ||
| 79 | + onClick={() => setVisible(true)} | ||
| 80 | + > | ||
| 81 | + <ApartmentOutlined style={{ paddingRight: '5px', color: '#999' }} /> | ||
| 82 | + {selectOrgs.map((org: { title: string; key: string }, index) => ( | ||
| 83 | + <Tag closable color={'blue'} key={org.key} onClose={() => handleRemove(index)}> | ||
| 84 | + {org.title} | ||
| 85 | + </Tag> | ||
| 86 | + ))} | ||
| 87 | + </div> | ||
| 88 | + {!props.readOnly && ( | ||
| 89 | + <PosSelectorDialog | ||
| 90 | + visible={visible} | ||
| 91 | + multiple={props.multiple} | ||
| 92 | + data={props.data} | ||
| 93 | + onOk={handleOk} | ||
| 94 | + request={props.request} | ||
| 95 | + onCancel={handleCancel} | ||
| 96 | + /> | ||
| 97 | + )} | ||
| 98 | + </> | ||
| 99 | + ); | ||
| 100 | +}; | ||
| 101 | + | ||
| 102 | +export default QxRoleSelector; |
src/qx-role-selector/src/service.ts
0 → 100644
| 1 | +/** | ||
| 2 | + * 获取选人组件中的角色 | ||
| 3 | + */ | ||
| 4 | +export function getRoles(request: any, appCode: string) { | ||
| 5 | + return request.get(`/qx-apaas-uc/role/authTree`); | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 获取选人组件中的角色(设计时角色控制) | ||
| 10 | + */ | ||
| 11 | +export function getRolesByAppCode(request: any, appCode: string) { | ||
| 12 | + return request.get(`/qx-apaas-lowcode/appRole/${appCode}/authTree`); | ||
| 13 | +} |
src/qx-role-selector/src/style.less
0 → 100644
| 1 | +.qx-role-selector__dialog { | ||
| 2 | + .ant-modal-body { | ||
| 3 | + display: flex; | ||
| 4 | + flex-direction: column; | ||
| 5 | + padding: 0; | ||
| 6 | + | ||
| 7 | + > .ant-row { | ||
| 8 | + flex: 1; | ||
| 9 | + } | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + .ant-checkbox-wrapper { | ||
| 13 | + width: 100%; | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +.qx-role-selected__temp { | ||
| 18 | + display: flex; | ||
| 19 | + flex-wrap: wrap; | ||
| 20 | + align-items: center; | ||
| 21 | + height: 60px; | ||
| 22 | + padding: 5px; | ||
| 23 | + overflow: auto; | ||
| 24 | + border-bottom: 1px solid #f0f0f0; | ||
| 25 | + | ||
| 26 | + .ant-tag { | ||
| 27 | + margin: 1px 2px; | ||
| 28 | + } | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +.qx-role-selector__content { | ||
| 32 | + height: 300px; | ||
| 33 | + overflow: auto; | ||
| 34 | +} |