Quellcode durchsuchen

multi entry : index and design 2

mike vor 6 Jahren
Ursprung
Commit
7895c0d50b
50 geänderte Dateien mit 8854 neuen und 0 gelöschten Zeilen
  1. 30 0
      public/design.html
  2. 220 0
      src/devApp/App.jsx
  3. 690 0
      src/devApp/developVersion/DevelopVersion.jsx
  4. 52 0
      src/devApp/developVersion/common/Graphiql.jsx
  5. 64 0
      src/devApp/developVersion/common/deploy/Deploy.jsx
  6. 22 0
      src/devApp/developVersion/common/deploy/ali/AliConfig.jsx
  7. 22 0
      src/devApp/developVersion/common/deploy/amazon/AmazonConfig.js
  8. 22 0
      src/devApp/developVersion/common/deploy/huawei/HuaweiConfig.jsx
  9. 69 0
      src/devApp/developVersion/common/deploy/index.css
  10. 490 0
      src/devApp/developVersion/common/deploy/tencent/TencentConfig.js
  11. 70 0
      src/devApp/developVersion/common/deploy/tencent/apiGroupCard/APIGroupCardFetch.js
  12. 396 0
      src/devApp/developVersion/common/deploy/tencent/apiGroupCard/APIGroupCardRender.js
  13. 85 0
      src/devApp/developVersion/common/deploy/tencent/apiPathCard/APIPathCardFetch.js
  14. 287 0
      src/devApp/developVersion/common/deploy/tencent/apiPathCard/APIPathCardRender.js
  15. 75 0
      src/devApp/developVersion/common/deploy/tencent/deployCard/DeployCardFetch.js
  16. 412 0
      src/devApp/developVersion/common/deploy/tencent/deployCard/DeployCardRender.js
  17. 53 0
      src/devApp/developVersion/common/deploy/tencent/notificationCard/NotificationCardFetch.js
  18. 242 0
      src/devApp/developVersion/common/deploy/tencent/notificationCard/NotificationCardRender.js
  19. 22 0
      src/devApp/developVersion/common/manage/AliyunResult.js
  20. 22 0
      src/devApp/developVersion/common/manage/AmazonResult.js
  21. 63 0
      src/devApp/developVersion/common/manage/Manage.jsx
  22. 403 0
      src/devApp/developVersion/common/manage/TencentResult.js
  23. 115 0
      src/devApp/developVersion/graphqlService/TrialCase.jsx
  24. 109 0
      src/devApp/developVersion/graphqlService/UserCreate.jsx
  25. 24 0
      src/devApp/developVersion/graphqlService/component/application/Application.js
  26. 19 0
      src/devApp/developVersion/graphqlService/component/caseMetabase/CaseMetabase.jsx
  27. 3 0
      src/devApp/developVersion/graphqlService/component/caseMetabase/index.css
  28. 178 0
      src/devApp/developVersion/graphqlService/component/generateJs/GenerateJs.jsx
  29. 3 0
      src/devApp/developVersion/graphqlService/component/generateJs/index.css
  30. 89 0
      src/devApp/developVersion/graphqlService/component/graphql/Graphql.jsx
  31. 1767 0
      src/devApp/developVersion/graphqlService/component/graphql/index.css
  32. 164 0
      src/devApp/developVersion/graphqlService/component/schema/Create.js
  33. 801 0
      src/devApp/developVersion/graphqlService/component/schema/Schema.jsx
  34. 471 0
      src/devApp/developVersion/graphqlService/component/schema/Table.js
  35. 144 0
      src/devApp/developVersion/graphqlService/component/schema/index.css
  36. 54 0
      src/devApp/developVersion/graphqlService/dataAnalysis/DataAnalysis.jsx
  37. 15 0
      src/devApp/developVersion/graphqlService/dataAnalysis/metabase/Metabase.jsx
  38. 58 0
      src/devApp/developVersion/graphqlService/dataStorage/DataStorage.jsx
  39. 14 0
      src/devApp/developVersion/graphqlService/dataStorage/databaseSetting/DatabaseSetting.jsx
  40. 83 0
      src/devApp/developVersion/index.css
  41. 63 0
      src/devApp/developVersion/quantService/QuantService.jsx
  42. 17 0
      src/devApp/developVersion/quantService/quantConfig/QuantConfig.jsx
  43. 16 0
      src/devApp/developVersion/quantService/quantManage/QuantManage.jsx
  44. 72 0
      src/devApp/developVersion/wechatService/WxTrialCase.js
  45. 94 0
      src/devApp/developVersion/wechatService/WxUserCreate.js
  46. 370 0
      src/devApp/developVersion/wechatService/wxConfig/WxConfig.jsx
  47. 5 0
      src/devApp/developVersion/wechatService/wxConfig/index.css
  48. 178 0
      src/devApp/developVersion/wechatService/wxCreate/WxCreate.js
  49. 3 0
      src/devApp/developVersion/wechatService/wxCreate/index.css
  50. 114 0
      src/devApp/index.css

+ 30 - 0
public/design.html

@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/ioo.ico">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
+    <meta name="theme-color" content="#000000">
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+    <script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
+    <script>
+      if ('addEventListener' in document) {
+        document.addEventListener('DOMContentLoaded', function() {
+          FastClick.attach(document.body);
+        }, false);
+      }
+      if(!window.Promise) {
+        document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>');
+      }
+    </script>
+    <title>Ioobot workbench design</title>
+  </head>
+  <body>
+
+    <noscript>
+      You need to enable JavaScript to run this app.
+    </noscript>
+    <div id="root" style="height: 100%"></div>
+
+  </body>
+</html>

+ 220 - 0
src/devApp/App.jsx

@@ -0,0 +1,220 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Layout, Button, Spin, Col, Row, Tabs} from 'antd';
+
+import {withRouter} from "react-router-dom";
+
+import './developVersion/index.css';
+import {request} from 'graphql-request'
+import {graphqlUrl} from "../config";
+import {SHOW_ALL_CASE} from "../gql";
+import './index.css';
+
+const {Header, Footer, Content} = Layout;
+const TabPane = Tabs.TabPane;
+
+class App extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            examplesFirst: [],
+            examplesSecond: [],
+            examplesThird: []
+        }
+    }
+
+    componentWillMount() {
+        this._isMounted = true;
+
+        request(graphqlUrl, SHOW_ALL_CASE, {}).then(data => {
+                let cases = data.case_by_props;
+                let examplesFirst = cases.filter(cases => cases.title.includes('预约'));
+                let examplesSecond = cases.filter(cases => cases.title.includes('服装'));
+                let examplesThird = cases.filter(cases => cases.title.includes('小小杂志'));
+                if (this._isMounted) {
+                    this.setState({
+                        examplesFirst,
+                        examplesSecond,
+                        examplesThird
+                    })
+                }
+            }
+        )
+    }
+
+    componentWillUnmount() {
+        this._isMounted = false;
+    }
+
+
+    render() {
+        let {examplesFirst, examplesSecond, examplesThird} = this.state;
+        const tabStyle = {
+            paddingBottom: '50px',
+            height: '550px',
+            display: 'inline-block',
+        };
+
+        return (
+            <div>
+                <Layout className="layout-home" style={{minWidth: 1600}}>
+                    <Header>
+                        <div className="logo-wrapper">
+                            <div className='logo'/>
+                        </div>
+                    </Header>
+                    <Content>
+                        <div className='intro-wrapper'>
+                            <Row>
+                                <Col span={8} offset={12}>
+                                    <div className="intro" style={{padding: '80px 60px 40px 60px'}}>
+                                        <h2>无年费&nbsp;&nbsp;&nbsp;&nbsp;数据完全私有</h2>
+                                        <h3>即刻拥有自己的微信公众号、小程序</h3>
+                                    </div>
+                                    <div className="home-btn-group">
+                                        <Button size='large'
+                                                className='home-btn'
+                                                style={{marginLeft: '10px'}}
+                                                onClick={() => {
+                                                    this.props.history.push({
+                                                        pathname: `/developer`
+                                                    })
+                                                }}>开发设计
+                                        </Button>
+                                    </div>
+                                    <div className="intro-below" style={{padding: '0 60px'}}>
+                                        <h4>或致电:177 1810 6480</h4>
+                                    </div>
+                                </Col>
+                            </Row>
+                        </div>
+
+
+                        <div className={'cases-show-wrapper'}>
+                            <div className={'cases-show-title'}>行业模板案例</div>
+                            <div className={'case-show-tabs'}>
+                                <Tabs tabPosition='left' size='large' tabBarStyle={tabStyle} tabBarGutter={100}>
+                                    <TabPane tab="预约" key="1">
+                                        <div style={{marginLeft: 100}}>
+                                            {
+                                                examplesFirst.length === 0 ?
+                                                    <Spin/> :
+                                                    <CasesShow
+                                                        examples={examplesFirst}
+                                                    />
+                                            }
+                                        </div>
+                                    </TabPane>
+                                    <TabPane tab="电商" key="2">
+                                        <div style={{marginLeft: 100}}>
+                                            {
+                                                examplesSecond.length === 0 ?
+                                                    <Spin/> :
+                                                    <CasesShow
+                                                        examples={examplesSecond}
+                                                    />
+                                            }
+                                        </div>
+                                    </TabPane>
+                                    <TabPane tab="杂志" key="3">
+                                        <div style={{marginLeft: 100}}>
+                                            {
+                                                examplesThird.length === 0 ?
+                                                    <Spin/> :
+                                                    <CasesShow
+                                                        examples={examplesThird}
+                                                    />
+                                            }
+                                        </div>
+                                    </TabPane>
+                                </Tabs>
+                            </div>
+                        </div>
+
+
+                    </Content>
+                    <Footer style={{textAlign: 'center'}}>
+                        版权所有 © 2019 栋天科技
+                        {/*All right ©2019 Created by Ioobot*/}
+                    </Footer>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default withRouter(App);
+
+
+class CasesShow extends Component {
+    constructor(props) {
+        super(props);
+        console.log(props);
+        this.state = {
+            showCaseID: props.examples[0].id,
+            index: 0
+        }
+    }
+
+    render() {
+        let {examples} = this.props;
+        let {showCaseID, index} = this.state;
+        let thisCase = examples[index];
+        return (
+            <Row>
+                <Col span={16}>
+                    <Row>
+                        <div>
+                            <div className={'case-detail-title'}>{thisCase.title}</div>
+                            <div
+                                className={'case-detail-description'}>{thisCase.detailDescription ? thisCase.detailDescription : '暂无简介'}</div>
+                            <div className='cover-div'>
+                                <img
+                                    className='cover-img'
+                                    src={thisCase.img}
+                                    alt={thisCase.title + '' + thisCase.description}
+                                />
+                                <div style={{marginTop: 5}}>—— 打开微信扫码体验 ——</div>
+                            </div>
+                        </div>
+                    </Row>
+                    <Row>
+                        {
+                            examples.map((item, index) => (
+                                <div
+                                    className={showCaseID === item.id ? 'logo-cover-div logo-cover-div-on' : 'logo-cover-div'}
+                                    onMouseEnter={() => {
+                                        this.setState({
+                                            showCaseID: item.id,
+                                            index
+                                        })
+                                    }}
+                                    key={index}
+                                >
+                                    <img
+                                        className='logo-cover-img'
+                                        src={item.img}
+                                        alt={item.title + '' + item.description}
+                                    />
+                                    <div>{item.description}</div>
+                                </div>
+                            ))
+                        }
+                    </Row>
+                </Col>
+                <Col span={8}>
+                    <div className={'detail-images'}>
+                        <img
+                            key={thisCase.detailImages[0]}
+                            src={thisCase.detailImages[0]}
+                            alt=""
+                            height="500"/>
+                    </div>
+                </Col>
+            </Row>
+        )
+    }
+}
+
+
+

+ 690 - 0
src/devApp/developVersion/DevelopVersion.jsx

@@ -0,0 +1,690 @@
+import React, {Component} from 'react';
+import {Layout, Menu, Button, Spin, Icon, LocaleProvider, Dropdown, Avatar, Badge} from 'antd';
+import {BrowserRouter as Router, Route, Link, Switch, Redirect} from "react-router-dom";
+import {Query} from "react-apollo";
+import gql from "graphql-tag";
+
+import moment from 'moment';
+import 'moment/locale/zh-cn';
+import {FormattedMessage} from 'react-intl';
+import zhCN from 'antd/lib/locale-provider/zh_CN';
+
+import QuantService from "./quantService/QuantService";
+import TrialCase from "./graphqlService/TrialCase";
+import UserCreate from "./graphqlService/UserCreate";
+import WxTrialCase from "./wechatService/WxTrialCase";
+import WxUserCreate from "./wechatService/WxUserCreate";
+import Login from "../../login/Login";
+import './graphqlService/component/graphql/index.css';
+import './index.css'
+import Create from "./graphqlService/component/schema/Create";
+import WxCreate from "./wechatService/wxCreate/WxCreate";
+
+import {CASE_AND_PROJECT, GET_USER} from "../../gql";
+import axios from 'axios';
+import {getCookie, setCookie} from "../../cookie";
+import {logoutUrl} from "../../config";
+import Graphiql from "./common/Graphiql";
+
+axios.defaults.withCredentials = true;
+
+const {SubMenu} = Menu;
+const {Header, Sider} = Layout;
+moment.locale('en');
+
+class DevelopVersion extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            menuLevel1: "graphql-service",
+            sideBar: "ecommerce",
+            collapsed: false,
+            inlineCollapsed: false,
+            language: props.language,
+            locale: props.language === "中文" ? zhCN : undefined,
+            languageButton: props.language === "中文" ? "English" : "中文",
+            visible: false,
+            wxVisible: false,
+            userID: '',
+            avatar: ''
+        };
+    }
+
+    componentWillMount() {
+        let urlList = window.location.pathname.split("/");
+        let urlListLength = urlList.length;
+        if (urlListLength > 2) {
+            this.setState({menuLevel1: urlList[1]});
+            if (urlList[3] !== 'index') {
+                this.setState({sideBar: urlList[3]});
+            }
+        }
+
+        let userID = getCookie('user_id');
+        if (userID === undefined || '') {
+            axios.get(this.state.getIdUrl)
+                .then((res) => {
+                    if (res.data !== '') {
+                        setCookie("user_id", res.data);
+                        this.setState({
+                            userID: res.data
+                        })
+                    }
+                })
+                .catch(function (err) {
+                    console.log(err);
+                });
+        } else {
+            this.setState({
+                userID
+            })
+        }
+    }
+
+    onCollapse = (collapsed) => {
+        this.setState({collapsed});
+    };
+
+    switchMenu = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    switchMenuTab = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+        if (e.key === "graphql-service") {
+            this.setState({sideBar: "ecommerce"});
+        }
+    };
+
+    switchSidebar = (value) => {
+        this.setState({
+            sideBar: value,
+        });
+    };
+
+    switchMenuLevel = (menuName, value) => {
+        this.setState({
+            [menuName]: value,
+        });
+    };
+
+    showModal = () => {
+        this.setState({
+            visible: true,
+        });
+    };
+
+    wxShowModal = () => {
+        this.setState({
+            wxVisible: true,
+        });
+    };
+
+    hideModal = () => {
+        this.setState({
+            visible: false,
+        });
+    };
+
+    wxHideModal = () => {
+        this.setState({
+            wxVisible: false,
+        });
+    };
+
+    changeLocale = (e) => {
+        e.stopPropagation();
+        let {language} = this.state;
+        // console.log('app language',language);
+        let changeLanguage = language === "中文" ? "English" : "中文";
+        let local = language === "中文" ? undefined : zhCN;
+        console.log(language);
+        let languageButton = language === "中文" ? "中文" : "English";
+
+        sessionStorage.setItem("language", changeLanguage);
+        this.props.changeLanguage(changeLanguage);
+        this.setState({
+            language: changeLanguage,
+            locale: local,
+            languageButton: languageButton
+        });
+        if (!local) {
+            moment.locale('en');
+        } else {
+            moment.locale('zh-cn');
+        }
+    };
+
+    render() {
+        const {locale, languageButton, visible, wxVisible} = this.state;
+
+        return (
+            <Layout style={{minHeight: '100vh'}}>
+                <Header className="header" style={{position: 'fixed', zIndex: 1, width: '100%'}}>
+                    <Link to="../"
+                          onClick={() => this.setState({menuLevel1: "graphql-service", sideBar: "ecommerce"})}>
+                        <div className="logo-wrapper">
+                            <div className='logo'/>
+                        </div>
+                    </Link>
+                    <Menu
+                        theme="dark"
+                        mode="horizontal"
+                        selectedKeys={[this.state.menuLevel1]}
+                        style={{lineHeight: '64px'}}
+                        onClick={(e) => this.switchMenuTab('menuLevel1', e)}
+                    >
+                        <Menu.Item key="graphql-service">
+                            <Link to="/developer/graphql-service/trial-case/index"><FormattedMessage
+                                id="Graphql Service"/></Link>
+                        </Menu.Item>
+                        <Menu.Item key="wechat-service">
+                            <Link to="/developer/wechat-service/trial-case/index"><FormattedMessage
+                                id="Wechat Service"/></Link>
+                        </Menu.Item>
+                        <Menu.Item key="quant-service">
+                            <Link to="/developer/quant-service/trial-case/index"><FormattedMessage id="Quantization Service"/></Link>
+                        </Menu.Item>
+                    </Menu>
+
+                    {
+                        this.state.userID === '' ?
+                            <Link to="/login">
+                                <Button className='login-button' type='primary'
+                                        onClick={() => this.switchMenuLevel('menuLevel1', 'user')}>
+                                    <FormattedMessage id="Login"/></Button>
+                            </Link>
+                            :
+                            <User
+                                userID={this.state.userID}
+                                languageButton={this.state.languageButton}
+                                changeLocale={this.changeLocale}
+                                switchMenuLevel={this.switchMenuLevel}
+                                history={this.props.history}
+                            />
+                    }
+
+                </Header>
+
+
+                {(() => {
+                    switch (this.state.menuLevel1) {
+                        case 'graphql-service':
+                            return (
+                                <Sider
+                                    width={200}
+                                    style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
+                                    collapsible
+                                    collapsed={this.state.collapsed}
+                                    onCollapse={this.onCollapse}
+                                >
+                                    <GraphqlSidebar inlineCollapsed={this.state.inlineCollapsed}
+                                                    sideBar={this.state.sideBar} switchMenu={this.switchMenu}
+                                                    showModal={this.showModal}/>
+
+                                </Sider>
+                            );
+                        case 'wechat-service':
+                            return (
+                                <Sider
+                                    width={200}
+                                    style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
+                                    collapsible
+                                    collapsed={this.state.collapsed}
+                                    onCollapse={this.onCollapse}
+                                >
+                                    <WxConfigSiderbar inlineCollapsed={this.state.inlineCollapsed}
+                                                      sideBar={this.state.sideBar} switchMenu={this.switchMenu}
+                                                      wxShowModal={this.wxShowModal}/>
+                                </Sider>
+                            );
+                        case 'quant-service':
+                            return (
+                                <Sider
+                                    width={200}
+                                    style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
+                                    collapsible
+                                    collapsed={this.state.collapsed}
+                                    onCollapse={this.onCollapse}
+                                >
+                                    <Menu
+                                        theme="dark"
+                                        mode="inline"
+                                        inlineCollapsed={this.state.inlineCollapsed}
+                                        defaultSelectedKeys={['quant-service']}
+                                        defaultOpenKeys={['trial-case']}
+                                        // openKeys={['cloud-function']}
+                                        onClick={(e) => this.switchMenu('sideBar', e)}
+                                        selectedKeys={['quant-service']}
+                                        style={{
+                                            borderRight: 0,
+                                            overflow: 'auto',
+                                            height: '100vh',
+                                            left: '0',
+                                            width: '200px',
+                                            position: 'fixed'
+                                        }}
+                                    >
+                                        <SubMenu key="trial-case"
+                                                 title={<span><Icon type="appstore" theme="twoTone"/>
+                                                         <span><FormattedMessage id="Case Show"/></span></span>}>
+                                            <Menu.Item key="quant-service">
+                                                <Link to="/developer/quant-service/trial-case/quant case">quant case</Link>
+                                            </Menu.Item>
+                                        </SubMenu>
+
+                                        <Menu.Item key="instructions">
+                                            <a href="https://ioobot-document.netlify.com/" title="instructions"
+                                               target="instructions">
+                                                <Icon type="file-text" theme="twoTone"/>
+                                                <span><FormattedMessage id="Instructions"/></span>
+                                            </a>
+                                        </Menu.Item>
+                                    </Menu>
+                                </Sider>
+                            );
+                        default:
+                            return (
+                                <Sider
+                                    width={200}
+                                    style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
+                                    collapsible
+                                    collapsed={this.state.collapsed}
+                                    onCollapse={this.onCollapse}
+                                >
+                                    <GraphqlSidebar inlineCollapsed={this.state.inlineCollapsed}
+                                                    sideBar={this.state.sideBar} switchMenu={this.switchMenu}
+                                                    showModal={this.showModal}/>
+                                </Sider>
+                            );
+                    }
+                })()}
+                <Create visible={visible} hideModal={this.hideModal} switchSidebar={this.switchSidebar}/>
+                <WxCreate visible={wxVisible} hideModal={this.wxHideModal} switchSidebar={this.switchSidebar}/>
+
+                <LocaleProvider locale={locale}>
+                    <Layout style={{marginTop: '64px', zIndex: '0'}}
+                            key={locale ? locale.locale : 'en'/* Have to refresh for production environment */}>
+                        <Switch>
+                            <Route path="/developer" exact component={TrialCase}/>
+                            <Route path="/developer/graphql-service/trial-case/:case" component={TrialCase}/>
+                            <Route path="/developer/graphql-service/my-create/:case" component={UserCreate}/>
+                            <Route path="/developer/wechat-service/trial-case/:case" component={WxTrialCase}/>
+                            <Route path="/developer/wechat-service/my-create/:case" component={WxUserCreate}/>
+                            <Route path="/developer/quant-service/:sidebar/:case" component={QuantService}/>
+                            <Route path="/developer/login/:setting" component={Login}/>
+                            <Route path="/developer/login" component={Login}/>
+                            <Route path="/developer/graphiql" component={Graphiql}/>
+                            <Redirect path="*" to="/"/>
+                        </Switch>
+                    </Layout>
+                </LocaleProvider>
+            </Layout>
+        );
+    }
+}
+
+export default DevelopVersion;
+
+class GraphqlSidebar extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            userID: getCookie('user_id'),
+        }
+    }
+
+    render() {
+        return (
+            <Query query={gql(CASE_AND_PROJECT)} variables={{projectType: 'graphql', user_id: this.state.userID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) return (
+                            <Menu
+                                theme="dark"
+                                mode="inline"
+                                style={{
+                                    borderRight: 0,
+                                    overflow: 'auto',
+                                    height: '100vh',
+                                    left: '0',
+                                    width: '200px',
+                                    position: 'fixed'
+                                }}
+                            >
+                                <Menu.Item >
+                                    <Icon type="loading" />
+                                    <span><FormattedMessage id="loading"/></span>
+                                </Menu.Item>
+                            </Menu>
+                        );
+                        if (error) return 'error!';
+                        // console.log('CASE_AND_PROJECT data', data);
+                        data.caseProject.forEach((project) => {
+                            switch (project.schema_id.schemaName) {
+                                case 'ecommerce' :
+                                    localStorage.setItem('ecommerce', project.schema_id.schemaData);
+                                    break;
+                                case 'order':
+                                    localStorage.setItem('order', project.schema_id.schemaData);
+                                    break;
+                                case 'bills':
+                                    localStorage.setItem('bills', project.schema_id.schemaData);
+                                    break;
+                                default:
+                                    break;
+                            }
+                        });
+                        return (
+                            <Menu
+                                theme="dark"
+                                mode="inline"
+                                inlineCollapsed={this.props.inlineCollapsed}
+                                defaultSelectedKeys={['ecommerce']}
+                                defaultOpenKeys={['trial-case', 'my-create']}
+                                // openKeys={['trial-case', 'my-create']}
+                                onClick={(e) => this.props.switchMenu('sideBar', e)}
+                                selectedKeys={[this.props.sideBar]}
+                                style={{
+                                    borderRight: 0,
+                                    overflow: 'auto',
+                                    height: '100vh',
+                                    left: '0',
+                                    width: '200px',
+                                    position: 'fixed'
+                                }}
+                            >
+                                <SubMenu
+                                    key="trial-case"
+                                    title={<span><Icon type="appstore" theme="twoTone"/><span><FormattedMessage
+                                        id="Case Show"/></span></span>}>
+                                    {
+                                        data.caseProject.map((project) =>
+                                            <Menu.Item key={project.projectName}>
+                                                <Link to={{
+                                                    pathname: `/developer/graphql-service/trial-case/${project.projectName}`,
+                                                    state: {
+                                                        schemaName: project.projectName,
+                                                        schemaID: project.schema_id.id,
+                                                        projectID: project.id
+                                                    }
+                                                }}><FormattedMessage id={project.projectName}/></Link>
+                                            </Menu.Item>)
+                                    }
+                                </SubMenu>
+
+                                <Menu.Item key="create-graphql" onClick={this.props.showModal}>
+                                    <Icon type="edit" theme="twoTone"/>
+                                    <span><FormattedMessage id="Create"/></span>
+                                    <Icon type="plus" style={{
+                                        position: 'absolute',
+                                        top: '35%',
+                                        right: '6px',
+                                        color: 'white'
+                                    }}/>
+                                </Menu.Item>
+
+                                <SubMenu
+                                    key="my-create"
+                                    title={<span><Icon type="user" theme="outlined"/><span><FormattedMessage
+                                        id="My Create"/></span></span>}>
+                                    {
+                                        data.project.map((project) =>
+                                            <Menu.Item key={project.projectName}>
+                                                <Link to={{
+                                                    pathname: `/developer/graphql-service/my-create/${project.projectName}`,
+                                                    state: {
+                                                        schemaName: project.projectName,
+                                                        schemaID: project.schema_id.id,
+                                                        projectID: project.id
+                                                    }
+                                                }}>{project.projectName}</Link>
+                                            </Menu.Item>)
+                                    }
+                                </SubMenu>
+
+                                <Menu.Item key="instructions">
+                                    <a href="https://ioobot-document.netlify.com/" title="instructions" target="_blank"
+                                       rel="noopener noreferrer">
+                                        <Icon type="file-text" theme="twoTone"/>
+                                        <span><FormattedMessage id="Instructions"/></span>
+                                    </a>
+                                </Menu.Item>
+
+                            </Menu>
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+class WxConfigSiderbar extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            userID: getCookie('user_id'),
+        }
+    }
+
+    render() {
+        return (
+            <Query query={gql(CASE_AND_PROJECT)} variables={{projectType: 'wx', user_id: this.state.userID}}>
+                {
+                    ({loading, error, data}) => {
+                        // console.log('CASE_WXCONFIG_AND_PROJECT data', data);
+                        if (loading) return (
+                            <Menu
+                                theme="dark"
+                                mode="inline"
+                                style={{
+                                    borderRight: 0,
+                                    overflow: 'auto',
+                                    height: '100vh',
+                                    left: '0',
+                                    width: '200px',
+                                    position: 'fixed'
+                                }}
+                            >
+                                <Menu.Item >
+                                    <Icon type="loading" />
+                                    <span><FormattedMessage id="loading"/></span>
+                                </Menu.Item>
+                            </Menu>
+                        );
+                        if (error) return 'error!';
+                        return (
+                            <Menu
+                                theme="dark"
+                                mode="inline"
+                                inlineCollapsed={this.props.inlineCollapsed}
+                                // defaultSelectedKeys={['my-wechat']}
+                                defaultOpenKeys={['trial-case', 'my-create']}
+                                // openKeys={['trial-case', 'my-create']}
+                                onClick={(e) => this.props.switchMenu('sideBar', e)}
+                                selectedKeys={[this.props.sideBar]}
+                                style={{
+                                    borderRight: 0,
+                                    overflow: 'auto',
+                                    height: '100vh',
+                                    left: '0',
+                                    width: '200px',
+                                    position: 'fixed'
+                                }}
+                            >
+                                <SubMenu key="trial-case" title={<span><Icon type="appstore" theme="twoTone"/>
+                                    <span><FormattedMessage id="Case Show"/></span>
+                                </span>}>
+                                    {
+                                        data.caseProject.map((project) => {
+                                            if (project) {
+                                                let appName = project.projectName;
+                                                let configID = project.wxConfig_id.id;
+                                                let projectID = project.id;
+                                                return (
+                                                    <Menu.Item key={appName}>
+                                                        <Link to={{
+                                                            pathname: `/developer/wechat-service/trial-case/${appName}`,
+                                                            state: {
+                                                                appName,
+                                                                configID,
+                                                                projectID
+                                                            }
+                                                        }}><FormattedMessage id={appName}/></Link>
+                                                    </Menu.Item>
+                                                )
+                                            }
+                                            return false;
+                                        })
+                                    }
+                                </SubMenu>
+
+                                <Menu.Item key="create-config" onClick={this.props.wxShowModal}>
+                                    <Icon type="edit" theme="twoTone"/>
+                                    <span><FormattedMessage id="Create"/></span>
+                                    <Icon type="plus" style={{
+                                        position: 'absolute',
+                                        top: '35%',
+                                        right: '6px',
+                                        color: 'white'
+                                    }}/>
+                                </Menu.Item>
+
+                                <SubMenu key="my-create" title={<span><Icon type="user" theme="outlined"/>
+                                    <span><FormattedMessage id="My Create"/></span>
+                                </span>}>
+                                    {
+                                        data.project.map((project) => {
+                                            if (project) {
+                                                let appName = project.projectName;
+                                                let configID = project.wxConfig_id.id;
+                                                let projectID = project.id;
+                                                return (
+                                                    <Menu.Item key={appName}>
+                                                        <Link to={{
+                                                            pathname: `/developer/wechat-service/my-create/${appName}`,
+                                                            state: {
+                                                                appName,
+                                                                configID,
+                                                                projectID
+                                                            }
+                                                        }}>{appName}</Link>
+                                                    </Menu.Item>
+                                                )
+                                            }
+                                            return false;
+                                        })
+                                    }
+                                </SubMenu>
+
+                                <Menu.Item key="instructions">
+                                    <a href="https://ioobot-document.netlify.com/" title="instructions" target="_blank"
+                                       rel="noopener noreferrer">
+                                        <Icon type="file-text" theme="twoTone"/>
+                                        <span><FormattedMessage id="Instructions"/></span>
+                                    </a>
+                                </Menu.Item>
+
+                            </Menu>
+
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+class User extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            show: false
+        }
+    }
+
+    logout = () => {
+
+        axios.get(logoutUrl)
+            .then((res) => {
+               console.log('logout success',res);
+                setCookie("user_id", '');
+            })
+            .catch((err) => {
+        });
+    };
+
+    render() {
+        return (
+            <Query query={gql(GET_USER)} variables={{id: this.props.userID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin className='login-nickname'/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+                        let user = data.user_by_id;
+                        if (user !== null) {
+                            const menu = (
+                                <Menu className={'user-detail'}>
+                                    <Menu.Item className={'user-info'}>
+                                        <p className={'user-info-nickname'}>{user.nickname}</p>
+                                        <p className={'user-info-email'}>{user.email}</p>
+                                    </Menu.Item>
+                                    <Menu.Item>
+                                        <a href='https://www.ioobot.com' onClick={(e) => {
+                                            e.preventDefault();
+                                            this.props.changeLocale(e)
+                                        }}>{this.props.languageButton}</a>
+                                    </Menu.Item>
+                                    <Menu.Item>
+                                        <Link to="/login">
+                                            <div>
+                                                <FormattedMessage id="Account center"/>
+                                            </div>
+                                        </Link>
+                                    </Menu.Item>
+                                    <Menu.Item>
+                                        <Link to="/common/communication">
+                                            <div>
+                                                <FormattedMessage id="Submit Support Ticket"/>
+                                            </div>
+                                        </Link>
+                                    </Menu.Item>
+                                    <Menu.Item className={'login-out'}>
+                                        <a href='https://www.ioobot.com' onClick={(e) => {
+                                            e.preventDefault();
+                                            this.logout();
+                                            this.props.history.push({
+                                                pathname: '/'
+                                            })
+                                        }}><FormattedMessage id="exit"/></a>
+                                    </Menu.Item>
+                                </Menu>
+                            );
+                            return (
+                                <div className='login-nickname' onClick={()=>{}}>
+                                    <Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
+                                        <div>
+                                            <span style={{ marginRight: 5 }}>
+                                                <Badge dot><Avatar shape="user" icon="user" /></Badge>
+                                            </span>
+                                            {/*<Icon type="down" />*/}
+                                        </div>
+                                    </Dropdown>
+                                </div>
+                            )
+                        }
+                    }
+                }
+            </Query>
+        )
+    }
+}

+ 52 - 0
src/devApp/developVersion/common/Graphiql.jsx

@@ -0,0 +1,52 @@
+import React, {Component} from 'react';
+import {Input, Spin} from 'antd';
+import GraphiQL from "graphiql";
+import fetch from "isomorphic-fetch";
+
+const Search = Input.Search;
+
+class Graphiql extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            api: 'http://123.206.193.98:3123/graphql',
+        };
+    };
+
+    shouldComponentUpdate(nextProps,nextState){
+        if(nextState.api !== this.state.api){
+            return true;
+        }
+        return false;
+    }
+
+    graphQLFetcher = (graphQLParams) => {
+        // console.log('graphQLParams',graphQLParams);
+        // 已经存在的 magazine api, 设想: 用户生成 schema,返回 api 自动替换这里
+        return fetch(this.state.api, {
+            method: 'post',
+            headers: {'Content-Type': 'application/json'},
+            body: JSON.stringify(graphQLParams),
+        }).then(response => response.json());
+    };
+
+    render() {
+        return (
+            <div>
+                <Search
+                    style={{marginTop: 10}}
+                    placeholder="input api url"
+                    defaultValue={this.state.api}
+                    enterButton="POST"
+                    onSearch={value => {
+                        // console.log(value);
+                        this.setState({api: value});
+                    }}
+                />
+                <GraphiQL fetcher={this.graphQLFetcher.bind(this)}/>
+            </div>
+        )
+    }
+}
+
+export default Graphiql;

+ 64 - 0
src/devApp/developVersion/common/deploy/Deploy.jsx

@@ -0,0 +1,64 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Card} from 'antd';
+
+import TencentConfig from './tencent/TencentConfig';
+import AliConfig from './ali/AliConfig';
+import AmazonConfig from './amazon/AmazonConfig';
+
+import './index.css';
+
+const tabListNoTitle =
+[{
+    key: 'tencent',
+    tab: <FormattedMessage id='Tencent'/>,
+}, {
+    key: 'aliyun',
+    tab: <FormattedMessage id='Aliyun'/>,
+}, {
+    key: 'amazon',
+    tab: <FormattedMessage id='AWS'/>,
+}];
+
+
+class Deploy extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            cloud: 'tencent'
+        };
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            projectID: next.projectID
+        })
+    }
+
+    render() {
+        const contentListNoTitle = {
+            tencent: <TencentConfig userID={this.props.userID} projectID={this.props.projectID} trialcase={this.props.trialcase} kind={this.props.kind}/>,
+            aliyun: <AliConfig/>,
+            amazon: <AmazonConfig/>,
+        };
+
+        return (
+            <div>
+                <Card
+                    style={{width: '100%'}}
+                    tabList={tabListNoTitle}
+                    activeTabKey={this.state.cloud}
+                    onTabChange={(cloud) => {
+                        this.setState({
+                            cloud
+                        })
+                    }}
+                >
+                    {contentListNoTitle[this.state.cloud]}
+                </Card>
+            </div>
+        )
+    }
+}
+
+export default Deploy;

+ 22 - 0
src/devApp/developVersion/common/deploy/ali/AliConfig.jsx

@@ -0,0 +1,22 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+class AliConfig extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+        };
+    }
+
+
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+            </div>
+        )
+    }
+}
+
+export default AliConfig;

+ 22 - 0
src/devApp/developVersion/common/deploy/amazon/AmazonConfig.js

@@ -0,0 +1,22 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+class AmazonConfig extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+        };
+    }
+
+
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+            </div>
+        )
+    }
+}
+
+export default AmazonConfig;

+ 22 - 0
src/devApp/developVersion/common/deploy/huawei/HuaweiConfig.jsx

@@ -0,0 +1,22 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+class HuaweiConfig extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+        };
+    }
+
+
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+            </div>
+        )
+    }
+}
+
+export default HuaweiConfig;

+ 69 - 0
src/devApp/developVersion/common/deploy/index.css

@@ -0,0 +1,69 @@
+.current {
+    background-color: #0F83E8;
+    color: white;
+    text-align: center;
+    height: 50px;
+    line-height: 50px;
+    margin-top: 3px;
+}
+
+.title {
+    font-weight: bold;
+    font-size: 16px;
+    margin: 10px 0 0 5px;
+}
+
+p.show {
+    margin: 5px 0 3px 10px;
+    cursor: pointer;
+}
+
+.table-title {
+    font-weight: bold;
+    font-size: large;
+    display: block;
+    height: 35px;
+}
+
+.table-title::before {
+    content: '|';
+    display: inline-block;
+    font-weight: 900;
+    color: #0B7FC7;
+    /*border-right: */
+}
+
+.vice-title {
+    width: 150px;
+    display: inline-block;
+    font-size: 14px;
+}
+
+.cloud-name {
+    font-weight: bold;
+    font-size: medium;
+    display: block;
+    height: 35px;
+}
+
+.deploy {
+    width: 50px;
+    height: 30px;
+    background-color: green;
+    color: #ffffff;
+    font-weight: bold;
+    padding-top: 500px;
+}
+
+.kind {
+
+}
+
+.item {
+
+}
+
+.item-title {
+    display: inline-block;
+    width: 180px;
+}

+ 490 - 0
src/devApp/developVersion/common/deploy/tencent/TencentConfig.js

@@ -0,0 +1,490 @@
+import React, {Component} from 'react';
+import {Row, Col, Card, Button, Spin, Alert, Steps, Progress} from 'antd';
+import axios from 'axios';
+import APIGroupCardFetch from './apiGroupCard/APIGroupCardFetch';
+import APIPathCardFetch from './apiPathCard/APIPathCardFetch';
+import DeployCardFetch from './deployCard/DeployCardFetch';
+import NotificationCardFetch from './notificationCard/NotificationCardFetch';
+
+import {GET_PROJECT, SHOW_APIGWPATH, UPDATE_PROJECT_ONLY_STATUS} from "../../../../../gql";
+import {deployUrl, graphqlUrl} from "../../../../../config";
+import {FormattedMessage} from 'react-intl';
+import {request} from 'graphql-request'
+
+const Step = Steps.Step;
+
+class TencentConfig extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            region: '',
+            couldDeploy: false,
+            deployIdPassToPath: '',
+            groupIdPassToPath: '',
+            pathIdPassToConfig: '',
+            reachStep: '',
+            currentStep: '',
+            stepAllShow: false,
+            deploying: '',
+            stepUpdated: false,
+        };
+    }
+
+    componentWillMount() {
+        let projectID = this.props.projectID ? this.props.projectID : this.props.kind === 'graphql' ? 'ecommerce_projectID' : 'ecommerce_projectID_wx';
+
+        request(graphqlUrl, GET_PROJECT, {id: projectID}).then(
+            data => {
+                let dataProject = data.project_by_id;
+                if (dataProject !== null) {
+                    this.setState({
+                        schemaState: dataProject.schema_id ? dataProject.schema_id.schemaState : '',
+                        projectType: dataProject.projectType
+                    });
+                    switch (dataProject.projectStatus) {
+                        case 'deployed':
+                            this.setState({
+                                currentStep: 5,
+                                reachStep: 5,
+                                stepAllShow: true,
+                                deploying: 'deployed'
+                            });
+                            break;
+                        case 'notificationed':
+                            this.setState({
+                                currentStep: 4,
+                                reachStep: 4,
+                            });
+                            break;
+                        case 'pathed':
+                            this.setState({
+                                currentStep: 3,
+                                reachStep: 3,
+                            });
+                            break;
+                        case 'grouped':
+                            this.setState({
+                                currentStep: 2,
+                                reachStep: 2
+                            });
+                            break;
+                        case 'functioned':
+                            this.setState({
+                                currentStep: 1,
+                                reachStep: 1
+                            });
+                            break;
+                        case 'created':
+                            this.setState({
+                                currentStep: 0,
+                                reachStep: 0
+                            });
+                            break;
+                        case 'updated':
+                            this.setState({
+                                stepUpdated: true,
+                                stepAllShow: true,
+                                deploying: 'updated'
+                            });
+                            break;
+                        case 'deploying':
+                            this.setState({
+                                deploying: 'deploying'
+                            });
+                            break;
+                        default:
+                            this.setState({
+                                currentStep: 0,
+                                reachStep: 0
+                            });
+                            break;
+                    }
+                } else {
+                    console.log('project 没存 status');
+                }
+            }
+        )
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            couldDeploy: false,
+            region: '',
+            deployIdPassToPath: '',
+            groupIdPassToPath: '',
+            currentStep: '',
+            deploying: '',
+            stepAllShow: false,
+            stepUpdated: false
+        });
+
+        let projectID = next.projectID ? next.projectID : 'ecommerce_projectID';
+
+        request(graphqlUrl, GET_PROJECT, {id: projectID}).then(
+            data => {
+                let dataProject = data.project_by_id;
+                if (dataProject !== null) {
+                    this.setState({
+                        schemaState: dataProject.schema_id ? dataProject.schema_id.schemaState : '',
+                        projectType: dataProject.projectType
+                    });
+                    switch (dataProject.projectStatus) {
+                        case 'deployed':
+                            this.setState({
+                                deploying: 'deployed',
+                                currentStep: 5,
+                                reachStep: 5,
+                                stepAllShow: true
+                            });
+                            break;
+                        case 'notificationed':
+                            this.setState({
+                                currentStep: 4,
+                                reachStep: 4
+                            });
+                            break;
+                        case 'pathed':
+                            this.setState({
+                                currentStep: 3,
+                                reachStep: 3
+                            });
+                            break;
+                        case 'grouped':
+                            this.setState({
+                                currentStep: 2,
+                                reachStep: 2
+                            });
+                            break;
+                        case 'functioned':
+                            this.setState({
+                                currentStep: 1,
+                                reachStep: 1
+                            });
+                            break;
+                        case 'created':
+                            this.setState({
+                                currentStep: 0,
+                                reachStep: 0
+                            });
+                            break;
+                        case 'updated':
+                            this.setState({
+                                stepUpdated: true,
+                                stepAllShow: true,
+                                deploying: 'updated'
+                            });
+                            break;
+                        case 'deploying':
+                            this.setState({
+                                deploying: 'deploying'
+                            });
+                            break;
+                        default:
+                            this.setState({
+                                currentStep: 0,
+                                reachStep: 0
+                            });
+                            break;
+                    }
+                } else {
+                    console.log('project 没存 status');
+                }
+            }
+        )
+    }
+
+
+    switchRegion = (e) => {
+        this.setState({
+            region: e.target.value
+        });
+    };
+
+    deployFC = () => {
+        request(graphqlUrl, GET_PROJECT, {id: this.props.projectID}).then(
+            data => {
+                let dataProject = data.project_by_id;
+                if (dataProject !== null) {
+                    let schema = dataProject.schema_id.id;
+                    let deploy = dataProject.deploy_id.id;
+                    let group = dataProject.apiGWGroup_id.id;
+                    request(graphqlUrl, SHOW_APIGWPATH, {apiGWGroup_id: group}).then(
+                        data => {
+                            if (data.apiGWPath_by_props !== null) {
+
+                                // 给 project 加 deploying 状态
+                                request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                    id: this.props.projectID,
+                                    updatedAt: new Date().getTime(),
+                                    projectStatus: 'deploying'
+                                });
+
+                                // console.log(data);
+                                let api = data.apiGWPath_by_props[0].id;
+                                console.log('schema', schema);
+                                console.log('deploy', deploy);
+                                console.log('api', api);
+                                console.log('group', group);
+                                if (schema && deploy && api && group) {
+                                    console.log('deploying');
+                                    this.setState({
+                                        deploying: 'deploying'
+                                    });
+                                    axios.get(`${deployUrl}`,
+                                        {
+                                            params: {
+                                                'cloud-id': 'tencent_CloudID',
+                                                'cloud-name': 'tencent',
+                                                schema,
+                                                deploy,
+                                                api,
+                                                group
+                                            }
+                                        })
+                                        .then((res) => {
+                                            console.log('deploy res', res);
+                                            this.setState({
+                                                deploying: 'deployed'
+                                            });
+
+                                            // 给 project 加 deployed 状态
+                                            request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                                id: this.props.projectID,
+                                                updatedAt: new Date().getTime(),
+                                                projectStatus: 'deployed'
+                                            })
+
+                                        })
+                                        .catch((err) => {
+                                            this.setState({
+                                                deploying: 'error'
+                                            });
+
+                                            // 给 project 加 error 状态
+                                            request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                                id: this.props.projectID,
+                                                updatedAt: new Date().getTime(),
+                                                projectStatus: 'error'
+                                            });
+                                            console.log('err', err);
+                                            console.log('err.response', err.response);
+                                            console.log('err.response.data', err.response.data);
+                                        });
+                                }
+                            } else {
+                                console.log('path 没存');
+                            }
+                        }
+                    );
+                } else {
+                    console.log('project 没存 status');
+                }
+            }
+        );
+    };
+
+    stepByStep = (bool) => {
+        return (stepNum) => {
+            this.setState({
+                currentStep: stepNum,
+                reachStep: bool ? stepNum : this.state.reachStep
+            })
+        };
+    };
+
+    stepStatus = (value) => {
+        if (this.state.reachStep === value)
+            return '进行中';
+        else if (this.state.reachStep > value)
+            return '完成';
+        else
+            return '等待';
+    };
+
+    render() {
+        let projectID = this.props.projectID ? this.props.projectID : 'ecommerce_projectID';
+        return (
+            <div>
+                {
+                    this.state.projectType === 'graphql' ?
+                        this.state.schemaState === 'ok' ?
+                            ''
+                            :
+                            this.props.trialcase ?
+                                ''
+                                :
+                                <div style={{marginBottom: 10}}>
+                                    <Alert message="数据表结构不符合规范,暂不能部署,请修改" type="warning"
+                                           banner closable/>
+                                </div>
+                        :
+                        ''
+                }
+                <div style={{padding: '30px'}}>
+                    <Row gutter={16}>
+                        <Col span={14}>
+
+                            {
+                                this.state.currentStep !== '' || this.state.deploying === 'deploying' || this.state.deploying === 'deployed' ?
+                                    <Steps current={this.state.deploying !== 'deploying' ? this.state.currentStep : 4}
+                                           style={{marginBottom: 30}}>
+                                        <Step onClick={() => {
+                                            this.stepByStep(false)(0)
+                                        }} title={this.stepStatus(0)} description="云函数配置"/>
+                                        <Step onClick={() => {
+                                            if (this.state.reachStep > 0)
+                                                this.stepByStep(false)(1);
+                                        }} title={this.stepStatus(1)} description="服务配置"/>
+                                        <Step onClick={() => {
+                                            if (this.state.reachStep > 1)
+                                                this.stepByStep(false)(2)
+                                        }} title={this.stepStatus(2)} description="API 配置"/>
+                                        <Step onClick={() => {
+                                            if (this.state.reachStep > 2)
+                                                this.stepByStep(false)(3)
+                                        }} title={this.stepStatus(3)} description="通知配置"/>
+                                    </Steps>
+                                    :
+                                    ''
+                            }
+
+                            {
+                                this.state.currentStep === 0 || this.state.stepAllShow ?
+                                    <FormattedMessage id="fc Deploy">
+                                        {
+                                            msg =>
+                                                <Card title={msg} style={{marginBottom: 10}}>
+                                                    <DeployCardFetch
+                                                        switchRegion={this.switchRegion}
+                                                        region={this.state.region}
+                                                        trialcase={this.props.trialcase}
+                                                        stepByStep={this.stepByStep(true)}
+                                                        projectID={projectID}
+                                                    />
+                                                </Card>
+                                        }
+                                    </FormattedMessage>
+                                    :
+                                    ''
+                            }
+
+                            {
+                                this.state.currentStep === 1 || this.state.stepAllShow ?
+                                    <FormattedMessage id="API Group">
+                                        {
+                                            msg =>
+                                                <Card title={msg} style={{marginBottom: 10}}>
+                                                    <APIGroupCardFetch
+                                                        switchRegion={this.switchRegion}
+                                                        region={this.state.region}
+                                                        trialcase={this.props.trialcase}
+                                                        stepByStep={this.stepByStep(true)}
+                                                        userID={this.props.userID}
+                                                        projectID={projectID}
+                                                        stepAllShow={this.state.stepAllShow}
+                                                    />
+                                                </Card>
+                                        }
+                                    </FormattedMessage>
+                                    :
+                                    ''
+                            }
+                            {
+                                this.state.currentStep === 2 || this.state.stepAllShow ?
+                                    <FormattedMessage id="API Path">
+                                        {
+                                            msg =>
+                                                <Card title={msg} style={{marginBottom: 10}}>
+                                                    <APIPathCardFetch
+                                                        trialcase={this.props.trialcase}
+                                                        stepByStep={this.stepByStep(true)}
+                                                        userID={this.props.userID}
+                                                        projectID={projectID}
+                                                        stepAllShow={this.state.stepAllShow}
+                                                    />
+                                                </Card>
+                                        }
+                                    </FormattedMessage>
+                                    :
+                                    ''
+                            }
+
+                            {
+                                this.state.currentStep === 3 || this.state.stepAllShow ?
+                                    <FormattedMessage id="Notification">
+                                        {
+                                            msg =>
+                                                <Card title={msg}>
+                                                    <NotificationCardFetch
+                                                        userID={this.props.userID}
+                                                        trialcase={this.props.trialcase}
+                                                        stepByStep={this.stepByStep(true)}
+                                                        projectID={projectID}
+                                                        stepAllShow={this.state.stepAllShow}
+                                                    />
+                                                </Card>
+                                        }
+                                    </FormattedMessage>
+                                    :
+                                    ''
+                            }
+
+                        </Col>
+                        <Col span={8} offset={2}>
+                            {
+                                this.state.currentStep === 4 || this.state.stepAllShow ?
+                                    this.props.trialcase ?
+                                        ''
+                                        :
+                                        this.state.projectType === 'graphql' ?
+
+                                            this.state.schemaState === 'ok' ?
+
+                                                this.state.deploying === '' ?
+                                                    <Button type='primary' onClick={this.deployFC}><FormattedMessage
+                                                        id="deploy"/>!</Button>
+                                                    :
+                                                    ''
+                                                :
+                                                <Button type='primary'
+                                                        disabled><FormattedMessage
+                                                    id="deploy"/>!</Button>
+                                            :
+                                            <Button type='primary' onClick={() => {
+                                            }}><FormattedMessage id="deploy"/>!</Button>
+                                    :
+                                    ''
+                            }
+
+                            {
+                                this.props.trialcase ?
+                                    <Progress type="circle" percent={100}/>
+                                    :
+                                    this.state.deploying === 'deploying' ?
+                                        <div>
+                                            <Spin size="large"/>
+                                            &nbsp;&nbsp;
+                                            <span><b>大约需要等待 30s</b></span>
+                                        </div>
+                                        :
+                                        this.state.deploying === 'deployed' ?
+                                            <Progress type="circle" percent={100}/>
+                                            :
+                                            this.state.deploying === 'error' ?
+                                                <Progress type="circle" percent={99} status="exception"/>
+                                                :
+                                                this.state.deploying === 'updated' ?
+                                                    <Button type='primary' onClick={this.deployFC}><FormattedMessage
+                                                        id="redeploy"/>!</Button>
+                                                    :
+                                                    ''
+                            }
+                        </Col>
+                    </Row>
+                </div>
+            </div>
+        );
+    }
+}
+
+export default TencentConfig;

+ 70 - 0
src/devApp/developVersion/common/deploy/tencent/apiGroupCard/APIGroupCardFetch.js

@@ -0,0 +1,70 @@
+import React, {Component} from 'react';
+import {Spin} from 'antd';
+import {GET_PROJECT} from "../../../../../../gql";
+import {Query} from "react-apollo";
+import gql from "graphql-tag";
+import APIGroupCardRender from "./APIGroupCardRender";
+import {removeSpace} from "../../../../../../func";
+
+class APIGroupCardFetch extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+
+        }
+    }
+
+    render() {
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: this.props.projectID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+
+                        let group = '',
+                            cloudID = 'tencent_CloudID';
+
+                        let dataProject = data.project_by_id;
+                        let {cloud_id, apiGWGroup_id, projectName, projectStatus} = dataProject;
+
+                        if (cloud_id !== null && cloud_id.cloudName === 'tencent') {
+                            group = apiGWGroup_id;
+                            cloudID = cloud_id.id;
+                        }
+
+                        return (
+                            <APIGroupCardRender
+                                // props
+                                switchRegion={this.props.switchRegion}
+                                region={this.props.region}
+                                trialcase={this.props.trialcase}
+                                stepByStep={this.props.stepByStep}
+                                userID={this.props.userID}
+                                projectID={this.props.projectID}
+                                stepAllShow={this.props.stepAllShow}
+
+                                // this
+                                switchConfig={this.switchConfig}
+                                ok={this.ok}
+
+                                // query
+                                defalutName={removeSpace(projectName)}
+                                group={group}
+                                cloudID={cloudID}
+                                projectStatus={projectStatus}
+                            />
+                        )
+                    }
+                }
+            </Query>
+
+        )
+    }
+}
+
+export default APIGroupCardFetch;

+ 396 - 0
src/devApp/developVersion/common/deploy/tencent/apiGroupCard/APIGroupCardRender.js

@@ -0,0 +1,396 @@
+import React, {Component} from 'react';
+import {Input, Collapse, Button, Radio, Icon, Tooltip} from 'antd';
+import {idGen} from "../../../../../../func";
+import {graphqlUrl, manageUsers} from "../../../../../../config";
+import {FormattedMessage} from 'react-intl';
+import {ADD_APIGROUP, UPDATE_APIGROUP, UPDATE_PROJECT_GROUP, UPDATE_PROJECT_ONLY_STATUS} from "../../../../../../gql";
+import {request} from 'graphql-request'
+import {removePrefix, shiftPrefix} from "../../../../../../func";
+
+const Panel = Collapse.Panel;
+
+// 如需改变显示,请在此处 value 处更改
+// 如需添加中文,请在此处 value 处添加
+// eg: 'xxxx': <F..  id='xxx'/>
+const valueToKey = {
+    'groupName': 'groupName',
+    'environmentName': 'environmentName',
+    'defaultDomain': 'defaultDomain',
+    'frontType': 'frontType',
+    'userDomain': 'userDomain',
+    'userStatus': 'userStatus',
+    'region': 'region',
+};
+
+const toolTipTitle = {
+    'groupName': 'its groupName',
+    'environmentName': 'its environmentName',
+    'defaultDomain': 'its defaultDomain',
+    'frontType': 'its frontType',
+    'userDomain': 'its userDomain',
+    'userStatus': 'its userStatus',
+    'region': 'its region',
+};
+
+const youMustFill = {
+    'groupName': true,
+    'environmentName': true,
+    'defaultDomain': true,
+    'frontType': true,
+    'userDomain': false,
+    'userStatus': true,
+    'region': true,
+};
+
+class APIGroupCardRender extends Component {
+    constructor(props) {
+        super(props);
+        if (props.group !== '' && props.group !== null) {
+            let {groupName, environmentName, defaultDomain, frontType, userDomain, userStatus, region} = props.group;
+            this.state = {
+                showOK: false,
+                groupName,
+                environmentName,
+                defaultDomain,
+                frontType,
+                userDomain,
+                userStatus,
+                groupRegion: region
+            }
+        } else {
+            this.state = {
+                showOK: false,
+                groupName: props.defalutName,
+                environmentName: 'test',
+                defaultDomain: '',
+                frontType: 'http&https',
+                userDomain: '',
+                userStatus: 'open',
+                groupRegion: props.region === '' ? 'ap-beijing' : props.region,
+            };
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        if (next.group !== '' && next.group !== null) {
+            let {groupName, environmentName, defaultDomain, frontType, userDomain, userStatus, region} = next.group;
+            this.setState({
+                groupName,
+                environmentName,
+                defaultDomain,
+                frontType,
+                userDomain,
+                userStatus,
+                groupRegion: next.region === '' ? region : next.region
+            })
+        } else {
+            this.setState({
+                groupName: next.defalutName,
+                environmentName: 'test',
+                defaultDomain: '',
+                frontType: 'http&https',
+                userDomain: '',
+                userStatus: 'open',
+                groupRegion: next.region === '' ? 'ap-beijing' : next.region
+            });
+        }
+    };
+
+    switchConfig = (label) => {
+        return (e) => {
+            this.setState({
+                [label]: e.target.value
+            })
+        };
+    };
+
+    ok = (id, userID, projectID, cloudID, group, projectStatus) => {
+        let {userStatus, userDomain, groupName, groupRegion, frontType, environmentName} = this.state;
+        let region = shiftPrefix('ap-', groupRegion);
+        let varObj = {
+            id,
+            cloud_id: cloudID,
+            user_id: userID,
+            userStatus,
+            userDomain,
+            groupName,
+            frontType,
+            region,
+            environmentName,
+            defaultDomain: '',
+            status: '',
+            serviceId: '',
+            createdAt: new Date().getTime(),
+            updatedAt: ''
+        };
+        if (group === '' || group === null) {
+            request(graphqlUrl, ADD_APIGROUP, varObj).then(
+                data => {
+                    if (data.create_apiGWGroup !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        // 写回 project
+                        request(graphqlUrl, UPDATE_PROJECT_GROUP, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            apiGWGroup_id: id,
+                            projectStatus: 'grouped'
+                        }).then(data => {
+                            if (data.update_project !== null)
+                                this.props.stepByStep(2);
+                        });
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        } else {
+            let {userStatus, userDomain, groupName, groupRegion, frontType, environmentName} = this.state;
+            let region = shiftPrefix('ap-', groupRegion);
+            let varObj = {
+                id: group.id,
+                userStatus,
+                userDomain,
+                groupName,
+                frontType,
+                region,
+                environmentName,
+                updatedAt: new Date().getTime()
+            };
+            request(graphqlUrl, UPDATE_APIGROUP, varObj).then(
+                data => {
+                    if (data.update_apiGWGroup !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            projectStatus: projectStatus === 'deployed' ? 'updated': 'grouped'
+                        }).then(data => {
+                            if(data.update_project !== null)
+                                this.props.stepByStep(2);
+                        })
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        }
+    };
+
+    render() {
+        const customPanelStyle = {
+            background: '#f7f7f7',
+            borderRadius: 4,
+            marginBottom: 24,
+            border: 0,
+            overflow: 'hidden',
+        };
+
+        return (
+            <div>
+                <div style={{marginBottom: 10}}>
+                    <span className='vice-title'>
+                        {
+                            youMustFill['groupName'] ?
+                                <span
+                                    style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                :
+                                ''
+                        }
+                        <FormattedMessage id={valueToKey['groupName']}/>
+                        &nbsp;
+                        <Tooltip placement="top" title={toolTipTitle['groupName']}>
+                            <Icon type="question-circle"/>
+                        </Tooltip>
+                    </span>
+                    <Input value={this.state.groupName} style={{width: 400}}
+                           onChange={this.switchConfig('groupName')}/>
+                </div>
+
+                <div style={{marginBottom: 10}}>
+                    <span className='vice-title'>
+                        {
+                            youMustFill['region'] ?
+                                <span
+                                    style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                :
+                                ''
+                        }
+                        <FormattedMessage id={valueToKey['region']}/>
+                        &nbsp;
+                        <Tooltip placement="top" title={toolTipTitle['region']}>
+                            <Icon type="question-circle"/>
+                        </Tooltip>
+                    </span>
+                    <Radio.Group onChange={(e) => {
+                        // 如果不想一变所有都变,就将 onchange 改成
+                        // onChange={this.switchConfig('groupRegion')}
+                        this.props.switchRegion(e);
+                    }} value={removePrefix('ap-', this.state.groupRegion)} buttonStyle="solid">
+                        <Radio.Button value="guangzhou"><FormattedMessage id="Guangzhou"/></Radio.Button>
+                        <Radio.Button value="shanghai"><FormattedMessage id="Shanghai"/></Radio.Button>
+                        <Radio.Button value="beijing"><FormattedMessage id="Beijing"/></Radio.Button>
+                        <Radio.Button value="chengdu"><FormattedMessage id="Chengdu"/></Radio.Button>
+                    </Radio.Group>
+                </div>
+
+                <div style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill['frontType'] ?
+                                        <span style={{
+                                            color: 'red',
+                                            display: 'inline',
+                                            marginRight: 10
+                                        }}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey['frontType']}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle['frontType']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                    <Radio.Group onChange={this.switchConfig('frontType')}
+                                 defaultValue={this.state.frontType}
+                                 buttonStyle="solid">
+                        <Radio.Button value="http">http</Radio.Button>
+                        <Radio.Button value="https">https</Radio.Button>
+                        <Radio.Button value="http&https">http&https</Radio.Button>
+                    </Radio.Group>
+                </div>
+
+                <div style={{marginBottom: 10}}>
+                    <span className='vice-title'>
+                        {
+                            youMustFill['environmentName'] ?
+                                <span
+                                    style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                :
+                                ''
+                        }
+                        <FormattedMessage id={valueToKey['environmentName']}/>
+                        &nbsp;
+                        <Tooltip placement="top" title={toolTipTitle['environmentName']}>
+                            <Icon type="question-circle"/>
+                        </Tooltip>
+                    </span>
+                    <Radio.Group onChange={this.switchConfig('environmentName')}
+                                 defaultValue={this.state.environmentName}
+                                 buttonStyle="solid">
+                        <Radio.Button value="test"><FormattedMessage id="test"/></Radio.Button>
+                        <Radio.Button value="prepub"><FormattedMessage id="prepub"/></Radio.Button>
+                        <Radio.Button value="release"><FormattedMessage id="release"/></Radio.Button>
+                    </Radio.Group>
+                </div>
+                <Collapse bordered={false}>
+                    <Panel header={<FormattedMessage id="Want more options?"/>}
+                           style={customPanelStyle}>
+                        {/*<div style={{marginBottom: 10}}>*/}
+                        {/*<span className='vice-title'>{valueToKey['defaultDomain']}*/}
+                        {/*&nbsp;*/}
+                        {/*<Tooltip placement="top" title={toolTipTitle['defaultDomain']}>*/}
+                        {/*<Icon type="question-circle"/>*/}
+                        {/*</Tooltip>*/}
+                        {/*</span>*/}
+                        {/*<Input value={this.state.defaultDomain} style={{width: 400}} disabled/>*/}
+                        {/*</div>*/}
+
+                        <div style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill['userDomain'] ?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey['userDomain']}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle['userDomain']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input value={this.state.userDomain} style={{width: 400}}
+                                   onChange={this.switchConfig('userDomain')}/>
+                        </div>
+                        {/*<div style={{marginBottom: 10}}>*/}
+                        {/*<span className='vice-title'>{valueToKey['userStatus']}*/}
+                        {/*&nbsp;*/}
+                        {/*<Tooltip placement="top" title={toolTipTitle['userStatus']}>*/}
+                        {/*<Icon type="question-circle"/>*/}
+                        {/*</Tooltip>*/}
+                        {/*</span>*/}
+                        {/*<Radio.Group onChange={this.switchConfig('userStatus')} defaultValue={this.state.userStatus}*/}
+                        {/*buttonStyle="solid">*/}
+                        {/*<Radio.Button value="open">open</Radio.Button>*/}
+                        {/*<Radio.Button value="close">close</Radio.Button>*/}
+                        {/*</Radio.Group>*/}
+                        {/*</div>*/}
+                    </Panel>
+                </Collapse>
+                {
+                    manageUsers.includes(this.props.userID) ?
+                        <div>
+                            <div>
+                                {
+                                    this.props.stepAllShow ? '' :
+                                        <Button onClick={() => {
+                                            this.props.stepByStep(0)
+                                        }}><FormattedMessage id="previous-step"/></Button>
+
+                                }
+                                <Button onClick={() => {
+                                    const id = idGen('group');
+                                    this.ok(id, this.props.userID, this.props.projectID, this.props.cloudID, this.props.group, this.props.projectStatus);
+                                }} type='primary'><FormattedMessage id="save"/></Button>
+                            </div>
+                            {
+                                this.state.showOK === true ?
+                                    <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/>
+                                    :
+                                    ''
+                            }
+                        </div>
+                        :
+                        this.props.trialcase ?
+                            ''
+                            :
+                            <div>
+                                <div>
+                                    {
+                                        this.props.stepAllShow ? '' :
+                                            <Button onClick={() => {
+                                                this.props.stepByStep(0)
+                                            }}><FormattedMessage id="previous-step"/></Button>
+
+                                    }
+                                    <Button onClick={() => {
+                                        const id = idGen('group');
+                                        this.ok(id, this.props.userID, this.props.projectID, this.props.cloudID, this.props.group, this.props.projectStatus);
+                                    }} type='primary'><FormattedMessage id="save"/></Button>
+                                </div>
+                                {
+                                    this.state.showOK === true ?
+                                        <Icon type="check-circle" theme="twoTone"
+                                              twoToneColor="#52c41a"/>
+                                        :
+                                        ''
+                                }
+                            </div>
+                }
+            </div>
+        )
+    }
+}
+
+export default APIGroupCardRender;

+ 85 - 0
src/devApp/developVersion/common/deploy/tencent/apiPathCard/APIPathCardFetch.js

@@ -0,0 +1,85 @@
+import React, {Component} from 'react';
+import {Collapse, Spin} from 'antd';
+import {GET_PROJECT, SHOW_APIGWPATH} from "../../../../../../gql";
+import gql from "graphql-tag";
+import {Query} from "react-apollo";
+import APIPathCardRender from "./APIPathCardRender";
+import {removeSpace} from "../../../../../../func";
+
+const Panel = Collapse.Panel;
+
+class APIPathCardFetch extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {}
+    }
+
+    render() {
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: this.props.projectID}} fetchPolicy={'network-only'}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+
+                        let group = '',
+                            deploy = '',
+                            path = '';
+
+                        let dataProject = data.project_by_id;
+                        let {cloud_id, apiGWGroup_id, deploy_id, projectName, projectStatus} = dataProject;
+
+                        if (cloud_id !== null && cloud_id.cloudName === 'tencent') {
+                            group = apiGWGroup_id;
+                            deploy = deploy_id;
+                        }
+
+                        return (
+                            <Query query={gql(SHOW_APIGWPATH)} variables={{apiGWGroup_id: group? group.id : ''}}>
+                                {
+                                    ({loading, error, data}) => {
+                                        if (loading) {
+                                            return <Spin style={{marginLeft: 3}}/>
+                                        }
+                                        if (error) {
+                                            return 'error!';
+                                        }
+
+                                        if (data.apiGWPath_by_props.length > 0)
+                                            path = data.apiGWPath_by_props[0];
+
+                                        return (
+                                            <APIPathCardRender
+                                                // props
+                                                trialcase={this.props.trialcase}
+                                                stepByStep={this.props.stepByStep}
+                                                userID={this.props.userID}
+                                                projectID={this.props.projectID}
+                                                stepAllShow={this.props.stepAllShow}
+
+                                                // query1
+                                                deployID={deploy.id}
+                                                groupID={group? group.id : ''}
+
+                                                // query2
+                                                defalutName={removeSpace(projectName)}
+                                                path={path}
+                                                projectStatus={projectStatus}
+                                            />
+                                        )
+                                    }
+                                }
+                            </Query>
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+export default APIPathCardFetch;

+ 287 - 0
src/devApp/developVersion/common/deploy/tencent/apiPathCard/APIPathCardRender.js

@@ -0,0 +1,287 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Input, Radio, Collapse, Button, Icon, Tooltip} from 'antd';
+import {idGen} from "../../../../../../func";
+import {graphqlUrl, manageUsers} from "../../../../../../config";
+import {ADD_APIGWPATH, UPDATE_APIGWPATH, UPDATE_PROJECT_ONLY_STATUS} from "../../../../../../gql";
+import {request} from 'graphql-request'
+
+const Panel = Collapse.Panel;
+
+// 如需改变显示,请在此处 value 处更改
+// 如需添加中文,请在此处 value 处添加
+// eg: 'xxxx': <F..  id='xxx'/>
+const valueToKey = {
+    'apiGWName': 'apiGWName',
+    'apiGWDesc': 'apiGWDesc',
+    'requestMethod': 'requestMethod'
+};
+
+const toolTipTitle = {
+    'apiGWName': 'its apiGWName',
+    'apiGWDesc': 'its apiGWDesc',
+    'requestMethod': 'its requestMethod'
+};
+
+const youMustFill = {
+    'apiGWName': true,
+    'apiGWDesc': false,
+    'requestMethod': true
+};
+
+class APIPathCardRender extends Component {
+    constructor(props) {
+        super(props);
+        if(props.path !== '' && props.path !== null) {
+            let {apiGWName, apiGWDesc, requestMethod} = props.path;
+            this.state = {
+                configs: ['apiGWDesc'],
+                showOK: false,
+                apiGWName,
+                apiGWDesc,
+                requestMethod: requestMethod
+            }
+        } else {
+            this.state = {
+                configs: ['apiGWDesc'],
+                showOK: false,
+                apiGWName: props.defalutName,
+                apiGWDesc: '',
+                requestMethod: 'GET',
+            };
+        }
+    }
+
+
+    componentWillReceiveProps(next) {
+        if (next.path !== '' && next.path !== null) {
+            let {apiGWName, apiGWDesc, requestMethod} = next.path;
+            this.setState({
+                apiGWName,
+                apiGWDesc,
+                requestMethod
+            })
+        } else {
+            this.setState({
+                apiGWName: next.defalutName,
+                apiGWDesc: '',
+                requestMethod: 'GET',
+            });
+        }
+    };
+
+    switchConfig = (label) => {
+        return (e) => {
+            this.setState({
+                [label]: e.target.value
+            })
+        };
+    };
+
+    ok = (id, userID, projectID, groupID, deployID, path, projectStatus) => {
+        let {apiGWName, apiGWDesc, requestMethod} = this.state;
+        let varObj = {
+            id,
+            user_id: userID,
+            apiGWGroup_id: groupID,
+            deploy_id: deployID,
+            apiGWName,
+            apiGWDesc,
+            requestMethod,
+            serviceType: 'SCF',
+            apiGWPath: '/graphql',
+            timeout: 300,
+            apiId: '',
+            createdAt: new Date().getTime(),
+            updatedAt: ''
+        };
+        if (path === '' || path === null) {
+            if (deployID !== '' && groupID !== '' && deployID !== null && groupID !== null) {
+                request(graphqlUrl, ADD_APIGWPATH, varObj).then(
+                    data => {
+                        if (data.create_apiGWPath !== null) {
+                            this.setState({
+                                showOK: true
+                            });
+                            // 写回 project
+                            request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                id: projectID,
+                                updatedAt: new Date().getTime(),
+                                projectStatus: 'pathed'
+                            }).then(data => {
+                                if(data.update_project !== null)
+                                    this.props.stepByStep(3);
+                            })
+
+                        }
+                        setTimeout(() => {
+                            this.setState({
+                                showOK: false
+                            })
+                        }, 1500)
+                    }
+                )
+            }
+        } else {
+            let {apiGWName, apiGWDesc, requestMethod} = this.state;
+            let varObj = {
+                id: path.id,
+                apiGWName,
+                apiGWDesc,
+                requestMethod,
+                updatedAt: new Date().getTime()
+            };
+            request(graphqlUrl, UPDATE_APIGWPATH, varObj).then(
+                data => {
+                    if (data.update_apiGWPath !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            projectStatus: projectStatus === 'deployed' ? 'updated': 'pathed'
+                        }).then(data => {
+                            if(data.update_project !== null)
+                                this.props.stepByStep(3);
+                        })
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        }
+    };
+
+    render() {
+        const customPanelStyle = {
+            background: '#f7f7f7',
+            borderRadius: 4,
+            marginBottom: 24,
+            border: 0,
+            overflow: 'hidden',
+        };
+
+        return (
+            <div>
+                <div key={'apiGWName'} style={{marginBottom: 10}}>
+                    <span className='vice-title'>
+                        {
+                            youMustFill['apiGWName']?
+                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                :
+                                ''
+                        }
+                        <FormattedMessage id={valueToKey['apiGWName']}/>
+                        &nbsp;
+                        <Tooltip placement="top" title={toolTipTitle['apiGWName']}>
+                            <Icon type="question-circle"/>
+                        </Tooltip>
+                    </span>
+                    <Input value={this.state.apiGWName} style={{width: 400}}
+                           onChange={this.switchConfig('apiGWName')}/>
+                </div>
+                <div style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill['requestMethod']?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey['requestMethod']}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle['requestMethod']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                    <Radio.Group onChange={this.switchConfig('requestMethod')}
+                                 defaultValue={this.state.requestMethod}
+                                 buttonStyle="solid">
+                        <Radio.Button value="GET">get</Radio.Button>
+                        <Radio.Button value="POST">post</Radio.Button>
+                    </Radio.Group>
+                </div>
+                <Collapse bordered={false}>
+                    <Panel header={<FormattedMessage id="Want more options?"/>} style={customPanelStyle}>
+                        {
+                            this.state.configs.map(config => (
+                                <div key={config} style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                        {
+                                            youMustFill[config]?
+                                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                                :
+                                                ''
+                                        }
+                                        <FormattedMessage id={valueToKey[config]}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle[config]}>
+                                            <Icon type="question-circle"/>
+                                        </Tooltip>
+                                    </span>
+                                    <Input value={this.state[config]} style={{width: 400}}
+                                           onChange={this.switchConfig(config)}/>
+                                </div>
+                            ))
+                        }
+
+                    </Panel>
+                </Collapse>
+                {
+                    manageUsers.includes(this.props.userID) ?
+                        <div>
+                            <div>
+                                {
+                                    this.props.stepAllShow? '' :
+                                        <Button onClick={()=>{
+                                            this.props.stepByStep(1)
+                                        }}><FormattedMessage id="previous-step"/></Button>
+                                }
+                                <Button onClick={()=>{
+                                    const id = idGen('path');
+                                    this.ok(id, this.props.userID, this.props.projectID, this.props.groupID, this.props.deployID, this.props.path, this.props.projectStatus);
+                                }} type='primary'><FormattedMessage id="save"/></Button>
+                            </div>
+                            {
+                                this.state.showOK === true?
+                                    <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/>
+                                    :
+                                    ''
+                            }
+                        </div>
+                        :
+                        this.props.trialcase?
+                            ''
+                            :
+                            <div>
+                                <div>
+                                    {
+                                        this.props.stepAllShow? '' :
+                                            <Button onClick={()=>{
+                                                this.props.stepByStep(1)
+                                            }}><FormattedMessage id="previous-step"/></Button>
+                                    }
+                                    <Button onClick={()=>{
+                                        const id = idGen('path');
+                                        this.ok(id, this.props.userID, this.props.projectID, this.props.groupID, this.props.deployID, this.props.path, this.props.projectStatus);
+                                    }} type='primary'><FormattedMessage id="save"/></Button>
+                                </div>
+                                {
+                                    this.state.showOK === true?
+                                        <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/>
+                                        :
+                                        ''
+                                }
+                            </div>
+                }
+
+            </div>
+        )
+    }
+}
+
+export default APIPathCardRender;

+ 75 - 0
src/devApp/developVersion/common/deploy/tencent/deployCard/DeployCardFetch.js

@@ -0,0 +1,75 @@
+import React, {Component} from 'react';
+import {Spin} from 'antd';
+import {GET_PROJECT} from "../../../../../../gql";
+import {Query} from "react-apollo";
+import gql from "graphql-tag";
+import DeployCardRender from "./DeployCardRender";
+import {removeSpace} from "../../../../../../func";
+
+const removePrefix = (prefix, value) => {
+    let r = new RegExp(prefix);
+    return value.replace(r, '');
+};
+
+const shiftPrefix = (prefix, value) => {
+    value = removePrefix(prefix, value);
+    return prefix + value;
+};
+
+class DeployCardFetch extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+
+        }
+    }
+
+    render() {
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: this.props.projectID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+
+                        let deploy = '',
+                            cloudID = 'tencent_CloudID';
+
+                        let dataProject = data.project_by_id;
+                        let {cloud_id, deploy_id, projectName, projectStatus} = dataProject;
+
+                        if (cloud_id !== null && cloud_id.cloudName === 'tencent') {
+                            deploy = deploy_id;
+                            cloudID = cloud_id.id;
+                        }
+
+                        return (
+                            <DeployCardRender
+                                // props
+                                switchRegion={this.props.switchRegion}
+                                region={this.props.region}
+                                trialcase={this.props.trialcase}
+                                stepByStep={this.props.stepByStep}
+                                userID={this.props.userID}
+                                projectID={this.props.projectID}
+
+                                // query
+                                defalutName={removeSpace(projectName)}
+                                deploy={deploy}
+                                cloudID={cloudID}
+                                projectStatus={projectStatus}
+                            />
+                        )
+                    }
+                }
+            </Query>
+
+        )
+    }
+}
+
+export default DeployCardFetch;

+ 412 - 0
src/devApp/developVersion/common/deploy/tencent/deployCard/DeployCardRender.js

@@ -0,0 +1,412 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Input, Radio, Collapse, Button, Icon, Tooltip} from 'antd';
+import {idGen} from "../../../../../../func";
+import {graphqlUrl, manageUsers} from "../../../../../../config";
+import {
+    ADD_DEPLOY,
+    UPDATE_DEPLOY,
+    UPDATE_PROJECT_DEPLOY_AND_CLOUD,
+    UPDATE_PROJECT_ONLY_STATUS
+} from "../../../../../../gql";
+import {request} from 'graphql-request'
+import {removePrefix, shiftPrefix, pushPostfix} from "../../../../../../func";
+
+const Panel = Collapse.Panel;
+
+// 如需改变显示,请在此处 value 处更改
+// 如需添加中文,请在此处 value 处添加
+// eg: 'xxxx': <F..  id='xxx'/>
+const valueToKey = {
+    'functionName': 'functionName',
+    'cosBucketName': 'cosBucketName',
+    'cosObjectName': 'cosObjectName',
+    'cosBucketRegion': 'cosBucketRegion',
+    'serviceName': 'serviceName',
+    'subnetId': 'subnetId',
+    'vpcId': 'vpcId',
+    'region': 'region',
+    'description': 'description',
+};
+
+const toolTipTitle = {
+    'functionName': 'its funcname',
+    'cosBucketName': 'its cosBucketName',
+    'cosObjectName': 'its cosObjectName',
+    'cosBucketRegion': 'its cosBucketRegion',
+    'serviceName': 'its serviceName',
+    'subnetId': 'its subnetId',
+    'vpcId': 'its vpcId',
+    'region': 'its region',
+    'description': 'its description',
+};
+
+const youMustFill = {
+    'functionName': true,
+    'cosBucketName': true,
+    'cosObjectName': true,
+    'cosBucketRegion': true,
+    'serviceName': true,
+    'subnetId': false,
+    'vpcId': false,
+    'region': true,
+    'description': false,
+};
+
+class DeployCardRender extends Component {
+    constructor(props) {
+        super(props);
+        if(props.deploy !== '' && props.deploy !== null) {
+            let {functionName, cosBucketName, cosObjectName, cosBucketRegion, serviceName, vpcId, subnetId, region, description} = props.deploy;
+            this.state = {
+                configs: ['description', 'vpcId', 'subnetId'],
+                description,
+                showOK: false,
+                functionName,
+                region,
+                cosBucketName,
+                cosObjectName: removePrefix('.jar', cosObjectName),
+                cosBucketRegion,
+                serviceName,
+                vpcId,
+                subnetId
+            }
+        } else {
+            this.state = {
+                configs: ['description', 'vpcId', 'subnetId'],
+                description: '',
+                showOK: false,
+                functionName: props.defalutName,
+                region: props.region === '' ? 'ap-beijing' : props.region,
+                cosBucketName: 'graphqlfc',
+                cosObjectName: props.defalutName,
+                cosBucketRegion: props.region === '' ? 'ap-beijing' : props.region,
+                serviceName: '',
+                vpcId: '',
+                subnetId: '',
+            };
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        if (next.deploy !== '' && next.deploy !== null) {
+            let {functionName, cosBucketName, cosObjectName, cosBucketRegion, serviceName, vpcId, subnetId, region, description} = next.deploy;
+            this.setState({
+                description,
+                functionName,
+                region: next.region === '' ? region : next.region,
+                cosBucketName,
+                cosObjectName: removePrefix('.jar', cosObjectName),
+                cosBucketRegion: next.region === '' ? cosBucketRegion : next.region,
+                serviceName,
+                vpcId,
+                subnetId
+            })
+        } else {
+            this.setState({
+                functionName: next.defalutName,
+                region: next.region === '' ? 'ap-beijing' : next.region,
+                cosBucketName: 'graphqlfc',
+                cosObjectName: next.defalutName,
+                cosBucketRegion: next.region === '' ? 'ap-beijing' : next.region,
+                serviceName: '',
+                vpcId: '',
+                subnetId: ''
+            });
+        }
+    };
+
+    switchConfig = (label) => {
+        return (e) => {
+            this.setState({
+                [label]: e.target.value
+            })
+        };
+    };
+
+    ok = (id, userID, projectID, cloudID, deploy, projectStatus) => {
+        let {description, cosBucketName, subnetId, cosObjectName, region, vpcId, cosBucketRegion, functionName} = this.state;
+        let varObj = {
+            id,
+            cloud_id: cloudID,
+            user_id: userID,
+            cosObjectName: pushPostfix('.jar', cosObjectName),
+            region: shiftPrefix('ap-', region),
+            cosBucketRegion: shiftPrefix('ap-', cosBucketRegion),
+            description,
+            cosBucketName,
+            vpcId,
+            subnetId,
+            functionName,
+            memorySize: 512,
+            timeout: 300,
+            handler: 'tencent_graphql.Bridge::handler',
+            serviceName: "",
+            fc_id: '',
+            createdAt: new Date().getTime(),
+            updatedAt: ''
+        };
+        if (deploy === '' || deploy === null) {
+            request(graphqlUrl, ADD_DEPLOY, varObj).then(
+                data => {
+                    if (data.create_deploy !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        // 写回 project
+                        request(graphqlUrl, UPDATE_PROJECT_DEPLOY_AND_CLOUD, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            deploy_id: id,
+                            cloud_id: cloudID,
+                            projectStatus: 'functioned'
+                        }).then(data => {
+                            if(data.update_project !== null)
+                                this.props.stepByStep(1);
+                        })
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        } else {
+            let {description, cosBucketName, subnetId, cosObjectName, region, vpcId, cosBucketRegion, functionName} = this.state;
+            let varObj = {
+                id: deploy.id,
+                cosObjectName: pushPostfix('.jar', cosObjectName),
+                region: shiftPrefix('ap-', region),
+                cosBucketRegion: shiftPrefix('ap-', cosBucketRegion),
+                description,
+                subnetId,
+                cosBucketName,
+                vpcId,
+                functionName,
+                updatedAt: new Date().getTime()
+            };
+            request(graphqlUrl, UPDATE_DEPLOY, varObj).then(
+                data => {
+                    if (data.update_deploy !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            projectStatus: projectStatus === 'deployed' ? 'updated': 'functioned'
+                        }).then(data => {
+                            if(data.update_project !== null)
+                                this.props.stepByStep(1);
+                        })
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        }
+    };
+
+    render() {
+        const customPanelStyle = {
+            background: '#f7f7f7',
+            borderRadius: 4,
+            marginBottom: 24,
+            border: 0,
+            overflow: 'hidden',
+        };
+
+        const ioobotCloudID = ['tencent_CloudID', 'aliyun_CloudID'];
+
+        const disable = {
+            'subnetId': ioobotCloudID.includes(this.props.cloudID) ? true : '',
+            'vpcId': ioobotCloudID.includes(this.props.cloudID) ? true : '',
+        };
+
+        return (
+            <div>
+                <div style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                        {
+                                            youMustFill['functionName']?
+                                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                                :
+                                                ''
+                                        }
+                                        <FormattedMessage id={valueToKey['functionName']}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle['functionName']}>
+                                            <Icon type="question-circle"/>
+                                        </Tooltip>
+                                    </span>
+                    <Input value={this.state.functionName} style={{width: 400}}
+                           onChange={this.switchConfig('functionName')}/>
+                </div>
+                <div style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                        {
+                            youMustFill['region']?
+                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                :
+                                ''
+                        }
+                                        <FormattedMessage id={valueToKey['region']}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle['region']}>
+                            <Icon type="question-circle"/>
+                        </Tooltip>
+                    </span>
+                    <Radio.Group onChange={(e) => {
+                        this.props.switchRegion(e);
+                    }} defaultValue={this.state.region} value={removePrefix('ap-', this.state.region)}
+                                 buttonStyle="solid">
+                        <Radio.Button value="guangzhou"><FormattedMessage id="Guangzhou"/></Radio.Button>
+                        <Radio.Button value="shanghai"><FormattedMessage id="Shanghai"/></Radio.Button>
+                        <Radio.Button value="beijing"><FormattedMessage id="Beijing"/></Radio.Button>
+                        <Radio.Button value="chengdu"><FormattedMessage id="Chengdu"/></Radio.Button>
+                    </Radio.Group>
+                </div>
+                <div style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                {
+                                    youMustFill['cosBucketRegion']?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                        <FormattedMessage id={valueToKey['cosBucketRegion']}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle['cosBucketRegion']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                    <Radio.Group onChange={(e) => {
+                        // 如果不想一变所有都变,就将 onchange 改成
+                        // onChange={this.switchConfig('cosBucketRegion')}
+                        this.props.switchRegion(e);
+                    }} defaultValue={this.state.region}
+                                 value={removePrefix('ap-', this.state.cosBucketRegion)} buttonStyle="solid">
+                        <Radio.Button value="guangzhou"><FormattedMessage id="Guangzhou"/></Radio.Button>
+                        <Radio.Button value="shanghai"><FormattedMessage id="Shanghai"/></Radio.Button>
+                        <Radio.Button value="beijing"><FormattedMessage id="Beijing"/></Radio.Button>
+                        <Radio.Button value="chengdu"><FormattedMessage id="Chengdu"/></Radio.Button>
+                    </Radio.Group>
+                </div>
+                <div key={'cosBucketName'} style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                        {
+                                            youMustFill['cosBucketName']?
+                                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                                :
+                                                ''
+                                        }
+                                        <FormattedMessage id={valueToKey['cosBucketName']}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle['cosBucketName']}>
+                                            <Icon type="question-circle"/>
+                                        </Tooltip>
+                                    </span>
+                    <Input value={this.state.cosBucketName} style={{width: 400}}
+                           disabled={disable['cosBucketName'] === true}
+                           onChange={this.switchConfig('cosBucketName')}/>
+                </div>
+                <div key={'cosObjectName'} style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                        {
+                                            youMustFill['cosObjectName']?
+                                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                                :
+                                                ''
+                                        }
+                                        <FormattedMessage id={valueToKey['cosObjectName']}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle['cosObjectName']}>
+                                            <Icon type="question-circle"/>
+                                        </Tooltip>
+                                    </span>
+                    <Input value={this.state.cosObjectName} style={{width: 400}}
+                           disabled={disable['cosObjectName'] === true} addonAfter=".jar"
+                           onChange={this.switchConfig('cosObjectName')}/>
+                </div>
+                <Collapse bordered={false}>
+                    <Panel header={<FormattedMessage id="Want more options?"/>} style={customPanelStyle}>
+
+                        {/*腾讯云为空字符串,不显示,这里未作区分*/}
+                        {/*<div style={{marginBottom: 10}}>*/}
+                        {/*<span className='vice-title'>{valueToKey['serviceName']}*/}
+                        {/*<span> </span>*/}
+                        {/*<Tooltip placement="top" title={toolTipTitle['serviceName']}>*/}
+                        {/*<Icon type="question-circle"/>*/}
+                        {/*</Tooltip>*/}
+                        {/*</span>*/}
+                        {/*<Input value={this.state.serviceName} style={{width: 400}} disabled*/}
+                        {/*onChange={this.switchConfig('serviceName')}/>*/}
+                        {/*</div>*/}
+                        {
+                            this.state.configs.map(config => (
+                                <div key={config} style={{marginBottom: 10}}>
+                                    <span className='vice-title'>
+                                        {
+                                            youMustFill[config]?
+                                                <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                                :
+                                                ''
+                                        }
+                                        <FormattedMessage id={valueToKey[config]}/>
+                                        &nbsp;
+                                        <Tooltip placement="top" title={toolTipTitle[config]}>
+                                            <Icon type="question-circle"/>
+                                        </Tooltip>
+                                    </span>
+                                    <Input value={this.state[config]} style={{width: 400}}
+                                           disabled={disable[config] === true}
+                                           onChange={this.switchConfig(config)}/>
+                                </div>
+                            ))
+                        }
+                    </Panel>
+                </Collapse>
+                <div>
+                    {
+                        manageUsers.includes(this.props.userID) ?
+                            <div>
+                                <Button onClick={() => {
+                                    const id = idGen('deploy');
+                                    this.ok(id, this.props.userID, this.props.projectID, this.props.cloudID, this.props.deploy, this.props.projectStatus);
+                                }} type='primary'><FormattedMessage id="save"/></Button>
+                                {
+                                    this.state.showOK === true ?
+                                        <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/>
+                                        :
+                                        ''
+                                }
+                            </div>
+                            :
+                            this.props.trialcase ?
+                                ''
+                                :
+                                <div>
+                                    <Button onClick={() => {
+                                        const id = idGen('deploy');
+                                        this.ok(id, this.props.userID, this.props.projectID, this.props.cloudID, this.props.deploy, this.props.projectStatus);
+                                    }} type='primary'><FormattedMessage id="save"/></Button>
+                                    {
+                                        this.state.showOK === true ?
+                                            <Icon type="check-circle" theme="twoTone" twoToneColor="#52c41a"/>
+                                            :
+                                            ''
+                                    }
+                                </div>
+                    }
+
+                </div>
+            </div>
+        )
+    }
+}
+
+export default DeployCardRender;

+ 53 - 0
src/devApp/developVersion/common/deploy/tencent/notificationCard/NotificationCardFetch.js

@@ -0,0 +1,53 @@
+import React, {Component} from 'react';
+import {Spin} from 'antd';
+import {GET_PROJECT} from "../../../../../../gql";
+import {Query} from "react-apollo";
+import gql from "graphql-tag";
+import {removeSpace} from "../../../../../../func";
+import NotificationCardRender from './NotificationCardRender';
+import DeployCardRender from "../deployCard/DeployCardRender";
+
+class NotificationCardFetch extends Component {
+    render() {
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: this.props.projectID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+
+
+                        let dataProject = data.project_by_id;
+                        let {projectName, projectStatus} = dataProject;
+
+                        let notification = dataProject.notification_id || '';
+
+                        return (
+                            <NotificationCardRender
+                                // props
+                                trialcase={this.props.trialcase}
+                                stepByStep={this.props.stepByStep}
+                                userID={this.props.userID}
+                                projectID={this.props.projectID}
+                                stepAllShow={this.props.stepAllShow}
+
+                                // query
+                                notification={notification}
+                                defaultName={removeSpace(projectName)}
+                                projectStatus={projectStatus}
+                            />
+                        )
+                    }
+                }
+            </Query>
+
+        )
+
+    }
+}
+
+export default NotificationCardFetch;

+ 242 - 0
src/devApp/developVersion/common/deploy/tencent/notificationCard/NotificationCardRender.js

@@ -0,0 +1,242 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Input, Tooltip, Icon, Button} from 'antd';
+import {graphqlUrl, manageUsers} from "../../../../../../config";
+import {ADD_NOTIFICATION, UPDATE_NOTIFICATION, UPDATE_PROJECT_ONLY_STATUS} from "../../../../../../gql";
+import {request} from 'graphql-request'
+import {idGen} from "../../../../../../func";
+
+const valueToKey = {
+    'dingding webhook': 'dingding webhook',
+    'notification name': 'requestMethod'
+};
+
+const toolTipTitle = {
+    'dingding webhook': 'its dingding webhook',
+    'notification name': 'its notification name'
+};
+
+const youMustFill = {
+    'dingding webhook': false,
+    'notification name': false
+};
+
+
+class NotificationCardFetch extends Component {
+    constructor(props) {
+        super(props);
+        if(props.notification !== '' && props.notification !== null) {
+            let {type, webhook, name} = props.notification;
+            this.state = {
+                type,
+                webhook,
+                name
+            }
+        } else {
+            this.state = {
+                type: 'dingding',
+                webhook: '',
+                name: props.defaultName
+            }
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        if(next.notification !== '' && next.notification !== null) {
+            let {type, webhook, name} = next.notification;
+            this.setState({
+                type,
+                webhook,
+                name
+            })
+        } else {
+            this.setState({
+                type: 'dingding',
+                webhook: '',
+                name: next.defaultName
+            })
+        }
+    }
+
+    ok = (id, userID, projectID, notification, projectStatus) => {
+        let {type, webhook, name} = this.state;
+        let varObj = {
+            id,
+            user_id: userID,
+            type,
+            webhook,
+            name,
+            createdAt: new Date().getTime(),
+            updatedAt: ''
+        };
+        if (notification === '' || notification === null) {
+            request(graphqlUrl, ADD_NOTIFICATION, varObj).then(
+                data => {
+                    if (data.create_notification !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        // 写回 project
+                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            projectStatus: 'notificationed'
+                        }).then(data => {
+                            if (data.update_project !== null)
+                                this.props.stepByStep(4);
+                        })
+
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+
+        } else {
+            let varObj = {
+                id: notification.id,
+                type,
+                webhook,
+                name,
+                updatedAt: new Date().getTime()
+            };
+            request(graphqlUrl, UPDATE_NOTIFICATION, varObj).then(
+                data => {
+                    if (data.update_notification !== null) {
+                        this.setState({
+                            showOK: true
+                        });
+                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                            id: projectID,
+                            updatedAt: new Date().getTime(),
+                            projectStatus: projectStatus === 'deployed' ? 'updated': 'notificationed'
+                        }).then(data => {
+                            if (data.update_project !== null)
+                                this.props.stepByStep(4);
+                        })
+                    }
+                    setTimeout(() => {
+                        this.setState({
+                            showOK: false
+                        })
+                    }, 1500)
+                }
+            )
+        }
+    };
+
+    render() {
+        return (
+            <div>
+                <div>
+                    <p style={{marginBottom: 10}}><b><FormattedMessage id="Ding Talk"/></b></p>
+                    <div>
+                        <p style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill['dingding webhook'] ?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id="Web hook"/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle['dingding webhook']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input style={{width: 400}} value={this.state.webhook} onChange={(e) => {
+                                this.setState({
+                                    webhook: e.target.value
+                                })
+                            }}/>
+                        </p>
+                        <p style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill['notification name'] ?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id="Notification Name"/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle['notification name']}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input style={{width: 300}} value={this.state.name} onChange={(e) => {
+                                this.setState({
+                                    name: e.target.value
+                                })
+                            }}/></p>
+                    </div>
+                </div>
+
+                {/*<div className='kind'>*/}
+                {/*<p><b>QQ</b></p>*/}
+                {/*<div className='item'>*/}
+                {/*<p>*/}
+                {/*<span className='item-title'>*/}
+                {/*<span>Web hook  </span>*/}
+                {/*<Tooltip placement="top" title='hahahha'>*/}
+                {/*<Icon type="question-circle"/>*/}
+                {/*</Tooltip>*/}
+                {/*</span>*/}
+                {/*<Input style={{width: 600}}/>*/}
+                {/*</p>*/}
+                {/*<p>*/}
+                {/*<span className='item-title'>*/}
+                {/*<span>Notification Name  </span>*/}
+                {/*<Tooltip placement="top" title='heheheheh'>*/}
+                {/*<Icon type="question-circle"/>*/}
+                {/*</Tooltip>*/}
+                {/*</span>*/}
+                {/*<Input style={{width: 300}}/></p>*/}
+                {/*</div>*/}
+                {/*</div>*/}
+
+                <div>
+                    {
+                        manageUsers.includes(this.props.userID) ?
+                            <div>
+                                {
+                                    this.props.stepAllShow? '' :
+                                        <Button onClick={()=>{
+                                            this.props.stepByStep(2)
+                                        }}><FormattedMessage id="previous-step"/></Button>
+                                }
+                                <Button type="primary" onClick={() => {
+                                    const id = idGen('notification');
+                                    this.ok(id, this.props.userID, this.props.projectID, this.props.notification, this.props.projectStatus);
+                                }}><FormattedMessage id="save"/></Button>
+                            </div>
+
+                            :
+                            this.props.trialcase ?
+                                ''
+                                :
+                                <div>
+                                    {
+                                        this.props.stepAllShow? '' :
+                                            <Button onClick={()=>{
+                                                this.props.stepByStep(2)
+                                            }}><FormattedMessage id="previous-step"/></Button>
+                                    }
+                                    <Button type="primary" onClick={() => {
+                                        const id = idGen('notification');
+                                        this.ok(id, this.props.userID, this.props.projectID, this.props.notification, this.props.projectStatus);
+                                    }}><FormattedMessage id="save1"/></Button>
+                                </div>
+                    }
+                </div>
+            </div>
+        )
+
+    }
+}
+
+export default NotificationCardFetch;

+ 22 - 0
src/devApp/developVersion/common/manage/AliyunResult.js

@@ -0,0 +1,22 @@
+import React, {Component} from 'react';
+import {Switch, Input, Icon} from 'antd';
+import {FormattedMessage} from 'react-intl';
+
+class AliyunResult extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+
+        }
+    }
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+            </div>
+        )
+    }
+}
+
+export default AliyunResult;

+ 22 - 0
src/devApp/developVersion/common/manage/AmazonResult.js

@@ -0,0 +1,22 @@
+import React, {Component} from 'react';
+import {Switch, Input, Icon} from 'antd';
+import {FormattedMessage} from 'react-intl';
+class AmazonResult extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            url: '1111',
+            checked: true
+        }
+    }
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+            </div>
+        )
+    }
+}
+
+export default AmazonResult;

+ 63 - 0
src/devApp/developVersion/common/manage/Manage.jsx

@@ -0,0 +1,63 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Card} from 'antd';
+
+import TencentResult from './TencentResult';
+import AliyunResult from './AliyunResult';
+import AmazonResult from './AmazonResult';
+
+
+const tabListNoTitle =
+[{
+    key: 'tencent',
+    tab: <FormattedMessage id='Tencent'/>,
+}, {
+    key: 'aliyun',
+    tab: <FormattedMessage id='Aliyun'/>,
+}, {
+    key: 'amazon',
+    tab: <FormattedMessage id='AWS'/>,
+}];
+
+
+class Manage extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            cloud: 'tencent'
+        };
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            projectID: next.projectID
+        })
+    }
+
+    render() {
+        const contentListNoTitle = {
+            tencent: <TencentResult userID={this.props.userID} projectID={this.props.projectID} trialcase={this.props.trialcase} switchMenu={this.props.switchMenu} kind={this.props.kind}/>,
+            aliyun: <AliyunResult/>,
+            amazon: <AmazonResult/>,
+        };
+
+        return (
+            <div>
+                <Card
+                    style={{width: '100%'}}
+                    tabList={tabListNoTitle}
+                    activeTabKey={this.state.cloud}
+                    onTabChange={(cloud) => {
+                        this.setState({
+                            cloud
+                        })
+                    }}
+                >
+                    {contentListNoTitle[this.state.cloud]}
+                </Card>
+            </div>
+        )
+    }
+}
+
+export default Manage;

+ 403 - 0
src/devApp/developVersion/common/manage/TencentResult.js

@@ -0,0 +1,403 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Switch, Input, Icon, Spin, Row, Col, message} from 'antd';
+import {
+    DELETE_APIGWPATH,
+    DELETE_APIGROUP,
+    GET_PROJECT,
+    SHOW_APIGWPATH,
+    UPDATE_APIGROUP,
+    UPDATE_PROJECT_ONLY_STATUS
+} from "../../../../gql";
+import {request} from 'graphql-request'
+import gql from "graphql-tag";
+import {Query, Mutation} from "react-apollo";
+import copy from 'copy-to-clipboard';
+import axios from 'axios';
+import {removeAPI, graphqlUrl} from "../../../../config";
+
+axios.defaults.withCredentials = true;
+
+class TencentResult extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {}
+    }
+
+    render() {
+        let projectID = this.props.projectID ? this.props.projectID : this.props.kind === 'graphql' ? 'ecommerce_projectID' : 'ecommerce_projectID_wx';
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: projectID}} fetchPolicy={'network-only'}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+                        let group = data.project_by_id.apiGWGroup_id || {};
+                        let projectType = data.project_by_id.projectType || 'graphql';
+                        let projectStatus = data.project_by_id.projectStatus || 'created';
+                        let cloudID = data.project_by_id.cloud_id || 'tencent_CloudID';
+                        return (
+                            <div>
+                                {
+                                    projectStatus === 'deployed'?
+                                        <div>
+                                            <div className={'schema-name'}><FormattedMessage id='service manage'/></div>
+                                            <div className={'schema-table-list-title'}>
+                                                <Row>
+                                                    <Col span={4}><span className={'schema-table-title'}><FormattedMessage id='groupName'/></span></Col>
+                                                    <Col span={10}><span className={'schema-table-title'}><FormattedMessage id='defaultDomain'/></span></Col>
+                                                    <Col span={3}><span className={'schema-table-title'}><FormattedMessage id='frontType'/></span></Col>
+                                                    <Col span={4}><span className={'schema-table-title'}><FormattedMessage id='environmentName'/></span></Col>
+                                                    <Col span={3}><span className={'schema-table-title'}><FormattedMessage id='operation'/></span></Col>
+                                                </Row>
+                                            </div>
+                                            <div className={'schema-table-list-content'}>
+                                                <Row>
+                                                    <Col span={4}>
+                                                        <span className={'schema-table-content'}>{group.serviceId}</span>
+                                                    </Col>
+                                                    <Col span={10}>
+                                                        <span className={'schema-table-content'}>{group.defaultDomain} </span>
+                                                        <Icon type="copy" theme="twoTone" onClick={() => {
+                                                            copy(group.defaultDomain);
+                                                            message.success('复制成功.');
+                                                        }}/>
+                                                    </Col>
+                                                    <Col span={3}>
+                                                        <span className={'schema-table-content'}>{group.frontType}</span>
+                                                    </Col>
+                                                    <Col span={4}>
+                                                        <span className={'schema-table-content'}>{group.environmentName}</span>
+                                                    </Col>
+                                                    <Col span={3}>
+                                                    <span className={'schema-table-content'}>
+                                                        {
+                                                            this.props.trialcase?
+                                                                <Switch checked={true} disabled/>
+                                                                :
+                                                                <SwitchStatus group={group}/>
+                                                        }
+                                                        &nbsp;
+                                                        {
+                                                            this.props.trialcase ?
+                                                                ''
+                                                                :
+                                                                <DeleteGroupSpan
+                                                                cloudID={cloudID}
+                                                                groupID={group.id}
+                                                                projectID={projectID}
+                                                                userID={this.props.userID}
+                                                            />
+                                                        }
+                                                    </span>
+                                                    </Col>
+                                                </Row>
+                                            </div>
+                                            <div style={{marginTop: 30}}>
+                                                <div className={'schema-name'}><FormattedMessage id='API manage'/></div>
+                                                <APIGWPathResult
+                                                    group={group}
+                                                    projectType={projectType}
+                                                    switchMenu={this.props.switchMenu}
+                                                    cloudID={cloudID}
+                                                    projectID={projectID}
+                                                    userID={this.props.userID}
+                                                    trialcase={this.props.trialcase}
+                                                />
+                                            </div>
+                                        </div>
+                                        :
+                                        projectStatus === 'updated'?
+                                            <FormattedMessage id='deploy updated'/>
+                                            :
+                                            <FormattedMessage id='No deploy'/>
+                                }
+                            </div>
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+export default TencentResult;
+
+class SwitchStatus extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            checked: props.group.userStatus === 'open'
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            checked: next.group.userStatus === 'open'
+        })
+    }
+
+    render() {
+        let {group} = this.props;
+        let {id, userDomain, groupName, frontType, region, environmentName} = group;
+        return (
+            <div style={{display: 'inline', marginRight: '10px'}}>
+                <Mutation
+                    mutation={gql(UPDATE_APIGROUP)}
+                    onCompleted={(data) => {
+                        this.setState({
+                            checked: data.update_apiGWGroup.userStatus === 'open'
+                        })
+                    }}
+                >
+
+                    {(update_apiGWGroup, {loading, error}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+                        return (
+                            <Switch checked={this.state.checked} onChange={(checked) => {
+                                update_apiGWGroup({
+                                    variables: {
+                                        id,
+                                        userStatus: checked ? 'open' : 'close',
+                                        userDomain,
+                                        groupName,
+                                        frontType,
+                                        region,
+                                        environmentName,
+                                        updatedAt: new Date().getTime()
+                                    }
+                                })
+                            }}/>
+                        )
+                    }}
+                </Mutation>
+            </div>
+        )
+    }
+}
+
+class APIGWPathResult extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {}
+    }
+
+    render() {
+        let {group, projectType} = this.props;
+        let {id} = group;
+        return (
+            <Query query={gql(SHOW_APIGWPATH)} variables={{apiGWGroup_id: id}} fetchPolicy={'network-only'}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+                        let paths = data.apiGWPath_by_props || [];
+                        return (
+                            <div>
+                                {
+                                    Object.keys(paths).length !== 0 ?
+                                        <div>
+                                            <div className={'schema-table-list-title'}>
+                                                <Row>
+                                                    <Col span={6}><span className={'schema-table-title'}><FormattedMessage id='name'/></span></Col>
+                                                    <Col span={6}><span className={'schema-table-title'}><FormattedMessage id='path'/></span></Col>
+                                                    <Col span={3}><span className={'schema-table-title'}><FormattedMessage id='method'/></span></Col>
+                                                    <Col span={6}><span className={'schema-table-title'}><FormattedMessage id='description'/></span></Col>
+                                                    <Col span={3}><span className={'schema-table-title'}><FormattedMessage id='operation'/></span></Col>
+                                                </Row>
+                                            </div>
+                                            {
+                                                paths.map(path => (
+                                                    <div key={path.apiGWPath} className={'schema-table-list-content'}>
+                                                        <Row>
+                                                            <Col span={6}>
+                                                                <span className={'schema-table-content'}>{path.apiGWName}</span>
+                                                            </Col>
+                                                            <Col span={6}>
+                                                                <span className={'schema-table-content'}>{path.apiGWPath}</span>
+                                                            </Col>
+                                                            <Col span={3}>
+                                                                <span className={'schema-table-content'}>{path.requestMethod}</span>
+                                                            </Col>
+                                                            <Col span={6}>
+                                                                <span className={'schema-table-content'}>{path.apiGWDesc}</span>
+                                                            </Col>
+                                                            <Col span={3}>
+                                                                {
+                                                                    projectType === 'graphql'?
+                                                                        <span className={'schema-table-content name'}
+                                                                              onClick={() => {
+                                                                                  this.props.switchMenu('menuLevel2', {key: 'graphiql'});
+                                                                              }}>
+                                                                            <FormattedMessage id='try'/>
+                                                                        </span>
+                                                                        :
+                                                                        ''
+                                                                }
+                                                                &nbsp;
+                                                                {
+                                                                    this.props.trialcase?
+                                                                        ''
+                                                                        :
+                                                                        <DeletePathSpan
+                                                                            cloudID={this.props.cloudID}
+                                                                            groupID={id}
+                                                                            path={path}
+                                                                            projectID={this.props.projectID}
+                                                                            userID={this.props.userID}
+                                                                        />
+                                                                }
+
+                                                            </Col>
+                                                        </Row>
+                                                    </div>
+                                                ))
+                                            }
+                                        </div>
+                                        :
+                                        '路径不存在'
+                                }
+                            </div>
+
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+class DeletePathSpan extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+
+        }
+    }
+
+    render() {
+        return (
+            <Mutation
+                mutation={gql(DELETE_APIGWPATH)}
+                refetchQueries={[{query: gql(SHOW_APIGWPATH), variables: {apiGWGroup_id: this.props.groupID}}]}
+            >
+                {(delete_apiGWPath, {loading, error}) => {
+                    if (error)
+                        return 'error';
+                    if (loading)
+                        return <Spin style={{marginLeft: 3}}/>;
+
+                    return (
+                        <span className={'schema-table-content name'} onClick={() => {
+                                  let _this = this;
+                                  axios.get(`${removeAPI}`, {
+                                      params: {
+                                          'cloud-id': `${_this.props.cloudID}`,
+                                          'group-id': `${_this.props.groupID}`,
+                                          'api-id': `${_this.props.path.id}`
+                                      }
+                                  })
+                                      .then((res) => {
+                                          console.log('delete api');
+                                          if (res.data !== '') {
+
+                                              console.log('path id', _this.props.path.id, 'user id', _this.props.userID);
+
+                                              delete_apiGWPath({variables: {id:_this.props.path.id, user_id: _this.props.userID}});
+
+                                              // 写回 project 状态
+                                              request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                                  id: this.props.projectID,
+                                                  updatedAt: new Date().getTime(),
+                                                  projectStatus: 'grouped'
+                                              });
+
+                                              console.log(res.data);
+                                          } else {
+                                              console.log('error');
+                                          }
+                                      })
+                                      .catch((err) => {
+                                          console.log(err);
+                                      });
+                              }}
+                        >
+                            删除
+                        </span>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}
+
+class DeleteGroupSpan extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+        }
+    }
+
+    render() {
+        let {projectID, cloudID, groupID, userID} = this.props;
+        return (
+            <Mutation
+                mutation={gql(DELETE_APIGROUP)}
+                refetchQueries={[{query: gql(GET_PROJECT), variables: {id: projectID}}]}
+            >
+                {(delete_apiGWGroup, {loading, error}) => {
+                    if (error)
+                        return 'error';
+                    if (loading)
+                        return <Spin style={{marginLeft: 3}}/>;
+
+                    return (
+                        <span className={'schema-table-content name'} onClick={() => {
+                            axios.get(`${removeAPI}`, {
+                                params: {
+                                    'cloud-id': `${cloudID}`,
+                                    'group-id': `${groupID}`,
+                                }
+                            })
+                                .then((res) => {
+                                    console.log('delete service');
+                                    if (res.data !== '') {
+
+                                        delete_apiGWGroup({variables: {id: groupID, user_id: userID}});
+
+                                        // 写回 project 状态
+                                        request(graphqlUrl, UPDATE_PROJECT_ONLY_STATUS, {
+                                            id: projectID,
+                                            updatedAt: new Date().getTime(),
+                                            projectStatus: 'functioned'
+                                        });
+
+                                        console.log(res.data);
+                                    } else {
+                                        console.log('error');
+                                    }
+                                })
+                                .catch((err) => {
+                                    console.log(err);
+                                });
+                        }}
+                        >
+                            删除
+                        </span>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}

+ 115 - 0
src/devApp/developVersion/graphqlService/TrialCase.jsx

@@ -0,0 +1,115 @@
+import React, {Component} from 'react';
+import {Layout, Menu} from 'antd';
+import {FormattedMessage} from 'react-intl';
+
+import GenerateJs from "./component/generateJs/GenerateJs";
+import Deploy from '../common/deploy/Deploy';
+import Manage from '../common/manage/Manage';
+import Schema from './component/schema/Schema';
+import Graphql from "./component/graphql/Graphql";
+import CaseMetabase from "./component/caseMetabase/CaseMetabase";
+import Application from "./component/application/Application";
+import axios from 'axios';
+import {getIdUrl} from "../../../config";
+import classnames from 'classnames';
+
+axios.defaults.withCredentials = true;
+
+const {Content} = Layout;
+
+class TrialCase extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            menuLevel2: "preview",
+            // default user
+            userID: "ioobot",
+            showPadding: true,
+            api: ''
+        }
+    }
+
+    switchMenu = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    switchAPI = (api) => {
+        this.setState({
+            api
+        })
+    };
+
+    componentWillMount() {
+        let _this = this;
+        axios.get(getIdUrl)
+            .then((res) => {
+                if (res.data !== '') {
+                    _this.setState({
+                        userID: res.data
+                    })
+                }
+            })
+            .catch(function (err) {
+                console.log(err);
+            });
+    }
+
+    render() {
+        let schemaID = this.props.location.state ? this.props.location.state.schemaID : "ecommerce_schemaID";
+        let schemaName = this.props.location.state ? this.props.location.state.schemaName : "ecommerce";
+        let projectID = this.props.location.state ? this.props.location.state.projectID : "";
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['preview']}
+                    style={{padding: '0 24px', position: 'fixed', width: '100%', zIndex: '5',lineHeight:'50px',fontWeight:600}}
+                    onClick={(e) => this.switchMenu('menuLevel2', e)}
+                    selectedKeys={[this.state.menuLevel2]}
+                >
+                    <Menu.Item key="preview"><FormattedMessage id="preview"/></Menu.Item>
+                    <Menu.Item key="schema"><FormattedMessage id="schema"/></Menu.Item>
+                    <Menu.Item key="deploy"><FormattedMessage id="deploy"/></Menu.Item>
+                    <Menu.Item key="manage"><FormattedMessage id="manage"/></Menu.Item>
+                    <Menu.Item key="graphiql"><FormattedMessage id="graphql IDE"/></Menu.Item>
+                    <Menu.Item key="template"><FormattedMessage id="template"/></Menu.Item>
+                    <Menu.Item key="metabase"><FormattedMessage id="metabase"/></Menu.Item>
+                </Menu>
+
+
+                <Layout style={{padding: '24px', zIndex: '0'}}>
+                    <Content className={classnames({'layout-content': this.state.showPadding})}>
+                        {
+                            (() => {
+                                switch (this.state.menuLevel2) {
+                                    case 'schema':
+                                        return <Schema trialcase={true} userID={this.state.userID} projectID={projectID} schemaName={schemaName} schemaID={schemaID} history={this.props.history} location={this.props.location}/>;
+                                    case 'deploy':
+                                        return <Deploy trialcase={true} userID={this.state.userID} projectID={projectID} kind={'graphql'}/>;
+                                    case 'manage':
+                                        return <Manage trialcase={true} userID={this.state.userID} projectID={projectID} switchMenu={this.switchMenu} kind={'graphql'}/>;
+                                    case 'graphiql':
+                                        return <Graphql api={this.state.api} projectID={projectID}/>;
+                                    case 'template':
+                                        return <GenerateJs schemaID={schemaID} schemaName={schemaName}/>;
+                                    case 'preview':
+                                        return <Application location={this.props.location}/>;
+                                    case 'metabase':
+                                        return <CaseMetabase/>;
+                                    default:
+                                        return <Graphql/>;
+                                }
+                            })()
+                        }
+
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default TrialCase;
+

+ 109 - 0
src/devApp/developVersion/graphqlService/UserCreate.jsx

@@ -0,0 +1,109 @@
+import React, {Component} from 'react';
+import {Layout, Menu} from 'antd';
+import {FormattedMessage} from 'react-intl';
+
+import GenerateJs from "./component/generateJs/GenerateJs";
+import Deploy from '../common/deploy/Deploy';
+import Manage from '../common/manage/Manage';
+import Schema from './component/schema/Schema';
+import Graphql from "./component/graphql/Graphql";
+import CaseMetabase from "./component/caseMetabase/CaseMetabase";
+import axios from 'axios';
+import {getIdUrl} from "../../../config";
+import Application from "./component/application/Application";
+
+axios.defaults.withCredentials = true;
+
+const {Content} = Layout;
+
+class UserCreate extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            menuLevel2: "schema",
+            userID: "",
+            api: ''
+        }
+    }
+
+    componentWillMount() {
+        let _this = this;
+        axios.get(getIdUrl)
+            .then((res) => {
+                if (res.data !== '') {
+                    _this.setState({
+                        userID: res.data
+                    })
+                }
+            })
+            .catch(function (err) {
+                console.log(err);
+            });
+    }
+
+    switchMenu = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    switchAPI = (api) => {
+        this.setState({
+            api
+        })
+    };
+
+    render() {
+        let schemaID = this.props.location.state ? this.props.location.state.schemaID : "ecommerce_schemaID";
+        let schemaName = this.props.location.state ? this.props.location.state.schemaName : "ecommerce";
+        let projectID = this.props.location.state ? this.props.location.state.projectID : "";
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['schema']}
+                    style={{padding: '0 24px', position: 'fixed', width: '100%', zIndex: '5',lineHeight:'50px',fontWeight:600}}
+                    onClick={(e) => this.switchMenu('menuLevel2', e)}
+                    selectedKeys={[this.state.menuLevel2]}
+                >
+                    <Menu.Item key="schema"><FormattedMessage id="schema"/></Menu.Item>
+                    <Menu.Item key="deploy"><FormattedMessage id="deploy"/></Menu.Item>
+                    <Menu.Item key="manage"><FormattedMessage id="manage"/></Menu.Item>
+                    <Menu.Item key="graphiql"><FormattedMessage id="graphql IDE"/></Menu.Item>
+                    <Menu.Item key="template"><FormattedMessage id="template"/></Menu.Item>
+                    <Menu.Item key="metabase"><FormattedMessage id="metabase"/></Menu.Item>
+                </Menu>
+
+
+                <Layout style={{padding: '24px', zIndex: '0'}}>
+                    <Content style={{padding: '24px', minHeight: 280, background: '#fff', marginTop: '48px'}}>
+                        {
+                            (() => {
+                                switch (this.state.menuLevel2) {
+                                    case 'schema':
+                                        return <Schema trialcase={false} userID={this.state.userID} projectID={projectID} schemaName={schemaName} schemaID={schemaID} history={this.props.history} location={this.props.location}/>;
+                                    case 'deploy':
+                                        return <Deploy trialcase={false} userID={this.state.userID} projectID={projectID}/>;
+                                    case 'manage':
+                                        return <Manage trialcase={false} userID={this.state.userID} projectID={projectID} switchMenu={this.switchMenu}/>;
+                                    case 'graphiql':
+                                        return <Graphql api={this.state.api} projectID={projectID}/>;
+                                    case 'template':
+                                        return <GenerateJs schemaID={schemaID} schemaName={schemaName}/>;
+                                    case 'metabase':
+                                        return <CaseMetabase/>;
+                                    default:
+                                        return <Application location={this.props.location}/>;
+                                }
+                            })()
+                        }
+
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default UserCreate;
+

+ 24 - 0
src/devApp/developVersion/graphqlService/component/application/Application.js

@@ -0,0 +1,24 @@
+import React, {Component} from 'react';
+import ShopApp from '../../../../../case/ShopApp/src/index'
+import BillApp from '../../../../../case/BillApp/src/App'
+import OrderApp from '../../../../../case/OrderApp/src/index'
+class Application extends Component {
+
+    render() {
+        let schemaName = this.props.location.state ? this.props.location.state.schemaName : 'e-commerce';
+        return (
+            <div style={{height: '100%'}}>
+                {
+                    schemaName === "appointment template" ?
+                        <OrderApp/> :
+                        schemaName === 'account template'?
+                            <BillApp /> :
+                                <ShopApp />
+
+                }
+            </div>
+        )
+    }
+}
+
+export default Application;

+ 19 - 0
src/devApp/developVersion/graphqlService/component/caseMetabase/CaseMetabase.jsx

@@ -0,0 +1,19 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+import './index.css';
+
+class CaseMetabase extends Component {
+
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+                {/*Only members to use this website.*/}
+            </div>
+        )
+    }
+}
+
+export default CaseMetabase;

+ 3 - 0
src/devApp/developVersion/graphqlService/component/caseMetabase/index.css

@@ -0,0 +1,3 @@
+p {
+    font-size: 16px;
+}

+ 178 - 0
src/devApp/developVersion/graphqlService/component/generateJs/GenerateJs.jsx

@@ -0,0 +1,178 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import axios from 'axios';
+import {BackTop, Tabs, Button, Spin, Alert} from 'antd';
+import saveAs from 'file-saver';
+import beautify from 'js-beautify';
+import {genJsUrl} from '../../../../../config';
+
+import './index.css';
+
+const TabPane = Tabs.TabPane;
+axios.defaults.withCredentials = true;
+
+class GenerateJs extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            schemaID: props.schemaID,
+            schemaName: props.schemaName,
+            graphqlJs: '',
+            show: false
+        };
+    }
+
+    componentDidMount() {
+        this._isMounted = true;
+        let _this = this;
+        let {schemaID} = this.state;
+        axios.get(`${genJsUrl}?schema=${schemaID}`)
+            .then((res) => {
+                if (this._isMounted) {
+                    if (res.data !== '') {
+                        // console.log('js res', res.data);
+                        let graphqlJs = beautify(res.data, {indent_size: 2, space_in_empty_paren: true});
+                        // console.log('beautify graphqlJs',graphqlJs);
+                        _this.setState({
+                            graphqlJs,
+                            show: true
+                        });
+                    } else {
+                        _this.setState({
+                            show: true
+                        })
+                    }
+                }
+            })
+            .catch((err) => {
+                console.log(err);
+            });
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            schemaID: next.schemaID,
+        });
+    }
+
+    componentDidUpdate() {
+        this._isMounted = true;
+        let _this = this;
+        let {schemaID} = this.state;
+        // axios.get(`${genJsUrl}?schema=${'onlineSchema'}`)
+        // axios.get(`${genJsUrl}?schema=${'commentsSchema'}`)
+        // axios.get(`${genJsUrl}?schema=${'order_schemaID'}`)
+        axios.get(`${genJsUrl}?schema=${schemaID}`)
+            .then((res) => {
+                if (this._isMounted) {
+                    if (res.data !== '') {
+                        // console.log('js res', res.data);
+                        let graphqlJs = beautify(res.data, {indent_size: 2, space_in_empty_paren: true});
+                        // console.log('beautify graphqlJs',graphqlJs);
+                        _this.setState({
+                            graphqlJs,
+                            show: true
+                        });
+                    } else {
+                        _this.setState({
+                            show: true
+                        })
+                    }
+                }
+            })
+            .catch((err) => {
+                console.log(err);
+            });
+    }
+
+    componentWillUnmount() {
+        this._isMounted = false;
+    }
+
+    saveFile = () => {
+        let {schemaName, graphqlJs} = this.state;
+        let blob = new Blob([graphqlJs], {type: "text/plain;charset=utf-8"});
+        saveAs(blob, `${schemaName}_graphql.js`);
+    };
+
+    render() {
+        let {graphqlJs} = this.state;
+        let queryList = [];
+        let mutationList = [];
+        let graphqlJsList = beautify(graphqlJs, {indent_size: 2, space_in_empty_paren: true});
+        // console.log('beautify graphqlJs',graphqlJsList);
+        graphqlJsList.split("}\n}\n").forEach((item) => {
+            if (item.indexOf('query') > -1) queryList.push(item + "}\n}\n");
+            else mutationList.push(item + "}\n}\n");
+        });
+
+        return (
+            <div>
+                <Tabs
+                    defaultActiveKey="query"
+                    tabPosition="left"
+                >
+                    <TabPane tab="Query" key="query">
+                        <div className='download-button'>
+                            <Button type="primary" style={{float: 'right'}} onClick={() => this.saveFile()}>
+                                <FormattedMessage id="Download"/>
+                            </Button>
+                        </div>
+                        <br/>
+                        {this.state.show ?
+                            <GraphqlJs graphqlList={queryList}/>
+                            :
+                            <Loading/>
+                        }
+                    </TabPane>
+
+                    <TabPane tab="Mutation" key="mutation">
+                        <div className='download-button'>
+                            <Button type="primary" style={{float: 'right'}} onClick={() => this.saveFile()}>
+                                <FormattedMessage id="Download"/>
+                            </Button>
+                        </div>
+                        <br/>
+                        {this.state.show ?
+                            <GraphqlJs graphqlList={mutationList}/>
+                            :
+                            <Loading/>
+                        }
+                    </TabPane>
+                </Tabs>
+                <div>
+                    <BackTop/>
+                </div>
+            </div>
+        )
+    }
+}
+
+export default GenerateJs;
+
+const GraphqlJs = (props) => {
+    return (
+        <div>
+            <pre>
+                <code>
+                    {props.graphqlList}
+                </code>
+            </pre>
+        </div>
+    );
+};
+
+const Loading = () => {
+    return (
+        <div style={{width: '100%', height: window.innerHeight - 164}}>
+            <Spin
+                tip={'loading...'}
+                style={{
+                    position: 'relative',
+                    top: '50%',
+                    left: '50%',
+                    transform: 'translate(-50%,-50%)'
+                }}/>
+        </div>
+    );
+};

+ 3 - 0
src/devApp/developVersion/graphqlService/component/generateJs/index.css

@@ -0,0 +1,3 @@
+.ant-back-top-content{
+    background-color: #1890ff;
+}

+ 89 - 0
src/devApp/developVersion/graphqlService/component/graphql/Graphql.jsx

@@ -0,0 +1,89 @@
+import React, {Component} from 'react';
+import {Input, Spin} from 'antd';
+import GraphiQL from "graphiql";
+import fetch from "isomorphic-fetch";
+import gql from "graphql-tag";
+import {Query} from "react-apollo";
+
+import {GET_PROJECT} from "../../../../../gql";
+
+class Graphql extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            api: props.api,
+            show: props.api !== ''
+        };
+    };
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            api: next.api,
+            show: next.api !== ''
+        })
+    }
+
+    graphQLFetcher = (graphQLParams) => {
+        // 已经存在的 magazine api, 设想: 用户生成 schema,返回 api 自动替换这里
+        // return fetch(this.state.api, {
+        return fetch('http://service-dan6exu1-1254337200.ap-shanghai.apigateway.myqcloud.com/test/graphql', {
+            method: 'post',
+            headers: {'Content-Type': 'application/json'},
+            body: JSON.stringify(graphQLParams),
+        }).then(response => response.json());
+    };
+
+    render() {
+        let projectID = this.props.projectID ? this.props.projectID : 'ecommerce_projectID';
+        return (
+            <Query query={gql(GET_PROJECT)} variables={{id: projectID}} fetchPolicy={'network-only'}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+                        let group = data.project_by_id.apiGWGroup_id;
+
+                        if (this.state.api === '') {
+                            if (group !== null) {
+                                let domain = group.userDomain ? group.userDomain : (group.defaultDomain + '/' + group.environmentName);
+                                this.setState({
+                                    api: group.frontType.slice(0, 4) + '://' + domain + '/graphql',
+                                    show: true
+                                })
+                            } else {
+                                this.setState({
+                                    api: '尚未部署成功',
+                                    show: true
+                                })
+                            }
+                        }
+
+                        return (
+                            <div>
+                                <Input
+                                    style={{marginTop: 10}}
+                                    addonBefore="POST"
+                                    defaultValue={this.state.api}
+                                    onChange={(e) => {
+                                        this.setState({api: e.target.value})
+                                    }}/>
+                                {
+                                    this.state.show ?
+                                        <GraphiQL fetcher={this.graphQLFetcher}/>
+                                        :
+                                        ''
+                                }
+                            </div>
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+export default Graphql;

+ 1767 - 0
src/devApp/developVersion/graphqlService/component/graphql/index.css

@@ -0,0 +1,1767 @@
+.graphiql-container,
+.graphiql-container button,
+.graphiql-container input {
+    color: #141823;
+    font-family:
+            system,
+            -apple-system,
+            'San Francisco',
+            '.SFNSDisplay-Regular',
+            'Segoe UI',
+            Segoe,
+            'Segoe WP',
+            'Helvetica Neue',
+            helvetica,
+            'Lucida Grande',
+            arial,
+            sans-serif;
+    font-size: 14px;
+}
+
+.graphiql-container {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: horizontal;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: row;
+    flex-direction: row;
+    height: 800px;
+    margin-top: 10px;
+    overflow: hidden;
+    width: 100%;
+}
+
+.graphiql-container .editorWrap {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    overflow-x: hidden;
+}
+
+.graphiql-container .title {
+    font-size: 18px;
+}
+
+.graphiql-container .title em {
+    font-family: georgia;
+    font-size: 19px;
+}
+
+.graphiql-container .topBarWrap {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: horizontal;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: row;
+    flex-direction: row;
+}
+
+.graphiql-container .topBar {
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2));
+    background: linear-gradient(#f7f7f7, #e2e2e2);
+    border-bottom: 1px solid #d0d0d0;
+    cursor: default;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: horizontal;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: row;
+    flex-direction: row;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    height: 45px;
+    overflow-y: visible;
+    padding: 7px 14px 6px;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .toolbar {
+    overflow-x: visible;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+}
+
+.graphiql-container .docExplorerShow,
+.graphiql-container .historyShow {
+    background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2));
+    background: linear-gradient(#f7f7f7, #e2e2e2);
+    border-radius: 0;
+    border-bottom: 1px solid #d0d0d0;
+    border-right: none;
+    border-top: none;
+    color: #3B5998;
+    cursor: pointer;
+    font-size: 14px;
+    margin: 0;
+    outline: 0;
+    padding: 2px 20px 0 18px;
+}
+
+.graphiql-container .docExplorerShow {
+    border-left: 1px solid rgba(0, 0, 0, 0.2);
+}
+
+.graphiql-container .historyShow {
+    border-right: 1px solid rgba(0, 0, 0, 0.2);
+    border-left: 0;
+}
+
+.graphiql-container .docExplorerShow:before {
+    border-left: 2px solid #3B5998;
+    border-top: 2px solid #3B5998;
+    content: '';
+    display: inline-block;
+    height: 9px;
+    margin: 0 3px -1px 0;
+    position: relative;
+    -webkit-transform: rotate(-45deg);
+    transform: rotate(-45deg);
+    width: 9px;
+}
+
+.graphiql-container .editorBar {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: horizontal;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: row;
+    flex-direction: row;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+}
+
+.graphiql-container .queryWrap {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+}
+
+.graphiql-container .resultWrap {
+    border-left: solid 1px #e0e0e0;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    position: relative;
+}
+
+.graphiql-container .docExplorerWrap,
+.graphiql-container .historyPaneWrap {
+    background: white;
+    -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
+    box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
+    position: relative;
+    z-index: 3;
+}
+
+.graphiql-container .historyPaneWrap {
+    min-width: 230px;
+    z-index: 5;
+}
+
+.graphiql-container .docExplorerResizer {
+    cursor: col-resize;
+    height: 100%;
+    left: -5px;
+    position: absolute;
+    top: 0;
+    width: 10px;
+    z-index: 10;
+}
+
+.graphiql-container .docExplorerHide {
+    cursor: pointer;
+    font-size: 18px;
+    margin: -7px -8px -6px 0;
+    padding: 18px 16px 15px 12px;
+}
+
+.graphiql-container div .query-editor {
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    position: relative;
+}
+
+.graphiql-container .variable-editor {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    height: 30px;
+    position: relative;
+}
+
+.graphiql-container .variable-editor-title {
+    background: #eeeeee;
+    border-bottom: 1px solid #d6d6d6;
+    border-top: 1px solid #e0e0e0;
+    color: #777;
+    font-variant: small-caps;
+    font-weight: bold;
+    letter-spacing: 1px;
+    line-height: 14px;
+    padding: 6px 0 8px 43px;
+    text-transform: lowercase;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .codemirrorWrap {
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    height: 100%;
+    position: relative;
+}
+
+.graphiql-container .result-window {
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    height: 100%;
+    position: relative;
+}
+
+.graphiql-container .footer {
+    background: #f6f7f8;
+    border-left: 1px solid #e0e0e0;
+    border-top: 1px solid #e0e0e0;
+    margin-left: 12px;
+    position: relative;
+}
+
+.graphiql-container .footer:before {
+    background: #eeeeee;
+    bottom: 0;
+    content: " ";
+    left: -13px;
+    position: absolute;
+    top: -1px;
+    width: 12px;
+}
+
+/* No `.graphiql-container` here so themes can overwrite */
+.result-window .CodeMirror {
+    background: #f6f7f8;
+}
+
+.graphiql-container .result-window .CodeMirror-gutters {
+    background-color: #eeeeee;
+    border-color: #e0e0e0;
+    cursor: col-resize;
+}
+
+.graphiql-container .result-window .CodeMirror-foldgutter,
+.graphiql-container .result-window .CodeMirror-foldgutter-open:after,
+.graphiql-container .result-window .CodeMirror-foldgutter-folded:after {
+    padding-left: 3px;
+}
+
+.graphiql-container .toolbar-button {
+    background: #fdfdfd;
+    background: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec));
+    background: linear-gradient(#f9f9f9, #ececec);
+    border-radius: 3px;
+    -webkit-box-shadow:
+            inset 0 0 0 1px rgba(0,0,0,0.20),
+            0 1px 0 rgba(255,255,255, 0.7),
+            inset 0 1px #fff;
+    box-shadow:
+            inset 0 0 0 1px rgba(0,0,0,0.20),
+            0 1px 0 rgba(255,255,255, 0.7),
+            inset 0 1px #fff;
+    color: #555;
+    cursor: pointer;
+    display: inline-block;
+    margin: 0 5px;
+    padding: 3px 11px 5px;
+    text-decoration: none;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    max-width: 150px;
+}
+
+.graphiql-container .toolbar-button:active {
+    background: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#d5d5d5));
+    background: linear-gradient(#ececec, #d5d5d5);
+    -webkit-box-shadow:
+            0 1px 0 rgba(255, 255, 255, 0.7),
+            inset 0 0 0 1px rgba(0,0,0,0.10),
+            inset 0 1px 1px 1px rgba(0, 0, 0, 0.12),
+            inset 0 0 5px rgba(0, 0, 0, 0.1);
+    box-shadow:
+            0 1px 0 rgba(255, 255, 255, 0.7),
+            inset 0 0 0 1px rgba(0,0,0,0.10),
+            inset 0 1px 1px 1px rgba(0, 0, 0, 0.12),
+            inset 0 0 5px rgba(0, 0, 0, 0.1);
+}
+
+.graphiql-container .toolbar-button.error {
+    background: -webkit-gradient(linear, left top, left bottom, from(#fdf3f3), to(#e6d6d7));
+    background: linear-gradient(#fdf3f3, #e6d6d7);
+    color: #b00;
+}
+
+.graphiql-container .toolbar-button-group {
+    margin: 0 5px;
+    white-space: nowrap;
+}
+
+.graphiql-container .toolbar-button-group > * {
+    margin: 0;
+}
+
+.graphiql-container .toolbar-button-group > *:not(:last-child) {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+}
+
+.graphiql-container .toolbar-button-group > *:not(:first-child) {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+    margin-left: -1px;
+}
+
+.graphiql-container .execute-button-wrap {
+    height: 34px;
+    margin: 0 14px 0 28px;
+    position: relative;
+}
+
+.graphiql-container .execute-button {
+    background: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#d2d3d6));
+    background: linear-gradient(#fdfdfd, #d2d3d6);
+    border-radius: 17px;
+    border: 1px solid rgba(0,0,0,0.25);
+    -webkit-box-shadow: 0 1px 0 #fff;
+    box-shadow: 0 1px 0 #fff;
+    cursor: pointer;
+    fill: #444;
+    height: 34px;
+    margin: 0;
+    padding: 0;
+    width: 34px;
+}
+
+.graphiql-container .execute-button svg {
+    pointer-events: none;
+}
+
+.graphiql-container .execute-button:active {
+    background: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6), to(#c3c3c3));
+    background: linear-gradient(#e6e6e6, #c3c3c3);
+    -webkit-box-shadow:
+            0 1px 0 #fff,
+            inset 0 0 2px rgba(0, 0, 0, 0.2),
+            inset 0 0 6px rgba(0, 0, 0, 0.1);
+    box-shadow:
+            0 1px 0 #fff,
+            inset 0 0 2px rgba(0, 0, 0, 0.2),
+            inset 0 0 6px rgba(0, 0, 0, 0.1);
+}
+
+.graphiql-container .execute-button:focus {
+    outline: 0;
+}
+
+.graphiql-container .toolbar-menu,
+.graphiql-container .toolbar-select {
+    position: relative;
+}
+
+.graphiql-container .execute-options,
+.graphiql-container .toolbar-menu-items,
+.graphiql-container .toolbar-select-options {
+    background: #fff;
+    -webkit-box-shadow:
+            0 0 0 1px rgba(0,0,0,0.1),
+            0 2px 4px rgba(0,0,0,0.25);
+    box-shadow:
+            0 0 0 1px rgba(0,0,0,0.1),
+            0 2px 4px rgba(0,0,0,0.25);
+    margin: 0;
+    padding: 6px 0;
+    position: absolute;
+    z-index: 100;
+}
+
+.graphiql-container .execute-options {
+    min-width: 100px;
+    top: 37px;
+    left: -1px;
+}
+
+.graphiql-container .toolbar-menu-items {
+    left: 1px;
+    margin-top: -1px;
+    min-width: 110%;
+    top: 100%;
+    visibility: hidden;
+}
+
+.graphiql-container .toolbar-menu-items.open {
+    visibility: visible;
+}
+
+.graphiql-container .toolbar-select-options {
+    left: 0;
+    min-width: 100%;
+    top: -5px;
+    visibility: hidden;
+}
+
+.graphiql-container .toolbar-select-options.open {
+    visibility: visible;
+}
+
+.graphiql-container .execute-options > li,
+.graphiql-container .toolbar-menu-items > li,
+.graphiql-container .toolbar-select-options > li {
+    cursor: pointer;
+    display: block;
+    margin: none;
+    max-width: 300px;
+    overflow: hidden;
+    padding: 2px 20px 4px 11px;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.graphiql-container .execute-options > li.selected,
+.graphiql-container .toolbar-menu-items > li.hover,
+.graphiql-container .toolbar-menu-items > li:active,
+.graphiql-container .toolbar-menu-items > li:hover,
+.graphiql-container .toolbar-select-options > li.hover,
+.graphiql-container .toolbar-select-options > li:active,
+.graphiql-container .toolbar-select-options > li:hover,
+.graphiql-container .history-contents > p:hover,
+.graphiql-container .history-contents > p:active {
+    background: #e10098;
+    color: #fff;
+}
+
+.graphiql-container .toolbar-select-options > li > svg {
+    display: inline;
+    fill: #666;
+    margin: 0 -6px 0 6px;
+    pointer-events: none;
+    vertical-align: middle;
+}
+
+.graphiql-container .toolbar-select-options > li.hover > svg,
+.graphiql-container .toolbar-select-options > li:active > svg,
+.graphiql-container .toolbar-select-options > li:hover > svg {
+    fill: #fff;
+}
+
+.graphiql-container .CodeMirror-scroll {
+    overflow-scrolling: touch;
+}
+
+.graphiql-container .CodeMirror {
+    color: #141823;
+    font-family:
+            'Consolas',
+            'Inconsolata',
+            'Droid Sans Mono',
+            'Monaco',
+            monospace;
+    font-size: 13px;
+    height: 100%;
+    left: 0;
+    position: absolute;
+    top: 0;
+    width: 100%;
+}
+
+.graphiql-container .CodeMirror-lines {
+    padding: 20px 0;
+}
+
+.CodeMirror-hint-information .content {
+    box-orient: vertical;
+    color: #141823;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif;
+    font-size: 13px;
+    line-clamp: 3;
+    line-height: 16px;
+    max-height: 48px;
+    overflow: hidden;
+    text-overflow: -o-ellipsis-lastline;
+}
+
+.CodeMirror-hint-information .content p:first-child {
+    margin-top: 0;
+}
+
+.CodeMirror-hint-information .content p:last-child {
+    margin-bottom: 0;
+}
+
+.CodeMirror-hint-information .infoType {
+    color: #CA9800;
+    cursor: pointer;
+    display: inline;
+    margin-right: 0.5em;
+}
+
+.autoInsertedLeaf.cm-property {
+    -webkit-animation-duration: 6s;
+    animation-duration: 6s;
+    -webkit-animation-name: insertionFade;
+    animation-name: insertionFade;
+    border-bottom: 2px solid rgba(255, 255, 255, 0);
+    border-radius: 2px;
+    margin: -2px -4px -1px;
+    padding: 2px 4px 1px;
+}
+
+@-webkit-keyframes insertionFade {
+    from, to {
+        background: rgba(255, 255, 255, 0);
+        border-color: rgba(255, 255, 255, 0);
+    }
+
+    15%, 85% {
+        background: #fbffc9;
+        border-color: #f0f3c0;
+    }
+}
+
+@keyframes insertionFade {
+    from, to {
+        background: rgba(255, 255, 255, 0);
+        border-color: rgba(255, 255, 255, 0);
+    }
+
+    15%, 85% {
+        background: #fbffc9;
+        border-color: #f0f3c0;
+    }
+}
+
+div.CodeMirror-lint-tooltip {
+    background-color: white;
+    border-radius: 2px;
+    border: 0;
+    color: #141823;
+    -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+    font-family:
+            system,
+            -apple-system,
+            'San Francisco',
+            '.SFNSDisplay-Regular',
+            'Segoe UI',
+            Segoe,
+            'Segoe WP',
+            'Helvetica Neue',
+            helvetica,
+            'Lucida Grande',
+            arial,
+            sans-serif;
+    font-size: 13px;
+    line-height: 16px;
+    max-width: 430px;
+    opacity: 0;
+    padding: 8px 10px;
+    -webkit-transition: opacity 0.15s;
+    transition: opacity 0.15s;
+    white-space: pre-wrap;
+}
+
+div.CodeMirror-lint-tooltip > * {
+    padding-left: 23px;
+}
+
+div.CodeMirror-lint-tooltip > * + * {
+    margin-top: 12px;
+}
+
+/* COLORS */
+
+.graphiql-container .CodeMirror-foldmarker {
+    border-radius: 4px;
+    background: #08f;
+    background: -webkit-gradient(linear, left top, left bottom, from(#43A8FF), to(#0F83E8));
+    background: linear-gradient(#43A8FF, #0F83E8);
+    -webkit-box-shadow:
+            0 1px 1px rgba(0, 0, 0, 0.2),
+            inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+    box-shadow:
+            0 1px 1px rgba(0, 0, 0, 0.2),
+            inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+    color: white;
+    font-family: arial;
+    font-size: 12px;
+    line-height: 0;
+    margin: 0 3px;
+    padding: 0px 4px 1px;
+    text-shadow: 0 -1px rgba(0, 0, 0, 0.1);
+}
+
+.graphiql-container div.CodeMirror span.CodeMirror-matchingbracket {
+    color: #555;
+    text-decoration: underline;
+}
+
+.graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket {
+    color: #f00;
+}
+
+/* Comment */
+.cm-comment {
+    color: #999;
+}
+
+/* Punctuation */
+.cm-punctuation {
+    color: #555;
+}
+
+/* Keyword */
+.cm-keyword {
+    color: #B11A04;
+}
+
+/* OperationName, FragmentName */
+.cm-def {
+    color: #D2054E;
+}
+
+/* FieldName */
+.cm-property {
+    color: #1F61A0;
+}
+
+/* FieldAlias */
+.cm-qualifier {
+    color: #1C92A9;
+}
+
+/* ArgumentName and ObjectFieldName */
+.cm-attribute {
+    color: #8B2BB9;
+}
+
+/* Number */
+.cm-number {
+    color: #2882F9;
+}
+
+/* String */
+.cm-string {
+    color: #D64292;
+}
+
+/* Boolean */
+.cm-builtin {
+    color: #D47509;
+}
+
+/* EnumValue */
+.cm-string-2 {
+    color: #0B7FC7;
+}
+
+/* Variable */
+.cm-variable {
+    color: #397D13;
+}
+
+/* Directive */
+.cm-meta {
+    color: #B33086;
+}
+
+/* Type */
+.cm-atom {
+    color: #CA9800;
+}
+/* BASICS */
+
+.CodeMirror {
+    /* Set height, width, borders, and global font properties here */
+    color: black;
+    font-family: monospace;
+    height: 300px;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+    padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+    padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+    background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+    border-right: 1px solid #ddd;
+    background-color: #f7f7f7;
+    white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+    color: #999;
+    min-width: 20px;
+    padding: 0 3px 0 5px;
+    text-align: right;
+    white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror .CodeMirror-cursor {
+    border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+    border-left: 1px solid silver;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
+    background: #7e7;
+    border: 0;
+    width: auto;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
+    z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+    -webkit-animation: blink 1.06s steps(1) infinite;
+    animation: blink 1.06s steps(1) infinite;
+    border: 0;
+    width: auto;
+}
+@-webkit-keyframes blink {
+    0% { background: #7e7; }
+    50% { background: none; }
+    100% { background: #7e7; }
+}
+@keyframes blink {
+    0% { background: #7e7; }
+    50% { background: none; }
+    100% { background: #7e7; }
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+div.CodeMirror-overwrite div.CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+    border-left: 1px solid #ccc;
+    position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+    background: white;
+    overflow: hidden;
+    position: relative;
+}
+
+.CodeMirror-scroll {
+    height: 100%;
+    /* 30px is the magic margin used to hide the element's real scrollbars */
+    /* See overflow: hidden in .CodeMirror */
+    margin-bottom: -30px; margin-right: -30px;
+    outline: none; /* Prevent dragging from highlighting the element */
+    overflow: scroll !important; /* Things will break if this is overridden */
+    padding-bottom: 30px;
+    position: relative;
+}
+.CodeMirror-sizer {
+    border-right: 30px solid transparent;
+    position: relative;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actual scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+    display: none;
+    position: absolute;
+    z-index: 6;
+}
+.CodeMirror-vscrollbar {
+    overflow-x: hidden;
+    overflow-y: scroll;
+    right: 0; top: 0;
+}
+.CodeMirror-hscrollbar {
+    bottom: 0; left: 0;
+    overflow-x: scroll;
+    overflow-y: hidden;
+}
+.CodeMirror-scrollbar-filler {
+    right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+    left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+    min-height: 100%;
+    position: absolute; left: 0; top: 0;
+    z-index: 3;
+}
+.CodeMirror-gutter {
+    display: inline-block;
+    height: 100%;
+    margin-bottom: -30px;
+    vertical-align: top;
+    white-space: normal;
+    /* Hack to make IE7 behave */
+    *zoom:1;
+    *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+    background: none !important;
+    border: none !important;
+    position: absolute;
+    z-index: 4;
+}
+.CodeMirror-gutter-background {
+    position: absolute;
+    top: 0; bottom: 0;
+    z-index: 4;
+}
+.CodeMirror-gutter-elt {
+    cursor: default;
+    position: absolute;
+    z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.CodeMirror-lines {
+    cursor: text;
+    min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+    -webkit-tap-highlight-color: transparent;
+    /* Reset some styles that the rest of the page might have set */
+    background: transparent;
+    border-radius: 0;
+    border-width: 0;
+    color: inherit;
+    font-family: inherit;
+    font-size: inherit;
+    -webkit-font-variant-ligatures: none;
+    font-variant-ligatures: none;
+    line-height: inherit;
+    margin: 0;
+    overflow: visible;
+    position: relative;
+    white-space: pre;
+    word-wrap: normal;
+    z-index: 2;
+}
+.CodeMirror-wrap pre {
+    word-wrap: break-word;
+    white-space: pre-wrap;
+    word-break: normal;
+}
+
+.CodeMirror-linebackground {
+    position: absolute;
+    left: 0; right: 0; top: 0; bottom: 0;
+    z-index: 0;
+}
+
+.CodeMirror-linewidget {
+    overflow: auto;
+    position: relative;
+    z-index: 2;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+    outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+    -webkit-box-sizing: content-box;
+    box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+    height: 0;
+    overflow: hidden;
+    position: absolute;
+    visibility: hidden;
+    width: 100%;
+}
+
+.CodeMirror-cursor { position: absolute; }
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+    position: relative;
+    visibility: hidden;
+    z-index: 3;
+}
+div.CodeMirror-dragcursors {
+    visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+    visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+    background: #ffa;
+    background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+    /* Hide the cursor when printing */
+    .CodeMirror div.CodeMirror-cursors {
+        visibility: hidden;
+    }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
+
+.CodeMirror-dialog {
+    background: inherit;
+    color: inherit;
+    left: 0; right: 0;
+    overflow: hidden;
+    padding: .1em .8em;
+    position: absolute;
+    z-index: 15;
+}
+
+.CodeMirror-dialog-top {
+    border-bottom: 1px solid #eee;
+    top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+    border-top: 1px solid #eee;
+    bottom: 0;
+}
+
+.CodeMirror-dialog input {
+    background: transparent;
+    border: 1px solid #d3d6db;
+    color: inherit;
+    font-family: monospace;
+    outline: none;
+    width: 20em;
+}
+
+.CodeMirror-dialog button {
+    font-size: 70%;
+}
+.graphiql-container .doc-explorer {
+    background: white;
+}
+
+.graphiql-container .doc-explorer-title-bar,
+.graphiql-container .history-title-bar {
+    cursor: default;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    height: 50px;
+    line-height: 14px;
+    padding: 8px 8px 5px;
+    position: relative;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .doc-explorer-title,
+.graphiql-container .history-title {
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    font-weight: bold;
+    overflow-x: hidden;
+    padding: 10px 0 5px 10px;
+    text-align: center;
+    text-overflow: ellipsis;
+    -webkit-user-select: initial;
+    -moz-user-select: initial;
+    -ms-user-select: initial;
+    user-select: initial;
+    white-space: nowrap;
+}
+
+.graphiql-container .doc-explorer-back {
+    color: #3B5998;
+    cursor: pointer;
+    margin: -7px 0 -6px -8px;
+    overflow-x: hidden;
+    padding: 17px 12px 16px 16px;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.doc-explorer-narrow .doc-explorer-back {
+    width: 0;
+}
+
+.graphiql-container .doc-explorer-back:before {
+    border-left: 2px solid #3B5998;
+    border-top: 2px solid #3B5998;
+    content: '';
+    display: inline-block;
+    height: 9px;
+    margin: 0 3px -1px 0;
+    position: relative;
+    -webkit-transform: rotate(-45deg);
+    transform: rotate(-45deg);
+    width: 9px;
+}
+
+.graphiql-container .doc-explorer-rhs {
+    position: relative;
+}
+
+.graphiql-container .doc-explorer-contents,
+.graphiql-container .history-contents {
+    background-color: #ffffff;
+    border-top: 1px solid #d6d6d6;
+    bottom: 0;
+    left: 0;
+    overflow-y: auto;
+    padding: 20px 15px;
+    position: absolute;
+    right: 0;
+    top: 47px;
+}
+
+.graphiql-container .doc-explorer-contents {
+    min-width: 300px;
+}
+
+.graphiql-container .doc-type-description p:first-child ,
+.graphiql-container .doc-type-description blockquote:first-child {
+    margin-top: 0;
+}
+
+.graphiql-container .doc-explorer-contents a {
+    cursor: pointer;
+    text-decoration: none;
+}
+
+.graphiql-container .doc-explorer-contents a:hover {
+    text-decoration: underline;
+}
+
+.graphiql-container .doc-value-description > :first-child {
+    margin-top: 4px;
+}
+
+.graphiql-container .doc-value-description > :last-child {
+    margin-bottom: 4px;
+}
+
+.graphiql-container .doc-category {
+    margin: 20px 0;
+}
+
+.graphiql-container .doc-category-title {
+    border-bottom: 1px solid #e0e0e0;
+    color: #777;
+    cursor: default;
+    font-size: 14px;
+    font-variant: small-caps;
+    font-weight: bold;
+    letter-spacing: 1px;
+    margin: 0 -15px 10px 0;
+    padding: 10px 0;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .doc-category-item {
+    margin: 12px 0;
+    color: #555;
+}
+
+.graphiql-container .keyword {
+    color: #B11A04;
+}
+
+.graphiql-container .type-name {
+    color: #CA9800;
+}
+
+.graphiql-container .field-name {
+    color: #1F61A0;
+}
+
+.graphiql-container .field-short-description {
+    color: #999;
+    margin-left: 5px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.graphiql-container .enum-value {
+    color: #0B7FC7;
+}
+
+.graphiql-container .arg-name {
+    color: #8B2BB9;
+}
+
+.graphiql-container .arg {
+    display: block;
+    margin-left: 1em;
+}
+
+.graphiql-container .arg:first-child:last-child,
+.graphiql-container .arg:first-child:nth-last-child(2),
+.graphiql-container .arg:first-child:nth-last-child(2) ~ .arg {
+    display: inherit;
+    margin: inherit;
+}
+
+.graphiql-container .arg:first-child:nth-last-child(2):after {
+    content: ', ';
+}
+
+.graphiql-container .arg-default-value {
+    color: #43A047;
+}
+
+.graphiql-container .doc-deprecation {
+    background: #fffae8;
+    -webkit-box-shadow: inset 0 0 1px #bfb063;
+    box-shadow: inset 0 0 1px #bfb063;
+    color: #867F70;
+    line-height: 16px;
+    margin: 8px -8px;
+    max-height: 80px;
+    overflow: hidden;
+    padding: 8px;
+    border-radius: 3px;
+}
+
+.graphiql-container .doc-deprecation:before {
+    content: 'Deprecated:';
+    color: #c79b2e;
+    cursor: default;
+    display: block;
+    font-size: 9px;
+    font-weight: bold;
+    letter-spacing: 1px;
+    line-height: 1;
+    padding-bottom: 5px;
+    text-transform: uppercase;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .doc-deprecation > :first-child {
+    margin-top: 0;
+}
+
+.graphiql-container .doc-deprecation > :last-child {
+    margin-bottom: 0;
+}
+
+.graphiql-container .show-btn {
+    -webkit-appearance: initial;
+    display: block;
+    border-radius: 3px;
+    border: solid 1px #ccc;
+    text-align: center;
+    padding: 8px 12px 10px;
+    width: 100%;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    background: #fbfcfc;
+    color: #555;
+    cursor: pointer;
+}
+
+.graphiql-container .search-box {
+    border-bottom: 1px solid #d3d6db;
+    display: block;
+    font-size: 14px;
+    margin: -15px -15px 12px 0;
+    position: relative;
+}
+
+.graphiql-container .search-box:before {
+    content: '\26b2';
+    cursor: pointer;
+    display: block;
+    font-size: 24px;
+    position: absolute;
+    top: -2px;
+    -webkit-transform: rotate(-45deg);
+    transform: rotate(-45deg);
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .search-box .search-box-clear {
+    background-color: #d0d0d0;
+    border-radius: 12px;
+    color: #fff;
+    cursor: pointer;
+    font-size: 11px;
+    padding: 1px 5px 2px;
+    position: absolute;
+    right: 3px;
+    top: 8px;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.graphiql-container .search-box .search-box-clear:hover {
+    background-color: #b9b9b9;
+}
+
+.graphiql-container .search-box > input {
+    border: none;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    font-size: 14px;
+    outline: none;
+    padding: 6px 24px 8px 20px;
+    width: 100%;
+}
+
+.graphiql-container .error-container {
+    font-weight: bold;
+    left: 0;
+    letter-spacing: 1px;
+    opacity: 0.5;
+    position: absolute;
+    right: 0;
+    text-align: center;
+    text-transform: uppercase;
+    top: 50%;
+    -webkit-transform: translate(0, -50%);
+    transform: translate(0, -50%);
+}
+.CodeMirror-foldmarker {
+    color: blue;
+    cursor: pointer;
+    font-family: arial;
+    line-height: .3;
+    text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+}
+.CodeMirror-foldgutter {
+    width: .7em;
+}
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+    cursor: pointer;
+}
+.CodeMirror-foldgutter-open:after {
+    content: "\25BE";
+}
+.CodeMirror-foldgutter-folded:after {
+    content: "\25B8";
+}
+.graphiql-container .history-contents,
+.graphiql-container .history-contents input {
+    font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
+    padding: 0;
+}
+
+.graphiql-container .history-contents p {
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    font-size: 12px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    margin: 0;
+    padding: 8px;
+    border-bottom: 1px solid #e0e0e0;
+}
+
+.graphiql-container .history-contents p.editable {
+    padding-bottom: 6px;
+    padding-top: 7px;
+}
+
+.graphiql-container .history-contents input {
+    -webkit-box-flex: 1;
+    -ms-flex-positive: 1;
+    flex-grow: 1;
+    font-size: 12px;
+}
+
+.graphiql-container .history-contents p:hover {
+    cursor: pointer;
+}
+
+.graphiql-container .history-contents p span.history-label {
+    -webkit-box-flex: 1;
+    -ms-flex-positive: 1;
+    flex-grow: 1;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}.CodeMirror-info {
+     background: white;
+     border-radius: 2px;
+     -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+     -webkit-box-sizing: border-box;
+     box-sizing: border-box;
+     color: #555;
+     font-family:
+             system,
+             -apple-system,
+             'San Francisco',
+             '.SFNSDisplay-Regular',
+             'Segoe UI',
+             Segoe,
+             'Segoe WP',
+             'Helvetica Neue',
+             helvetica,
+             'Lucida Grande',
+             arial,
+             sans-serif;
+     font-size: 13px;
+     line-height: 16px;
+     margin: 8px -8px;
+     max-width: 400px;
+     opacity: 0;
+     overflow: hidden;
+     padding: 8px 8px;
+     position: fixed;
+     -webkit-transition: opacity 0.15s;
+     transition: opacity 0.15s;
+     z-index: 50;
+ }
+
+.CodeMirror-info :first-child {
+    margin-top: 0;
+}
+
+.CodeMirror-info :last-child {
+    margin-bottom: 0;
+}
+
+.CodeMirror-info p {
+    margin: 1em 0;
+}
+
+.CodeMirror-info .info-description {
+    color: #777;
+    line-height: 16px;
+    margin-top: 1em;
+    max-height: 80px;
+    overflow: hidden;
+}
+
+.CodeMirror-info .info-deprecation {
+    background: #fffae8;
+    -webkit-box-shadow: inset 0 1px 1px -1px #bfb063;
+    box-shadow: inset 0 1px 1px -1px #bfb063;
+    color: #867F70;
+    line-height: 16px;
+    margin: -8px;
+    margin-top: 8px;
+    max-height: 80px;
+    overflow: hidden;
+    padding: 8px;
+}
+
+.CodeMirror-info .info-deprecation-label {
+    color: #c79b2e;
+    cursor: default;
+    display: block;
+    font-size: 9px;
+    font-weight: bold;
+    letter-spacing: 1px;
+    line-height: 1;
+    padding-bottom: 5px;
+    text-transform: uppercase;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.CodeMirror-info .info-deprecation-label + * {
+    margin-top: 0;
+}
+
+.CodeMirror-info a {
+    text-decoration: none;
+}
+
+.CodeMirror-info a:hover {
+    text-decoration: underline;
+}
+
+.CodeMirror-info .type-name {
+    color: #CA9800;
+}
+
+.CodeMirror-info .field-name {
+    color: #1F61A0;
+}
+
+.CodeMirror-info .enum-value {
+    color: #0B7FC7;
+}
+
+.CodeMirror-info .arg-name {
+    color: #8B2BB9;
+}
+
+.CodeMirror-info .directive-name {
+    color: #B33086;
+}
+.CodeMirror-jump-token {
+    text-decoration: underline;
+    cursor: pointer;
+}
+/* The lint marker gutter */
+.CodeMirror-lint-markers {
+    width: 16px;
+}
+
+.CodeMirror-lint-tooltip {
+    background-color: infobackground;
+    border-radius: 4px 4px 4px 4px;
+    border: 1px solid black;
+    color: infotext;
+    font-family: monospace;
+    font-size: 10pt;
+    max-width: 600px;
+    opacity: 0;
+    overflow: hidden;
+    padding: 2px 5px;
+    position: fixed;
+    -webkit-transition: opacity .4s;
+    transition: opacity .4s;
+    white-space: pre-wrap;
+    z-index: 100;
+}
+
+.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
+    background-position: left bottom;
+    background-repeat: repeat-x;
+}
+
+.CodeMirror-lint-mark-error {
+    background-image:
+            url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
+;
+}
+
+.CodeMirror-lint-mark-warning {
+    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
+    background-position: center center;
+    background-repeat: no-repeat;
+    cursor: pointer;
+    display: inline-block;
+    height: 16px;
+    position: relative;
+    vertical-align: middle;
+    width: 16px;
+}
+
+.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
+    background-position: top left;
+    background-repeat: no-repeat;
+    padding-left: 18px;
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
+    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
+    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-multiple {
+    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
+    background-position: right bottom;
+    background-repeat: no-repeat;
+    width: 100%; height: 100%;
+}
+.graphiql-container .spinner-container {
+    height: 36px;
+    left: 50%;
+    position: absolute;
+    top: 50%;
+    -webkit-transform: translate(-50%, -50%);
+    transform: translate(-50%, -50%);
+    width: 36px;
+    z-index: 10;
+}
+
+.graphiql-container .spinner {
+    -webkit-animation: rotation .6s infinite linear;
+    animation: rotation .6s infinite linear;
+    border-bottom: 6px solid rgba(150, 150, 150, .15);
+    border-left: 6px solid rgba(150, 150, 150, .15);
+    border-radius: 100%;
+    border-right: 6px solid rgba(150, 150, 150, .15);
+    border-top: 6px solid rgba(150, 150, 150, .8);
+    display: inline-block;
+    height: 24px;
+    position: absolute;
+    vertical-align: middle;
+    width: 24px;
+}
+
+@-webkit-keyframes rotation {
+    from { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
+    to { -webkit-transform: rotate(359deg); transform: rotate(359deg); }
+}
+
+@keyframes rotation {
+    from { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
+    to { -webkit-transform: rotate(359deg); transform: rotate(359deg); }
+}
+.CodeMirror-hints {
+    background: white;
+    -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
+    font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
+    font-size: 13px;
+    list-style: none;
+    margin-left: -6px;
+    margin: 0;
+    max-height: 14.5em;
+    overflow-y: auto;
+    overflow: hidden;
+    padding: 0;
+    position: absolute;
+    z-index: 10;
+}
+
+.CodeMirror-hint {
+    border-top: solid 1px #f7f7f7;
+    color: #141823;
+    cursor: pointer;
+    margin: 0;
+    max-width: 300px;
+    overflow: hidden;
+    padding: 2px 6px;
+    white-space: pre;
+}
+
+li.CodeMirror-hint-active {
+    background-color: #08f;
+    border-top-color: white;
+    color: white;
+}
+
+.CodeMirror-hint-information {
+    border-top: solid 1px #c0c0c0;
+    max-width: 300px;
+    padding: 4px 6px;
+    position: relative;
+    z-index: 1;
+}
+
+.CodeMirror-hint-information:first-child {
+    border-bottom: solid 1px #c0c0c0;
+    border-top: none;
+    margin-bottom: -1px;
+}
+
+.CodeMirror-hint-deprecation {
+    background: #fffae8;
+    -webkit-box-shadow: inset 0 1px 1px -1px #bfb063;
+    box-shadow: inset 0 1px 1px -1px #bfb063;
+    color: #867F70;
+    font-family:
+            system,
+            -apple-system,
+            'San Francisco',
+            '.SFNSDisplay-Regular',
+            'Segoe UI',
+            Segoe,
+            'Segoe WP',
+            'Helvetica Neue',
+            helvetica,
+            'Lucida Grande',
+            arial,
+            sans-serif;
+    font-size: 13px;
+    line-height: 16px;
+    margin-top: 4px;
+    max-height: 80px;
+    overflow: hidden;
+    padding: 6px;
+}
+
+.CodeMirror-hint-deprecation .deprecation-label {
+    color: #c79b2e;
+    cursor: default;
+    display: block;
+    font-size: 9px;
+    font-weight: bold;
+    letter-spacing: 1px;
+    line-height: 1;
+    padding-bottom: 5px;
+    text-transform: uppercase;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.CodeMirror-hint-deprecation .deprecation-label + * {
+    margin-top: 0;
+}
+
+.CodeMirror-hint-deprecation :last-child {
+    margin-bottom: 0;
+}

+ 164 - 0
src/devApp/developVersion/graphqlService/component/schema/Create.js

@@ -0,0 +1,164 @@
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import {Modal, Input, notification, Spin} from 'antd';
+import {Mutation} from "react-apollo";
+import gql from "graphql-tag";
+import {ADD_PROJECT_AND_SCHEMA, SHOW_PROJECT} from '../../../../../gql'
+import './index.css';
+import {getCookie} from "../../../../../cookie";
+import {idGen, removeSpace} from "../../../../../func";
+import {FormattedMessage} from 'react-intl';
+
+class Create extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            schemaName: '',
+            schemaID: '',
+            visible: false,
+            confirmLoading: false,
+        };
+    }
+
+    redirectToLogin = () => {
+        this.props.history.push({
+            pathname: `/login`,
+        });
+    };
+
+    componentWillReceiveProps(nextProps) {
+        this.setState({
+            visible: nextProps.visible,
+        });
+    }
+
+
+    handleOk = (userID, create_project_and_schema) => {
+        let schemaId = idGen('schema');
+        let projectId = idGen('project');
+        let schemaName = this.state.schemaName;
+        let createdAt = new Date().getTime();
+
+        let schemaVarObj = {
+            schemaId,
+            schemaName,
+            user_id: userID,
+            schemaCreatedAt: createdAt,
+            schemaUpdatedAt: '',
+            schemaState: 'create',
+            schemaData: JSON.stringify([]),
+            reference: ''
+        };
+        let projectVarObj = {
+            projectCreatedAt: createdAt,
+            projectUpdatedAt: '',
+            database_id: '',
+            apiGWGroup_id: '',
+            projectName: removeSpace(schemaName),
+            deploy_id: '',
+            case_id: '',
+            projectId,
+            projectType: 'graphql',
+            cloud_id: '',
+            user_id: userID,
+            wxConfig_id: '',
+            schema_id: schemaId,
+            projectStatus: 'created'
+        };
+
+        this.setState({
+            confirmLoading: true,
+        });
+
+        create_project_and_schema({
+            variables: {...schemaVarObj, ...projectVarObj},
+            refetchQueries: [{query: gql(SHOW_PROJECT), variables: {projectType: 'graphql', user_id: userID}}]
+        });
+        setTimeout(() => {
+            this.setState({
+                visible: false,
+                confirmLoading: false,
+            });
+        }, 1000);
+        this.props.hideModal();
+        this.props.switchSidebar(schemaName);
+        this.props.history.push({
+            pathname: `/graphql-service/my-create/${schemaName}`,
+            state: {
+                schemaName,
+                schemaID: schemaId,
+                projectID: projectId,
+                create: true
+            }
+        });
+    };
+
+    render() {
+        let userID = this.props.userID || getCookie('user_id');
+        const {visible, confirmLoading} = this.state;
+
+        return (
+            <div>
+                <Mutation
+                    mutation={gql(ADD_PROJECT_AND_SCHEMA)}
+                >
+                    {(create_project_and_schema, {loading, error}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+                        return (
+                            <FormattedMessage id="Create Graphql Service">
+                                {(msg) => (
+                                    <Modal title={msg}
+                                           centered
+                                           visible={visible}
+                                           onOk={() => {
+                                               if (userID !== '' && userID !== undefined)
+                                                   this.handleOk(userID, create_project_and_schema)
+                                           }}
+                                           confirmLoading={confirmLoading}
+                                           onCancel={() => {
+                                               this.props.hideModal();
+                                           }}
+                                    >
+                                        <div>
+                                            <p><FormattedMessage id="schema name"/></p>
+                                            <FormattedMessage id="input schema name">
+                                                {(msg) => (
+                                                    <Input
+                                                        className='add-input'
+                                                        placeholder={msg}
+                                                        onChange={e => {
+                                                            e.persist();
+                                                            if (userID === '' || undefined) {
+                                                                notification.open({
+                                                                    message: '提醒',
+                                                                    description: '需要登录.',
+                                                                });
+                                                                this.redirectToLogin();
+                                                                this.props.hideModal();
+                                                            } else {
+                                                                this.setState({
+                                                                    schemaName: e.target.value,
+                                                                });
+                                                            }
+                                                        }}
+                                                    />
+                                                )}
+
+                                            </FormattedMessage>
+                                        </div>
+                                    </Modal>
+                                )}
+                            </FormattedMessage>
+
+                        )
+                    }}
+                </Mutation>
+            </div>
+        )
+    }
+}
+
+export default withRouter(Create);

+ 801 - 0
src/devApp/developVersion/graphqlService/component/schema/Schema.jsx

@@ -0,0 +1,801 @@
+import React, {Component} from 'react';
+
+import {Button, Col, Icon, Modal, Pagination, Row, Spin, Input, notification} from 'antd';
+import './index.css';
+import {Mutation, Query} from "react-apollo";
+import gql from "graphql-tag";
+import {
+    DELETE_PROJECT,
+    SHOW_SCHEMA,
+    SHOW_TABLE,
+    UPDATE_SCHEMA,
+    UPDATE_SCHEMA_PROJECT_NAME,
+    SEARCH_SCHEMA,ADD_PROJECT_AND_SCHEMA,
+    ADD_SCHEMA, SHOW_PROJECT
+} from '../../../../../gql'
+import Table from "./Table";
+import {request} from 'graphql-request'
+import {idGen, removeSpace} from "../../../../../func";
+import {graphqlUrl} from "../../../../../config";
+import {manageUsers} from "../../../../../config";
+import {FormattedMessage} from 'react-intl';
+
+const confirm = Modal.confirm;
+const Search = Input.Search;
+
+
+class Schema extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            currentTable: props.location.state === undefined ? '' : props.location.state.create ? 'add' : '',
+            // default schemaID and schemaName
+            schemaID: props.location.state === undefined ? props.schemaID : props.location.state.schemaID,
+            schemaName: props.location.state === undefined ? props.schemaName : props.location.state.schemaName,
+            projectID: props.location.state === undefined ? props.projectID : props.location.state.projectID,
+            editSchemaName: '',
+            allData: '',
+            data: '',
+            page: '1',
+            pageSize: '15',
+            once: 0
+        };
+    }
+
+    shouldComponentUpdate(nextProps, nextState) {
+        return true;
+    }
+
+    switchTable = (table) => {
+        this.setState({
+            currentTable: table
+        })
+    };
+
+    changeEditSchemaName = (e) => {
+        this.setState({
+            editSchemaName: e.target.value
+        })
+    };
+
+    clearEditSchemaName = () => {
+        this.setState({
+            editSchemaName: ''
+        })
+    };
+
+    findColumns = data => this.state.currentTable === '' ? [] : data.find(table => table.name === this.state.currentTable) ? data.find(table => table.name === this.state.currentTable).cols : [];
+
+    findRemark = data => this.state.currentTable === '' ? '' : data.find(table => table.name === this.state.currentTable) ? data.find(table => table.name === this.state.currentTable).remark : '';
+
+    goBack = () => {
+        this.setState({
+            currentTable: ''
+        })
+    };
+
+    showTablePagination = (page, pageSize, data) => {
+        // console.log(page);
+        // console.log(pageSize);
+        // console.log(data);
+        // 这个之所以这么麻烦,是因为 'apollo 不能存数据' 而 '分页又得用数据展示',
+        // 所以展示 table 的时候分了两个,
+        // 首先进入展示 15个 , 这前 15 个没有任何问题。
+        // 如果数据多于 15个,在按下第二页的时候 将数据通过 antd 的分页组件的回调函数传入 state,之后 table 的展示由 this.state.data 来接管
+        // 但是这又引起一个问题,通过 this.state.data 的展示如果进行删除,是不会通过 apollo 的,这也意味着数据库被删除了,而页面还存在
+        // 于是,在使用 this.state.data 的页面的 deleteTableButton 组件内调用该函数,重新刷新 this.state.data
+        this.setState({
+            page,
+            pageSize,
+            allData: data,
+            data: data.slice((page - 1) * pageSize, page * pageSize)
+        });
+    };
+
+    fetchData = (referenceID) => {
+        let schemaData;
+        if (localStorage.getItem('ecommerce') && localStorage.getItem('bills') && localStorage.getItem('order')) {
+            switch (referenceID) {
+                case 'ecommerce_schemaID':
+                    schemaData = localStorage.getItem('ecommerce');
+                    break;
+                case 'bills_schemaID':
+                    schemaData = localStorage.getItem('bills');
+                    break;
+                case 'order_schemaID':
+                    schemaData = localStorage.getItem('order');
+                    break;
+                default:
+                    schemaData = ''
+            }
+            return Promise.resolve(schemaData);
+        } else {
+            return new Promise((resolve, reject) => {
+                request(graphqlUrl, SEARCH_SCHEMA, {id: referenceID}).then(
+                    data => {
+                        if (data.schema_by_id !== null) {
+                            localStorage.setItem('ecommerce', data.caseSchema.find(obj => obj.schemaName === 'ecommerce').schemaData);
+                            localStorage.setItem('order', data.caseSchema.find(obj => obj.schemaName === 'order').schemaData);
+                            localStorage.setItem('bills', data.caseSchema.find(obj => obj.schemaName === 'bills').schemaData);
+                            resolve(data.schema_by_id.schemaData);
+                        }
+                    }
+                )
+            });
+
+        }
+    };
+
+    receiveDataFromTable = (data) => {
+        this.setState({
+            data
+        })
+    };
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            currentTable: next.location.state === undefined ? '' : next.location.state.create ? 'add' : '',
+            schemaID: next.schemaID,
+            schemaName: next.schemaName,
+            projectID:next.projectID,
+            data: '',
+            once: 0
+        });
+    }
+
+    render() {
+        let userID = this.props.userID;
+        let trialcase = this.props.trialcase;
+        return (
+            <Query query={gql(SHOW_TABLE)} variables={{schema_id: this.state.schemaID}}>
+
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+
+                        let copy;
+                        if (this.props.location.state)
+                            copy = this.props.location.state.copy;
+
+                        // 找到 copy 一定几率不显示的 问题原因,异步导致的未存先取
+                        if (data.schema_by_id === null && copy !== true) data = [];
+                        else {
+                            let reference = data.schema_by_id ? data.schema_by_id.reference : this.props.location.state.reference;
+                            data = data.schema_by_id ? JSON.parse(data.schema_by_id.schemaData) : [];
+                            if (data.length === 0 && reference !== '' && this.state.once === 0) {
+                                this.fetchData(reference).then((data) => {
+                                    // 会执行好多好多次
+                                    this.setState({
+                                        data: JSON.parse(data),
+                                        once: 1
+                                    })
+                                });
+
+                            }
+
+                        }
+
+                        return (
+                            <div>
+                                {
+                                    this.state.currentTable === '' ?
+                                        <div>
+                                            {
+                                                this.state.editSchemaName ?
+                                                    <ModifySchemaNameInput
+                                                        projectID={this.state.projectID}
+                                                        editSchemaName={this.state.editSchemaName}
+                                                        changeEditSchemaName={this.changeEditSchemaName}
+                                                        clearEditSchemaName={this.clearEditSchemaName}
+                                                        schemaID={this.state.schemaID}
+                                                        userID={userID}
+                                                        schemaName={this.state.schemaName}
+                                                        history={this.props.history}
+                                                    />
+                                                    :
+                                                    <div className={'schema'}>
+                                                        <span className={'schema-name'}>{
+                                                            trialcase?
+                                                                <FormattedMessage id={this.state.schemaName}/>
+                                                                :
+                                                                this.state.schemaName
+                                                        }</span>
+                                                        {
+                                                            userID.indexOf(manageUsers) > -1 ?
+                                                                <Icon style={{marginLeft: 15}}
+                                                                      type="edit"
+                                                                      theme="twoTone"
+                                                                      onClick={
+                                                                          () => {
+                                                                              this.setState({editSchemaName: this.state.schemaName})
+                                                                          }
+                                                                      }
+                                                                />
+                                                                :
+                                                                trialcase ?
+                                                                    ''
+                                                                    :
+                                                                    <Icon style={{marginLeft: 15}}
+                                                                          type="edit"
+                                                                          theme="twoTone"
+                                                                          onClick={
+                                                                              () => {
+                                                                                  this.setState({editSchemaName: this.state.schemaName})
+                                                                              }
+                                                                          }
+                                                                    />
+                                                        }
+                                                    </div>
+                                            }
+
+                                            <div className={'schema-table-list-title'}>
+                                                <Row>
+                                                    <Col span={10}><span
+                                                        className={'schema-table-title'}><FormattedMessage
+                                                        id="Name"/></span></Col>
+                                                    <Col span={10}><span
+                                                        className={'schema-table-title'}><FormattedMessage id="Remark"/></span></Col>
+                                                    <Col span={2} offset={2}>
+                                                        {
+                                                            userID.indexOf(manageUsers) > -1 ?
+                                                                <span
+                                                                    className={'schema-table-title'}
+                                                                    onClick={() => {
+                                                                        this.setState({
+                                                                            currentTable: 'add'
+                                                                        })
+                                                                    }}>
+                                                                    <Icon type="plus"/>
+                                                                </span>
+                                                                :
+                                                                trialcase ?
+                                                                    ''
+                                                                    :
+                                                                    <span
+                                                                        className={'schema-table-title'}
+                                                                        onClick={() => {
+                                                                            this.setState({
+                                                                                currentTable: 'add'
+                                                                            })
+                                                                        }}>
+                                                                        <Icon type="plus" style={{color: '#096dd9',cursor: 'pointer'}}/>
+                                                                </span>
+                                                        }
+
+                                                    </Col>
+                                                </Row>
+                                            </div>
+                                            <div>
+                                                {
+                                                    this.state.data ?
+                                                        this.state.data.map(table => (
+                                                            <div key={table.name}
+                                                                 className={'schema-table-list-content'}>
+                                                                <Row>
+                                                                    <Col
+                                                                        span={10}
+                                                                        onClick={() => this.switchTable(table.name)}
+                                                                    >
+                                                                        <span
+                                                                            className={'schema-table-content name'}>{table.name}</span>
+                                                                    </Col>
+                                                                    <Col span={10}>
+                                                                        <span
+                                                                            className={'schema-table-content'}>{table.remark}</span>
+                                                                    </Col>
+                                                                    <Col span={2} offset={2}>
+
+                                                                        <span className={'schema-table-content'}>
+                                                                            {
+                                                                                userID.indexOf(manageUsers) > -1 ?
+                                                                                    <DeleteTableButton
+                                                                                        currentTable={table.name}
+                                                                                        schemaData={data}
+                                                                                        schemaID={this.state.schemaID}
+                                                                                        userID={userID}
+                                                                                        showTablePagination={this.showTablePagination}
+                                                                                        page={this.state.page}
+                                                                                        pageSize={this.state.pageSize}
+                                                                                        fetchData={this.fetchData}
+                                                                                        location={this.props.location}
+                                                                                    />
+                                                                                    :
+                                                                                    trialcase ?
+                                                                                        ''
+                                                                                        :
+                                                                                        <DeleteTableButton
+                                                                                            currentTable={table.name}
+                                                                                            schemaData={data}
+                                                                                            schemaID={this.state.schemaID}
+                                                                                            userID={userID}
+                                                                                            showTablePagination={this.showTablePagination}
+                                                                                            page={this.state.page}
+                                                                                            pageSize={this.state.pageSize}
+                                                                                            fetchData={this.fetchData}
+                                                                                            location={this.props.location}
+                                                                                        />
+                                                                            }
+                                                                        </span>
+                                                                    </Col>
+                                                                </Row>
+                                                            </div>
+                                                        ))
+                                                        :
+                                                        data.slice(0, 15).map(table => (
+                                                            <div key={table.name}
+                                                                 className={'schema-table-list-content'}>
+                                                                <Row>
+                                                                    <Col
+                                                                        span={10}
+                                                                        onClick={() => this.switchTable(table.name)}
+                                                                    >
+                                                                <span
+                                                                    className={'schema-table-content name'}>{table.name}</span>
+                                                                    </Col>
+                                                                    <Col span={10}>
+                                                                <span
+                                                                    className={'schema-table-content'}>{table.remark}</span>
+                                                                    </Col>
+                                                                    <Col span={2} offset={2}>
+                                                                <span className={'schema-table-content'}>
+                                                                    {
+                                                                        userID.indexOf(manageUsers) > -1 ?
+                                                                            <DeleteTableButton
+                                                                                currentTable={table.name}
+                                                                                schemaData={data}
+                                                                                schemaID={this.state.schemaID}
+                                                                                showTablePagination={this.showTablePagination}
+                                                                                page={this.state.page}
+                                                                                pageSize={this.state.pageSize}
+                                                                                userID={userID}
+                                                                                fetchData={this.fetchData}
+                                                                                location={this.props.location}
+                                                                            />
+                                                                            :
+                                                                            trialcase ?
+                                                                                ''
+                                                                                :
+                                                                                <DeleteTableButton
+                                                                                    currentTable={table.name}
+                                                                                    schemaData={data}
+                                                                                    schemaID={this.state.schemaID}
+                                                                                    showTablePagination={this.showTablePagination}
+                                                                                    page={this.state.page}
+                                                                                    pageSize={this.state.pageSize}
+                                                                                    userID={userID}
+                                                                                    fetchData={this.fetchData}
+                                                                                    location={this.props.location}
+                                                                                />
+                                                                    }
+                                                                </span>
+                                                                    </Col>
+                                                                </Row>
+                                                            </div>
+                                                        ))
+                                                }
+                                            </div>
+
+                                            <div className={'schema-bottom'}>
+                                                <Row>
+                                                    <Col span={4}>
+                                                        {
+                                                            userID.indexOf(manageUsers) > -1 ?
+                                                                trialcase ?
+                                                                    <div>
+                                                                        <div style={{display: 'inline-block'}}
+                                                                             className={'delete-schema'}>
+                                                                            <CopySchemaButton
+                                                                                userID={userID}
+                                                                                history={this.props.history}
+                                                                                schemaID={this.state.schemaID}
+                                                                                schemaName={this.state.schemaName}
+                                                                            />
+                                                                        </div>
+                                                                        <div style={{display: 'inline-block'}}
+                                                                             className={'delete-schema'}>
+                                                                            <DeleteProjectButton
+                                                                                userID={userID}
+                                                                                projectID={this.state.projectID}
+                                                                                history={this.props.history}
+                                                                            />
+                                                                        </div>
+                                                                    </div>
+                                                                    :
+                                                                    <div className={'delete-schema'}>
+                                                                        <DeleteProjectButton
+                                                                            userID={userID}
+                                                                            projectID={this.state.projectID}
+                                                                            history={this.props.history}
+                                                                        />
+                                                                    </div>
+                                                                :
+                                                                trialcase ?
+                                                                    <div className={'delete-schema'}>
+                                                                        <CopySchemaButton
+                                                                            userID={userID}
+                                                                            history={this.props.history}
+                                                                            schemaID={this.state.schemaID}
+                                                                            schemaName={this.state.schemaName}
+                                                                        />
+                                                                    </div>
+                                                                    :
+                                                                    <div className={'delete-schema'}>
+                                                                        <DeleteProjectButton
+                                                                            userID={userID}
+                                                                            projectID={this.state.projectID}
+                                                                            history={this.props.history}
+                                                                        />
+                                                                    </div>
+                                                        }
+                                                    </Col>
+                                                    <Col span={4} offset={16}>
+                                                        <div className={'pagination'}>
+                                                            <Pagination
+                                                                simple
+                                                                defaultCurrent={1}
+                                                                hideOnSinglePage={true}
+                                                                defaultPageSize={15}
+                                                                total={data.length}
+                                                                onChange={(page, pageSize) => {
+                                                                    this.showTablePagination(page, pageSize, data)
+                                                                }}
+                                                            />
+                                                        </div>
+                                                    </Col>
+                                                </Row>
+                                            </div>
+                                        </div>
+                                        :
+                                        this.state.currentTable === 'add' ?
+                                            <Table
+                                                currentTable={''}
+                                                schemaData={data}
+                                                columns={[]}
+                                                remark=''
+                                                schemaID={this.state.schemaID}
+                                                schemaName={this.state.schemaName}
+                                                userID={userID}
+                                                goBack={this.goBack}
+                                                trialcase={trialcase}
+                                                fetchData={this.fetchData}
+                                                showTablePagination={this.showTablePagination}
+                                                page={this.state.page}
+                                                pageSize={this.state.pageSize}
+                                                history={this.props.history}
+                                                add={'add'}
+                                            /> :
+                                            <Table
+                                                currentTable={this.state.currentTable}
+                                                schemaData={data}
+                                                columns={this.findColumns(data.length !== 0 ? data : this.state.data)}
+                                                remark={this.findRemark(data.length !== 0 ? data : this.state.data)}
+                                                schemaID={this.state.schemaID}
+                                                schemaName={this.state.schemaName}
+                                                userID={userID}
+                                                goBack={this.goBack}
+                                                trialcase={trialcase}
+                                                fetchData={this.fetchData}
+                                                showTablePagination={this.showTablePagination}
+                                                page={this.state.page}
+                                                pageSize={this.state.pageSize}
+                                                history={this.props.history}
+                                                add={'whatever but not add'}
+                                            />
+                                }
+
+                            </div>
+                        );
+                    }
+                }
+            </Query>
+
+        )
+    }
+}
+
+export default Schema;
+
+class CopySchemaButton extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            schemaName: '',
+            schemaID: '',
+        };
+    }
+
+    redirectToLogin = () => {
+        this.props.history.push({
+            pathname: `/login`,
+        });
+    };
+
+    handleOk = (userID, create_project_and_schema) => {
+        let {history,schemaName,schemaID} = this.props;
+        let projectName = removeSpace(schemaName) + '_' + Math.random().toString().slice(-3) + Math.random().toString().slice(-4);
+        let schemaId = idGen('schema');
+        let projectId= idGen('project');
+        let createdAt = new Date().getTime();
+
+        let schemaVarObj = {
+            schemaId,
+            schemaName:projectName,
+            user_id: userID,
+            schemaCreatedAt: createdAt,
+            schemaUpdatedAt: '',
+            // copy的时候直接给 ok,不用检测
+            schemaState: 'ok',
+            schemaData: JSON.stringify([]),
+            reference:  schemaID
+        };
+        let projectVarObj = {
+            projectCreatedAt: createdAt,
+            projectUpdatedAt: '',
+            database_id: '',
+            apiGWGroup_id: '',
+            projectName,
+            deploy_id: '',
+            case_id: '',
+            projectId,
+            projectType: 'graphql',
+            cloud_id: '',
+            user_id: userID,
+            wxConfig_id: '',
+            schema_id: schemaId,
+            projectStatus: 'created'
+        };
+
+        create_project_and_schema({
+            variables: {...schemaVarObj,...projectVarObj},
+            refetchQueries:[{query: gql(SHOW_PROJECT), variables: {projectType:'graphql',user_id: userID}}]
+        });
+
+        history.push({
+            pathname: `/graphql-service/my-create/${projectName}`,
+            state: {
+                schemaName:projectName,
+                schemaID:schemaId,
+                projectID: projectId,
+                copy: true,
+                reference: schemaID
+            }
+        });
+    };
+
+    render() {
+        let {userID} = this.props;
+        // let schemaID = schemaName + '_schemaID';
+        return (
+            <div>
+                <Mutation
+                    mutation={gql(ADD_PROJECT_AND_SCHEMA)}
+                    // refetchQueries={[{query: gql(SHOW_SCHEMA), variables: {user_id: userID}}]}
+                >
+                    {(create_project_and_schema, {loading, error}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+
+                        return (
+                            <Button
+                                type="primary"
+                                onClick={() => {
+                                    if(userID !== '' && userID !== undefined && userID !== 'ioobot') {
+                                        this.handleOk(userID, create_project_and_schema);
+                                    }
+                                    else {
+                                        notification.open({
+                                            message: '提醒',
+                                            description: '需要登录.',
+                                        });
+                                        this.redirectToLogin();
+                                    }
+
+
+                                }}
+
+                            ><FormattedMessage id="copy"/></Button>
+                        )
+                    }}
+                </Mutation>
+            </div>
+        )
+    }
+}
+
+class DeleteProjectButton extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            projectID:props.projectID
+        }
+    }
+
+    showConfirm = (delete_graphql_project, projectID, userID) => {
+        let _this = this;
+        confirm({
+            title: 'Do you want to delete this schema?',
+            content: 'It cannot be found back!',
+            onOk() {
+                delete_graphql_project({variables: {id:projectID, user_id: userID}});
+                _this.props.history.push({
+                    pathname: '/graphql-service',
+                });
+            },
+            onCancel() {
+            },
+        });
+    };
+
+    render() {
+        let userID = this.props.userID;
+        let {projectID} = this.state;
+
+        return (
+            <Mutation
+                mutation={gql(DELETE_PROJECT)}
+                refetchQueries={[{query: gql(SHOW_PROJECT), variables: {projectType:'graphql',user_id: userID}}]}
+            >
+                {(delete_graphql_project, {loading, error}) => {
+                    if (error)
+                        return 'error';
+                    if (loading)
+                        return <Spin style={{marginLeft: 3}}/>;
+                    return (
+                        <Button
+                            type="danger"
+                            onClick={() => {
+                                this.showConfirm(delete_graphql_project, projectID, userID);
+                            }}
+                        >
+                            <FormattedMessage id="delete"/>
+                        </Button>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}
+
+class DeleteTableButton extends Component {
+    render() {
+        let schemaID = this.props.schemaID;
+        let userID = this.props.userID;
+
+        let varobj = {
+            id: schemaID,
+            updatedAt: new Date().getTime(),
+            schemaState: 'updated-delete-table',
+        };
+
+        return (
+            <Query query={gql(SHOW_TABLE)} variables={{schema_id: schemaID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+
+                        let schemaData = data;
+                        let referenceID = data.schema_by_id ? data.schema_by_id.reference : this.props.location.state.reference;
+
+                        return (
+                            <Mutation
+                                mutation={gql(UPDATE_SCHEMA)}
+                                refetchQueries={[{query: gql(SHOW_TABLE), variables: {schema_id: schemaID}}]}
+                            >
+                                {(update_schema, {loading, error}) => {
+                                    if (loading)
+                                        return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                                    if (error)
+                                        return 'error';
+
+                                    // 先 query 再 mutation,然后删除
+                                    let schemaCols;
+                                    if (schemaData.schema_by_id === null) schemaCols = [];
+                                    else schemaCols = JSON.parse(schemaData.schema_by_id.schemaData);
+
+                                    const index = this.props.schemaData.findIndex(obj => obj.name === this.props.currentTable);
+                                    if (index === -1) {
+                                        // 先取数据,然后删除,然后填充
+                                        this.props.fetchData(referenceID).then(value => {
+                                            schemaCols = JSON.parse(value);
+                                            const index = schemaCols.findIndex(obj => obj.name === this.props.currentTable);
+                                            schemaCols.splice(index, 1);
+                                        });
+                                    } else {
+                                        schemaCols.splice(index, 1);
+                                    }
+
+                                    return (
+                                        <Icon type="delete"
+                                              theme="twoTone"
+                                              onClick={() => {
+                                                  update_schema({
+                                                      variables: {
+                                                          ...varobj,
+                                                          schemaData: JSON.stringify(schemaCols)
+                                                      }
+                                                  });
+                                                  this.props.showTablePagination(this.props.page, this.props.pageSize, schemaCols)
+                                              }}>
+
+                                        </Icon>
+                                    )
+                                }}
+                            </Mutation>
+                        )
+                    }
+                }
+
+            </Query>
+
+
+        )
+    }
+}
+
+class ModifySchemaNameInput extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {}
+    }
+
+    render() {
+        let userID = this.props.userID;
+        let schemaName = this.props.schemaName;
+
+        return (
+            <Mutation mutation={gql(UPDATE_SCHEMA_PROJECT_NAME)}>
+                {(update_schema_project_name, {loading, error}) => {
+                    if (error)
+                        return 'error';
+                    if (loading)
+                        return <Spin style={{marginLeft: 3}}/>;
+                    return (
+                        <div className={'schema'}>
+                            <FormattedMessage id="Confirm">
+                                {(msg) => (
+                                    <Search
+                                        value={this.props.editSchemaName}
+                                        enterButton={msg}
+                                        style={{width: 500}}
+                                        size="large"
+                                        onChange={this.props.changeEditSchemaName}
+                                        onSearch={value => {
+                                            update_schema_project_name({
+                                                variables: {
+                                                    schemaID: this.props.schemaID,
+                                                    projectID:this.props.projectID,
+                                                    schemaName: value,
+                                                    updateAt: new Date().getTime()
+                                                }
+                                            }).then((res)=>{
+                                                // console.log('update_schema_project_name res',res)
+                                            });
+                                            this.props.history.push({
+                                                pathname: `/graphql-service/my-create/${value}`,
+                                                state: {
+                                                    schemaName: value,
+                                                    schemaID: this.props.schemaID,
+                                                }
+                                            });
+                                            this.props.clearEditSchemaName();
+                                        }}
+                                    />)}
+                            </FormattedMessage>
+
+                        </div>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}

+ 471 - 0
src/devApp/developVersion/graphqlService/component/schema/Table.js

@@ -0,0 +1,471 @@
+import React, {Component} from 'react';
+import axios from 'axios';
+
+import {Layout, Select, Input, Icon, Button, notification, Spin, Modal} from 'antd';
+import {UPDATE_SCHEMA, SHOW_SCHEMA, SHOW_TABLE} from "../../../../../gql";
+import gql from "graphql-tag";
+import {FormattedMessage} from 'react-intl';
+
+import {Mutation, Query} from "react-apollo";
+import {getCookie} from "../../../../../cookie";
+import {manageUsers} from "../../../../../config";
+import {checkSchemaUrl} from '../../../../../config';
+
+const Option = Select.Option;
+const {Content} = Layout;
+const confirm = Modal.confirm;
+
+class Table extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            currentTable: props.currentTable,
+            remark: props.remark,
+            columns: props.columns,
+            newColName: '',
+            newColType: 'type',
+            types: ['ID', 'String', 'Int', 'Float', 'Boolean', 'DateTime'],
+            descriptions: ['description', 'key', 'non-null', 'non-null-list', 'list'],
+            characterTips: false,
+        }
+    }
+
+    // 下面的 handlexxxx 全是 state 内部方法,用于操作视图
+    // cache 仅在提交删除整体使用
+
+    handleNameChange = (index) => {
+        return (e) => {
+            let columns = this.state.columns;
+            columns[index].name = e.target.value;
+            this.setState({
+                columns
+            })
+        };
+    };
+
+    handleNameNew = (e) => {
+        let r = /^[^\u4e00-\u9fa5]*$/;
+        if (r.test(e.target.value)) {
+            this.setState({
+                newColName: e.target.value,
+            })
+        } else {
+            this.setState({
+                characterTips: true
+            });
+            setTimeout(() => {
+                this.setState({
+                    characterTips: false
+                })
+            }, 2000)
+        }
+    };
+
+    handleTypeChange = (index) => {
+        return (value) => {
+            let columns = this.state.columns;
+            columns[index].type = value;
+            this.setState({
+                columns
+            });
+        }
+    };
+
+    handleTypeNew = (value) => {
+        if (this.state.newColName !== '') {
+            let columns = this.state.columns;
+            columns.push({name: this.state.newColName, type: value, description: 'description'});
+            this.setState({
+                columns,
+                newColName: '',
+                newColType: 'type'
+            })
+        } else {
+            notification['warning']({
+                message: 'Notification',
+                description: 'Input a field name first.',
+            });
+        }
+    };
+
+    handleDescChange = (index) => {
+        return (value) => {
+            let columns = this.state.columns;
+            columns[index].description = value;
+            this.setState({
+                columns
+            });
+        };
+    };
+
+    handleColDelete = (index) => {
+        return () => {
+            let columns = this.state.columns;
+            columns.splice(index, 1);
+            this.setState({
+                columns
+            });
+        }
+    };
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            currentTable: next.currentTable,
+            columns: next.columns,
+            remark: next.remark
+        });
+    };
+
+    render() {
+        let schemaID = this.props.schemaID || this.props.history.location.state.schemaID;
+        let schemaName = this.props.schemaName || this.props.history.location.state.schemaName;
+        let userID = this.props.userID || getCookie('user_id');
+        let trialcase = this.props.trialcase;
+
+        return (
+            <div>
+                <div className="column-menu" onClick={this.props.goBack}>
+                    <Icon type="left" style={{color: '#3187FA'}}/> {schemaName}
+                </div>
+
+                <Layout style={{zIndex: '0'}}>
+                    <Content style={{padding: '24px', minHeight: 280, background: '#fff'}}>
+                        <div className="column-content">
+                            <span className='table-title'> <FormattedMessage id="Table name"/></span>
+                            <FormattedMessage id="please input table name">
+                                {(msg) => (<Input
+                                    value={this.state.currentTable}
+                                    placeholder={msg}
+                                    style={{width: 200, margin: '10px 0'}}
+                                    onChange={(e) => {
+                                        this.setState({currentTable: e.target.value})
+                                    }}/>)}
+                            </FormattedMessage>
+
+                        </div>
+
+                        <div style={{marginBottom: 20}}>
+                            <span className='table-title'> <FormattedMessage id="Table remark"/></span>
+                            <FormattedMessage id="please input table remark">
+                                {(msg) => (<Input
+                                    value={this.state.remark}
+                                    placeholder={msg}
+                                    style={{width: 250, margin: '10px 0'}}
+                                    onChange={(e) => {
+                                        this.setState({remark: e.target.value})
+                                    }}/>)}
+                            </FormattedMessage>
+
+                        </div>
+
+                        <div>
+                            <span className='table-title'> <FormattedMessage id="Table fields"/></span>
+                            <span className='column-title'><FormattedMessage id="name"/></span>
+                            <span className='column-title'><FormattedMessage id="type"/></span>
+                            <span className='column-title'><FormattedMessage id="description"/></span>
+                            {
+                                this.state.columns.map((col, index) =>
+                                    <div key={index} style={{marginBottom: 3}}>
+                                        <Input
+                                            className="column-details"
+                                            value={col.name}
+                                            onChange={this.handleNameChange(index)}
+                                        />
+                                        <Select
+                                            className="column-details"
+                                            defaultValue={col.type}
+                                            onChange={this.handleTypeChange(index)}>
+                                            {
+                                                this.state.types.map((value) =>
+                                                    <Option key={Math.random() + 'types'}
+                                                            value={value.toLowerCase()}>{value}</Option>
+                                                )
+                                            }
+                                        </Select>
+                                        <Select
+                                            className="column-details"
+                                            defaultValue={col.description}
+                                            onChange={this.handleDescChange(index)}>
+                                            {
+                                                this.state.descriptions.map((value) =>
+                                                    <Option key={Math.random() + 'descriptions'}
+                                                            value={value.toLowerCase()}>{value}</Option>
+                                                )
+                                            }
+                                        </Select>
+                                        {
+                                            userID.indexOf(manageUsers) > -1 ?
+                                                <Icon type="delete" theme="twoTone" style={{cursor: 'pointer'}}
+                                                      onClick={this.handleColDelete(index)}/>
+                                                :
+                                                trialcase ?
+                                                    ''
+                                                    :
+                                                    <Icon type="delete" theme="twoTone" style={{cursor: 'pointer'}}
+                                                          onClick={this.handleColDelete(index)}/>
+                                        }
+                                    </div>
+                                )
+                            }
+                            <div>
+                                <FormattedMessage id="field name">
+                                    {(msg) => (<Input
+                                        className="column-details"
+                                        placeholder={msg}
+                                        onChange={this.handleNameNew}
+                                        value={this.state.newColName}
+                                    />)}
+                                </FormattedMessage>
+
+                                <Select
+                                    className="column-details"
+                                    defaultValue="type"
+                                    onChange={this.handleTypeNew}
+                                    value={this.state.newColType}>
+                                    {
+                                        this.state.types.map((value) =>
+                                            <Option key={Math.random()} value={value.toLowerCase()}>{value}</Option>
+                                        )
+                                    }
+                                </Select>
+                            </div>
+                        </div>
+
+                        {
+                            userID.indexOf(manageUsers) > -1 ?
+                                <div style={{marginTop: 20}}>
+                                    <UpdateTableButton
+                                        currentTable={this.state.currentTable}
+                                        columns={this.state.columns}
+                                        remark={this.state.remark}
+                                        schemaID={schemaID}
+                                        userID={userID}
+                                        schemaData={this.props.schemaData}
+                                        fetchData={this.props.fetchData}
+                                        showTablePagination={this.props.showTablePagination}
+                                        page={this.props.page}
+                                        pageSize={this.props.pageSize}
+                                        history={this.props.history}
+                                        schemaName={schemaName}
+                                        add={this.props.add}
+                                    />
+                                </div>
+                                :
+                                trialcase ?
+                                    ''
+                                    :
+                                    <div style={{marginTop: 20}}>
+                                        <UpdateTableButton
+                                            currentTable={this.state.currentTable}
+                                            columns={this.state.columns}
+                                            remark={this.state.remark}
+                                            schemaID={schemaID}
+                                            userID={userID}
+                                            schemaData={this.props.schemaData}
+                                            fetchData={this.props.fetchData}
+                                            showTablePagination={this.props.showTablePagination}
+                                            page={this.props.page}
+                                            pageSize={this.props.pageSize}
+                                            history={this.props.history}
+                                            schemaName={schemaName}
+                                            add={this.props.add}
+                                        />
+                                    </div>
+                        }
+
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default Table;
+
+
+class UpdateTableButton extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            originTableName: props.currentTable,
+            schemaCheck: ''
+        }
+    }
+
+    showConfirm = (schemaName, schemaID, data) => {
+        let _this = this;
+
+        if (this.props.add !== 'add') {
+            if(data.update_schema.schemaState === 'ok')
+                this.props.history.push({
+                    pathname: `/graphql-service/my-create/${schemaName}`,
+                    state: {
+                        schemaName,
+                        schemaID
+                    }
+                });
+        } else {
+            confirm({
+                title: '添加成功',
+                content: '继续添加数据表?',
+                onOk() {
+                    console.log('继续添加');
+                },
+                onCancel() {
+                    _this.props.history.push({
+                        pathname: `/graphql-service/my-create/${schemaName}`,
+                        state: {
+                            schemaName,
+                            schemaID
+                        }
+                    });
+                }
+            });
+        }
+    };
+
+    checkSchema = (update_schema, schemaID, varobj, schemaCols) => {
+        update_schema({
+            variables: {
+                ...varobj,
+                schemaData: JSON.stringify(schemaCols)
+            }
+        });
+        let _this = this;
+
+        axios.get(`${checkSchemaUrl}?schema=${schemaID}`)
+            .then((res) => {
+                console.log('check schema res', res);
+                if (res.data !== '') {
+                    update_schema({
+                        variables: {
+                            ...varobj,
+                            schemaData: JSON.stringify(schemaCols),
+                            schemaState: 'ok',
+                        }
+                    });
+                    _this.setState({
+                        schemaCheck: '',
+                    });
+                }
+            })
+            .catch((err) => {
+                // console.log('err',err);
+                // console.log('err.response',err.response);
+                _this.setState({
+                    schemaCheck: err.response.data,
+                });
+                console.log('err.response.data', err.response.data);
+            });
+    };
+
+    render() {
+        let schemaID = this.props.schemaID;
+        let schemaName = this.props.schemaName;
+        let userID = this.props.userID;
+
+        let varobj = {
+            id: schemaID,
+            updatedAt: new Date().getTime(),
+            schemaState: 'updated-update-table',
+        };
+
+        return (
+            <Query query={gql(SHOW_TABLE)} variables={{schema_id: schemaID}}>
+
+                {
+                    ({loading, error, data}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+
+                        let schemaData = data;
+                        let referenceID = '';
+                        if (data.schema_by_id)
+                            referenceID = data.schema_by_id.reference;
+
+                        return (
+                            <Mutation
+                                mutation={gql(UPDATE_SCHEMA)}
+                                onCompleted={(data) => this.showConfirm(schemaName, schemaID, data)}
+                                refetchQueries={[{query: gql(SHOW_TABLE), variables: {schema_id: schemaID}}]}
+                            >
+
+                                {(update_schema, {loading, error}) => {
+                                    if (loading)
+                                        return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                                    if (error)
+                                        return 'error';
+
+                                    // 更新代码
+                                    // 先 query 再 mutation,然后替换,做一个判断
+                                    let schemaCols;
+                                    if (schemaData.schema_by_id === null) schemaCols = [];
+                                    else schemaCols = JSON.parse(schemaData.schema_by_id.schemaData);
+
+                                    // 处理一下description的问题
+
+                                    let cols = this.props.columns;
+                                    cols.map(obj => {
+                                        if (obj.description === 'description')
+                                            obj.description = '';
+                                        return obj
+                                    });
+
+                                    let newTable = {
+                                        name: this.props.currentTable,
+                                        remark: this.props.remark,
+                                        cols
+                                    };
+
+                                    const index = this.state.originTableName === '' ? -2 : this.props.schemaData.findIndex(obj => obj.name === this.state.originTableName);
+                                    if (index === -2) {
+                                        if (referenceID !== '' && schemaCols.length === 0) {
+                                            this.props.fetchData(referenceID).then(value => {
+                                                schemaCols = JSON.parse(value);
+                                                schemaCols.push(newTable);
+                                            });
+                                        } else {
+                                            schemaCols.push(newTable);
+                                        }
+
+                                    } else if (index === -1) {
+                                        // 先取数据,然后替换,然后填充
+                                        this.props.fetchData(referenceID).then(value => {
+                                            schemaCols = JSON.parse(value);
+                                            const index = schemaCols.findIndex(obj => obj.name === this.state.originTableName);
+                                            schemaCols.splice(index, 1, newTable);
+                                        });
+                                    } else {
+                                        schemaCols.splice(index, 1, newTable);
+                                    }
+
+                                    return (
+                                        <div>
+                                            <div style={{display: 'inline-block'}}>
+                                                <Button type="primary" onClick={() => {
+                                                    this.checkSchema(update_schema, schemaID, varobj, schemaCols);
+                                                    this.props.showTablePagination(this.props.page, this.props.pageSize, schemaCols);
+                                                }}>
+                                                    <FormattedMessage id="save"/>
+                                                </Button>
+                                            </div>
+                                            {
+                                                this.state.schemaCheck !== '' ?
+                                                    <span>  failed, cause '{this.state.schemaCheck}'</span>
+                                                    :
+                                                    ''
+                                            }
+                                        </div>
+
+                                    )
+                                }}
+                            </Mutation>
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}

+ 144 - 0
src/devApp/developVersion/graphqlService/component/schema/index.css

@@ -0,0 +1,144 @@
+.current {
+  background-color: #0F83E8;
+  color: white;
+  text-align: center;
+  height: 50px;
+  line-height: 50px;
+  margin-top: 3px;
+}
+
+.title {
+  font-weight: bolder;
+  font-size: 18px;
+  margin: 10px 0 0 5px;
+}
+
+p.show {
+  margin: 5px 0 3px 10px;
+  cursor: pointer;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.wrapper {
+  position: relative;
+}
+
+.add {
+  position: absolute;
+  right: 10px;
+  top: 10px;
+  z-index: 2;
+}
+
+.add-input {
+  margin-top: 10px;
+}
+
+.column-menu {
+  /*padding: 0 24px;*/
+  /*position: fixed;*/
+  width: 100%;
+  z-index: 1;
+  line-height: 50px;
+  font-weight: 600;
+  background-color: white;
+  border-bottom: 1px solid #dcdadb;
+  cursor: pointer;
+}
+
+.column-title {
+  /*padding-left: 10px;*/
+  font-weight: 500;
+  font-size: 16px;
+  display: inline-block;
+  width: 168px;
+  height: 35px;
+  color: #3a76c5;
+}
+
+.column-content {
+  margin-bottom: 20px;
+}
+
+.column-details {
+  width: 120px;
+  margin: 10px 48px 10px 0 !important;
+}
+
+.table-title {
+  font-weight: 500;
+  font-size: 20px;
+  display: block;
+  height: 35px;
+  margin: 10px 0;
+}
+
+.table-title::before {
+  content: '|';
+  display: inline-block;
+  font-weight: 900;
+  color: #3187FA;
+  /*border-right: */
+}
+
+.remark {
+  font-weight: lighter;
+  font-size: smaller;
+}
+
+.schema {
+  width: 100%;
+  height: 50px;
+  margin-bottom: 10px;
+  cursor: pointer;
+}
+
+.schema-name {
+  color: #3187FA;
+  font-weight: bold;
+  font-size: 20px;
+  line-height: 50px;
+}
+
+.schema-table-list-title {
+  background-color: #f7f7f7;
+  height: 40px;
+  padding-left: 10px;
+}
+
+.schema-table-title {
+  line-height: 40px;
+  font-weight: bold;
+}
+
+.schema-table-list-content {
+  height: 40px;
+  padding-left: 10px;
+  border-bottom: 1px solid #e9e9e9;
+}
+
+.schema-table-list-content .ant-row div:first-child{
+    cursor: pointer;
+}
+
+.name {
+    color: #006eff;
+    cursor: pointer;
+}
+
+.schema-table-list-content:last-child {
+  border-bottom: none;
+}
+
+.schema-table-content {
+  line-height: 40px;
+}
+
+.schema-bottom {
+  margin-top: 70px;
+}
+
+.delete-schema {
+
+}

+ 54 - 0
src/devApp/developVersion/graphqlService/dataAnalysis/DataAnalysis.jsx

@@ -0,0 +1,54 @@
+import React, {Component} from 'react';
+
+import {Layout, Menu} from 'antd';
+import Metabase from "./metabase/Metabase";
+
+
+const { Content } = Layout;
+
+class DataAnalysis extends Component {
+    constructor() {
+        super();
+        this.state = {
+            menuLevel2: "metabase",
+        }
+    }
+
+    switchMenu = (menuName, e) => {
+        console.log('menuName', menuName, 'e', e);
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    render() {
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['metabase']}
+                    style={{padding: '0 24px', position: 'fixed', width: '100%', zIndex: '1'}}
+                    onClick={(e) => this.switchMenu('menuLevel2', e)}
+                    selectedKeys={[this.state.menuLevel2]}
+                >
+                    <Menu.Item key="metabase">metabase</Menu.Item>
+                </Menu>
+
+                <Layout style={{ padding: '24px', zIndex: '0'}}>
+                    <Content style={{ padding: '24px', minHeight: 280, background: '#fff',marginTop: '48px'  }}>
+                        {(() => {
+                            switch (this.state.menuLevel2) {
+                                case 'metabase':
+                                    return <Metabase />;
+                                default:
+                                    return <Metabase />
+                            }
+                        })()}
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default DataAnalysis;

+ 15 - 0
src/devApp/developVersion/graphqlService/dataAnalysis/metabase/Metabase.jsx

@@ -0,0 +1,15 @@
+import React, {Component} from 'react';
+
+class Metabase extends Component {
+
+
+    render() {
+        return (
+            <div>
+                Metabase
+            </div>
+        )
+    }
+}
+
+export default Metabase;

+ 58 - 0
src/devApp/developVersion/graphqlService/dataStorage/DataStorage.jsx

@@ -0,0 +1,58 @@
+import React, {Component} from 'react';
+
+import {Layout, Menu} from 'antd';
+
+import SchemaCreate from "./schemaCreate/SchemaCreate";
+import DatabaseSetting from "./databaseSetting/DatabaseSetting";
+
+const { Content } = Layout;
+
+class DataStorage extends Component {
+    constructor() {
+        super();
+        this.state = {
+            menuLevel2: "schema-creations",
+        }
+    }
+
+    switchMenu = (menuName, e) => {
+        console.log('menuName', menuName, 'e', e);
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    render() {
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['schema-creations']}
+                    style={{padding: '0 24px', position: 'fixed', width: '100%', zIndex: '1'}}
+                    onClick={(e) => this.switchMenu('menuLevel2', e)}
+                    selectedKeys={[this.state.menuLevel2]}
+                >
+                    <Menu.Item key="schema-creations">schema creations</Menu.Item>
+                    <Menu.Item key="database-settings">database settings</Menu.Item>
+                </Menu>
+
+                <Layout style={{ padding: '24px', zIndex: '0'}}>
+                    <Content style={{ padding: '24px', minHeight: 280, background: '#fff',marginTop: '48px'  }}>
+                        {(() => {
+                            switch (this.state.menuLevel2) {
+                                case 'schema-creations':
+                                    return <SchemaCreate />;
+                                case 'database-settings':
+                                    return <DatabaseSetting />;
+                                default:
+                                    return <SchemaCreate />
+                            }
+                        })()}
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default DataStorage;

+ 14 - 0
src/devApp/developVersion/graphqlService/dataStorage/databaseSetting/DatabaseSetting.jsx

@@ -0,0 +1,14 @@
+import React, {Component} from 'react';
+
+class DatabaseSetting extends Component {
+
+    render() {
+        return (
+            <div>
+                Database Setting
+            </div>
+        )
+    }
+}
+
+export default DatabaseSetting;

+ 83 - 0
src/devApp/developVersion/index.css

@@ -0,0 +1,83 @@
+.logo-wrapper {
+    width: 122px;
+    height: 31px;
+    margin: 16px 28px 16px 0;
+    float: left;
+}
+
+.logo {
+    background-image: url("../../images/logo.png");
+    background-repeat:no-repeat;
+    background-size:100% 100%;
+}
+
+.change-locale {
+    position: absolute;
+    top: 0;
+    right: 24px;
+    float: right;
+}
+
+.login-button {
+     position: absolute;
+     top: 15px;
+     right: 20px;
+     float: right;
+ }
+
+.login-nickname {
+    position: absolute;
+    top: 0;
+    right: 20px;
+    float: right;
+    font-size: 20px;
+    color: white;
+}
+
+.layout-content {
+    padding: 24px;
+    min-height: 280px;
+    background: #fff;
+    margin-top: 48px;
+}
+
+.layout-content-deploy {
+    padding: 0;
+}
+
+.user-detail{
+    width: 200px;
+    /*height: 350px;*/
+    background-color: #fff;
+    border-radius: 4px;
+    box-shadow: 0 4px 8px 0 #c5d9e8;
+}
+
+.user-info {
+    background-color: #fff !important;
+    padding: 28px 16px 12px !important;
+    font-size: 14px;
+    height: 86px!important;
+    color: #3f536e;
+}
+
+.user-info-nickname {
+    color: #3f536e;
+    font-size: 16px;
+    line-height: 20px;
+    font-weight: 700;
+    margin-bottom: 5px;
+}
+
+.user-info-email {
+    color: #8dabc4;
+    font-size: 15px;
+    min-height: 30px;
+    line-height: 15px;
+    word-break: break-all;
+    white-space: normal;
+}
+
+.login-out {
+    margin-top: 20px;
+}

+ 63 - 0
src/devApp/developVersion/quantService/QuantService.jsx

@@ -0,0 +1,63 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+import {Layout, Menu} from 'antd';
+
+import QuantConfig from "./quantConfig/QuantConfig";
+import Deploy from "../common/deploy/Deploy";
+import QuantManage from "./quantManage/QuantManage";
+
+const { Content } = Layout;
+
+class QuantService extends Component {
+    constructor() {
+        super();
+        this.state = {
+            menuLevel3: "quant-config",
+        }
+    }
+
+    switchMenu = (menuName, e) => {
+        // console.log('menuName', menuName, 'e', e);
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    render() {
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['quant-config']}
+                    style={{padding: '0 24px', position: 'fixed', width: '100%', zIndex: '1',lineHeight:'50px',fontWeight:600}}
+                    onClick={(e) => this.switchMenu('menuLevel3', e)}
+                    selectedKeys={[this.state.menuLevel3]}
+                >
+                    <Menu.Item key="quant-config"><FormattedMessage id="config"/></Menu.Item>
+                    <Menu.Item key="quant-deploy"><FormattedMessage id="deploy"/></Menu.Item>
+                    <Menu.Item key="quant-manage"><FormattedMessage id="manage"/></Menu.Item>
+                </Menu>
+
+                <Layout style={{ padding: '24px', zIndex: '0'}}>
+                    <Content style={{ padding: '24px', minHeight: 280, background: '#fff',marginTop: '48px'  }}>
+                        {(() => {
+                            switch (this.state.menuLevel3) {
+                                case 'quant-config':
+                                    return <QuantConfig />;
+                                case 'quant-deploy':
+                                    // return <Deploy />;
+                                    return <QuantConfig />;
+                                case 'quant-manage':
+                                    return <QuantManage />;
+                                default:
+                                    return <QuantConfig />
+                            }
+                        })()}
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default QuantService;

+ 17 - 0
src/devApp/developVersion/quantService/quantConfig/QuantConfig.jsx

@@ -0,0 +1,17 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+class QuantConfig extends Component {
+
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+                {/*Only members to use this website.*/}
+            </div>
+        )
+    }
+}
+
+export default QuantConfig;

+ 16 - 0
src/devApp/developVersion/quantService/quantManage/QuantManage.jsx

@@ -0,0 +1,16 @@
+import React, {Component} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+class QuantManage extends Component {
+
+    render() {
+        return (
+            <div>
+                <p><b><FormattedMessage id="It is under development, please look forward to it. Thank you for your attention"/></b></p>
+                {/*Only members to use this website.*/}
+            </div>
+        )
+    }
+}
+
+export default QuantManage;

+ 72 - 0
src/devApp/developVersion/wechatService/WxTrialCase.js

@@ -0,0 +1,72 @@
+import React, {Component} from 'react';
+import {Layout, Menu} from 'antd';
+import {FormattedMessage} from 'react-intl';
+
+import WxConfig from "./wxConfig/WxConfig";
+import WxDeploy from "../common/deploy/Deploy";
+import Manage from '../common/manage/Manage';
+
+const {Content} = Layout;
+
+class WxTrialCase extends Component {
+    constructor() {
+        super();
+        this.state = {
+            menuLevel3: "wechat-config",
+            userID: 'ioobot'
+        }
+    }
+
+    switchMenu = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    render() {
+        let configID = this.props.location.state ? this.props.location.state.configID : "ecommerce_wxConfigID";
+        let appName = this.props.location.state ? this.props.location.state.appName : "ecommerce";
+        let projectID = this.props.location.state ? this.props.location.state.projectID : "";
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['wechat-config']}
+                    style={{
+                        padding: '0 24px',
+                        position: 'fixed',
+                        width: '100%',
+                        zIndex: '1',
+                        lineHeight: '50px',
+                        fontWeight: 600
+                    }}
+                    onClick={(e) => this.switchMenu('menuLevel3', e)}
+                    selectedKeys={[this.state.menuLevel3]}
+                >
+                    <Menu.Item key="wechat-config"><FormattedMessage id="config"/></Menu.Item>
+                    <Menu.Item key="wechat-deploy"><FormattedMessage id="deploy"/></Menu.Item>
+                    <Menu.Item key="wechat-manage"><FormattedMessage id="manage"/></Menu.Item>
+                </Menu>
+
+                <Layout style={{padding: '24px', zIndex: '0'}}>
+                    <Content style={{padding: '24px', minHeight: 280, background: '#fff', marginTop: '48px'}}>
+                        {(() => {
+                            switch (this.state.menuLevel3) {
+                                case 'wechat-config':
+                                    return <WxConfig trialcase={true} userID={this.state.userID} projectID={projectID} defaultAppName={'ecommerce'} defaultConfigID={'ecommerce_wxConfigID'} history={this.props.history} location={this.props.location}/>;
+                                case 'wechat-deploy':
+                                    return <WxDeploy trialcase={true} userID={this.state.userID} projectID={projectID} kind={'wx'}/>;
+                                case 'wechat-manage':
+                                    return <Manage trialcase={true} userID={this.state.userID} projectID={projectID} kind={'wx'}/>;
+                                default:
+                                    return <WxConfig/>
+                            }
+                        })()}
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default WxTrialCase;

+ 94 - 0
src/devApp/developVersion/wechatService/WxUserCreate.js

@@ -0,0 +1,94 @@
+import React, {Component} from 'react';
+
+import {Layout, Menu} from 'antd';
+
+import {getIdUrl} from '../../../config'
+
+import WxConfig from "./wxConfig/WxConfig";
+import WxDeploy from "../common/deploy/Deploy";
+import Manage from '../common/manage/Manage';
+import axios from 'axios';
+import {FormattedMessage} from 'react-intl';
+axios.defaults.withCredentials = true;
+
+const {Content} = Layout;
+
+class WxUserCreate extends Component {
+    constructor() {
+        super();
+        this.state = {
+            menuLevel3: "wechat-config",
+            userID: '',
+            getIdUrl
+        }
+    }
+
+    componentWillMount() {
+        let _this = this;
+        axios.get(this.state.getIdUrl)
+            .then((res) => {
+                if (res.data !== '') {
+                    _this.setState({
+                        userID: res.data
+                    })
+                }
+            })
+            .catch(function (err) {
+                console.log(err);
+            });
+    }
+
+    switchMenu = (menuName, e) => {
+        this.setState({
+            [menuName]: e.key,
+        });
+    };
+
+    render() {
+        let configID = this.props.location.state ? this.props.location.state.configID : "ecommerce_wxConfigID";
+        let appName = this.props.location.state ? this.props.location.state.appName : "ecommerce";
+        let projectID = this.props.location.state ? this.props.location.state.projectID : "";
+        // console.log('userId',this.state.userID,"projectID",projectID);
+        return (
+            <div>
+                <Menu
+                    mode="horizontal"
+                    defaultSelectedKeys={['wechat-config']}
+                    style={{
+                        padding: '0 24px',
+                        position: 'fixed',
+                        width: '100%',
+                        zIndex: '1',
+                        lineHeight: '50px',
+                        fontWeight: 600
+                    }}
+                    onClick={(e) => this.switchMenu('menuLevel3', e)}
+                    selectedKeys={[this.state.menuLevel3]}
+                >
+                    <Menu.Item key="wechat-config"><FormattedMessage id="config"/></Menu.Item>
+                    <Menu.Item key="wechat-deploy"><FormattedMessage id="deploy"/></Menu.Item>
+                    <Menu.Item key="wechat-manage"><FormattedMessage id="manage"/></Menu.Item>
+                </Menu>
+
+                <Layout style={{padding: '24px', zIndex: '0'}}>
+                    <Content style={{padding: '24px', minHeight: 280, background: '#fff', marginTop: '48px'}}>
+                        {(() => {
+                            switch (this.state.menuLevel3) {
+                                case 'wechat-config':
+                                    return <WxConfig trialcase={false} userID={this.state.userID} projectID={projectID} history={this.props.history} location={this.props.location}/>;
+                                case 'wechat-deploy':
+                                    return <WxDeploy trialcase={false} userID={this.state.userID} projectID={projectID}/>;
+                                case 'wechat-manage':
+                                    return <Manage trialcase={false} userID={this.state.userID} projectID={projectID}/>;
+                                default:
+                                    return <WxConfig/>
+                            }
+                        })()}
+                    </Content>
+                </Layout>
+            </div>
+        )
+    }
+}
+
+export default WxUserCreate;

+ 370 - 0
src/devApp/developVersion/wechatService/wxConfig/WxConfig.jsx

@@ -0,0 +1,370 @@
+import React, {Component} from 'react';
+import {Input, Spin, Button, Icon, Modal, Tooltip} from 'antd';
+import './index.css';
+import {UPDATE_WXCONFIG, SHOW_WXCONTENT, DELETE_WXCONFIG, SHOW_WXCONFIG,DELETE_PROJECT,SHOW_PROJECT} from "../../../../gql";
+import gql from "graphql-tag";
+import {Mutation, Query} from "react-apollo";
+import {getCookie} from "../../../../cookie";
+import {FormattedMessage} from 'react-intl';
+const confirm = Modal.confirm;
+
+const valueToKey = {
+    'appName': 'appName',
+    'appID': 'appID',
+    'appSecret': 'appSecret',
+    'enter_url': 'enter_url',
+    'token': 'token',
+    'welcome_words': 'welcome_words',
+    'pay_api_key': 'pay_api_key',
+    'attach': 'attach',
+    'mch_id': 'mch_id',
+    'body': 'body',
+    'spbill_create_ip': 'spbill_create_ip',
+    'notify_url': 'notify_url',
+};
+
+const toolTipTitle = {
+    'appName': 'its appName',
+    'appID': 'its appID',
+    'appSecret': 'its appSecret',
+    'enter_url': 'its enter_url',
+    'token': 'its token',
+    'welcome_words': 'its welcome_words',
+    'pay_api_key': 'its pay_api_key',
+    'attach': 'its attach',
+    'mch_id': 'its mch_id',
+    'body': 'its body',
+    'spbill_create_ip': 'its spbill_create_ip',
+    'notify_url': 'its notify_url',
+};
+
+const youMustFill = {
+    'appName': true,
+    'appID': true,
+    'appSecret': true,
+    'enter_url': true,
+    'token': true,
+    'welcome_words': true,
+    'pay_api_key': true,
+    'attach': true,
+    'mch_id': true,
+    'body': true,
+    'spbill_create_ip': true,
+    'notify_url': true,
+};
+
+class WxConfig extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            config1s: ['appName', 'appID', 'appSecret'],
+            config2s: ['enter_url', 'token', 'welcome_words'],
+            config3s: ['pay_api_key', 'attach', 'mch_id', 'body', 'spbill_create_ip', 'notify_url'],
+            configID: props.location.state === undefined ? props.defaultConfigID : props.location.state.configID,
+            appName: props.location.state === undefined ? props.defaultAppName : props.location.state.appName,
+            projectID: props.location.state === undefined ? props.projectID : props.location.state.projectID,
+            userID:props.userID
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            userID:next.userID,
+            configID: next.location.state === undefined ? next.defaultConfigID : next.location.state.configID,
+            appName: next.location.state === undefined ? next.defaultAppName : next.location.state.appName,
+            projectID: next.location.state === undefined ? next.projectID : next.location.state.projectID,
+        });
+    }
+
+    render() {
+        return (
+            <Query query={gql(SHOW_WXCONTENT)} variables={{id: this.state.configID}}>
+                {
+                    ({loading, error, data}) => {
+                        if (loading) {
+                            return <Spin style={{marginLeft: 3}}/>
+                        }
+                        if (error) {
+                            return 'error!';
+                        }
+                        let {history, location, trialcase} = this.props;
+                        let {appName, configID, config1s, config2s, config3s, projectID,userID} = this.state;
+                        return (
+                            <Display
+                                config1s={config1s}
+                                config2s={config2s}
+                                config3s={config3s}
+                                userID={userID}
+                                projectID={projectID}
+                                configID={configID}
+                                appName={appName}
+                                location={location}
+                                history={history}
+                                trialcase={trialcase}
+                                data={data.wxConfig_by_id}
+                            />
+                        )
+                    }
+                }
+            </Query>
+        )
+    }
+}
+
+export default WxConfig;
+
+class Display extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            userID:props.userID,
+            configID: props.configID,
+            appName: props.appName,
+            projectID:props.projectID,
+            mch_id: props.data === null ? '' : props.data.mch_id,
+            notify_url: props.data === null ? '' : props.data.notify_url,
+            appSecret: props.data === null ? '' : props.data.appSecret,
+            appID: props.data === null ? '' : props.data.appID,
+            token: props.data === null ? '' : props.data.token,
+            spbill_create_ip: props.data === null ? '' : props.data.spbill_create_ip,
+            enter_url: props.data === null ? '' : props.data.enter_url,
+            pay_api_key: props.data === null ? '' : props.data.pay_api_key,
+            body: props.data === null ? '' : props.data.body,
+            welcome_words: props.data === null ? '' : props.data.welcome_words,
+            attach: props.data === null ? '' : props.data.attach,
+        }
+    }
+
+    componentWillReceiveProps(next) {
+        this.setState({
+            userID:next.userID,
+            configID: next.configID,
+            appName: next.appName,
+            projectID:next.projectID,
+            mch_id: next.data === null ? '' : next.data.mch_id,
+            notify_url: next.data === null ? '' : next.data.notify_url,
+            appSecret: next.data === null ? '' : next.data.appSecret,
+            appID: next.data === null ? '' : next.data.appID,
+            token: next.data === null ? '' : next.data.token,
+            spbill_create_ip: next.data === null ? '' : next.data.spbill_create_ip,
+            enter_url: next.data === null ? '' : next.data.enter_url,
+            pay_api_key: next.data === null ? '' : next.data.pay_api_key,
+            body: next.data === null ? '' : next.data.body,
+            welcome_words: next.data === null ? '' : next.data.welcome_words,
+            attach: next.data === null ? '' : next.data.attach,
+        });
+    }
+
+
+    switchConfig = (label) => {
+        return (e) => {
+            this.setState({
+                [label]: e.target.value
+            })
+        };
+    };
+
+    render() {
+        let {config1s, config2s, config3s} = this.props;
+        let {userID,configID, appName,projectID, mch_id, notify_url, appSecret, appID, token, spbill_create_ip, enter_url, pay_api_key, body, welcome_words, attach} = this.state;
+        return (
+            <div>
+                <div className={'schema-name'}>
+                    <FormattedMessage id="WeChat Subscription Data"/>
+                </div>
+                {
+                    config1s.map(config => (
+                        <div key={config} style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill[config]?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey[config]}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle[config]}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input value={this.state[config]} style={{width: 200}}
+                                   onChange={this.switchConfig(config)}/>
+                        </div>
+                    ))
+                }
+                <div className={'schema-name'}>
+                    <FormattedMessage id="Server configurations"/>
+                </div>
+                {
+                    config2s.map(config => (
+                        <div key={config} style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill[config]?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey[config]}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle[config]}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input value={this.state[config]} style={{width: 200}}
+                                   onChange={this.switchConfig(config)}/>
+                        </div>
+                    ))
+                }
+                <div className={'schema-name'}>
+                    <FormattedMessage id="Pay configurations"/>
+                </div>
+                {
+                    config3s.map(config => (
+                        <div key={config} style={{marginBottom: 10}}>
+                            <span className='vice-title'>
+                                {
+                                    youMustFill[config]?
+                                        <span style={{color: 'red', display: 'inline', marginRight: 10}}>*</span>
+                                        :
+                                        ''
+                                }
+                                <FormattedMessage id={valueToKey[config]}/>
+                                &nbsp;
+                                <Tooltip placement="top" title={toolTipTitle[config]}>
+                                    <Icon type="question-circle"/>
+                                </Tooltip>
+                            </span>
+                            <Input value={this.state[config]} style={{width: 200}}
+                                   onChange={this.switchConfig(config)}/>
+                        </div>
+                    ))
+                }
+                {
+                    this.props.trialcase ?
+                        ""
+                        :
+                        <div>
+                            <UpdateWXConfigButton
+                                id={configID}
+                                appName={appName}
+                                mch_id={mch_id}
+                                notify_url={notify_url}
+                                appSecret={appSecret}
+                                appID={appID}
+                                token={token}
+                                spbill_create_ip={spbill_create_ip}
+                                enter_url={enter_url}
+                                pay_api_key={pay_api_key}
+                                body={body}
+                                welcome_words={welcome_words}
+                                attach={attach}
+                            />
+                            {/*<DeleteWXProjectButton*/}
+                                {/*id={configID}*/}
+                                {/*userID={userID}*/}
+                                {/*projectID={projectID}*/}
+                                {/*history={this.props.history}*/}
+                            {/*/>*/}
+                        </div>
+
+                }
+            </div>
+        )
+    }
+}
+
+class UpdateWXConfigButton extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {}
+    }
+
+    render() {
+        return (
+            <Mutation mutation={gql(UPDATE_WXCONFIG)}>
+                {(update_wxConfig, {loading, error}) => {
+                    if (loading)
+                        return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                    if (error)
+                        return 'error';
+                    let {id, appName, mch_id, notify_url, appSecret, appID, token, spbill_create_ip, enter_url, pay_api_key, body, welcome_words, attach} = this.props;
+                    return (
+                        <Button type={'primary'} onClick={() => {
+                            update_wxConfig({
+                                variables: {
+                                    id,
+                                    appName,
+                                    mch_id,
+                                    notify_url,
+                                    appSecret,
+                                    appID,
+                                    token,
+                                    spbill_create_ip,
+                                    enter_url,
+                                    pay_api_key,
+                                    body,
+                                    welcome_words,
+                                    attach,
+                                    updatedAt: new Date().getTime()
+                                }
+                            });
+                        }}><FormattedMessage id="save"/></Button>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}
+
+class DeleteWXProjectButton extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            userID: props.userID,
+            projectID:props.projectID
+        }
+    }
+
+    showConfirm = (delete_wx_project,  projectID, userID) => {
+        let _this = this;
+        confirm({
+            title: 'Do you want to delete this config?',
+            content: 'It cannot be found back!',
+            onOk() {
+                delete_wx_project({variables: {id:projectID, user_id: userID}});
+                _this.props.history.push({
+                    pathname: `/wechat-service/trial-case/index`,
+                });
+            },
+            onCancel() {
+            },
+        });
+    };
+
+    render() {
+        let {userID,projectID} = this.state;
+
+        return (
+            <Mutation
+                mutation={gql(DELETE_PROJECT)}
+                refetchQueries={[{query: gql(SHOW_PROJECT), variables: {projectType:'wx',user_id: userID}}]}
+            >
+                {(delete_wx_project, {loading, error}) => {
+                    if (loading)
+                        return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                    if (error)
+                        return 'error';
+                    return (
+                        <Button
+                            type={'danger'}
+                            onClick={() => {
+                                this.showConfirm(delete_wx_project, projectID, userID);
+                            }}><FormattedMessage id="delete"/></Button>
+                    )
+                }}
+            </Mutation>
+        )
+    }
+}

+ 5 - 0
src/devApp/developVersion/wechatService/wxConfig/index.css

@@ -0,0 +1,5 @@
+.vice-title {
+  width: 150px;
+  display: inline-block;
+  font-size: 14px;
+}

+ 178 - 0
src/devApp/developVersion/wechatService/wxCreate/WxCreate.js

@@ -0,0 +1,178 @@
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import {Modal, Input, notification, Spin} from 'antd';
+
+import {Mutation, Query} from "react-apollo";
+import gql from "graphql-tag";
+
+import {ADD_PROJECT_AND_WX, SHOW_PROJECT} from '../../../../gql'
+import './index.css';
+import {getCookie} from "../../../../cookie";
+import {idGen} from "../../../../func";
+
+class WxCreate extends Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            appName: '',
+            configID: '',
+            visible: false,
+            confirmLoading: false,
+            mch_id: '',
+            notify_url: '',
+            appSecret: '',
+            appID: '',
+            token: '',
+            spbill_create_ip: '',
+            enter_url: '',
+            pay_api_key: '',
+            body: '',
+            welcome_words: '',
+            attach: ''
+        };
+    }
+
+    componentWillReceiveProps(nextProps) {
+        this.setState({
+            visible: nextProps.visible,
+        });
+    }
+
+    redirectToLogin = () => {
+        this.props.history.push({
+            pathname: `/login`,
+        });
+    };
+
+    handleOk = (userID, create_project_and_wxConfig) => {
+        let {mch_id, notify_url, appSecret, appID, token, spbill_create_ip, enter_url, pay_api_key, body, welcome_words, attach} = this.state;
+        let wxConfigId = idGen('wxConfig');
+        let projectId= idGen('project');
+        let appName = this.state.appName;
+        let createdAt = new Date().getTime();
+
+        let wxConfigVarObj = {
+            wxConfigId,
+            appName,
+            user_id: userID,
+            wxCreatedAt: createdAt,
+            wxUpdatedAt: '',
+            mch_id,
+            notify_url,
+            appSecret,
+            appID,
+            token,
+            spbill_create_ip,
+            enter_url,
+            pay_api_key,
+            body,
+            welcome_words,
+            attach
+        };
+        let projectVarObj = {
+            projectCreatedAt: createdAt,
+            projectUpdatedAt: '',
+            database_id: '',
+            apiGWGroup_id: '',
+            projectName: appName,
+            deploy_id: '',
+            projectId,
+            projectType: 'wx',
+            cloud_id: '',
+            user_id: userID,
+            wxConfig_id: wxConfigId,
+            schema_id: '',
+            case_id: '',
+            projectStatus: 'created'
+        };
+
+        this.setState({
+            confirmLoading: true,
+        });
+
+        create_project_and_wxConfig({
+            variables: {
+                ...wxConfigVarObj,
+                ...projectVarObj
+            },
+            refetchQueries:[{query: gql(SHOW_PROJECT), variables: {projectType:'wx',user_id: userID}}]
+        });
+
+        setTimeout(() => {
+            this.setState({
+                visible: false,
+                confirmLoading: false,
+            });
+        }, 1000);
+
+        this.props.hideModal();
+        this.props.switchSidebar(appName);
+        this.props.history.push({
+            pathname: `/wechat-service/my-create/${appName}`,
+            state: {
+                appName,
+                configID: wxConfigId,
+                create: true
+            }
+        });
+    };
+
+    render() {
+        let userID = this.props.userID || getCookie('user_id');
+        const {visible, confirmLoading} = this.state;
+
+        return (
+            <div>
+                <Mutation
+                    mutation={gql(ADD_PROJECT_AND_WX)}
+                >
+                    {(create_project_and_wxConfig, {loading, error}) => {
+                        if (loading)
+                            return <Spin style={{marginLeft: 30, marginTop: 10}}/>;
+                        if (error)
+                            return 'error';
+                        return (
+                            <Modal title="Create Wechat Service"
+                                   centered
+                                   visible={visible}
+                                   onOk={() => {
+                                       if(userID !== '' && userID !== undefined)
+                                           this.handleOk(userID, create_project_and_wxConfig)
+                                   }}
+                                   confirmLoading={confirmLoading}
+                                   onCancel={() => {
+                                       this.props.hideModal();
+                                   }}
+                            >
+                                <div>
+                                    <p>app name</p>
+                                    <Input
+                                        className='add-input'
+                                        placeholder="input app_name"
+                                        onChange={e => {
+                                            e.persist();
+                                            if(userID === '' || undefined) {
+                                                notification.open({
+                                                    message: '提醒',
+                                                    description: '需要登录.',
+                                                });
+                                                this.redirectToLogin();
+                                                this.props.hideModal();
+                                            } else {
+                                                this.setState({
+                                                    appName: e.target.value,
+                                                });
+                                            }
+                                        }}
+                                    />
+                                </div>
+                            </Modal>
+                        )
+                    }}
+                </Mutation>
+            </div>
+        )
+    }
+}
+
+export default withRouter(WxCreate);

+ 3 - 0
src/devApp/developVersion/wechatService/wxCreate/index.css

@@ -0,0 +1,3 @@
+.add-input {
+  margin-top: 10px;
+}

+ 114 - 0
src/devApp/index.css

@@ -0,0 +1,114 @@
+.layout-home {
+    background-color: white;
+}
+
+/* header 相关开始 */
+.logo {
+    width: 25%;
+    height: 31px;
+    padding: 16px 24px 16px 70px;
+    float: left;
+}
+
+.logo img {
+    width: 25%;
+    padding: 0 0 40px 20px
+}
+/* header 结束 */
+
+/* intro 相关开始 */
+.intro-wrapper {
+    width: 100%;
+    height: 500px;
+    background: url("https://workbench-1254337200.cos.ap-shanghai.myqcloud.com/bgt2.jpg") no-repeat;
+    background-size: cover;
+}
+
+.intro h2 {
+    color: white;
+    font-size: 40px;
+    font-weight: 800;
+}
+
+.intro h3 {
+    color: lightgrey;
+    font-size: 25px;
+    font-weight: 800;
+}
+
+.home-btn-group {
+    padding: 60px 60px 30px 60px;
+}
+
+.home-btn {
+    border-radius: 30px;
+}
+
+.intro-below h4 {
+    color: lightgrey;
+    font-size: 20px;
+    font-weight: 400;
+}
+
+/* intro 结束 */
+
+/*case 相关开始*/
+.cases-show-wrapper {
+    margin-top: 100px;
+}
+
+.cases-show-title {
+    text-align: center;
+    font-weight: bold;
+    font-size: 38px;
+    line-height: 40px;
+}
+
+.case-show-tabs {
+    width: 60%;
+    margin: 60px auto 0;
+}
+
+.cover-div {
+
+}
+
+.cover-img {
+    width: 170px;
+    height: 170px;
+    margin-top: 50px;
+    border: 5px solid #ebebeb;
+}
+
+.logo-cover-div {
+    display: inline-block;
+    margin: 50px 50px 20px 0;
+    text-align: center;
+    opacity: 0.25
+}
+
+.logo-cover-div-on {
+    opacity: 1
+}
+
+.logo-cover-img {
+    width: 100px;
+    height: 100px;
+}
+
+.case-detail-title {
+    font-weight: bold;
+    font-size: 38px;
+    line-height: 40px;
+}
+
+.case-detail-attention {
+    color: red;
+}
+
+.case-detail-description {
+    font-size: 22px;
+    line-height: 30px;
+    margin-top: 20px;
+}
+/*case 结束*/