mirror of
https://github.com/whyour/qinglong.git
synced 2026-07-17 01:14:50 +08:00
重构环境变量管理,添加脚本查看
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { PureComponent, Fragment, useState, useEffect } from 'react';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import { Button, message, Modal, TreeSelect } from 'antd';
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
@@ -11,27 +11,42 @@ const Config = () => {
|
||||
const [marginTop, setMarginTop] = useState(-72);
|
||||
const [value, setValue] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [title, setTitle] = useState('config.sh');
|
||||
const [select, setSelect] = useState('config.sh');
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
|
||||
const getConfig = () => {
|
||||
const getConfig = (name: string) => {
|
||||
request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => {
|
||||
setValue(data.data);
|
||||
});
|
||||
};
|
||||
|
||||
const getFiles = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}config/config`)
|
||||
.get(`${config.apiPrefix}configs/files`)
|
||||
.then((data: any) => {
|
||||
setValue(data.data);
|
||||
setData(data.data);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const updateConfig = () => {
|
||||
request
|
||||
.post(`${config.apiPrefix}save`, {
|
||||
data: { content: value, name: 'config.sh' },
|
||||
.post(`${config.apiPrefix}configs/save`, {
|
||||
data: { content: value, name: select },
|
||||
})
|
||||
.then((data: any) => {
|
||||
message.success(data.msg);
|
||||
});
|
||||
};
|
||||
|
||||
const onSelect = (value: any, node: any) => {
|
||||
setSelect(value);
|
||||
setTitle(node.value);
|
||||
getConfig(node.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (document.body.clientWidth < 768) {
|
||||
setWidth('auto');
|
||||
@@ -42,14 +57,24 @@ const Config = () => {
|
||||
setMarginLeft(0);
|
||||
setMarginTop(-72);
|
||||
}
|
||||
getConfig();
|
||||
getFiles();
|
||||
getConfig('config.sh');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
className="ql-container-wrapper"
|
||||
title="config.sh"
|
||||
className="ql-container-wrapper config-wrapper"
|
||||
title={title}
|
||||
extra={[
|
||||
<TreeSelect
|
||||
className="config-select"
|
||||
value={select}
|
||||
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
||||
treeData={data}
|
||||
key="value"
|
||||
defaultValue="config.sh"
|
||||
onSelect={onSelect}
|
||||
/>,
|
||||
<Button key="1" type="primary" onClick={updateConfig}>
|
||||
保存
|
||||
</Button>,
|
||||
|
||||
@@ -15,7 +15,7 @@ const Crontab = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const getConfig = () => {
|
||||
request.get(`${config.apiPrefix}config/config`).then((data) => {
|
||||
request.get(`${config.apiPrefix}configs/config.sh`).then((data) => {
|
||||
setValue(data.data);
|
||||
});
|
||||
};
|
||||
@@ -23,7 +23,7 @@ const Crontab = () => {
|
||||
const getSample = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}config/sample`)
|
||||
.get(`${config.apiPrefix}config/config.sample.sh`)
|
||||
.then((data) => {
|
||||
setSample(data.data);
|
||||
})
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import React, { PureComponent, Fragment, useState, useEffect } from 'react';
|
||||
import { Button, message, Modal } from 'antd';
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { request } from '@/utils/http';
|
||||
|
||||
const Crontab = () => {
|
||||
const [width, setWidth] = useState('100%');
|
||||
const [marginLeft, setMarginLeft] = useState(0);
|
||||
const [marginTop, setMarginTop] = useState(-72);
|
||||
const [value, setValue] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const getConfig = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}config/extra`)
|
||||
.then((data) => {
|
||||
setValue(data.data);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const updateConfig = () => {
|
||||
request
|
||||
.post(`${config.apiPrefix}save`, {
|
||||
data: { content: value, name: 'extra.sh' },
|
||||
})
|
||||
.then((data) => {
|
||||
message.success(data.msg);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (document.body.clientWidth < 768) {
|
||||
setWidth('auto');
|
||||
setMarginLeft(0);
|
||||
setMarginTop(0);
|
||||
} else {
|
||||
setWidth('100%');
|
||||
setMarginLeft(0);
|
||||
setMarginTop(-72);
|
||||
}
|
||||
getConfig();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
className="ql-container-wrapper"
|
||||
title="extra.sh"
|
||||
extra={[
|
||||
<Button key="1" type="primary" onClick={updateConfig}>
|
||||
保存
|
||||
</Button>,
|
||||
]}
|
||||
header={{
|
||||
style: {
|
||||
padding: '4px 16px 4px 15px',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 20,
|
||||
marginTop,
|
||||
width,
|
||||
marginLeft,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CodeMirror
|
||||
value={value}
|
||||
options={{
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
mode: 'shell',
|
||||
}}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setValue(value);
|
||||
}}
|
||||
onChange={(editor, data, value) => {}}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Crontab;
|
||||
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, message, Input, Form } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
const EditNameModal = ({
|
||||
ids,
|
||||
handleCancel,
|
||||
visible,
|
||||
}: {
|
||||
ids?: string[];
|
||||
visible: boolean;
|
||||
handleCancel: () => void;
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleOk = async (values: any) => {
|
||||
console.log(values);
|
||||
console.log(ids);
|
||||
setLoading(true);
|
||||
const { code, data } = await request.put(`${config.apiPrefix}envs/name`, {
|
||||
data: {
|
||||
ids,
|
||||
name: values.name,
|
||||
},
|
||||
});
|
||||
if (code === 200) {
|
||||
message.success('更新环境变量名称成功');
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
setLoading(false);
|
||||
handleCancel();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
}, [ids]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="修改环境变量名称"
|
||||
visible={visible}
|
||||
forceRender
|
||||
onOk={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
handleOk(values);
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log('Validate Failed:', info);
|
||||
});
|
||||
}}
|
||||
onCancel={() => handleCancel()}
|
||||
confirmLoading={loading}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
name="edit_name_modal"
|
||||
preserve={false}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入新的环境变量名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入新的环境变量名称" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditNameModal;
|
||||
+85
-97
@@ -19,8 +19,8 @@ import {
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { request } from '@/utils/http';
|
||||
import QRCode from 'qrcode.react';
|
||||
import CookieModal from './modal';
|
||||
import EnvModal from './modal';
|
||||
import EditNameModal from './editNameModal';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import './index.less';
|
||||
@@ -28,17 +28,12 @@ import './index.less';
|
||||
const { Text } = Typography;
|
||||
|
||||
enum Status {
|
||||
'未获取',
|
||||
'正常',
|
||||
'已启用',
|
||||
'已禁用',
|
||||
'已失效',
|
||||
'状态异常',
|
||||
}
|
||||
|
||||
enum StatusColor {
|
||||
'default',
|
||||
'success',
|
||||
'warning',
|
||||
'error',
|
||||
}
|
||||
|
||||
@@ -106,7 +101,7 @@ const DragableBodyRow = ({
|
||||
);
|
||||
};
|
||||
|
||||
const Config = () => {
|
||||
const Env = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
@@ -116,25 +111,17 @@ const Config = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickname',
|
||||
key: 'nickname',
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'center' as const,
|
||||
width: '15%',
|
||||
render: (text: string, record: any, index: number) => {
|
||||
const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/);
|
||||
const val = (match && match[1]) || '未匹配用户名';
|
||||
return (
|
||||
<span style={{ cursor: 'text' }}>{record.nickname || val} </span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '值',
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
align: 'center' as const,
|
||||
width: '50%',
|
||||
width: '45%',
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<span
|
||||
@@ -143,6 +130,7 @@ const Config = () => {
|
||||
display: 'inline-block',
|
||||
wordBreak: 'break-all',
|
||||
cursor: 'text',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
@@ -150,28 +138,24 @@ const Config = () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remarks',
|
||||
key: 'remarks',
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
dataIndex: 'status',
|
||||
align: 'center' as const,
|
||||
width: '15%',
|
||||
width: 60,
|
||||
render: (text: string, record: any, index: number) => {
|
||||
return (
|
||||
<Space size="middle" style={{ cursor: 'text' }}>
|
||||
<Tag
|
||||
color={StatusColor[record.status] || StatusColor[3]}
|
||||
style={{ marginRight: 0 }}
|
||||
>
|
||||
<Tag color={StatusColor[record.status]} style={{ marginRight: 0 }}>
|
||||
{Status[record.status]}
|
||||
</Tag>
|
||||
{record.status !== Status.已禁用 && (
|
||||
<Tooltip title="刷新">
|
||||
<a onClick={() => refreshStatus(record, index)}>
|
||||
<SyncOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
@@ -183,12 +167,12 @@ const Config = () => {
|
||||
render: (text: string, record: any, index: number) => (
|
||||
<Space size="middle">
|
||||
<Tooltip title="编辑">
|
||||
<a onClick={() => editCookie(record, index)}>
|
||||
<a onClick={() => editEnv(record, index)}>
|
||||
<EditOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title={record.status === Status.已禁用 ? '启用' : '禁用'}>
|
||||
<a onClick={() => enabledOrDisabledCookie(record, index)}>
|
||||
<a onClick={() => enabledOrDisabledEnv(record, index)}>
|
||||
{record.status === Status.已禁用 ? (
|
||||
<CheckCircleOutlined />
|
||||
) : (
|
||||
@@ -197,7 +181,7 @@ const Config = () => {
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title="删除">
|
||||
<a onClick={() => deleteCookie(record, index)}>
|
||||
<a onClick={() => deleteEnv(record, index)}>
|
||||
<DeleteOutlined />
|
||||
</a>
|
||||
</Tooltip>
|
||||
@@ -211,39 +195,27 @@ const Config = () => {
|
||||
const [value, setValue] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [editedCookie, setEditedCookie] = useState();
|
||||
const [isEditNameModalVisible, setIsEditNameModalVisible] = useState(false);
|
||||
const [editedEnv, setEditedEnv] = useState();
|
||||
const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]);
|
||||
|
||||
const getCookies = () => {
|
||||
const getEnvs = () => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}cookies`)
|
||||
.get(`${config.apiPrefix}envs`)
|
||||
.then((data: any) => {
|
||||
setValue(data.data);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const refreshStatus = (record: any, index: number) => {
|
||||
request
|
||||
.get(`${config.apiPrefix}cookies/${record._id}/refresh`)
|
||||
.then(async (data: any) => {
|
||||
if (data.data && data.data.value) {
|
||||
(value as any).splice(index, 1, data.data);
|
||||
setValue([...(value as any)] as any);
|
||||
} else {
|
||||
message.error('更新状态失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const enabledOrDisabledCookie = (record: any, index: number) => {
|
||||
const enabledOrDisabledEnv = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: `确认${record.status === Status.已禁用 ? '启用' : '禁用'}`,
|
||||
content: (
|
||||
<>
|
||||
确认{record.status === Status.已禁用 ? '启用' : '禁用'}
|
||||
Cookie{' '}
|
||||
Env{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.value}
|
||||
</Text>{' '}
|
||||
@@ -253,7 +225,7 @@ const Config = () => {
|
||||
onOk() {
|
||||
request
|
||||
.put(
|
||||
`${config.apiPrefix}cookies/${
|
||||
`${config.apiPrefix}envs/${
|
||||
record.status === Status.已禁用 ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
@@ -284,22 +256,22 @@ const Config = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const addCookie = () => {
|
||||
setEditedCookie(null as any);
|
||||
const addEnv = () => {
|
||||
setEditedEnv(null as any);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const editCookie = (record: any, index: number) => {
|
||||
setEditedCookie(record);
|
||||
const editEnv = (record: any, index: number) => {
|
||||
setEditedEnv(record);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const deleteCookie = (record: any, index: number) => {
|
||||
const deleteEnv = (record: any, index: number) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: (
|
||||
<>
|
||||
确认删除Cookie{' '}
|
||||
确认删除Env{' '}
|
||||
<Text style={{ wordBreak: 'break-all' }} type="warning">
|
||||
{record.value}
|
||||
</Text>{' '}
|
||||
@@ -308,7 +280,7 @@ const Config = () => {
|
||||
),
|
||||
onOk() {
|
||||
request
|
||||
.delete(`${config.apiPrefix}cookies`, { data: [record._id] })
|
||||
.delete(`${config.apiPrefix}envs`, { data: [record._id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
message.success('删除成功');
|
||||
@@ -326,25 +298,25 @@ const Config = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = (cookies?: any[]) => {
|
||||
const handleCancel = (env?: any[]) => {
|
||||
setIsModalVisible(false);
|
||||
if (cookies && cookies.length > 0) {
|
||||
handleCookies(cookies);
|
||||
}
|
||||
handleEnv(env);
|
||||
};
|
||||
|
||||
const handleCookies = (cookies: any[]) => {
|
||||
const handleEditNameCancel = (env?: any[]) => {
|
||||
setIsEditNameModalVisible(false);
|
||||
getEnvs();
|
||||
};
|
||||
|
||||
const handleEnv = (env: any) => {
|
||||
const result = [...value];
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i];
|
||||
const index = value.findIndex((x) => x._id === cookie._id);
|
||||
if (index === -1) {
|
||||
result.push(cookie);
|
||||
} else {
|
||||
result.splice(index, 1, {
|
||||
...cookie,
|
||||
});
|
||||
}
|
||||
const index = value.findIndex((x) => x._id === env._id);
|
||||
if (index === -1) {
|
||||
result.push(env);
|
||||
} else {
|
||||
result.splice(index, 1, {
|
||||
...env,
|
||||
});
|
||||
}
|
||||
setValue(result);
|
||||
};
|
||||
@@ -366,7 +338,7 @@ const Config = () => {
|
||||
newData.splice(hoverIndex, 0, dragRow);
|
||||
setValue([...newData]);
|
||||
request
|
||||
.put(`${config.apiPrefix}cookies/${dragRow._id}/move`, {
|
||||
.put(`${config.apiPrefix}envs/${dragRow._id}/move`, {
|
||||
data: { fromIndex: dragIndex, toIndex: hoverIndex },
|
||||
})
|
||||
.then((data: any) => {
|
||||
@@ -387,18 +359,18 @@ const Config = () => {
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
const delCookies = () => {
|
||||
const delEnvs = () => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: <>确认删除选中的Cookie吗</>,
|
||||
content: <>确认删除选中的Env吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.delete(`${config.apiPrefix}cookies`, { data: selectedRowIds })
|
||||
.delete(`${config.apiPrefix}envs`, { data: selectedRowIds })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
message.success('批量删除成功');
|
||||
setSelectedRowIds([]);
|
||||
getCookies();
|
||||
getEnvs();
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
@@ -410,18 +382,18 @@ const Config = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const operateCookies = (operationStatus: number) => {
|
||||
const operateEnvs = (operationStatus: number) => {
|
||||
Modal.confirm({
|
||||
title: `确认${OperationName[operationStatus]}`,
|
||||
content: <>确认{OperationName[operationStatus]}选中的Cookie吗</>,
|
||||
content: <>确认{OperationName[operationStatus]}选中的Env吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}cookies/${OperationPath[operationStatus]}`, {
|
||||
.put(`${config.apiPrefix}envs/${OperationPath[operationStatus]}`, {
|
||||
data: selectedRowIds,
|
||||
})
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
getCookies();
|
||||
getEnvs();
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
@@ -433,6 +405,10 @@ const Config = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const modifyName = () => {
|
||||
setIsEditNameModalVisible(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (document.body.clientWidth < 768) {
|
||||
setWidth('auto');
|
||||
@@ -443,16 +419,16 @@ const Config = () => {
|
||||
setMarginLeft(0);
|
||||
setMarginTop(-72);
|
||||
}
|
||||
getCookies();
|
||||
getEnvs();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
className="session-wrapper"
|
||||
title="Session管理"
|
||||
className="env-wrapper"
|
||||
title="环境变量"
|
||||
extra={[
|
||||
<Button key="2" type="primary" onClick={() => addCookie()}>
|
||||
添加Cookie
|
||||
<Button key="2" type="primary" onClick={() => addEnv()}>
|
||||
添加Env
|
||||
</Button>,
|
||||
]}
|
||||
header={{
|
||||
@@ -473,20 +449,27 @@ const Config = () => {
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ marginBottom: 5 }}
|
||||
onClick={delCookies}
|
||||
onClick={modifyName}
|
||||
>
|
||||
批量修改变量名称
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ marginBottom: 5, marginLeft: 8 }}
|
||||
onClick={delEnvs}
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => operateCookies(0)}
|
||||
onClick={() => operateEnvs(0)}
|
||||
style={{ marginLeft: 8, marginBottom: 5 }}
|
||||
>
|
||||
批量启用
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => operateCookies(1)}
|
||||
onClick={() => operateEnvs(1)}
|
||||
style={{ marginLeft: 8, marginRight: 8 }}
|
||||
>
|
||||
批量禁用
|
||||
@@ -516,13 +499,18 @@ const Config = () => {
|
||||
}}
|
||||
/>
|
||||
</DndProvider>
|
||||
<CookieModal
|
||||
<EnvModal
|
||||
visible={isModalVisible}
|
||||
handleCancel={handleCancel}
|
||||
cookie={editedCookie}
|
||||
env={editedEnv}
|
||||
/>
|
||||
<EditNameModal
|
||||
visible={isEditNameModalVisible}
|
||||
handleCancel={handleEditNameCancel}
|
||||
ids={selectedRowIds}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Config;
|
||||
export default Env;
|
||||
+26
-35
@@ -3,12 +3,12 @@ import { Modal, message, Input, Form } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
const CookieModal = ({
|
||||
cookie,
|
||||
const EnvModal = ({
|
||||
env,
|
||||
handleCancel,
|
||||
visible,
|
||||
}: {
|
||||
cookie?: any;
|
||||
env?: any;
|
||||
visible: boolean;
|
||||
handleCancel: (cks?: any[]) => void;
|
||||
}) => {
|
||||
@@ -16,42 +16,28 @@ const CookieModal = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleOk = async (values: any) => {
|
||||
const cookies = values.value
|
||||
.split('\n')
|
||||
.map((x: any) => x.trim().replace(/\s/g, ''));
|
||||
let flag = false;
|
||||
for (const coo of cookies) {
|
||||
if (!/pt_key=\S*;\s*pt_pin=\S*;\s*/.test(coo)) {
|
||||
message.error(`${coo}格式有误`);
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const method = cookie ? 'put' : 'post';
|
||||
const payload = cookie ? { value: cookies[0], _id: cookie._id } : cookies;
|
||||
const { code, data } = await request[method](`${config.apiPrefix}cookies`, {
|
||||
const method = env ? 'put' : 'post';
|
||||
const payload = env ? { ...values, _id: env._id } : values;
|
||||
const { code, data } = await request[method](`${config.apiPrefix}envs`, {
|
||||
data: payload,
|
||||
});
|
||||
if (code === 200) {
|
||||
message.success(cookie ? '更新Cookie成功' : '添加Cookie成功');
|
||||
message.success(env ? '更新Env成功' : '添加Env成功');
|
||||
} else {
|
||||
message.error(data);
|
||||
}
|
||||
setLoading(false);
|
||||
handleCancel(cookie ? [data] : data);
|
||||
handleCancel(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
}, [cookie]);
|
||||
}, [env]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={cookie ? '编辑Cookie' : '新建Cookie'}
|
||||
title={env ? '编辑Env' : '新建Env'}
|
||||
visible={visible}
|
||||
forceRender
|
||||
onOk={() => {
|
||||
@@ -71,29 +57,34 @@ const CookieModal = ({
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
name="form_in_modal"
|
||||
name="env_modal"
|
||||
preserve={false}
|
||||
initialValues={cookie}
|
||||
initialValues={env}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{ required: true, message: '请输入环境变量名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入环境变量名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="value"
|
||||
rules={[
|
||||
{ required: true, message: '请输入Cookie' },
|
||||
{
|
||||
pattern: /pt_key=\S*;\s*pt_pin=\S*;\s*/,
|
||||
message: 'Cookie格式错误,注意分号(pt_key=***;pt_pin=***;)',
|
||||
},
|
||||
]}
|
||||
label="值"
|
||||
rules={[{ required: true, message: '请输入环境变量值' }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
autoSize={true}
|
||||
placeholder="请输入cookie,可直接换行输入多个cookie"
|
||||
placeholder="请输入环境变量值"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="remarks" label="备注">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CookieModal;
|
||||
export default EnvModal;
|
||||
@@ -0,0 +1,37 @@
|
||||
@import '~@/styles/variable.less';
|
||||
|
||||
.left-tree {
|
||||
&-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 128px);
|
||||
height: calc(100vh - var(--vh-offset, 0px) - 128px);
|
||||
width: @tree-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
&-scroller {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
&-search {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.log-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:global {
|
||||
.log-wrapper {
|
||||
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
width: calc(100% - 32px - @tree-width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import { useState, useEffect, useCallback, Key } from 'react';
|
||||
import { TreeSelect, Tree, Input } from 'antd';
|
||||
import config from '@/utils/config';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { request } from '@/utils/http';
|
||||
import styles from './index.module.less';
|
||||
|
||||
function getFilterData(keyword: string, data: any) {
|
||||
const expandedKeys: string[] = [];
|
||||
if (keyword) {
|
||||
const tree: any = [];
|
||||
data.forEach((item: any) => {
|
||||
if (item.title.toLocaleLowerCase().includes(keyword)) {
|
||||
tree.push(item);
|
||||
expandedKeys.push(...item.children.map((x: any) => x.key));
|
||||
} else {
|
||||
const children: any[] = [];
|
||||
(item.children || []).forEach((subItem: any) => {
|
||||
if (subItem.title.toLocaleLowerCase().includes(keyword)) {
|
||||
children.push(subItem);
|
||||
}
|
||||
});
|
||||
if (children.length > 0) {
|
||||
tree.push({
|
||||
...item,
|
||||
children,
|
||||
});
|
||||
expandedKeys.push(...children.map((x) => x.key));
|
||||
}
|
||||
}
|
||||
});
|
||||
return { tree, expandedKeys };
|
||||
}
|
||||
return { tree: data, expandedKeys };
|
||||
}
|
||||
|
||||
const Script = () => {
|
||||
const [width, setWidth] = useState('100%');
|
||||
const [marginLeft, setMarginLeft] = useState(0);
|
||||
const [marginTop, setMarginTop] = useState(-72);
|
||||
const [title, setTitle] = useState('请选择脚本文件');
|
||||
const [value, setValue] = useState('请选择脚本文件');
|
||||
const [select, setSelect] = useState();
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [filterData, setFilterData] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isPhone, setIsPhone] = useState(false);
|
||||
|
||||
const getScripts = () => {
|
||||
request.get(`${config.apiPrefix}scripts/files`).then((data) => {
|
||||
setData(data.data);
|
||||
setFilterData(data.data);
|
||||
});
|
||||
};
|
||||
|
||||
const getDetail = (node: any) => {
|
||||
setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}scripts/${node.value}`)
|
||||
.then((data) => {
|
||||
setValue(data.data);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const onSelect = (value: any, node: any) => {
|
||||
console.log(value);
|
||||
console.log(node);
|
||||
setSelect(value);
|
||||
setTitle(node.parent || node.value);
|
||||
getDetail(node);
|
||||
};
|
||||
|
||||
const onTreeSelect = useCallback((keys: Key[], e: any) => {
|
||||
onSelect(keys[0], e.node);
|
||||
}, []);
|
||||
|
||||
const onSearch = useCallback(
|
||||
(e) => {
|
||||
const keyword = e.target.value;
|
||||
const { tree } = getFilterData(keyword.toLocaleLowerCase(), data);
|
||||
setFilterData(tree);
|
||||
},
|
||||
[data, setFilterData],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (document.body.clientWidth < 768) {
|
||||
setWidth('auto');
|
||||
setMarginLeft(0);
|
||||
setMarginTop(0);
|
||||
setIsPhone(true);
|
||||
} else {
|
||||
setWidth('100%');
|
||||
setMarginLeft(0);
|
||||
setMarginTop(-72);
|
||||
setIsPhone(false);
|
||||
}
|
||||
getScripts();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
className="ql-container-wrapper log-wrapper"
|
||||
title={title}
|
||||
extra={
|
||||
isPhone && [
|
||||
<TreeSelect
|
||||
className="log-select"
|
||||
value={select}
|
||||
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
||||
treeData={data}
|
||||
placeholder="请选择脚本文件"
|
||||
showSearch
|
||||
key="value"
|
||||
onSelect={onSelect}
|
||||
/>,
|
||||
]
|
||||
}
|
||||
header={{
|
||||
style: {
|
||||
padding: '4px 16px 4px 15px',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 20,
|
||||
marginTop,
|
||||
width,
|
||||
marginLeft,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className={`${styles['log-container']}`}>
|
||||
{!isPhone && (
|
||||
<div className={styles['left-tree-container']}>
|
||||
<Input.Search
|
||||
className={styles['left-tree-search']}
|
||||
onChange={onSearch}
|
||||
></Input.Search>
|
||||
<div className={styles['left-tree-scroller']}>
|
||||
<Tree
|
||||
className={styles['left-tree']}
|
||||
treeData={filterData}
|
||||
onSelect={onTreeSelect}
|
||||
></Tree>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<CodeMirror
|
||||
value={value}
|
||||
options={{
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
mode: 'shell',
|
||||
readOnly: true,
|
||||
}}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setValue(value);
|
||||
}}
|
||||
onChange={(editor, data, value) => {}}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Script;
|
||||
Reference in New Issue
Block a user