From ff1cf01edf74d30bec31c668bc56acd6b132e341 Mon Sep 17 00:00:00 2001 From: jialin Date: Sun, 23 Jun 2024 15:15:12 +0800 Subject: [PATCH] chore: user apikeys --- config/proxy.ts | 2 +- config/routes.ts | 3 +- package.json | 1 + pnpm-lock.yaml | 67 ++++--- src/access.ts | 3 +- src/app.tsx | 18 +- src/assets/styles/common.less | 4 + src/layouts/index.tsx | 22 +- src/layouts/rightRender.tsx | 48 +++-- src/models/global.ts | 12 +- src/pages/api-keys/apis/index.ts | 26 +++ src/pages/api-keys/components/add-apikey.tsx | 39 ++-- src/pages/api-keys/config/index.ts | 7 + src/pages/api-keys/config/types.ts | 15 ++ src/pages/api-keys/index.tsx | 201 +++++++++++++++---- src/pages/llmodels/index.tsx | 6 +- src/pages/login/apis/index.ts | 29 +++ src/pages/login/index.tsx | 42 +++- src/pages/users/apis/index.ts | 4 +- src/pages/users/components/add-modal.tsx | 34 +++- src/pages/users/config/types.ts | 3 +- src/pages/users/index.tsx | 61 ++++-- src/request-config.ts | 6 + src/utils/index.ts | 8 +- 24 files changed, 481 insertions(+), 180 deletions(-) create mode 100644 src/pages/api-keys/apis/index.ts create mode 100644 src/pages/api-keys/config/index.ts create mode 100644 src/pages/api-keys/config/types.ts create mode 100644 src/pages/login/apis/index.ts diff --git a/config/proxy.ts b/config/proxy.ts index a887ff36..4d04c15f 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -1,4 +1,4 @@ -const proxyTableList = ['cli', 'v1']; +const proxyTableList = ['cli', 'v1', 'auth']; // @ts-ingore export default function createProxyTable(target?: string) { diff --git a/config/routes.ts b/config/routes.ts index d816a057..6417ac85 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -4,7 +4,8 @@ export default [ key: 'dashboard', layout: false, icon: 'home', - redirect: '/dashboard' + redirect: '/dashboard', + access: 'canLogin' }, { name: 'Dashboard', diff --git a/package.json b/package.json index 2aa51e84..a0fda671 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dayjs": "^1.11.11", "lodash": "^4.17.21", "numeral": "^2.0.6", + "query-string": "^9.0.0", "umi-presets-pro": "^2.0.3" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 906959c9..aaeeb11d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ dependencies: numeral: specifier: ^2.0.6 version: 2.0.6 + query-string: + specifier: ^9.0.0 + version: 9.0.0 umi-presets-pro: specifier: ^2.0.3 version: 2.0.3(@babel/core@7.24.5)(@types/react-dom@18.3.0)(@types/react@18.3.1)(antd@5.17.0)(dva@2.5.0-beta.2)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1)(umi@4.2.1) @@ -4246,7 +4249,7 @@ packages: dependencies: '@babel/core': 7.24.5 postcss: 7.0.39 - postcss-syntax: 0.36.2(postcss@7.0.39) + postcss-syntax: 0.36.2(postcss@8.4.38) transitivePeerDependencies: - supports-color dev: false @@ -4273,7 +4276,7 @@ packages: postcss-syntax: '>=0.36.2' dependencies: postcss: 7.0.39 - postcss-syntax: 0.36.2(postcss@7.0.39) + postcss-syntax: 0.36.2(postcss@8.4.38) remark: 13.0.0 unist-util-find-all-after: 3.0.2 transitivePeerDependencies: @@ -7852,10 +7855,15 @@ packages: dev: false /decode-uri-component@0.2.2: - resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==, tarball: https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz} engines: {node: '>=0.10'} dev: false + /decode-uri-component@0.4.1: + resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==, tarball: https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz} + engines: {node: '>=14.16'} + dev: false + /deep-equal@1.1.2: resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} engines: {node: '>= 0.4'} @@ -9361,10 +9369,15 @@ packages: to-regex-range: 5.0.1 /filter-obj@1.1.0: - resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==, tarball: https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz} engines: {node: '>=0.10.0'} dev: false + /filter-obj@5.1.0: + resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==, tarball: https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz} + engines: {node: '>=14.16'} + dev: false + /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -12380,7 +12393,7 @@ packages: dependencies: htmlparser2: 3.10.1 postcss: 7.0.39 - postcss-syntax: 0.36.2(postcss@7.0.39) + postcss-syntax: 0.36.2(postcss@8.4.38) dev: false /postcss-image-set-function@4.0.7(postcss@8.4.38): @@ -12670,30 +12683,6 @@ packages: lodash: 4.17.21 postcss: 8.4.38 - /postcss-syntax@0.36.2(postcss@7.0.39): - resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==} - peerDependencies: - postcss: '>=5.0.0' - postcss-html: '*' - postcss-jsx: '*' - postcss-less: '*' - postcss-markdown: '*' - postcss-scss: '*' - peerDependenciesMeta: - postcss-html: - optional: true - postcss-jsx: - optional: true - postcss-less: - optional: true - postcss-markdown: - optional: true - postcss-scss: - optional: true - dependencies: - postcss: 7.0.39 - dev: false - /postcss-syntax@0.36.2(postcss@8.4.38): resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==} peerDependencies: @@ -12955,7 +12944,7 @@ packages: dev: false /query-string@6.14.1: - resolution: {integrity: sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==} + resolution: {integrity: sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==, tarball: https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz} engines: {node: '>=6'} dependencies: decode-uri-component: 0.2.2 @@ -12964,6 +12953,15 @@ packages: strict-uri-encode: 2.0.0 dev: false + /query-string@9.0.0: + resolution: {integrity: sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw==, tarball: https://registry.npmjs.org/query-string/-/query-string-9.0.0.tgz} + engines: {node: '>=18'} + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + dev: false + /querystring-es3@0.2.1: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} @@ -15408,10 +15406,15 @@ packages: dev: false /split-on-first@1.1.0: - resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==, tarball: https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz} engines: {node: '>=6'} dev: false + /split-on-first@3.0.0: + resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==, tarball: https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz} + engines: {node: '>=12'} + dev: false + /split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -15756,7 +15759,7 @@ packages: postcss-sass: 0.4.4 postcss-scss: 2.1.1 postcss-selector-parser: 6.0.16 - postcss-syntax: 0.36.2(postcss@7.0.39) + postcss-syntax: 0.36.2(postcss@8.4.38) postcss-value-parser: 4.2.0 resolve-from: 5.0.0 slash: 3.0.0 diff --git a/src/access.ts b/src/access.ts index f81d09e1..340d990a 100644 --- a/src/access.ts +++ b/src/access.ts @@ -6,6 +6,7 @@ export default (initialState: API.UserInfo) => { ); return { canSeeAdmin, - canDelete: true + canDelete: true, + canLogin: true }; }; diff --git a/src/app.tsx b/src/app.tsx index 0666fbed..7ebe2662 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -23,17 +23,15 @@ export async function getInitialState() { return undefined; }; - // if (![loginPath].includes(location.pathname)) { - // const currentUser = await fetchUserInfo(); - // return { - // fetchUserInfo, - // name: 'admin', - // ...currentUser - // }; - // } + if (![loginPath].includes(location.pathname)) { + const userInfo = await fetchUserInfo(); + return { + fetchUserInfo, + currentUser: userInfo + }; + } return { - fetchUserInfo, - name: 'admin' + fetchUserInfo }; } diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index a176d749..daf1724f 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -6,6 +6,10 @@ margin-left: 10px; } +.m-l-5 { + margin-left: 5px; +} + .flex { display: flex; } diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index bf232618..a9f0a524 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,5 +1,6 @@ // @ts-nocheck +import { logout } from '@/pages/login/apis'; import { useAccessMarkedRoutes } from '@@/plugin-access'; import { useModel } from '@@/plugin-model'; import { ProLayout } from '@ant-design/pro-components'; @@ -106,18 +107,19 @@ export default (props: any) => { // }); const runtimeConfig = { ...initialInfo, - logout: () => { - console.log('logout'); + logout: async (userInfo) => { + console.log('logout', userInfo); + await logout(); + navigate(loginPath); }, - notFound:
not found
+ notFound: 404 not found }; - console.log( - 'clientRoute==========2=', + console.log('clientRoute==========2=', { props, clientRoutes, runtimeConfig, initialInfo - ); + }); // 现在的 layout 及 wrapper 实现是通过父路由的形式实现的, 会导致路由数据多了冗余层级, proLayout 消费时, 无法正确展示菜单, 这里对冗余数据进行过滤操作 const newRoutes = filterRoutes( @@ -153,12 +155,12 @@ export default (props: any) => { navigate('/'); }} onPageChange={(route) => { - console.log('onRouteChange', route); + console.log('onRouteChange', initialState, route); const { location } = history; // 如果没有登录,重定向到 login - // if (!initialState?.currentUser && location.pathname !== loginPath) { - // history.push(loginPath); - // } + if (!initialState?.currentUser && location.pathname !== loginPath) { + history.push(loginPath); + } }} formatMessage={userConfig.formatMessage || formatMessage} menu={{ locale: userConfig.locale }} diff --git a/src/layouts/rightRender.tsx b/src/layouts/rightRender.tsx index 036d52c5..434c98c3 100644 --- a/src/layouts/rightRender.tsx +++ b/src/layouts/rightRender.tsx @@ -4,10 +4,9 @@ import avatarImg from '@/assets/images/avatar.png'; import { GlobalOutlined, LogoutOutlined, - SettingOutlined, - SunOutlined + SettingOutlined } from '@ant-design/icons'; -import { useNavigate } from '@umijs/max'; +import { history } from '@umijs/max'; import { Avatar, Dropdown, Menu, Spin, version } from 'antd'; export function getRightRenderContent(opts: { @@ -26,10 +25,10 @@ export function getRightRenderContent(opts: { } const showAvatar = - opts.initialState?.avatar || - opts.initialState?.name || + opts.initialState?.currentUser?.avatar || + opts.initialState?.currentUser?.username || opts.runtimeConfig.logout; - const disableAvatarImg = opts.initialState?.avatar === false; + const disableAvatarImg = opts.initialState?.currentUser?.avatar === false; const nameClassName = disableAvatarImg ? 'umi-plugin-layout-name umi-plugin-layout-hide-avatar-img' : 'umi-plugin-layout-name'; @@ -39,11 +38,13 @@ export function getRightRenderContent(opts: { ) : null} - {opts.initialState?.name} + + {opts.initialState?.currentUser?.username} + ) : null; @@ -58,8 +59,6 @@ export function getRightRenderContent(opts: { // 如果没有打开Locale,并且头像为空就取消掉这个返回的内容 if (!avatar) return null; - const navigate = useNavigate(); - const langMenu = { className: 'umi-plugin-layout-menu', selectedKeys: [], @@ -73,21 +72,21 @@ export function getRightRenderContent(opts: { ), onClick: () => { - navigate('/profile'); - } - }, - { - key: 'theme', - label: ( - <> - - 外观 - - ), - onClick: () => { - console.log('theme'); + history.push('/profile'); } }, + // { + // key: 'theme', + // label: ( + // <> + // + // 外观 + // + // ), + // onClick: () => { + // console.log('theme'); + // } + // }, { key: 'lang', label: ( @@ -109,14 +108,13 @@ export function getRightRenderContent(opts: { ), onClick: () => { - opts?.runtimeConfig?.logout?.(opts.initialState); + opts?.runtimeConfig?.logout?.(opts.initialState.currentUser); } } ] }; // antd@5 和 4.24 之后推荐使用 menu,性能更好 - console.log('version+++++++++=', opts.runtimeConfig, version); let dropdownProps; if (version.startsWith('5.') || version.startsWith('4.24.')) { dropdownProps = { menu: langMenu }; diff --git a/src/models/global.ts b/src/models/global.ts index 708ccff9..fd0bc4a2 100644 --- a/src/models/global.ts +++ b/src/models/global.ts @@ -1,13 +1,13 @@ // 全局共享数据示例 -import { DEFAULT_NAME } from '@/constants'; +// import { DEFAULT_NAME } from '@/constants'; import { useState } from 'react'; -const useUser = () => { - const [name, setName] = useState(DEFAULT_NAME); +const useGlobalState = () => { + const [globalState, setGlobalState] = useState({}); return { - name, - setName, + globalState, + setGlobalState }; }; -export default useUser; +export default useGlobalState; diff --git a/src/pages/api-keys/apis/index.ts b/src/pages/api-keys/apis/index.ts new file mode 100644 index 00000000..13d053ab --- /dev/null +++ b/src/pages/api-keys/apis/index.ts @@ -0,0 +1,26 @@ +import { request } from '@umijs/max'; +import { FormData, ListItem } from '../config/types'; + +export const APIS_KEYS_API = '/api_keys'; + +export async function queryApisKeysList( + params: Global.Pagination & { query?: string } +) { + return request>(`${APIS_KEYS_API}`, { + method: 'GET', + params + }); +} + +export async function createApisKey(params: { data: FormData }) { + return request(`${APIS_KEYS_API}`, { + method: 'POST', + data: params.data + }); +} + +export async function deleteApisKey(id: number) { + return request(`${APIS_KEYS_API}/${id}`, { + method: 'DELETE' + }); +} diff --git a/src/pages/api-keys/components/add-apikey.tsx b/src/pages/api-keys/components/add-apikey.tsx index b7ccdbce..7e025149 100644 --- a/src/pages/api-keys/components/add-apikey.tsx +++ b/src/pages/api-keys/components/add-apikey.tsx @@ -1,24 +1,20 @@ -import CopyButton from '@/components/copy-button'; import ModalFooter from '@/components/modal-footer'; import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import { PageActionType } from '@/config/types'; import { SyncOutlined } from '@ant-design/icons'; import { Form, Modal } from 'antd'; +import { expirationOptions } from '../config'; +import { FormData } from '../config/types'; type AddModalProps = { title: string; action: PageActionType; open: boolean; - onOk: () => void; + onOk: (values: FormData) => void; onCancel: () => void; }; -const expirationOptions = [ - { label: '1 Month', value: '1m' }, - { label: '6 Months', value: '6m' }, - { label: 'Never', value: 'never' } -]; const AddModal: React.FC = ({ title, action, @@ -36,11 +32,15 @@ const AddModal: React.FC = ({ /> ); + const handleSumit = () => { + form.submit(); + }; + return ( = ({ keyboard={false} width={600} styles={{}} - footer={} + footer={ + + } > -
- - - - - - } - > + + name="name" rules={[{ required: true }]}> + - + name="expires_in" rules={[{ required: true }]}> + name="description" rules={[{ required: false }]}> + +
); diff --git a/src/pages/api-keys/config/index.ts b/src/pages/api-keys/config/index.ts new file mode 100644 index 00000000..4a0a27f0 --- /dev/null +++ b/src/pages/api-keys/config/index.ts @@ -0,0 +1,7 @@ +export const expirationOptions = [ + { label: '7 days', type: 'day', value: 7 }, + { label: '1 month', type: 'month', value: 1 }, + { label: '6 months', type: 'month', value: 6 }, + // { label: '1 year', type: 'year', value: 1 }, + { label: 'never', type: 'never', value: -1 } +]; diff --git a/src/pages/api-keys/config/types.ts b/src/pages/api-keys/config/types.ts new file mode 100644 index 00000000..e3bf24bb --- /dev/null +++ b/src/pages/api-keys/config/types.ts @@ -0,0 +1,15 @@ +export interface ListItem { + name: string; + description: string; + id: number; + value: string; + created_at: string; + updated_at: string; + expires_at: string; +} + +export interface FormData { + name: string; + description: string; + expires_in: number | null; +} diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index 4be61616..e0850e6f 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -1,21 +1,33 @@ +import CopyButton from '@/components/copy-button'; import PageTools from '@/components/page-tools'; import { PageAction } from '@/config'; import type { PageActionType } from '@/config/types'; import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableSort from '@/hooks/use-table-sort'; -import { - DeleteOutlined, - EditOutlined, - PlusOutlined, - SyncOutlined -} from '@ant-design/icons'; +import { handleBatchRequest } from '@/utils'; +import { DeleteOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-components'; -import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd'; -import { useState } from 'react'; +import { + Button, + Input, + Modal, + Space, + Table, + Tag, + Tooltip, + message +} from 'antd'; +import dayjs from 'dayjs'; +import _ from 'lodash'; +import { useEffect, useState } from 'react'; +import { createApisKey, deleteApisKey, queryApisKeysList } from './apis'; import AddAPIKeyModal from './components/add-apikey'; +import { expirationOptions } from './config'; +import { FormData, ListItem } from './config/types'; + const { Column } = Table; -const dataSource = [ +const list = [ { key: '1', name: 'local', @@ -51,22 +63,32 @@ const Models: React.FC = () => { const { sortOrder, setSortOrder } = useTableSort({ defaultSortOrder: 'descend' }); + const [dataSource, setDataSource] = useState([]); const [total, setTotal] = useState(0); const [openAddModal, setOpenAddModal] = useState(false); const [loading, setLoading] = useState(false); const [action, setAction] = useState(PageAction.CREATE); const [title, setTitle] = useState(''); const [queryParams, setQueryParams] = useState({ - current: 1, - pageSize: 10, - name: '' + page: 1, + perPage: 10, + query: '' }); - const handleShowSizeChange = (current: number, size: number) => { - console.log(current, size); + + const handleShowSizeChange = (page: number, size: number) => { + console.log(page, size); + setQueryParams({ + ...queryParams, + perPage: size + }); }; const handlePageChange = (page: number, pageSize: number | undefined) => { console.log(page, pageSize); + setQueryParams({ + ...queryParams, + page: page + }); }; const handleTableChange = (pagination: any, filters: any, sorter: any) => { @@ -74,8 +96,40 @@ const Models: React.FC = () => { setSortOrder(sorter.order); }; + const getExpireValue = (val: number | null) => { + const expires_in = val; + if (expires_in === -1) { + return 0; + } + const selected = expirationOptions.find( + (item) => expires_in === item.value + ); + + const d1 = dayjs().add( + selected?.value as number, + `${selected?.type}` as never + ); + const d2 = dayjs(); + const res = d1.diff(d2, 'second'); + return res; + }; + const fetchData = async () => { - console.log('fetchData'); + setLoading(true); + try { + const params = { + ..._.pickBy(queryParams, (val: any) => !!val) + }; + const res = await queryApisKeysList(params); + console.log('res=======', res); + setDataSource(res.items || []); + setTotal(res.pagination.total); + } catch (error) { + console.log('error', error); + setDataSource([]); + } finally { + setLoading(false); + } }; const handleSearch = (e: any) => { fetchData(); @@ -84,7 +138,7 @@ const Models: React.FC = () => { const handleNameChange = (e: any) => { setQueryParams({ ...queryParams, - name: e.target.value + query: e.target.value }); }; @@ -94,13 +148,22 @@ const Models: React.FC = () => { setTitle('Add API Key'); }; - const handleClickMenu = (e: any) => { - console.log('click', e); - }; - - const handleModalOk = () => { + const handleModalOk = async (data: FormData) => { console.log('handleModalOk'); - setOpenAddModal(false); + + try { + const params = { + ...data, + expires_in: getExpireValue(data.expires_in) + }; + const res = await createApisKey({ data: params }); + setOpenAddModal(false); + message.success('successfully!'); + setDataSource([res, ...dataSource]); + setTotal(total + 1); + } catch (error) { + setOpenAddModal(false); + } }; const handleModalCancel = () => { @@ -108,13 +171,30 @@ const Models: React.FC = () => { setOpenAddModal(false); }; - const handleDelete = () => { + const handleDelete = (row: ListItem) => { Modal.confirm({ title: '', content: 'Are you sure you want to delete the selected keys?', - onOk() { + async onOk() { console.log('OK'); + await deleteApisKey(row.id); + message.success('successfully!'); + fetchData(); + }, + onCancel() { + console.log('Cancel'); + } + }); + }; + + const handleDeleteBatch = () => { + Modal.confirm({ + title: '', + content: 'Are you sure you want to delete the selected keys?', + async onOk() { + await handleBatchRequest(rowSelection.selectedRowKeys, deleteApisKey); message.success('successfully!'); + fetchData(); }, onCancel() { console.log('Cancel'); @@ -127,6 +207,34 @@ const Models: React.FC = () => { setAction(PageAction.EDIT); setTitle('Edit User'); }; + + const renderSecrectKey = (text: string, record: ListItem) => { + const { value } = record; + + return ( + + {text} + {value && ( + + + 确保立即复制您的个人访问密钥。您将无法再次看到它! + + + {`${value?.slice(0, 8)}...${value?.slice(-8, -1)}`} + + + + )} + + ); + }; + + useEffect(() => { + fetchData(); + }, [queryParams]); + return ( <> { - @@ -274,6 +310,7 @@ const Models: React.FC = () => { open={openAddModal} action={action} title={title} + data={currentData} onCancel={handleModalCancel} onOk={handleModalOk} > diff --git a/src/request-config.ts b/src/request-config.ts index c5556f0c..75b395d8 100644 --- a/src/request-config.ts +++ b/src/request-config.ts @@ -1,6 +1,8 @@ import { RequestConfig } from '@umijs/max'; import { message } from 'antd'; +const NoBaseURLAPIs = ['/auth']; + export const requestConfig: RequestConfig = { errorConfig: { errorThrower: (res: any) => { @@ -17,6 +19,10 @@ export const requestConfig: RequestConfig = { requestInterceptors: [ (url, options) => { console.log('requestInterceptors+++++++++++++++', url, options); + if (NoBaseURLAPIs.some((api) => url.startsWith(api))) { + options.baseURL = ''; + return { url, options }; + } return { url, options }; } ], diff --git a/src/utils/index.ts b/src/utils/index.ts index bf82ed5c..e805792e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,7 +2,13 @@ export const isNotEmptyValue = (value: any) => { if (Array.isArray(value)) { return value.length > 0; } - return value !== null && value !== undefined && value !== ''; + return ( + value !== null && + value !== undefined && + value !== '' && + value !== false && + value !== 0 + ); }; export const handleBatchRequest = async (