mirror of
https://github.com/whyour/qinglong.git
synced 2026-07-17 01:14:50 +08:00
添加openapi模块
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, message, Input, Form, Select } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
const AppModal = ({
|
||||
app,
|
||||
handleCancel,
|
||||
visible,
|
||||
}: {
|
||||
app?: any;
|
||||
visible: boolean;
|
||||
handleCancel: (needUpdate?: boolean) => void;
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleOk = async (values: any) => {
|
||||
setLoading(true);
|
||||
const method = app ? 'put' : 'post';
|
||||
const payload = { ...values };
|
||||
if (app) {
|
||||
payload._id = app._id;
|
||||
}
|
||||
const { code, data } = await request[method](`${config.apiPrefix}apps`, {
|
||||
data: payload,
|
||||
});
|
||||
if (code === 200) {
|
||||
message.success(app ? '更新应用成功' : '添加应用成功');
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
setLoading(false);
|
||||
handleCancel(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
}, [app, visible]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={app ? '编辑应用' : '新建应用'}
|
||||
visible={visible}
|
||||
forceRender
|
||||
onOk={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
handleOk(values);
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log('Validate Failed:', info);
|
||||
});
|
||||
}}
|
||||
onCancel={() => handleCancel()}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
name="form_app_modal"
|
||||
initialValues={app}
|
||||
>
|
||||
<Form.Item name="name" label="名称">
|
||||
<Input placeholder="请输入应用名称" />
|
||||
</Form.Item>
|
||||
<Form.Item name="scopes" label="权限" rules={[{ required: true }]}>
|
||||
<Select mode="multiple" allowClear style={{ width: '100%' }}>
|
||||
{config.scopes.map((x) => {
|
||||
return <Select.Option value={x.value}>{x.name}</Select.Option>;
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppModal;
|
||||
+224
-9
@@ -1,5 +1,18 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, Input, Form, Radio, Tabs } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Form,
|
||||
Radio,
|
||||
Tabs,
|
||||
Table,
|
||||
Tooltip,
|
||||
Space,
|
||||
Tag,
|
||||
Modal,
|
||||
message,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { request } from '@/utils/http';
|
||||
@@ -10,19 +23,87 @@ import {
|
||||
setFetchMethod,
|
||||
} from 'darkreader';
|
||||
import { history } from 'umi';
|
||||
import { useCtx } from '@/utils/hooks';
|
||||
import AppModal from './appModal';
|
||||
import {
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
ReloadOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
const { Text } = Typography;
|
||||
const optionsWithDisabled = [
|
||||
{ label: '亮色', value: 'light' },
|
||||
{ label: '暗色', value: 'dark' },
|
||||
{ label: '跟随系统', value: 'auto' },
|
||||
];
|
||||
|
||||
const Password = ({ headerStyle, isPhone }: any) => {
|
||||
const [value, setValue] = useState('');
|
||||
const Setting = ({ headerStyle, isPhone }: any) => {
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: 'Client ID',
|
||||
dataIndex: 'client_id',
|
||||
key: 'client_id',
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: 'Client Secret',
|
||||
dataIndex: 'client_secret',
|
||||
key: 'client_secret',
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '权限',
|
||||
dataIndex: 'scopes',
|
||||
key: 'scopes',
|
||||
align: 'center' as const,
|
||||
render: (text: string, record: any) => {
|
||||
return record.scopes.map((scope: any) => {
|
||||
return <Tag key={scope}>{(config.scopesMap as any)[scope]}</Tag>;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
align: 'center' as const,
|
||||
render: (text: string, record: any, index: number) => {
|
||||
const isPc = !isPhone;
|
||||
return (
|
||||
<Space size="middle" style={{ paddingLeft: 8 }}>
|
||||
<Tooltip title={isPc ? '编辑' : ''}>
|
||||
<a onClick={() => editApp(record, index)}>
|
||||
<EditOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title={isPc ? '重置secret' : ''}>
|
||||
<a onClick={() => resetSecret(record, index)}>
|
||||
<ReloadOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title={isPc ? '删除' : ''}>
|
||||
<a onClick={() => deleteApp(record, index)}>
|
||||
<DeleteOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const defaultDarken = localStorage.getItem('qinglong_dark_theme') || 'auto';
|
||||
const [theme, setTheme] = useState(defaultDarken);
|
||||
const [dataSource, setDataSource] = useState<any[]>([]);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [editedApp, setEditedApp] = useState();
|
||||
const [tabActiveKey, setTabActiveKey] = useState('person');
|
||||
|
||||
const handleOk = (values: any) => {
|
||||
request
|
||||
@@ -46,12 +127,116 @@ const Password = ({ headerStyle, isPhone }: any) => {
|
||||
localStorage.setItem('qinglong_dark_theme', e.target.value);
|
||||
};
|
||||
|
||||
const importJob = () => {
|
||||
request.get(`${config.apiPrefix}crons/import`).then((data: any) => {
|
||||
console.log(data);
|
||||
const getApps = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}apps`)
|
||||
.then((data: any) => {
|
||||
setDataSource(data.data);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const addApp = () => {
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const editApp = (record: any, index: number) => {
|
||||
setEditedApp(record);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const deleteApp = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: (
|
||||
<>
|
||||
确认删除应用{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.name}
|
||||
</Text>{' '}
|
||||
吗
|
||||
</>
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.delete(`${config.apiPrefix}apps`, { data: [record._id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
message.success('删除成功');
|
||||
const result = [...dataSource];
|
||||
result.splice(index, 1);
|
||||
setDataSource(result);
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const resetSecret = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确认重置',
|
||||
content: (
|
||||
<>
|
||||
确认重置应用{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.name}
|
||||
</Text>{' '}
|
||||
的Secret吗
|
||||
<br />
|
||||
<Text type="secondary">重置Secret会让当前应用所有token失效</Text>
|
||||
</>
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}apps/${record._id}/reset-secret`)
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
message.success('重置成功');
|
||||
handleApp(data.data);
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = (app?: any) => {
|
||||
setIsModalVisible(false);
|
||||
if (app) {
|
||||
handleApp(app);
|
||||
}
|
||||
};
|
||||
|
||||
const handleApp = (app: any) => {
|
||||
const index = dataSource.findIndex((x) => x._id === app._id);
|
||||
const result = [...dataSource];
|
||||
if (index === -1) {
|
||||
result.push(app);
|
||||
} else {
|
||||
result.splice(index, 1, {
|
||||
...app,
|
||||
});
|
||||
}
|
||||
setDataSource(result);
|
||||
};
|
||||
|
||||
const tabChange = (activeKey: string) => {
|
||||
setTabActiveKey(activeKey);
|
||||
if (activeKey === 'app') {
|
||||
getApps();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setFetchMethod(window.fetch);
|
||||
if (theme === 'dark') {
|
||||
@@ -70,8 +255,22 @@ const Password = ({ headerStyle, isPhone }: any) => {
|
||||
header={{
|
||||
style: headerStyle,
|
||||
}}
|
||||
extra={
|
||||
tabActiveKey === 'app'
|
||||
? [
|
||||
<Button key="2" type="primary" onClick={() => addApp()}>
|
||||
添加应用
|
||||
</Button>,
|
||||
]
|
||||
: []
|
||||
}
|
||||
>
|
||||
<Tabs defaultActiveKey="person" size="small" tabPosition="top">
|
||||
<Tabs
|
||||
defaultActiveKey="person"
|
||||
size="small"
|
||||
tabPosition="top"
|
||||
onChange={tabChange}
|
||||
>
|
||||
<Tabs.TabPane tab="个人设置" key="person">
|
||||
<Form onFinish={handleOk} layout="vertical">
|
||||
<Form.Item
|
||||
@@ -97,6 +296,17 @@ const Password = ({ headerStyle, isPhone }: any) => {
|
||||
</Button>
|
||||
</Form>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="应用设置" key="app">
|
||||
<Table
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
dataSource={dataSource}
|
||||
rowKey="_id"
|
||||
size="middle"
|
||||
scroll={{ x: 768 }}
|
||||
loading={loading}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="其他设置" key="theme">
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="主题设置" name="theme" initialValue={theme}>
|
||||
@@ -111,8 +321,13 @@ const Password = ({ headerStyle, isPhone }: any) => {
|
||||
</Form>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<AppModal
|
||||
visible={isModalVisible}
|
||||
handleCancel={handleCancel}
|
||||
app={editedApp}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Password;
|
||||
export default Setting;
|
||||
|
||||
Reference in New Issue
Block a user