Эх сурвалжийг харах

不理解出的问题就很大啊

tuonina 5 жил өмнө
parent
commit
0e5a7434ac

+ 37 - 0
src/api/config.js

@@ -0,0 +1,37 @@
+/*配置中心的API*/
+import { del, get, post, put } from '../axios/tools';
+
+
+/**
+ * 查询配置
+ * @param params
+ * @param headers
+ * @returns {Promise<unknown>}
+ */
+export const configQueryConfigList = ({ params, headers = {} }) => get({ url: '/v1/property/config', params, headers });
+
+/**
+ * 添加属性配置
+ * @param data
+ * @returns {Promise<unknown>}
+ */
+export const configAddConfig = (data) => post({ url: '/v1/property/config', data });
+
+/**
+ * 编辑配置项
+ * @param id
+ * @param data
+ * @returns {Promise<AxiosResponse<any>|never>}
+ */
+export function configEditConfig({ id, data }) {
+    return put({ url: `/v1/property/config/${id}`, data });
+}
+
+/**
+ * 删除配置项
+ * @param id
+ * @returns {Promise<unknown>}
+ */
+export function configDelConfig(id) {
+    return del({ url: `/v1/property/config/${id}` });
+}

+ 11 - 0
src/api/index.js

@@ -0,0 +1,11 @@
+/**
+ * 所有API统一出口
+ */
+
+import * as configAPI from './config'
+
+
+const apis={...configAPI};
+
+
+export default apis;

+ 2 - 2
src/axios/tools.js

@@ -37,7 +37,7 @@ export const get = ({ url, headers, params, msg = '接口异常' }) =>
  * @param msg       接口异常提示
  * @param msg       接口异常提示
  * @param headers   接口所需header配置
  * @param headers   接口所需header配置
  */
  */
-export const post = ({ url, data, msg = '接口异常', headers }) =>
+export const post = ({ url, data, msg = '接口异常', headers={} }) =>
     axios
     axios
         .post(checkURL(url), data, headers)
         .post(checkURL(url), data, headers)
         .then(res => res.data)
         .then(res => res.data)
@@ -70,4 +70,4 @@ export const del = ({ url, data, headers }) =>
         .then(resp => resp.data)
         .then(resp => resp.data)
         .catch(err => {
         .catch(err => {
             return Promise.reject(err);
             return Promise.reject(err);
-        });
+        });

+ 6 - 5
src/index.js

@@ -3,16 +3,17 @@ import ReactDOM from 'react-dom';
 import * as serviceWorker from './serviceWorker';
 import * as serviceWorker from './serviceWorker';
 import Page from './Page';
 import Page from './Page';
 import * as apis from './axios';
 import * as apis from './axios';
+import nApis from './api';
 // import { AppContainer } from 'react-hot-loader';
 // import { AppContainer } from 'react-hot-loader';
 import { AlitaProvider, setConfig } from 'redux-alita';
 import { AlitaProvider, setConfig } from 'redux-alita';
 import './style/lib/animate.css';
 import './style/lib/animate.css';
 import './style/antd/index.less';
 import './style/antd/index.less';
 import './style/index.less';
 import './style/index.less';
-import {LocaleProvider} from 'antd'
-import zh_CN from 'antd/lib/locale-provider/zh_CN'
+import { LocaleProvider } from 'antd';
+import zh_CN from 'antd/lib/locale-provider/zh_CN';
 
 
 
 
-setConfig(apis);
+setConfig({ ...apis, ...nApis });
 // const render = Component => { // 增加react-hot-loader保持状态刷新操作,如果不需要可去掉并把下面注释的打开
 // const render = Component => { // 增加react-hot-loader保持状态刷新操作,如果不需要可去掉并把下面注释的打开
 //     ReactDOM.render(
 //     ReactDOM.render(
 //         <AppContainer>
 //         <AppContainer>
@@ -49,10 +50,10 @@ setConfig(apis);
 ReactDOM.render(
 ReactDOM.render(
     // <AppContainer>
     // <AppContainer>
     <AlitaProvider>
     <AlitaProvider>
-        <LocaleProvider locale={zh_CN}><Page /></LocaleProvider>
+        <LocaleProvider locale={zh_CN}><Page/></LocaleProvider>
     </AlitaProvider>,
     </AlitaProvider>,
     // </AppContainer>
     // </AppContainer>
-    document.getElementById('root')
+    document.getElementById('root'),
 );
 );
 // If you want your app to work offline and load faster, you can change
 // If you want your app to work offline and load faster, you can change
 // unregister() to register() below. Note this comes with some pitfalls.
 // unregister() to register() below. Note this comes with some pitfalls.

+ 33 - 0
src/pages/TestComponent.jsx

@@ -0,0 +1,33 @@
+/**
+ * @Classname TestComponent
+ * @Description TODO
+ * @Date 2019/8/22 10:14
+ * @Created by Administrator
+ */
+
+import React from 'react';
+import * as PropTypes from 'prop-types'
+
+class TestComponent extends React.Component {
+
+    static propTypes={
+
+    };
+
+    static defaultProps={
+
+    };
+
+    constructor(props){
+        super(props);
+        this.state={
+
+        }
+    }
+
+    componentDidMount() {
+
+    }
+
+
+}

+ 2 - 1
src/pages/config/ConfigLabel.js

@@ -68,7 +68,8 @@ class ConfigLabel extends React.Component {
                 <Button htmlType="button" icon="edit" type="primary" size="small"
                 <Button htmlType="button" icon="edit" type="primary" size="small"
                         onClick={() => this.setState({ currentLabel: item })}/>
                         onClick={() => this.setState({ currentLabel: item })}/>
                 <Button htmlType="button" icon="delete" type="danger" size="small"
                 <Button htmlType="button" icon="delete" type="danger" size="small"
-                        onClick={this.removeLabel.bind(this, item)}/>
+                        onClick={this.removeLabel.bind(this, item)}
+                />
             </div>
             </div>
         </List.Item>);
         </List.Item>);
     }
     }

+ 119 - 0
src/pages/config/ConfigListPage.jsx

@@ -0,0 +1,119 @@
+/**
+ * @Classname ConfigListPage
+ * @Description TODO
+ * @Date 2019/8/22 10:26
+ * @Created by Administrator
+ */
+
+import React from 'react';
+import * as PropTypes from 'prop-types';
+import { Button, List, Tag, Pagination } from 'antd';
+import { connectAlita } from 'redux-alita';
+import AddConfigForm from './widget/AddConfigForm';
+
+class ConfigListPage extends React.Component {
+
+    static propTypes = {
+        configPage: PropTypes.object,
+    };
+
+    static defaultProps = {
+        configPage: { data: {}, isFetching: false },
+    };
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            addModalVisible: false,
+            currentConfig: undefined,
+        };
+        this.params = {
+            page: 1,
+            pageSize: 10,
+        };
+
+
+    }
+
+
+    // eslint-disable-next-line react/sort-comp
+    _queryConfigList(refresh = false) {
+        if (refresh) this.params.page = 1;
+        let params = { params: this.params };
+        this.props.setAlitaState({ funcName: 'configQueryConfigList', params, stateName: 'configPage' })
+            .then(resp => {
+                console.log('resp=>', resp);
+            })
+            .catch(e => {
+                console.log('queryConfigList err =>', e);
+            });
+    }
+
+
+    componentDidMount() {
+        this._queryConfigList(true);
+
+    }
+
+    renderListItem(item) {
+        return (<List.Item className="list-item" key={item.id}>
+            <div style={{ fontSize: 18, color: '#333' }}>{item.key}</div>
+            <div style={{ fontSize: 15, color: '#666', marginTop: 5 }}>{item.value}</div>
+            <div style={{ fontSize: 15, color: '#666', marginTop: 5 }}>{item.desc}</div>
+            <div className="item-footer">
+                <Tag color="red">{item.application ? item.application : '通用'}</Tag>
+                <Tag color="volcano">{item.profile}</Tag>
+                <Tag color="orange">{item.label}</Tag>
+                <div style={{ flex: 1 }}/>
+                <Button size="small" htmlType="button" icon="edit" onClick={() => {
+                    this.setState({ currentConfig: { ...item }, addModalVisible: true });
+                }}
+                />
+                <Button size="small" htmlType="button" icon="delete"/>
+            </div>
+
+        </List.Item>);
+    }
+
+    render() {
+
+        let { data: configPage, isFetching } = this.props.configPage || {};
+        let listPage = { current: 1, pageSize: 15, total: 0 };
+        if (configPage) {
+            listPage = configPage;
+        }
+
+
+        return (<div className="component-container">
+            <div className="headerLine">
+                <Button loading={isFetching} htmlType="button"
+                        onClick={() => this._queryConfigList(true)}
+                        icon="sync"
+                />
+                <Button htmlType="button"
+                        icon="plus"
+                        onClick={() => this.setState({ currentConfig: undefined, addModalVisible: true })}
+                />
+            </div>
+            <List dataSource={listPage.records}
+                  size="small" bordered
+                  renderItem={this.renderListItem.bind(this)}
+            />
+
+            <Pagination pageSize={this.params.pageSize} current={listPage.current} total={listPage.total}/>
+
+            <AddConfigForm onCancel={() => this.setState({ addModalVisible: false, currentConfig: undefined })}
+                           onRefresh={() => this._queryConfigList(true)}
+                           config={this.state.currentConfig}
+                           visible={this.state.addModalVisible}
+            />
+
+        </div>);
+
+    }
+
+
+}
+
+
+export default connectAlita(['configPage'])(ConfigListPage);

+ 193 - 0
src/pages/config/widget/AddConfigForm.jsx

@@ -0,0 +1,193 @@
+/**
+ * @Classname AddConfigForm
+ * @Description TODO
+ * @Date 2019/8/22 11:45
+ * @Created by Administrator
+ * 编辑或者添加微服务配置属性
+ */
+
+import React from 'react';
+import * as PropTypes from 'prop-types';
+import { connectAlita } from 'redux-alita';
+
+import { AutoComplete, Button, Form, Input, message, Modal, Select } from 'antd';
+
+class AddConfigForm extends React.Component {
+
+    static propTypes = {
+        onCancel: PropTypes.func,
+        visible: PropTypes.bool,
+        config: PropTypes.object,
+        onRefresh: PropTypes.func,
+    };
+
+    static defaultProps = {
+        config: {
+            id: undefined,
+            key: undefined,
+            value: undefined,
+            application: undefined,//暂时定如果为空,则是通用的
+            profile: 'dev',
+            label: 'master',
+            desc: undefined,
+        },
+    };
+
+    static getDerivedStateFromProps(nextProps, prevState) {
+        let { visible, config } = nextProps;
+        return {
+            visible, config: config || { profile: 'dev', label: 'master' },
+        };
+    }
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            visible: false,
+            config: {},
+        };
+    }
+
+    _queryLabelList(keywords) {
+        if (!keywords) return;
+        let params = { keywords, pageSize: 6 };
+        this.props.setAlitaState({ funcName: 'searchLabel', params });
+    }
+
+
+    handleAddConfig(values) {
+        this.props.setAlitaState({ funcName: 'configAddConfig', params: values })
+            .then(resp => {
+                message.success('添加成功!');
+                this.props.onRefresh && this.props.onRefresh();
+            });
+    }
+
+    handleEditConfig(values) {
+        let params = { id: this.state.config.id, data: values };
+        this.props.setAlitaState({ funcName: 'configEditConfig', params })
+            .then(() => {
+                message.success('修改成功!');
+                this.props.onCancel && this.props.onCancel();
+                this.props.onRefresh && this.props.onRefresh();
+            });
+    }
+
+    _onModalOkPress(e) {
+        e.preventDefault();
+        let _this = this;
+        this.props.form.validateFieldsAndScroll((err, values) => {
+            if (!err) {
+                if (_this.state.config.id) {
+                    _this.handleEditConfig(values);
+                } else {
+                    _this.handleAddConfig(values);
+                }
+            } else {
+                console.log('validateFields error=>', err);
+            }
+        });
+    }
+
+
+    _onModalCancel() {
+        this.props.onCancel && this.props.onCancel();
+        this.props.form.resetFields();
+    }
+
+    render() {
+        const { getFieldDecorator } = this.props.form;
+        let { visible } = this.state;
+        const { data: labelPage, isFetching: isSearching } = this.props.searchLabel || {};
+
+        let isSubmitting = false;
+        if (this.props.configAddConfig && this.props.configAddConfig.isFetching) {
+            isSubmitting = true;
+        }
+        if (this.props.configEditConfig && this.props.configEditConfig.isFetching) {
+            isSubmitting = true;
+        }
+
+        let labelList = [];
+        if (labelPage && labelPage.records) {
+            labelList = labelPage.records;
+        }
+
+        let config = this.state.config;
+
+        console.log('currentConfig: ', config);
+
+        return (<Modal title={config.id ? '编辑配置项' : '新增配置项'}
+                       onCancel={this._onModalCancel.bind(this)}
+                       onOk={this._onModalOkPress.bind(this)}
+                       footer={[
+                           <Button key="back" htmlType="button" onClick={this._onModalCancel.bind(this)}>取消</Button>,
+                           <Button key="submit" htmlType="button" type="primary"
+                                   onClick={this._onModalOkPress.bind(this)}
+                                   loading={isSubmitting}
+                           >提交</Button>,
+                       ]}
+                       visible={visible}
+        >
+            <Form>
+                <Form.Item>
+                    {getFieldDecorator('application', { initialValue: config.application })(<Select
+                        placeholder="请选择应用系统"
+                    >
+                        <Select.Option key="default_application" value={undefined}>通用</Select.Option>
+                        <Select.Option key="sys_application" value="sys">Sys</Select.Option>
+                        <Select.Option key="gateway_application" value="gateway">Gateway</Select.Option>
+                        <Select.Option key="config_application" value="config">Config</Select.Option>
+                    </Select>)}
+                </Form.Item>
+                <Form.Item>
+                    {getFieldDecorator('profile', { initialValue: config.profile })(<Select placeholder="请选择环境">
+                        <Select.Option key="profile_sit" value="sit">正式环境</Select.Option>
+                        <Select.Option key="profile_dev" value="dev">测试环境</Select.Option>
+                        <Select.Option key="profile_prod" value="prod">线上测试环境</Select.Option>
+                    </Select>)}
+                </Form.Item>
+
+                <Form.Item>
+                    {getFieldDecorator('label', { initialValue: config.label })(<Select
+                        placeholder="请选择分支"
+                    >
+                        <Select.Option key="label_master" value="master">Master</Select.Option>
+                    </Select>)}
+                </Form.Item>
+                <Form.Item>
+                    {getFieldDecorator('key',
+                        {
+                            initialValue: config.key,
+                            rules: [{ required: true, message: '请输入配置项' }],
+                        })(<AutoComplete
+                            dataSource={labelList}
+                            loading={isSearching}
+                            placeholder="请输入配置项"
+                            onSearch={this._queryLabelList.bind(this)}>
+                            {labelList.map(item => <AutoComplete.Option key={item.id}
+                                                                        value={item.path}>{item.path}</AutoComplete.Option>)}
+                        </AutoComplete>,
+                    )}
+                </Form.Item>
+
+                <Form.Item>
+                    {getFieldDecorator('value', { initialValue: config.value })(<Input
+                        placeholder="请输入值"
+                    />)}
+                </Form.Item>
+
+                <Form.Item>
+                    {getFieldDecorator('desc', { initialValue: config.desc })(<Input
+                        placeholder="输入配置简介"
+                    />)}
+                </Form.Item>
+            </Form>
+        </Modal>);
+    }
+
+
+}
+
+
+export default connectAlita(['searchLabel', 'configAddConfig', 'configEditConfig'])(Form.create({ name: 'AddConfigForm' })(AddConfigForm));

+ 2 - 1
src/routes/config.js

@@ -1,5 +1,6 @@
 import ConfigLabel from '../pages/config/ConfigLabel';
 import ConfigLabel from '../pages/config/ConfigLabel';
 import CommonConfig from '../pages/config/CommonConfig';
 import CommonConfig from '../pages/config/CommonConfig';
+import ConfigListPage from '../pages/config/ConfigListPage';
 
 
 export default {
 export default {
     menus: [
     menus: [
@@ -9,7 +10,7 @@ export default {
             key: '/app/config', title: '配置中心', icon: 'mobile',
             key: '/app/config', title: '配置中心', icon: 'mobile',
             subs: [
             subs: [
                 { key: '/app/config/label', title: '属性标签', component: ConfigLabel },
                 { key: '/app/config/label', title: '属性标签', component: ConfigLabel },
-                { key: '/app/config/properties', title: '微服务配置', component: 'SystemProperties' },
+                { key: '/app/config/properties', title: '微服务配置', component: ConfigListPage },
                 { key: '/app/config/common', title: '通用配置', component: CommonConfig },
                 { key: '/app/config/common', title: '通用配置', component: CommonConfig },
             ],
             ],
 
 

+ 34 - 0
src/style/custom/component.less

@@ -0,0 +1,34 @@
+/*通用的页面布局*/
+
+.component-container {
+  padding: 10px;
+
+  .headerLine {
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 10px;
+  }
+
+  .list-content {
+    flex: 1;
+  }
+
+  .list-item {
+    background: white;
+    display: block;
+    overflow: hidden;
+  }
+
+  .item-footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-items: flex-end;
+    margin-top: 10px;
+  }
+
+  .pagination-footer {
+
+  }
+
+}

+ 2 - 1
src/style/index.less

@@ -15,6 +15,7 @@
 @import "utils-size";
 @import "utils-size";
 @import "utils-border";
 @import "utils-border";
 @import "utils-spacing";
 @import "utils-spacing";
+@import "./custom/component";
 body {
 body {
   margin: 0;
   margin: 0;
   padding: 0;
   padding: 0;
@@ -69,4 +70,4 @@ body {
   .ant-card-head-title {
   .ant-card-head-title {
     font-size: 14px !important;
     font-size: 14px !important;
   }
   }
-}
+}