index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. import { Button, Checkbox, Drawer, Form, Input, Select, Table, message, Divider } from 'antd';
  2. import React, { Fragment, useState, useEffect } from 'react';
  3. import apis from '@/services';
  4. import { DimensionsItem, DimensionType } from '@/pages/system/dimensions/data';
  5. import { groupBy, cloneDeep } from 'lodash';
  6. import { FormComponentProps } from 'antd/es/form';
  7. import styles from './index.less';
  8. import DataAccess from './DataAccess';
  9. import encodeQueryParam from '@/utils/encodeParam';
  10. interface Props extends FormComponentProps {
  11. close: Function;
  12. target?: any;
  13. targetType?: string;
  14. type?: string;
  15. height?: string;
  16. showTarget?: boolean
  17. }
  18. interface State {
  19. dataAccessVisible: boolean;
  20. permissionList: any[];
  21. checkPermission: any;
  22. permissionType: string;
  23. searchText: string;
  24. dimensionList: any[];
  25. targetAutz: any[];
  26. tempAccess: any[];
  27. }
  28. const Authorization: React.FC<Props> = props => {
  29. const initState: State = {
  30. dataAccessVisible: false,
  31. permissionList: [],
  32. checkPermission: {},
  33. permissionType: props.targetType === 'tenant' ? 'tenant' : 'all',
  34. searchText: '',
  35. dimensionList: [],
  36. targetAutz: [],
  37. tempAccess: [],
  38. };
  39. const [permissionList, setPermissionList] = useState(initState.permissionList);
  40. const [dataAccessVisible, setDataAccessVisible] = useState(initState.dataAccessVisible);
  41. const [checkPermission, setCheckPermission] = useState(initState.checkPermission);
  42. const [permissionType, setPermissionType] = useState(initState.permissionType);
  43. const [searchText, setSearchText] = useState(initState.searchText);
  44. const [dimensionList, setDimensionList] = useState(initState.dimensionList);
  45. const [targetAutz, setTargetAutz] = useState(initState.targetAutz);
  46. const [tempAccess, setTempAccess] = useState(initState.tempAccess);
  47. const {
  48. form: { getFieldDecorator, setFieldsValue, getFieldValue },
  49. // form,
  50. } = props;
  51. const getDimensions = () => {
  52. apis.dimensions.typeList().then(e => {
  53. if (e && e.status === 200) {
  54. apis.dimensions.treeList().then(d => {
  55. if (d.status === 200) {
  56. const tempList = d.result.map((dr: DimensionsItem) => {
  57. const type = e.result.find((t: DimensionType) => t.id === dr.typeId);
  58. if (type) {
  59. const typeName = type.name;
  60. return { typeName, ...dr };
  61. }
  62. return dr;
  63. });
  64. setDimensionList(tempList);
  65. }
  66. });
  67. }
  68. });
  69. };
  70. useEffect(() => {
  71. apis.permission
  72. .listNoPaging()
  73. .then(response => {
  74. if (response.status === 200) {
  75. const temp: any[] = response.result;
  76. const list = temp.filter(item => (item.actions || []).length > 0);
  77. setPermissionList(list);
  78. }
  79. })
  80. .catch(() => { });
  81. if (props.target.id) {
  82. apis.authorization
  83. .list(
  84. encodeQueryParam({
  85. paging: false,
  86. terms: {
  87. dimensionTarget: props.target.id,
  88. },
  89. }),
  90. )
  91. .then(response => {
  92. if (response.status === 200) {
  93. // const { result } = response;
  94. // const resultTemp = result.map((item: any) => ({ id: item.permisseion, ...item }))
  95. setTargetAutz(response.result);
  96. }
  97. })
  98. .catch(() => { });
  99. }
  100. // apis.authorization.autzDetail({ type: props.targetType, id: props.targetId }).then(response => {
  101. // console.log(response, 'response');
  102. // })
  103. getDimensions();
  104. }, []);
  105. const saveDataAccess = (data: any) => {
  106. // 如果有,删除原数据
  107. const temp = tempAccess.filter(i => i.permissionId !== data.permissionId);
  108. // 保存数据
  109. setTempAccess([...temp, data]);
  110. // 字段权限
  111. // 先创建Map
  112. const tempMap = new Map();
  113. const key = new Set();
  114. // 遍历出所有操作
  115. const { fieldAccess } = data;
  116. // eslint-disable-next-line no-restricted-syntax
  117. for (const item of fieldAccess) {
  118. // eslint-disable-next-line no-restricted-syntax
  119. for (const action of item.action) {
  120. key.add(action);
  121. }
  122. }
  123. // 创建Map
  124. Array.from(key).forEach(i => tempMap.set(i, []));
  125. // 赋值
  126. // eslint-disable-next-line no-restricted-syntax
  127. for (const item of fieldAccess) {
  128. // eslint-disable-next-line no-restricted-syntax
  129. for (const action of item.action) {
  130. const tem = tempMap.get(action);
  131. tempMap.set(action, [...tem, item.name]);
  132. }
  133. }
  134. // 构造数据结构
  135. const tempFiledAccess = [...tempMap.keys()].map(action => ({
  136. action,
  137. type: 'DENY_FIELDS',
  138. describe: '不能操作的字段',
  139. config: {
  140. fields: tempMap.get(action),
  141. },
  142. }));
  143. // 数据权限
  144. const { dataAccess } = data;
  145. const tempDataMap = new Map();
  146. const dataKey = new Set();
  147. // eslint-disable-next-line no-restricted-syntax
  148. for (const item of dataAccess) {
  149. // eslint-disable-next-line no-restricted-syntax
  150. for (const type of item.action) {
  151. dataKey.add(type);
  152. }
  153. }
  154. //
  155. Array.from(dataKey).forEach(i => tempDataMap.set(i, []));
  156. // 赋值
  157. // eslint-disable-next-line no-restricted-syntax
  158. for (const item of dataAccess) {
  159. // eslint-disable-next-line no-restricted-syntax
  160. for (const action of item.action) {
  161. const temp1 = tempDataMap.get(action);
  162. tempDataMap.set(action, [...temp1, item.type]);
  163. }
  164. }
  165. // 构造数据结构
  166. const tempDataAccess = [...tempDataMap.keys()].map(action => ({
  167. action,
  168. type: 'DIMENSION_SCOPE',
  169. config: {
  170. scopeType: tempDataMap.get(action).join(','),
  171. children: true,
  172. },
  173. }));
  174. // 修改数据
  175. let item: any = {};
  176. const update = targetAutz.find(i => i.permission === data.permissionId);
  177. if (update) {
  178. item = update;
  179. item.dataAccesses = [...tempFiledAccess, ...tempDataAccess];
  180. } else {
  181. item = {
  182. permission: data.permissionId,
  183. dimensionType: props.targetType,
  184. dimensionTypeName: '用户',
  185. dimensionTarget: props.target.id,
  186. dimensionTargetName: props.target.name,
  187. state: 1,
  188. dataAccesses: [...tempFiledAccess, ...tempDataAccess],
  189. merge: 10,
  190. };
  191. }
  192. // 新增(查询出来的接口没有此权限)
  193. // 删除修改的数据。
  194. const tempAutz = targetAutz.filter(i => i.permission !== data.permissionId);
  195. // console.log(tempAutz, item, data, '更细的数据');
  196. setTargetAutz([...tempAutz, item]);
  197. message.success('保存成功');
  198. // autzSetting(data)
  199. // TODO 此处调用接口保存数据
  200. };
  201. const autzSetting = () => {
  202. // 注释原因:使用搜索过滤后,过滤前的数据无法获取。对应阿里云2324892缺陷
  203. // const { permissions } = form.getFieldsValue();
  204. // const tempAutz: any[] = [];
  205. // for (const key in permissions) {
  206. // if (permissions.hasOwnProperty(key)) {
  207. // const element = permissions[key];
  208. // if (element) {
  209. // const access = tempAccess.find(i => i.permissionId === key);
  210. // tempAutz.push({ id: key, actions: element, ...access });
  211. // }
  212. // }
  213. // }
  214. // const resultTemp = targetAutz.map((item: any) => ({ id: item.permisseion, ...item }));
  215. const autzData = targetAutz.map(i => ({ id: i.permission, actions: i.actions }));
  216. apis.authorization
  217. .saveAutz({
  218. targetId: props.target.id,
  219. targetType: props.targetType,
  220. permissionList: autzData, // 修复2324892缺陷 将tempAutz换成了targetAutz
  221. })
  222. .then(response => {
  223. if (response.status === 200) {
  224. message.success('授权成功');
  225. }
  226. })
  227. .catch(() => { });
  228. // console.log({
  229. // targetId: props.targetId,
  230. // targetType: props.targetType,
  231. // permissionList: tempAutz
  232. // }, 'auzt');
  233. // console.log(authorization, 'logs');
  234. // let tempAutz = {
  235. // targetId: authorization.autz.dimensionTarget,
  236. // targetType: 'user',
  237. // permisionList: [
  238. // ]
  239. // }
  240. // let tempAutz = {
  241. // permission: authorization.id,
  242. // dimensionType: props.targetType,
  243. // dimensionTypeName: '用户',
  244. // dimensionTarget: props.targetId,
  245. // dimensionTargetName: '用户名',
  246. // state: 1,
  247. // actions: (authorization.actions || []).map((i: any) => i.action),
  248. // id: 'test',
  249. // merge: true,
  250. // dataAccesses: (authorization.fieldAccess || []).map((i: any) => {
  251. // return {
  252. // action: i.name,
  253. // type: 'DENY_FIELDS',
  254. // describe: '不能操作字段',
  255. // config: {
  256. // }
  257. // }
  258. // })
  259. // }
  260. };
  261. const renderAccess = () => (
  262. <div>
  263. <Form style={{ paddingBottom: 20 }}>
  264. <Form.Item label="被授权主体" style={props.showTarget ? { display: 'none' } : {}}>
  265. {getFieldDecorator('targetId', {
  266. rules: [{ required: true }],
  267. initialValue: props.target.id ? props.target.name : '',
  268. })(
  269. <Select mode="multiple" disabled={!!props.target.id}>
  270. {Array.from(new Set<string>(dimensionList.filter((item: any) => item.typeName).map((item: any) => item.typeName))).map(
  271. type => {
  272. const typeData = groupBy(dimensionList, item => item.typeName)[type];
  273. return (
  274. <Select.OptGroup label={type} key={type}>
  275. {typeData.map((e: any) => (
  276. <Select.Option value={e.id} key={e.id}>
  277. {e.name}
  278. </Select.Option>
  279. ))}
  280. </Select.OptGroup>
  281. );
  282. },
  283. )}
  284. </Select>,
  285. )}
  286. </Form.Item>
  287. <Form.Item label="选择权限">
  288. <Input.Group compact style={{ marginBottom: '10px' }}>
  289. <Select
  290. style={{ width: '15%' }}
  291. defaultValue={props.targetType === 'tenant' ? 'tenant' : 'all'}
  292. onChange={(value: string) => setPermissionType(value)}
  293. disabled={props.targetType === 'tenant'}
  294. >
  295. <Select.Option value="all">全部</Select.Option>
  296. <Select.Option value="default">默认</Select.Option>
  297. <Select.Option value="system">系统</Select.Option>
  298. <Select.Option value="business">业务功能</Select.Option>
  299. <Select.Option value="open-api">OpenAPI</Select.Option>
  300. <Select.Option value="tenant">多租户</Select.Option>
  301. </Select>
  302. <Input.Search
  303. style={{ width: '85%' }}
  304. placeholder="请输入权限名称"
  305. onChange={event => {
  306. setSearchText(event.target.value);
  307. }}
  308. />
  309. {/* <Button type="primary" style={{ width: '5%' }}><Icon type='search' /></Button> */}
  310. </Input.Group>
  311. <Table
  312. rowKey="id"
  313. style={{ height: props.height || '65vh', overflow: 'auto' }}
  314. pagination={false}
  315. columns={[
  316. {
  317. dataIndex: 'name',
  318. title: '权限名称',
  319. width: 150,
  320. },
  321. {
  322. dataIndex: 'actions',
  323. title: '权限操作',
  324. render: (text: { action: string; name: string }[], record: any) => {
  325. const { id } = record;
  326. const tempText = Object.assign([], text || []);
  327. const temp = targetAutz.find(item => item.permission === id) || {};
  328. return (
  329. <div className={styles.permissionForm}>
  330. <Form.Item>
  331. {getFieldDecorator(`permissions.${id}`, {
  332. initialValue: temp.actions,
  333. })(
  334. <Checkbox.Group
  335. onChange={e => {
  336. if (!temp.permission) {
  337. targetAutz.push({
  338. id: record.id,
  339. permission: record.id,
  340. actions: e,
  341. });
  342. setTargetAutz(cloneDeep([...targetAutz]));
  343. } else {
  344. temp.actions = e;
  345. const t = targetAutz.filter(i => i.permission !== id);
  346. setTargetAutz(cloneDeep([temp, ...t]));
  347. }
  348. }}
  349. options={tempText.map((e: { action: string; name: string }) => ({
  350. label: e.name,
  351. value: e.action,
  352. }))}
  353. />,
  354. )}
  355. </Form.Item>
  356. </div>
  357. );
  358. },
  359. },
  360. {
  361. dataIndex: 'properties',
  362. title: '操作',
  363. width: 180,
  364. render: (text, record: any) => {
  365. const autz = targetAutz.find(item => item.permission === record.id);
  366. return (
  367. <Fragment>
  368. {getFieldValue(`permissions.${record.id}`) &&
  369. getFieldValue(`permissions.${record.id}`).length === record.actions.length ? (
  370. <a
  371. onClick={() => {
  372. const temp = targetAutz.filter(item => item.permission !== record.id);
  373. setTargetAutz(cloneDeep(temp));
  374. setFieldsValue({ [`permissions.${record.id}`]: [] });
  375. }}
  376. >
  377. 取消全选
  378. </a>
  379. ) : (
  380. <a
  381. onClick={() => {
  382. setFieldsValue({
  383. [`permissions.${record.id}`]: record.actions.map(
  384. (i: any) => i.action,
  385. ),
  386. });
  387. if (autz) {
  388. const temp = targetAutz.filter(item => item.permission !== record.id);
  389. autz.actions = record.actions.map((i: any) => i.action);
  390. setTargetAutz(cloneDeep([...temp, autz]));
  391. } else {
  392. targetAutz.push({
  393. id: record.id,
  394. permission: record.id,
  395. actions: record.actions.map((i: any) => i.action),
  396. });
  397. setTargetAutz(cloneDeep(targetAutz));
  398. }
  399. }}
  400. >
  401. 全选
  402. </a>
  403. )}
  404. {((text && text.supportDataAccessTypes) || []).some(
  405. (i: string) => i === 'DENY_FIELDS',
  406. ) && (
  407. <>
  408. {/* <Divider type="vertical" />
  409. <a
  410. onClick={() => {
  411. setDataAccessVisible(true);
  412. setCheckPermission({ ...record, autz });
  413. }}
  414. >
  415. 数据权限
  416. </a> */}
  417. </>
  418. )}
  419. </Fragment>
  420. );
  421. },
  422. },
  423. ]}
  424. dataSource={
  425. // eslint-disable-next-line no-nested-ternary
  426. permissionType !== 'all'
  427. ? searchText.length > 0
  428. ? JSON.parse(JSON.stringify(permissionList))
  429. .filter((item: any) => ((item.properties || {}).type || []).includes(permissionType))
  430. .filter((item: any) => item.name.indexOf(searchText) > -1)
  431. : JSON.parse(JSON.stringify(permissionList)).filter(
  432. (item: any) => ((item.properties || {}).type || []).includes(permissionType),
  433. )
  434. : searchText.length > 0
  435. ? JSON.parse(JSON.stringify(permissionList)).filter(
  436. (item: any) => item.name.indexOf(searchText) > -1,
  437. )
  438. : permissionList
  439. }
  440. />
  441. </Form.Item>
  442. </Form>
  443. <div
  444. style={{
  445. position: 'absolute',
  446. right: 0,
  447. bottom: 0,
  448. width: '100%',
  449. borderTop: '1px solid #e9e9e9',
  450. padding: '10px 16px',
  451. background: '#fff',
  452. textAlign: 'right',
  453. }}
  454. >
  455. {
  456. props.type !== 'simple' ? (
  457. <div>
  458. <Button
  459. onClick={() => {
  460. props.close();
  461. }}
  462. style={{ marginRight: 8 }}
  463. >
  464. 关闭
  465. </Button>
  466. <Button
  467. onClick={() => {
  468. autzSetting();
  469. }}
  470. type="primary"
  471. >
  472. 保存
  473. </Button>
  474. </div>
  475. ) : (
  476. <div>
  477. <Button
  478. onClick={() => {
  479. autzSetting();
  480. }}
  481. type="primary"
  482. >
  483. 更新权限信息
  484. </Button>
  485. </div>
  486. )
  487. }
  488. </div>
  489. {dataAccessVisible && (
  490. <DataAccess
  491. checkPermission={checkPermission}
  492. close={() => setDataAccessVisible(false)}
  493. save={(item: any) => {
  494. saveDataAccess(item);
  495. }}
  496. />
  497. )}
  498. </div>
  499. )
  500. const renderRoot = () =>
  501. props.type !== 'simple' ? (
  502. <Drawer title="授权" visible width="50VW" onClose={() => props.close()}>
  503. {renderAccess()}
  504. </Drawer>
  505. ) : renderAccess();
  506. return (
  507. renderRoot()
  508. );
  509. };
  510. export default Form.create<Props>()(Authorization);