Browse Source

feat: add configuration button for npm registry

master
Hans Chan 4 years ago
parent
commit
9ec849b5ad
  1. 34
      packages/core/src/api/index.ts
  2. 2
      packages/core/src/components/console/index.tsx
  3. 32
      packages/core/src/mods/header/configuration/editModal.tsx
  4. 10
      packages/core/src/mods/header/configuration/index.module.less
  5. 35
      packages/core/src/mods/header/configuration/index.tsx
  6. 16
      packages/core/src/mods/header/connectStatus/index.module.less
  7. 74
      packages/core/src/mods/header/connectStatus/index.tsx
  8. 55
      packages/core/src/mods/header/index.tsx

34
packages/core/src/api/index.ts

@ -1,9 +1,11 @@
import axios from 'axios';
import { message } from 'antd';
const LOCAL_CONFIG_KEY = 'IMOVE:LOCAL_CONFIG_KEY';
export interface ILocalConfig {
ip: string;
port: string;
npmRegistry: string;
}
export enum ActionType {
@ -52,17 +54,38 @@ const request = (function () {
};
})();
export const localConfig: ILocalConfig = {
ip: '127.0.0.1',
port: '3500',
/**
* get local config data (saved in localStorage)
* @returns local config
*/
export const getLocalConfig = (): ILocalConfig => {
const savedConfigString = localStorage.getItem(LOCAL_CONFIG_KEY) || '';
let savedConfig = {} as ILocalConfig;
try {
savedConfig = JSON.parse(savedConfigString);
} catch (e) {}
return {
ip: savedConfig.ip || '127.0.0.1',
port: savedConfig.port || '3500',
npmRegistry: savedConfig.npmRegistry || 'https://registry.npm.taobao.org',
};
};
// export const localConfig: ILocalConfig = getLocalConfig();
/**
* get local config data (saved in localStorage)
*/
export const updateLocalConfig = (config: ILocalConfig) => {
localConfig.ip = config.ip || localConfig.ip;
localConfig.port = config.port || localConfig.port;
const savedConfig = getLocalConfig();
savedConfig.ip = config.ip || savedConfig.ip;
savedConfig.port = config.port || savedConfig.port;
savedConfig.npmRegistry = config.npmRegistry || savedConfig.npmRegistry;
localStorage.setItem(LOCAL_CONFIG_KEY, JSON.stringify(savedConfig));
};
export const localConnect = () => {
const localConfig = getLocalConfig();
return fetch(`http://${localConfig.ip}:${localConfig.port}/api/connect`, {
method: 'GET',
headers: { 'content-type': 'application/json' },
@ -70,6 +93,7 @@ export const localConnect = () => {
};
export const localSave = (data: any) => {
const localConfig = getLocalConfig();
fetch(`http://${localConfig.ip}:${localConfig.port}/api/save`, {
method: 'POST',
headers: { 'content-type': 'application/json' },

2
packages/core/src/components/console/index.tsx

@ -93,7 +93,7 @@ const LogLine: React.FC<ILog> = (props) => {
const renderLink = (data: string) => {
return (
<a href={data} target={'_blank'}>
<a href={data} target={'_blank'} rel="noreferrer">
{data}
</a>
);

32
packages/core/src/mods/header/connectStatus/editModal.tsx → packages/core/src/mods/header/configuration/editModal.tsx

@ -1,5 +1,4 @@
import React, { useRef } from 'react';
import 'antd/es/form/style';
import 'antd/es/modal/style';
import 'antd/es/input/style';
@ -7,10 +6,12 @@ import styles from './index.module.less';
import { Input, Form, Modal } from 'antd';
import { FormInstance } from 'antd/lib/form';
import { ILocalConfig, localConfig, updateLocalConfig } from '../../../api';
import { ILocalConfig, getLocalConfig, updateLocalConfig } from '../../../api';
const PORT_REGEX = /^\d{1,5}$/;
const IP_REGEX = /^(localhost)|(((2(5[0-5]|[0-4]\d))|[01]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[01]?\d{1,2})){3})$/;
const NPM_REGISTRY_REGEX = /^https?:\/\//; // 简单 URL 检查
const makeRules = (regex: RegExp, errTip: string) => {
return [
{ required: true, message: '不能为空!' },
@ -27,13 +28,13 @@ interface IEditorModalProps {
onCancel: () => void;
}
const EditModal: React.FC<IEditorModalProps> = (props) => {
const EditModal: React.FC<IEditorModalProps> = (props: IEditorModalProps) => {
const { visible, onOk, onCancel } = props;
const formRef = useRef<FormInstance>(null);
// events
const onClickOk = (): void => {
formRef.current &&
if (formRef.current) {
formRef.current
.validateFields()
.then((formValues) => {
@ -43,6 +44,7 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
.catch((error) => {
console.log(error.message);
});
}
};
return (
@ -51,22 +53,34 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
visible={visible}
okText={'保存'}
cancelText={'取消'}
title={'本地连接配置'}
title={'配置'}
onOk={onClickOk}
onCancel={onCancel}
>
<Form ref={formRef} labelCol={{ span: 4 }} initialValues={localConfig}>
<Form
ref={formRef}
labelCol={{ span: 6 }}
labelAlign="right"
initialValues={getLocalConfig()}
>
<Form.Item
label={'ip'}
label={'本地连接 IP'}
name={'ip'}
rules={makeRules(IP_REGEX, 'IP格式不合法')}
>
<Input />
</Form.Item>
<Form.Item
label={'port'}
label={'本地连接端口'}
name={'port'}
rules={makeRules(PORT_REGEX, 'port格式不合法')}
rules={makeRules(PORT_REGEX, '端口格式不合法')}
>
<Input />
</Form.Item>
<Form.Item
label={'NPM 源'}
name={'npmRegistry'}
rules={makeRules(NPM_REGISTRY_REGEX, 'NPM 源地址不合法')}
>
<Input />
</Form.Item>

10
packages/core/src/mods/header/configuration/index.module.less

@ -0,0 +1,10 @@
.editModal {
width: 450px;
:global {
.ant-form-item:last-child {
margin-bottom: 0;
}
}
}

35
packages/core/src/mods/header/configuration/index.tsx

@ -0,0 +1,35 @@
import React, { useState, useCallback } from 'react';
import { Button } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
import EditModal from './editModal';
interface IProps {
confirmToSync: () => Promise<void>;
}
const Configuration: React.FC<IProps> = (props: IProps) => {
const [visible, setVisible] = useState<boolean>(false);
// events
const onOpenEditModal = useCallback(() => {
setVisible(true);
}, [setVisible]);
const onCloseEditModal = useCallback(() => {
setVisible(false);
}, []);
const onOk = useCallback(() => {
onCloseEditModal();
props.confirmToSync();
}, [onCloseEditModal]);
return (
<div>
<Button size="small" type="text" onClick={onOpenEditModal}>
<SettingOutlined />
</Button>
<EditModal visible={visible} onOk={onOk} onCancel={onCloseEditModal} />
</div>
);
};
export default Configuration;

16
packages/core/src/mods/header/connectStatus/index.module.less

@ -1,22 +1,6 @@
.container {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 20px;
.tag {
cursor: pointer;
}
}
.editModal {
width: 450px;
:global {
.ant-form-item:last-child {
margin-bottom: 0;
}
}
}

74
packages/core/src/mods/header/connectStatus/index.tsx

@ -1,31 +1,26 @@
import React, { useState, useEffect } from 'react';
import 'antd/es/tag/style';
import 'antd/es/modal/style';
import 'antd/es/message/style';
import { Tag } from 'antd';
import styles from './index.module.less';
import { Graph } from '@antv/x6';
import EditModal from './editModal';
import { Tag, Modal, message } from 'antd';
import { localConnect } from '../../../api';
enum Status {
export enum Status {
connected = 'success',
disconnected = 'error',
}
interface IProps {
flowChart: Graph;
// flowChart: Graph;
status: Status;
projectName: string;
syncLocal: () => Promise<void>;
confirmToSync: () => Promise<void>;
}
const PULSE_RATE = 10 * 1000; // try to connect to local servet per 10 seconds
const ConnectStatus: React.FC<IProps> = (props) => {
const { flowChart } = props;
const [visible, setVisible] = useState<boolean>(false);
const [projectName, setProjectName] = useState<string>('');
const [status, setStatus] = useState<Status>(Status.disconnected);
const ConnectStatus: React.FC<IProps> = (props: IProps) => {
const { status, confirmToSync, syncLocal, projectName } = props;
// life
useEffect(() => {
@ -34,61 +29,14 @@ const ConnectStatus: React.FC<IProps> = (props) => {
return () => clearInterval(timer);
}, []);
// network
const syncLocal = () => {
return localConnect()
.then((res) => res.json())
.then((data = {}) => {
const { projectName } = data;
setStatus(Status.connected);
setProjectName(projectName);
return data;
})
.catch((error) => {
setStatus(Status.disconnected);
console.log('connect local failed, the error is:', error.message);
});
};
const confirmToSync = () => {
syncLocal().then((data) => {
const { dsl } = data || {};
dsl &&
Modal.confirm({
title: '本地连接成功,是否将数据同步至当前项目?',
onOk() {
try {
flowChart.fromJSON(dsl);
} catch (error) {
message.error('同步失败!');
}
},
});
});
};
// events
const onOpenEditModal = (): void => {
setVisible(true);
};
const onCloseEditModal = (): void => {
setVisible(false);
};
const onOk = (): void => {
onCloseEditModal();
confirmToSync();
};
let tagText = '本地连接失败 点击修改配置';
let tagText = '本地连接失败';
if (status === Status.connected) {
tagText = [projectName, '本地连接成功'].join(' ');
}
return (
<div className={styles.container}>
<Tag className={styles.tag} color={status} onClick={onOpenEditModal}>
{tagText}
</Tag>
<EditModal visible={visible} onOk={onOk} onCancel={onCloseEditModal} />
<Tag color={status}>{tagText}</Tag>
</div>
);
};

55
packages/core/src/mods/header/index.tsx

@ -1,19 +1,60 @@
import React from 'react';
import React, { useState, useCallback } from 'react';
import styles from './index.module.less';
import { Graph } from '@antv/x6';
import { Modal, message } from 'antd';
import Guide from './guide';
import Export from './export';
import ImportDSL from './importDSL';
import ConnectStatus from './connectStatus';
import ConnectStatus, { Status } from './connectStatus';
import Configuration from './configuration';
import { localConnect } from '../../api';
interface IProps {
flowChart: Graph;
}
const Header: React.FC<IProps> = (props) => {
const Header: React.FC<IProps> = (props: IProps) => {
const { flowChart } = props;
const [projectName, setProjectName] = useState<string>('');
const [status, setStatus] = useState<Status>(Status.disconnected);
// network
const syncLocal = useCallback(() => {
return localConnect()
.then((res) => res.json())
.then((data = {}) => {
console.log(data);
setStatus(Status.connected);
setProjectName(data.projectName);
return data;
})
.catch((error) => {
console.log(error);
setStatus(Status.disconnected);
console.log('connect local failed, the error is:', error.message);
});
}, [setStatus, setProjectName]);
const confirmToSync = useCallback(() => {
return syncLocal().then((data) => {
const { dsl } = data || {};
if (dsl) {
Modal.confirm({
title: '本地连接成功,是否将数据同步至当前项目?',
onOk() {
try {
flowChart.fromJSON(dsl);
} catch (error) {
message.error('同步失败!');
}
},
});
}
});
}, [syncLocal, flowChart]);
return (
<div className={styles.container}>
<a href="https://github.com/imgcook/imove">
@ -23,7 +64,13 @@ const Header: React.FC<IProps> = (props) => {
<Guide />
<Export flowChart={flowChart} />
<ImportDSL flowChart={flowChart} />
<ConnectStatus flowChart={flowChart} />
<ConnectStatus
status={status}
projectName={projectName}
syncLocal={syncLocal}
confirmToSync={confirmToSync}
/>
<Configuration confirmToSync={confirmToSync} />
</div>
</div>
);

Loading…
Cancel
Save