|
|
@@ -0,0 +1,258 @@ |
|
|
|
import React, { useState, useEffect, useRef } from 'react'; |
|
|
|
import { useSetState } from 'ahooks'; |
|
|
|
import { Space, Table, Input, Divider, Tag, Card } from 'antd'; |
|
|
|
import type { TableColumnsType, InputRef } from 'antd'; |
|
|
|
import type { ColumnType, TableProps } from 'antd/es/table'; |
|
|
|
import { t } from '@/utils/i18n'; |
|
|
|
import { SearchOutlined, ExclamationCircleFilled } from '@ant-design/icons'; |
|
|
|
import { antdUtils } from '@/utils/antd'; |
|
|
|
import { useRequest } from '@/hooks/use-request'; |
|
|
|
import { formatDate } from '@/utils/formatTime' |
|
|
|
import oauth2Service from '@/request/service/oauth2'; |
|
|
|
import { |
|
|
|
OAuth2ClientVO, |
|
|
|
OAuth2ClientPageReqVO |
|
|
|
} from '@/models'; |
|
|
|
|
|
|
|
|
|
|
|
type DataIndex = keyof OAuth2ClientVO; |
|
|
|
|
|
|
|
export default () => { |
|
|
|
const [dataSource, setDataSource] = useState<OAuth2ClientVO[]>([]); |
|
|
|
|
|
|
|
const { runAsync: getPageData } = useRequest(oauth2Service.getOAuth2ClientPageApi, { manual: true }); |
|
|
|
const { runAsync: deleteClient } = useRequest(oauth2Service.deleteOAuth2ClientApi, { manual: true }); |
|
|
|
|
|
|
|
const [searchState, setSearchState] = useSetState<OAuth2ClientPageReqVO>({ |
|
|
|
pageNo: 1, |
|
|
|
pageSize: 10 |
|
|
|
}); |
|
|
|
const [total, setTotal] = useState(0) |
|
|
|
const searchInput = useRef<InputRef>(null); |
|
|
|
const [onSearching, setOnSearching] = useState(false); |
|
|
|
|
|
|
|
const load = async () => { |
|
|
|
console.log(searchState) |
|
|
|
const [error, { code, msg, data }] = await getPageData(searchState); |
|
|
|
setOnSearching(false); |
|
|
|
if (error || code !== 0) { |
|
|
|
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' }); |
|
|
|
return |
|
|
|
} |
|
|
|
setTotal(data.total); |
|
|
|
setDataSource(data.list); |
|
|
|
}; |
|
|
|
|
|
|
|
const getColumnSearchProps = (dataIndex: DataIndex, placeholder: string): ColumnType<OAuth2ClientVO> => ({ |
|
|
|
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => ( |
|
|
|
<div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}> |
|
|
|
<Input.Search |
|
|
|
ref={searchInput} |
|
|
|
placeholder={placeholder} |
|
|
|
value={selectedKeys[0]} |
|
|
|
onChange={(e) => { |
|
|
|
setSelectedKeys(e.target.value && e.target.value !== '' ? [e.target.value] : []) |
|
|
|
}} |
|
|
|
onSearch={(value) => { |
|
|
|
if (value === '' && clearFilters) { |
|
|
|
clearFilters!!() |
|
|
|
} |
|
|
|
confirm(); |
|
|
|
}} |
|
|
|
onPressEnter={() => confirm()} |
|
|
|
allowClear |
|
|
|
style={{ marginBottom: 8, display: 'block' }} |
|
|
|
enterButton="搜索" |
|
|
|
size="middle" |
|
|
|
loading={onSearching} |
|
|
|
/> |
|
|
|
</div> |
|
|
|
), |
|
|
|
filterIcon: (filtered: boolean) => ( |
|
|
|
<SearchOutlined style={{ color: filtered ? 'primaryColor' : undefined }} /> |
|
|
|
), |
|
|
|
onFilterDropdownOpenChange: (visible) => { |
|
|
|
if (visible) { |
|
|
|
setTimeout(() => searchInput.current?.select(), 100); |
|
|
|
} |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
const forceQuite = async (params: OAuth2ClientVO) => { |
|
|
|
antdUtils.modal?.confirm({ |
|
|
|
title: '系统提示', |
|
|
|
icon: <ExclamationCircleFilled />, |
|
|
|
content: '是否删除所选中数据?', |
|
|
|
okText: '删除', |
|
|
|
okType: 'danger', |
|
|
|
cancelText: '取消', |
|
|
|
onOk() { |
|
|
|
return new Promise(async (resolve) => { |
|
|
|
const [error, { code, msg }] = await deleteClient(params.id); |
|
|
|
if (error || code !== 0) { |
|
|
|
antdUtils.message?.open({ type: 'error', content: msg ?? '操作失败' }) |
|
|
|
} else { |
|
|
|
antdUtils.message?.open({ type: 'success', content: '操作成功' }) |
|
|
|
} |
|
|
|
resolve(''); |
|
|
|
await load(); |
|
|
|
}).catch(() => antdUtils.message?.open({ |
|
|
|
type: 'error', |
|
|
|
content: '操作失败', |
|
|
|
})); |
|
|
|
}, |
|
|
|
onCancel() { |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const columns: TableColumnsType<OAuth2ClientVO> = [ |
|
|
|
{ |
|
|
|
title: '客户端密钥', |
|
|
|
dataIndex: 'secret', |
|
|
|
key: 'secret', |
|
|
|
align: 'center', |
|
|
|
width: 120, |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '应用名', |
|
|
|
dataIndex: 'name', |
|
|
|
key: 'name', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '应用图标', |
|
|
|
dataIndex: 'logo', |
|
|
|
key: 'logo', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '状态', |
|
|
|
dataIndex: 'status', |
|
|
|
key: 'status', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
filters: [ |
|
|
|
{ text: '会员', value: 1 }, |
|
|
|
{ text: '管理员', value: 2 }, |
|
|
|
], |
|
|
|
filterMultiple: false, |
|
|
|
filterSearch: false, |
|
|
|
render: (value: number) => { |
|
|
|
if (value === 1) { |
|
|
|
return <Tag color="purple">会员</Tag> |
|
|
|
} else if (value === 2) { |
|
|
|
return <Tag color="blue">管理员</Tag> |
|
|
|
} else { |
|
|
|
return <Tag color="red">未知</Tag> |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '访问令牌的有效期', |
|
|
|
dataIndex: 'accessTokenValiditySeconds', |
|
|
|
key: 'accessTokenValiditySeconds', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '刷新令牌的有效期', |
|
|
|
dataIndex: 'refreshTokenValiditySeconds', |
|
|
|
key: 'refreshTokenValiditySeconds', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
title: '授权类型', |
|
|
|
dataIndex: 'authorizedGrantTypes', |
|
|
|
key: 'authorizedGrantTypes', |
|
|
|
fixed: 'left', |
|
|
|
align: 'center', |
|
|
|
width: 100, |
|
|
|
filters: [ |
|
|
|
{ text: '会员', value: 1 }, |
|
|
|
{ text: '管理员', value: 2 }, |
|
|
|
], |
|
|
|
filterMultiple: false, |
|
|
|
filterSearch: false, |
|
|
|
render: (value: number) => { |
|
|
|
if (value === 1) { |
|
|
|
return <Tag color="purple">会员</Tag> |
|
|
|
} else if (value === 2) { |
|
|
|
return <Tag color="blue">管理员</Tag> |
|
|
|
} else { |
|
|
|
return <Tag color="red">未知</Tag> |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: '创建时间', |
|
|
|
dataIndex: 'createTime', |
|
|
|
key: 'createTime', |
|
|
|
align: 'center', |
|
|
|
width: 150, |
|
|
|
render: (value: number) => { |
|
|
|
return formatDate(new Date(value), "YYYY-mm-dd HH:MM:SS") |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: t("QkOmYwne" /* 操作 */), |
|
|
|
key: 'action', |
|
|
|
fixed: 'right', |
|
|
|
align: 'center', |
|
|
|
width: 250, |
|
|
|
render: (value: OAuth2ClientVO, record) => ( |
|
|
|
<Space size="small" split={(<Divider type='vertical' />)}> |
|
|
|
<a onClick={() => { |
|
|
|
|
|
|
|
}}> 详情 </a> |
|
|
|
|
|
|
|
<a onClick={() => { |
|
|
|
forceQuite(value) |
|
|
|
}}> 强制退出 </a> |
|
|
|
</Space> |
|
|
|
), |
|
|
|
}, |
|
|
|
]; |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
load(); |
|
|
|
}, [searchState]); |
|
|
|
|
|
|
|
const onChange: TableProps<OAuth2ClientVO>['onChange'] = (pagination, filters, sorter, extra) => { |
|
|
|
const state: OAuth2ClientPageReqVO = { |
|
|
|
name: filters.name ? filters.name[0] as string : undefined, |
|
|
|
status: filters.status ? filters.status[0] as number : undefined, |
|
|
|
pageNo: pagination.current, |
|
|
|
pageSize: pagination.pageSize |
|
|
|
} |
|
|
|
setOnSearching(true); |
|
|
|
setSearchState(state); |
|
|
|
}; |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<div> |
|
|
|
<Card className='mt-[4px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> |
|
|
|
<Table rowKey="id" |
|
|
|
scroll={{ x: true }} |
|
|
|
columns={columns} |
|
|
|
dataSource={dataSource} |
|
|
|
onChange={onChange} |
|
|
|
className='bg-transparent' |
|
|
|
pagination={{ |
|
|
|
position: ['bottomRight'], |
|
|
|
current: searchState.pageNo, |
|
|
|
pageSize: searchState.pageSize, |
|
|
|
total |
|
|
|
}} |
|
|
|
/> |
|
|
|
</Card> |
|
|
|
</div> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |