Skip to content

Commit

Permalink
feat: 流水线功能,可在特定事件发生后执行自定义的任意js代码
Browse files Browse the repository at this point in the history
  • Loading branch information
Mereithhh committed Mar 17, 2023
1 parent 5ebf893 commit 63263fa
Show file tree
Hide file tree
Showing 26 changed files with 1,103 additions and 16 deletions.
1 change: 1 addition & 0 deletions packages/admin/config/routes.js
Expand Up @@ -50,6 +50,7 @@ export default [
routes: [
{ name: '数据管理', path: '/site/data', component: './DataManage' },
{ name: '评论管理', path: '/site/comment', component: './CommentManage' },
{ name: '流水线', path: '/site/pipeline', component: './Pipeline'},
{ name: '系统设置', path: '/site/setting', component: './SystemConfig' },
{ name: '自定义页面', path: '/site/customPage', component: './CustomPage' },
{ name: '日志管理', path: '/site/log', component: './LogManage' },
Expand Down
85 changes: 79 additions & 6 deletions packages/admin/src/pages/Code/index.tsx
Expand Up @@ -6,12 +6,17 @@ import {
getCustomPageFolderTreeByPath,
updateCustomPage,
updateCustomPageFileInFolder,
getPipelineById,
updatePipelineById,
getPipelineConfig
} from '@/services/van-blog/api';
import { DownOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Dropdown, Menu, message, Modal, Space, Spin, Tag, Tree } from 'antd';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { history } from 'umi';
import PipelineModal from '../Pipeline/components/PipelineModal';
import RunCodeModal from '../Pipeline/components/RunCodeModal';
import './index.less';
const { DirectoryTree } = Tree;

Expand All @@ -20,6 +25,7 @@ export default function () {
const [currObj, setCurrObj] = useState<any>({});
const [node, setNode] = useState();
const [selectedKeys, setSelectedKeys] = useState([]);
const [pipelineConfig, setPipelineConfig] = useState<any[]>([]);
const [pathPrefix, setPathPrefix] = useState('');
const [treeData, setTreeData] = useState([{ title: 'door', key: '123' }]);
const [uploadLoading, setUploadLoading] = useState(false);
Expand All @@ -29,13 +35,23 @@ export default function () {
const [editorHeight, setEditorHeight] = useState('calc(100vh - 82px)');
const type = history.location.query?.type;
const path = history.location.query?.path;
const id = history.location.query?.id;
const isFolder = type == 'folder';
const typeMap = {
file: '单文件页面',
folder: '多文件页面',
pipeline: '流水线',
};

useEffect(() => {
getPipelineConfig().then(({ data }) => {
setPipelineConfig(data);
});
}, [])
const language = useMemo(() => {
if (type == 'pipeline') {
return 'javascript';
}
if (!node) {
return 'html';
}
Expand Down Expand Up @@ -100,6 +116,30 @@ export default function () {
setEditorHeight(`calc(100vh - ${HeaderHeight + 12}px)`);
};

const onKeyDown = (ev) => {
let save = false;
if (ev.metaKey == true && ev.key.toLocaleLowerCase() == 's') {
save = true;
}
if (ev.ctrlKey == true && ev.key.toLocaleLowerCase() == 's') {
save = true;
}
if (save) {
event?.preventDefault();
ev?.preventDefault();
handleSave();
}
return false;
};

useEffect(() => {
window.addEventListener('keydown', onKeyDown);
return () => {
window.removeEventListener('keydown', onKeyDown);
};
}, [currObj, value, type]);


useEffect(() => {
setTimeout(() => {
updateEditorSize();
Expand All @@ -114,7 +154,7 @@ export default function () {
setEditorLoading(false);
};
const fetchData = useCallback(async () => {
if (!path) {
if (!path && !id) {
message.error('无有效信息,无法获取数据!');
return;
} else {
Expand All @@ -124,7 +164,20 @@ export default function () {
const { data } = await getCustomPageFolderTreeByPath(path);
if (data) setTreeData(data);
setTreeLoading(false);
} else {
} else if (type == "pipeline") {
if (!id) {
message.error('无有效信息,无法获取数据!');
return;
}
setEditorLoading(true);
const { data } = await getPipelineById(id);
if (data) {
setCurrObj(data);
setValue(data?.script || '');
}
setEditorLoading(false);
}
else {
setEditorLoading(true);
const { data } = await getCustomPageByPath(path);
if (data) {
Expand All @@ -147,7 +200,14 @@ export default function () {
await updateCustomPage({ ...currObj, html: value });
setEditorLoading(false);
message.success('当前编辑器内文件保存成功!');
} else {
} else if (type == "pipeline") {
setEditorLoading(true);
await updatePipelineById(currObj.id, { script: value });
setEditorLoading(false);
message.success('当前编辑器内脚本保存成功!');
}

else {
setEditorLoading(true);
await updateCustomPageFileInFolder(path, node?.key, value);
setEditorLoading(false);
Expand All @@ -164,6 +224,16 @@ export default function () {
label: '保存',
onClick: handleSave,
},
...(type == "pipeline" ? [
{
key: "runPipeline",
label: <RunCodeModal pipeline={currObj} trigger={<a>调试脚本</a>} />
},
{
key: 'editPipelineInfo',
label: <PipelineModal mode="edit" trigger={<a>编辑信息</a>} onFinish={(vals) => { console.log(vals) }} initialValues={currObj} />
}
] : []),
...(isFolder
? [
{
Expand Down Expand Up @@ -207,14 +277,13 @@ export default function () {
},
]
: []),

{
...(type == 'file' ? [{
key: 'view',
label: '查看',
onClick: () => {
window.open(`/c${path}`);
},
},
}] : [])
]}
></Menu>
);
Expand All @@ -230,6 +299,10 @@ export default function () {
<span title={currObj?.name}>{currObj?.name}</span>
<>
<Tag color="green">{typeMap[type] || '未知类型'}</Tag>
{type == "pipeline" && <>
<Tag color="blue">{pipelineConfig?.find(p => p.eventName == currObj.eventName)?.eventNameChinese}</Tag>
{pipelineConfig?.find(p => p.eventName == currObj.eventName)?.passive ? <Tag color="yellow">非阻塞</Tag> : <Tag color="red">阻塞</Tag>}
</>}
</>
</Space>
),
Expand Down
9 changes: 8 additions & 1 deletion packages/admin/src/pages/LogManage/index.jsx
Expand Up @@ -2,11 +2,13 @@ import { useTab } from '@/services/van-blog/useTab';
import { PageContainer } from '@ant-design/pro-layout';
import thinstyle from '../Welcome/index.less';
import Login from './tabs/Login';
import Pipeline from "./tabs/Pipeline";
export default function () {
const tabMap = {
login: <Login />,
pipeline: <Pipeline />
};
const [tab, setTab] = useTab('login', 'tab');
const [tab, setTab] = useTab('pipeline', 'tab');

return (
<PageContainer
Expand All @@ -16,10 +18,15 @@ export default function () {
className={thinstyle.thinheader}
tabActiveKey={tab}
tabList={[
{
tab: "流水线日志",
key: "pipeline"
},
{
tab: '登录日志',
key: 'login',
},

]}
onTabChange={setTab}
>
Expand Down
113 changes: 113 additions & 0 deletions packages/admin/src/pages/LogManage/tabs/Pipeline.tsx
@@ -0,0 +1,113 @@
import { getLog, getPipelineConfig } from '@/services/van-blog/api';
import { ProTable } from '@ant-design/pro-components';
import { Modal, Tag } from 'antd';
import { useEffect, useRef, useState } from 'react';
import { history } from "umi"

export default function () {
const actionRef = useRef();
const [pipelineConfig, setPipelineConfig] = useState<any[]>([]);
useEffect(() => {
getPipelineConfig().then(({ data }) => {
setPipelineConfig(data);
})
}, []);
const columns = [
{
title: '序号',
align: 'center',
width: 50,
render: (text, record, index) => {
return index + 1;
},
},
{
title: '流水线 id',
dataIndex: 'pipelineId',
key: 'pipelineId',
align: 'center',
},
{
title: '名称',
dataIndex: 'pipelineName',
key: 'pipelineName',
align: 'center',
render: (name,record) => <a onClick={() => { history.push('/code?type=pipeline&id=' +record.pipelineId) }}>{name}</a>
},
{
title: '触发事件',
dataIndex: 'eventName',
key: 'eventName',
align: 'center',
render: (eventName) => {
return <Tag color="blue">{pipelineConfig?.find((item) => item.eventName == eventName)?.eventNameChinese}</Tag>
}
},
{
title: '结果',
dataIndex: 'success',
key: 'success',
align: 'center',
render: (success) => {
return success ? <Tag color="green">成功</Tag> : <Tag color="red">失败</Tag>
}
},
{
title: "详情",
dataIndex: "detail",
key: "detail",
render: (_, record) => {
return <a onClick={() => {
Modal.info({
title: '详情',
width: 800,
content: <div style={{
maxHeight: "60vh",
overflow: "auto",
}}>
<p>脚本日志:</p>
<pre >{record.logs.map(l => <p>{l}</p>)}</pre>
<p>输入:</p>
<pre >{JSON.stringify(record.input, null, 2)}</pre>
<p>输出:</p>
<pre >{JSON.stringify(record.output, null, 2)}</pre>
</div>
})
}}>详情</a>
}
}
];
return (
<>
<ProTable
// ghost
cardBordered
rowKey="time"
columns={columns}
search={false}
dateFormatter="string"
actionRef={actionRef}
options={true}
headerTitle="流水线日志"
pagination={{
pageSize: 10,
simple: true,
hideOnSinglePage: true,
}}
request={async (params) => {
// console.log(params);
// const data = await fetchData();
const { data } = await getLog('runPipeline', params.current, params.pageSize);
return {
data: data.data,
// success 请返回 true,
// 不然 table 会停止解析数据,即使有数据
success: true,
// 不传会使用 data 的长度,如果是分页一定要传
total: data.total,
};
}}
/>
</>
);
}

0 comments on commit 63263fa

Please sign in to comment.