mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-22 22:36:06 +08:00
支持批量创建同名称环境变量,日志和脚本管理PC页支持宽度拖拽
This commit is contained in:
parent
594e871744
commit
2d6da9ebfe
|
@ -25,11 +25,13 @@ export default (app: Router) => {
|
|||
route.post(
|
||||
'/envs',
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
value: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
remarks: Joi.string().optional(),
|
||||
}),
|
||||
body: Joi.array().items(
|
||||
Joi.object({
|
||||
value: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
remarks: Joi.string().optional().allow(''),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
|
|
|
@ -16,25 +16,29 @@ export default class EnvService {
|
|||
});
|
||||
}
|
||||
|
||||
public async create(payload: Env): Promise<Env> {
|
||||
public async create(payloads: Env[]): Promise<Env[]> {
|
||||
const envs = await this.envs();
|
||||
let position = initEnvPosition;
|
||||
if (envs && envs.length > 0 && envs[envs.length - 1].position) {
|
||||
position = envs[envs.length - 1].position;
|
||||
}
|
||||
const tab = new Env({ ...payload, position: position / 2 });
|
||||
const doc = await this.insert(tab);
|
||||
const tabs = payloads.map((x) => {
|
||||
position = position / 2;
|
||||
const tab = new Env({ ...x, position });
|
||||
return tab;
|
||||
});
|
||||
const docs = await this.insert(tabs);
|
||||
await this.set_envs();
|
||||
return doc;
|
||||
return docs;
|
||||
}
|
||||
|
||||
public async insert(payload: Env): Promise<Env> {
|
||||
public async insert(payloads: Env[]): Promise<Env[]> {
|
||||
return new Promise((resolve) => {
|
||||
this.cronDb.insert(payload, (err, doc) => {
|
||||
this.cronDb.insert(payloads, (err, docs) => {
|
||||
if (err) {
|
||||
this.logger.error(err);
|
||||
} else {
|
||||
resolve(doc);
|
||||
resolve(docs);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -191,7 +191,7 @@ input:-webkit-autofill:active {
|
|||
}
|
||||
|
||||
.Resizer {
|
||||
background: #000;
|
||||
background: #fff;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
-moz-box-sizing: border-box;
|
||||
|
@ -200,6 +200,8 @@ input:-webkit-autofill:active {
|
|||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
border-left: 5px solid rgba(42, 161, 255, 0);
|
||||
border-right: 5px solid rgba(42, 161, 255, 0);
|
||||
}
|
||||
|
||||
.Resizer:hover {
|
||||
|
@ -210,29 +212,22 @@ input:-webkit-autofill:active {
|
|||
.Resizer.horizontal {
|
||||
height: 11px;
|
||||
margin: -5px 0;
|
||||
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: row-resize;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Resizer.horizontal:hover {
|
||||
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.Resizer.vertical {
|
||||
width: 11px;
|
||||
margin: 0 -5px;
|
||||
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.Resizer.horizontal:hover,
|
||||
.Resizer.vertical:hover {
|
||||
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-left-color: rgba(42, 161, 255, 0.5);
|
||||
border-right-color: rgba(42, 161, 255, 0.5);
|
||||
}
|
||||
|
||||
.Resizer.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
|
3
src/pages/env/index.tsx
vendored
3
src/pages/env/index.tsx
vendored
|
@ -317,7 +317,8 @@ const Env = () => {
|
|||
const result = [...value];
|
||||
const index = value.findIndex((x) => x._id === env._id);
|
||||
if (index === -1) {
|
||||
result.push(env);
|
||||
env = Array.isArray(env) ? env : [env];
|
||||
result.push(...env);
|
||||
} else {
|
||||
result.splice(index, 1, {
|
||||
...env,
|
||||
|
|
23
src/pages/env/modal.tsx
vendored
23
src/pages/env/modal.tsx
vendored
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, message, Input, Form } from 'antd';
|
||||
import { Modal, message, Input, Form, Radio } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
|
@ -17,8 +17,19 @@ const EnvModal = ({
|
|||
|
||||
const handleOk = async (values: any) => {
|
||||
setLoading(true);
|
||||
const { value, split, name, remarks } = values;
|
||||
const method = env ? 'put' : 'post';
|
||||
const payload = env ? { ...values, _id: env._id } : values;
|
||||
let payload = env ? { ...values, _id: env._id } : values;
|
||||
if (!env && split === '1') {
|
||||
const symbol = value.includes('&') ? '&' : '\n';
|
||||
payload = value.split(symbol).map((x: any) => {
|
||||
return {
|
||||
name: name,
|
||||
value: x,
|
||||
remarks: remarks,
|
||||
};
|
||||
});
|
||||
}
|
||||
const { code, data } = await request[method](`${config.apiPrefix}envs`, {
|
||||
data: payload,
|
||||
});
|
||||
|
@ -61,6 +72,14 @@ const EnvModal = ({
|
|||
>
|
||||
<Input placeholder="请输入环境变量名称" />
|
||||
</Form.Item>
|
||||
{!env && (
|
||||
<Form.Item name="split" label="自动拆分" initialValue="0">
|
||||
<Radio.Group>
|
||||
<Radio value="1">是</Radio>
|
||||
<Radio value="0">否</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
name="value"
|
||||
label="值"
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
background-color: #fff;
|
||||
height: calc(100vh - 128px);
|
||||
height: calc(100vh - var(--vh-offset, 0px) - 128px);
|
||||
width: @tree-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -20,4 +19,5 @@
|
|||
|
||||
.log-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { request } from '@/utils/http';
|
|||
import styles from './index.module.less';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { useCtx, useTheme } from '@/utils/hooks';
|
||||
import SplitPane from 'react-split-pane';
|
||||
|
||||
function getFilterData(keyword: string, data: any) {
|
||||
const expandedKeys: string[] = [];
|
||||
|
@ -133,24 +134,40 @@ const Log = () => {
|
|||
>
|
||||
<div className={`${styles['log-container']} 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']} ref={treeDom}>
|
||||
<Tree
|
||||
className={styles['left-tree']}
|
||||
treeData={filterData}
|
||||
showIcon={true}
|
||||
height={height}
|
||||
showLine={{ showLeafIcon: true }}
|
||||
onSelect={onTreeSelect}
|
||||
></Tree>
|
||||
<SplitPane split="vertical" size={200} maxSize={-100}>
|
||||
<div className={styles['left-tree-container']}>
|
||||
<Input.Search
|
||||
className={styles['left-tree-search']}
|
||||
onChange={onSearch}
|
||||
></Input.Search>
|
||||
<div className={styles['left-tree-scroller']} ref={treeDom}>
|
||||
<Tree
|
||||
className={styles['left-tree']}
|
||||
treeData={filterData}
|
||||
showIcon={true}
|
||||
height={height}
|
||||
showLine={{ showLeafIcon: true }}
|
||||
onSelect={onTreeSelect}
|
||||
></Tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
language="shell"
|
||||
theme={theme}
|
||||
value={value}
|
||||
options={{
|
||||
readOnly: true,
|
||||
fontSize: 12,
|
||||
lineNumbersMinChars: 3,
|
||||
fontFamily: 'Source Code Pro',
|
||||
folding: false,
|
||||
glyphMargin: false,
|
||||
wordWrap: 'on',
|
||||
}}
|
||||
/>
|
||||
</SplitPane>
|
||||
)}
|
||||
{isPhone ? (
|
||||
{isPhone && (
|
||||
<CodeMirror
|
||||
value={value}
|
||||
options={{
|
||||
|
@ -165,21 +182,6 @@ const Log = () => {
|
|||
}}
|
||||
onChange={(editor, data, value) => {}}
|
||||
/>
|
||||
) : (
|
||||
<Editor
|
||||
language="shell"
|
||||
theme={theme}
|
||||
value={value}
|
||||
options={{
|
||||
readOnly: true,
|
||||
fontSize: 12,
|
||||
lineNumbersMinChars: 3,
|
||||
fontFamily: 'Source Code Pro',
|
||||
folding: false,
|
||||
glyphMargin: false,
|
||||
wordWrap: 'on',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PageContainer>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
background-color: #fff;
|
||||
height: calc(100vh - 128px);
|
||||
height: calc(100vh - var(--vh-offset, 0px) - 128px);
|
||||
width: @tree-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -20,4 +19,5 @@
|
|||
|
||||
.log-container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import styles from './index.module.less';
|
|||
import EditModal from './editModal';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { useCtx, useTheme } from '@/utils/hooks';
|
||||
import SplitPane from 'react-split-pane';
|
||||
|
||||
function getFilterData(keyword: string, data: any) {
|
||||
if (keyword) {
|
||||
|
@ -122,24 +123,38 @@ const Script = () => {
|
|||
>
|
||||
<div className={`${styles['log-container']} 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']} ref={treeDom}>
|
||||
<Tree
|
||||
className={styles['left-tree']}
|
||||
treeData={filterData}
|
||||
showIcon={true}
|
||||
height={height}
|
||||
showLine={{ showLeafIcon: true }}
|
||||
onSelect={onTreeSelect}
|
||||
></Tree>
|
||||
<SplitPane split="vertical" size={200} maxSize={-100}>
|
||||
<div className={styles['left-tree-container']}>
|
||||
<Input.Search
|
||||
className={styles['left-tree-search']}
|
||||
onChange={onSearch}
|
||||
></Input.Search>
|
||||
<div className={styles['left-tree-scroller']} ref={treeDom}>
|
||||
<Tree
|
||||
className={styles['left-tree']}
|
||||
treeData={filterData}
|
||||
showIcon={true}
|
||||
height={height}
|
||||
showLine={{ showLeafIcon: true }}
|
||||
onSelect={onTreeSelect}
|
||||
></Tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
language={mode}
|
||||
value={value}
|
||||
theme={theme}
|
||||
options={{
|
||||
readOnly: true,
|
||||
fontSize: 12,
|
||||
lineNumbersMinChars: 3,
|
||||
folding: false,
|
||||
glyphMargin: false,
|
||||
}}
|
||||
/>
|
||||
</SplitPane>
|
||||
)}
|
||||
{isPhone ? (
|
||||
{isPhone && (
|
||||
<CodeMirror
|
||||
value={value}
|
||||
options={{
|
||||
|
@ -155,19 +170,6 @@ const Script = () => {
|
|||
}}
|
||||
onChange={(editor, data, value) => {}}
|
||||
/>
|
||||
) : (
|
||||
<Editor
|
||||
language={mode}
|
||||
value={value}
|
||||
theme={theme}
|
||||
options={{
|
||||
readOnly: true,
|
||||
fontSize: 12,
|
||||
lineNumbersMinChars: 3,
|
||||
folding: false,
|
||||
glyphMargin: false,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<EditModal
|
||||
visible={isLogModalVisible}
|
||||
|
|
Loading…
Reference in New Issue
Block a user