App.jsx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import React, {Component} from 'react';
  2. import {Layout, Menu, Button, Spin, Icon, LocaleProvider, Dropdown, Avatar, Badge} from 'antd';
  3. import {BrowserRouter as Router, Route, Link, Switch, Redirect} from "react-router-dom";
  4. import {Query} from "react-apollo";
  5. import gql from "graphql-tag";
  6. import moment from 'moment';
  7. import 'moment/locale/zh-cn';
  8. import {FormattedMessage} from 'react-intl';
  9. import zhCN from 'antd/lib/locale-provider/zh_CN';
  10. import QuantService from "./quantService/QuantService";
  11. import TrialCase from "./graphqlService/TrialCase";
  12. import UserCreate from "./graphqlService/UserCreate";
  13. import WxTrialCase from "./wechatService/WxTrialCase";
  14. import WxUserCreate from "./wechatService/WxUserCreate";
  15. import Login from "../login/Login";
  16. import Ticket from '../ticket/Ticket';
  17. import './graphqlService/component/graphql/index.css';
  18. import './index.css'
  19. import Create from "./graphqlService/component/schema/Create";
  20. import WxCreate from "./wechatService/wxCreate/WxCreate";
  21. import {CASE_AND_PROJECT, GET_USER} from "../gql";
  22. import axios from 'axios';
  23. import {getCookie, setCookie} from "../cookie";
  24. import {logoutUrl} from "../config";
  25. import Graphiql from "./common/Graphiql";
  26. axios.defaults.withCredentials = true;
  27. const {SubMenu} = Menu;
  28. const {Header, Sider} = Layout;
  29. moment.locale('en');
  30. class App extends Component {
  31. constructor(props) {
  32. super(props);
  33. this.state = {
  34. menuLevel1: "graphql-service",
  35. sideBar: "ecommerce",
  36. collapsed: false,
  37. inlineCollapsed: false,
  38. language: props.language,
  39. locale: props.language === "中文" ? zhCN : undefined,
  40. languageButton: props.language === "中文" ? "English" : "中文",
  41. visible: false,
  42. wxVisible: false,
  43. userID: '',
  44. avatar: ''
  45. };
  46. }
  47. componentWillMount() {
  48. let urlList = window.location.pathname.split("/");
  49. let urlListLength = urlList.length;
  50. if (urlListLength > 2) {
  51. this.setState({menuLevel1: urlList[1]});
  52. if (urlList[3] !== 'index') {
  53. this.setState({sideBar: urlList[3]});
  54. }
  55. }
  56. let userID = getCookie('user_id');
  57. if (userID === undefined || '') {
  58. axios.get(this.state.getIdUrl)
  59. .then((res) => {
  60. if (res.data !== '') {
  61. setCookie("user_id", res.data);
  62. this.setState({
  63. userID: res.data
  64. })
  65. }
  66. })
  67. .catch(function (err) {
  68. console.log(err);
  69. });
  70. } else {
  71. this.setState({
  72. userID
  73. })
  74. }
  75. }
  76. onCollapse = (collapsed) => {
  77. this.setState({collapsed});
  78. };
  79. switchMenu = (menuName, e) => {
  80. this.setState({
  81. [menuName]: e.key,
  82. });
  83. };
  84. switchMenuTab = (menuName, e) => {
  85. this.setState({
  86. [menuName]: e.key,
  87. });
  88. if (e.key === "graphql-service") {
  89. this.setState({sideBar: "ecommerce"});
  90. }
  91. };
  92. switchSidebar = (value) => {
  93. this.setState({
  94. sideBar: value,
  95. });
  96. };
  97. switchMenuLevel = (menuName, value) => {
  98. this.setState({
  99. [menuName]: value,
  100. });
  101. };
  102. showModal = () => {
  103. this.setState({
  104. visible: true,
  105. });
  106. };
  107. wxShowModal = () => {
  108. this.setState({
  109. wxVisible: true,
  110. });
  111. };
  112. hideModal = () => {
  113. this.setState({
  114. visible: false,
  115. });
  116. };
  117. wxHideModal = () => {
  118. this.setState({
  119. wxVisible: false,
  120. });
  121. };
  122. changeLocale = (e) => {
  123. e.stopPropagation();
  124. let {language} = this.state;
  125. // console.log('app language',language);
  126. let changeLanguage = language === "中文" ? "English" : "中文";
  127. let local = language === "中文" ? undefined : zhCN;
  128. let languageButton = language === "中文" ? "中文" : "English";
  129. sessionStorage.setItem("language", changeLanguage);
  130. this.props.changeLanguage(changeLanguage);
  131. this.setState({
  132. language: changeLanguage,
  133. locale: local,
  134. languageButton: languageButton
  135. });
  136. if (!local) {
  137. moment.locale('en');
  138. } else {
  139. moment.locale('zh-cn');
  140. }
  141. };
  142. render() {
  143. const {locale, languageButton, visible, wxVisible} = this.state;
  144. return (
  145. <Layout style={{minHeight: '100vh'}}>
  146. <Header className="header" style={{position: 'fixed', zIndex: 1, width: '100%'}}>
  147. <Link to="/"
  148. onClick={() => this.setState({menuLevel1: "graphql-service", sideBar: "ecommerce"})}>
  149. <div className="logo-wrapper">
  150. <div className='logo'/>
  151. </div>
  152. </Link>
  153. <Menu
  154. theme="dark"
  155. mode="horizontal"
  156. selectedKeys={[this.state.menuLevel1]}
  157. style={{lineHeight: '64px'}}
  158. onClick={(e) => this.switchMenuTab('menuLevel1', e)}
  159. >
  160. <Menu.Item key="graphql-service">
  161. <Link to="/developer/graphql-service/trial-case/index"><FormattedMessage
  162. id="Graphql Service"/></Link>
  163. </Menu.Item>
  164. <Menu.Item key="wechat-service">
  165. <Link to="/developer/wechat-service/trial-case/index"><FormattedMessage
  166. id="Wechat Service"/></Link>
  167. </Menu.Item>
  168. <Menu.Item key="quant-service">
  169. <Link to="/developer/quant-service/trial-case/index"><FormattedMessage id="Quantization Service"/></Link>
  170. </Menu.Item>
  171. </Menu>
  172. {
  173. this.state.userID === '' ?
  174. <Link to="/developer/login">
  175. <Button className='login-button' type='primary'
  176. onClick={() => this.switchMenuLevel('menuLevel1', 'user')}>
  177. <FormattedMessage id="Login"/></Button>
  178. </Link>
  179. :
  180. <User
  181. userID={this.state.userID}
  182. languageButton={this.state.languageButton}
  183. changeLocale={this.changeLocale}
  184. switchMenuLevel={this.switchMenuLevel}
  185. />
  186. }
  187. </Header>
  188. {(() => {
  189. switch (this.state.menuLevel1) {
  190. case 'graphql-service':
  191. return (
  192. <Sider
  193. width={200}
  194. style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
  195. collapsible
  196. collapsed={this.state.collapsed}
  197. onCollapse={this.onCollapse}
  198. >
  199. <GraphqlSidebar inlineCollapsed={this.state.inlineCollapsed}
  200. sideBar={this.state.sideBar} switchMenu={this.switchMenu}
  201. showModal={this.showModal}/>
  202. </Sider>
  203. );
  204. case 'wechat-service':
  205. return (
  206. <Sider
  207. width={200}
  208. style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
  209. collapsible
  210. collapsed={this.state.collapsed}
  211. onCollapse={this.onCollapse}
  212. >
  213. <WxConfigSiderbar inlineCollapsed={this.state.inlineCollapsed}
  214. sideBar={this.state.sideBar} switchMenu={this.switchMenu}
  215. wxShowModal={this.wxShowModal}/>
  216. </Sider>
  217. );
  218. case 'quant-service':
  219. return (
  220. <Sider
  221. width={200}
  222. style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
  223. collapsible
  224. collapsed={this.state.collapsed}
  225. onCollapse={this.onCollapse}
  226. >
  227. <Menu
  228. theme="dark"
  229. mode="inline"
  230. inlineCollapsed={this.state.inlineCollapsed}
  231. defaultSelectedKeys={['quant-service']}
  232. defaultOpenKeys={['trial-case']}
  233. // openKeys={['cloud-function']}
  234. onClick={(e) => this.switchMenu('sideBar', e)}
  235. selectedKeys={['quant-service']}
  236. style={{
  237. borderRight: 0,
  238. overflow: 'auto',
  239. height: '100vh',
  240. left: '0',
  241. width: '200px',
  242. position: 'fixed'
  243. }}
  244. >
  245. <SubMenu key="trial-case"
  246. title={<span><Icon type="appstore" theme="twoTone"/>
  247. <span><FormattedMessage id="Case Show"/></span></span>}>
  248. <Menu.Item key="quant-service">
  249. <Link to="/developer/quant-service/trial-case/quant case">quant case</Link>
  250. </Menu.Item>
  251. </SubMenu>
  252. <Menu.Item key="instructions">
  253. <a href="https://ioobot-document.netlify.com/" title="instructions"
  254. target="instructions">
  255. <Icon type="file-text" theme="twoTone"/>
  256. <span><FormattedMessage id="Instructions"/></span>
  257. </a>
  258. </Menu.Item>
  259. </Menu>
  260. </Sider>
  261. );
  262. case 'user':
  263. return (
  264. <Sider
  265. width={200}
  266. style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
  267. collapsible
  268. collapsed={this.state.collapsed}
  269. onCollapse={this.onCollapse}
  270. >
  271. <Menu
  272. theme="dark"
  273. defaultSelectedKeys={['cloud-settings']}
  274. onClick={(e) => this.switchMenu('sideBar', e)}
  275. selectedKeys={[this.state.sideBar]}
  276. style={{
  277. borderRight: 0,
  278. overflow: 'auto',
  279. height: '100vh',
  280. left: '0',
  281. width: '200px',
  282. position: 'fixed'
  283. }}
  284. >
  285. <Menu.Item key="account">
  286. <Icon type="setting" theme="twoTone"/>
  287. <span><FormattedMessage id="Account center"/></span>
  288. <Link to="/developer/login/account"/>
  289. </Menu.Item>
  290. <Menu.Item key="cloud-settings">
  291. <Icon type="cloud" theme="twoTone"/>
  292. <span><FormattedMessage id="Cloud settings"/></span>
  293. <Link to="/developer/login/cloud"/>
  294. </Menu.Item>
  295. </Menu>
  296. </Sider>
  297. );
  298. case 'ticket':
  299. return (
  300. ''
  301. );
  302. default:
  303. return (
  304. <Sider
  305. width={200}
  306. style={{background: '#fff', marginTop: '64px', zIndex: '0'}}
  307. collapsible
  308. collapsed={this.state.collapsed}
  309. onCollapse={this.onCollapse}
  310. >
  311. <GraphqlSidebar inlineCollapsed={this.state.inlineCollapsed}
  312. sideBar={this.state.sideBar} switchMenu={this.switchMenu}
  313. showModal={this.showModal}/>
  314. </Sider>
  315. );
  316. }
  317. })()}
  318. <Create visible={visible} hideModal={this.hideModal} switchSidebar={this.switchSidebar}/>
  319. <WxCreate visible={wxVisible} hideModal={this.wxHideModal} switchSidebar={this.switchSidebar}/>
  320. <LocaleProvider locale={locale}>
  321. <Layout style={{marginTop: '64px', zIndex: '0'}}
  322. key={locale ? locale.locale : 'en'/* Have to refresh for production environment */}>
  323. <Switch>
  324. <Route path="/developer" exact component={TrialCase}/>
  325. <Route path="/developer/graphql-service/trial-case/:case" component={TrialCase}/>
  326. <Route path="/developer/graphql-service/my-create/:case" component={UserCreate}/>
  327. <Route path="/developer/wechat-service/trial-case/:case" component={WxTrialCase}/>
  328. <Route path="/developer/wechat-service/my-create/:case" component={WxUserCreate}/>
  329. <Route path="/developer/quant-service/:sidebar/:case" component={QuantService}/>
  330. <Route path="/developer/login/:setting" component={Login}/>
  331. <Route path="/developer/login" component={Login}/>
  332. <Route path="/developer/ticket" component={Ticket}/>
  333. <Route path="/developer/graphiql" component={Graphiql}/>
  334. <Redirect path="*" to="/"/>
  335. </Switch>
  336. </Layout>
  337. </LocaleProvider>
  338. </Layout>
  339. );
  340. }
  341. }
  342. export default App;
  343. class GraphqlSidebar extends Component {
  344. constructor(props) {
  345. super(props);
  346. this.state = {
  347. userID: getCookie('user_id'),
  348. }
  349. }
  350. render() {
  351. return (
  352. <Query query={gql(CASE_AND_PROJECT)} variables={{projectType: 'graphql', user_id: this.state.userID}}>
  353. {
  354. ({loading, error, data}) => {
  355. if (loading) return <Spin style={{marginLeft: 3}}/>;
  356. if (error) return 'error!';
  357. // console.log('CASE_AND_PROJECT data', data);
  358. data.caseProject.forEach((project) => {
  359. switch (project.schema_id.schemaName) {
  360. case 'ecommerce' :
  361. localStorage.setItem('ecommerce', project.schema_id.schemaData);
  362. break;
  363. case 'order':
  364. localStorage.setItem('order', project.schema_id.schemaData);
  365. break;
  366. case 'bills':
  367. localStorage.setItem('bills', project.schema_id.schemaData);
  368. break;
  369. default:
  370. break;
  371. }
  372. });
  373. return (
  374. <Menu
  375. theme="dark"
  376. mode="inline"
  377. inlineCollapsed={this.props.inlineCollapsed}
  378. defaultSelectedKeys={['ecommerce']}
  379. defaultOpenKeys={['trial-case', 'my-create']}
  380. // openKeys={['trial-case', 'my-create']}
  381. onClick={(e) => this.props.switchMenu('sideBar', e)}
  382. selectedKeys={[this.props.sideBar]}
  383. style={{
  384. borderRight: 0,
  385. overflow: 'auto',
  386. height: '100vh',
  387. left: '0',
  388. width: '200px',
  389. position: 'fixed'
  390. }}
  391. >
  392. <SubMenu
  393. key="trial-case"
  394. title={<span><Icon type="appstore" theme="twoTone"/><span><FormattedMessage
  395. id="Case Show"/></span></span>}>
  396. {
  397. data.caseProject.map((project) =>
  398. <Menu.Item key={project.projectName}>
  399. <Link to={{
  400. pathname: `/developer/graphql-service/trial-case/${project.projectName}`,
  401. state: {
  402. schemaName: project.projectName,
  403. schemaID: project.schema_id.id,
  404. projectID: project.id
  405. }
  406. }}><FormattedMessage id={project.projectName}/></Link>
  407. </Menu.Item>)
  408. }
  409. </SubMenu>
  410. <Menu.Item key="create-graphql" onClick={this.props.showModal}>
  411. <Icon type="edit" theme="twoTone"/>
  412. <span><FormattedMessage id="Create"/></span>
  413. <Icon type="plus" style={{
  414. position: 'absolute',
  415. top: '35%',
  416. right: '6px',
  417. color: 'white'
  418. }}/>
  419. </Menu.Item>
  420. <SubMenu
  421. key="my-create"
  422. title={<span><Icon type="user" theme="outlined"/><span><FormattedMessage
  423. id="My Create"/></span></span>}>
  424. {
  425. data.project.map((project) =>
  426. <Menu.Item key={project.projectName}>
  427. <Link to={{
  428. pathname: `/developer/graphql-service/my-create/${project.projectName}`,
  429. state: {
  430. schemaName: project.projectName,
  431. schemaID: project.schema_id.id,
  432. projectID: project.id
  433. }
  434. }}>{project.projectName}</Link>
  435. </Menu.Item>)
  436. }
  437. </SubMenu>
  438. <Menu.Item key="instructions">
  439. <a href="https://ioobot-document.netlify.com/" title="instructions" target="_blank"
  440. rel="noopener noreferrer">
  441. <Icon type="file-text" theme="twoTone"/>
  442. <span><FormattedMessage id="Instructions"/></span>
  443. </a>
  444. </Menu.Item>
  445. </Menu>
  446. )
  447. }
  448. }
  449. </Query>
  450. )
  451. }
  452. }
  453. class WxConfigSiderbar extends Component {
  454. constructor(props) {
  455. super(props);
  456. this.state = {
  457. userID: getCookie('user_id'),
  458. }
  459. }
  460. render() {
  461. return (
  462. <Query query={gql(CASE_AND_PROJECT)} variables={{projectType: 'wx', user_id: this.state.userID}}>
  463. {
  464. ({loading, error, data}) => {
  465. // console.log('CASE_WXCONFIG_AND_PROJECT data', data);
  466. if (loading) return <Spin style={{marginLeft: 3}}/>;
  467. if (error) return 'error!';
  468. return (
  469. <Menu
  470. theme="dark"
  471. mode="inline"
  472. inlineCollapsed={this.props.inlineCollapsed}
  473. // defaultSelectedKeys={['my-wechat']}
  474. defaultOpenKeys={['trial-case', 'my-create']}
  475. // openKeys={['trial-case', 'my-create']}
  476. onClick={(e) => this.props.switchMenu('sideBar', e)}
  477. selectedKeys={[this.props.sideBar]}
  478. style={{
  479. borderRight: 0,
  480. overflow: 'auto',
  481. height: '100vh',
  482. left: '0',
  483. width: '200px',
  484. position: 'fixed'
  485. }}
  486. >
  487. <SubMenu key="trial-case" title={<span><Icon type="appstore" theme="twoTone"/>
  488. <span><FormattedMessage id="Case Show"/></span>
  489. </span>}>
  490. {
  491. data.caseProject.map((project) => {
  492. if (project) {
  493. let appName = project.projectName;
  494. let configID = project.wxConfig_id.id;
  495. let projectID = project.id;
  496. return (
  497. <Menu.Item key={appName}>
  498. <Link to={{
  499. pathname: `/developer/wechat-service/trial-case/${appName}`,
  500. state: {
  501. appName,
  502. configID,
  503. projectID
  504. }
  505. }}><FormattedMessage id={appName}/></Link>
  506. </Menu.Item>
  507. )
  508. }
  509. return false;
  510. })
  511. }
  512. </SubMenu>
  513. <Menu.Item key="create-config" onClick={this.props.wxShowModal}>
  514. <Icon type="edit" theme="twoTone"/>
  515. <span><FormattedMessage id="Create"/></span>
  516. <Icon type="plus" style={{
  517. position: 'absolute',
  518. top: '35%',
  519. right: '6px',
  520. color: 'white'
  521. }}/>
  522. </Menu.Item>
  523. <SubMenu key="my-create" title={<span><Icon type="user" theme="outlined"/>
  524. <span><FormattedMessage id="My Create"/></span>
  525. </span>}>
  526. {
  527. data.project.map((project) => {
  528. if (project) {
  529. let appName = project.projectName;
  530. let configID = project.wxConfig_id.id;
  531. let projectID = project.id;
  532. return (
  533. <Menu.Item key={appName}>
  534. <Link to={{
  535. pathname: `/developer/wechat-service/my-create/${appName}`,
  536. state: {
  537. appName,
  538. configID,
  539. projectID
  540. }
  541. }}>{appName}</Link>
  542. </Menu.Item>
  543. )
  544. }
  545. return false;
  546. })
  547. }
  548. </SubMenu>
  549. <Menu.Item key="instructions">
  550. <a href="https://ioobot-document.netlify.com/" title="instructions" target="_blank"
  551. rel="noopener noreferrer">
  552. <Icon type="file-text" theme="twoTone"/>
  553. <span><FormattedMessage id="Instructions"/></span>
  554. </a>
  555. </Menu.Item>
  556. </Menu>
  557. )
  558. }
  559. }
  560. </Query>
  561. )
  562. }
  563. }
  564. class User extends Component {
  565. constructor(props) {
  566. super(props);
  567. this.state = {
  568. show: false
  569. }
  570. }
  571. logout = () => {
  572. axios.get(logoutUrl)
  573. .then((res) => {
  574. console.log('logout success',res);
  575. setCookie("user_id", '');
  576. })
  577. .catch((err) => {
  578. });
  579. };
  580. render() {
  581. return (
  582. <Query query={gql(GET_USER)} variables={{id: this.props.userID}}>
  583. {
  584. ({loading, error, data}) => {
  585. if (loading) {
  586. return <Spin style={{marginLeft: 3}}/>
  587. }
  588. if (error) {
  589. return 'error!';
  590. }
  591. let user = data.user_by_id;
  592. if (user !== null) {
  593. const menu = (
  594. <Menu className={'user-detail'}>
  595. <Menu.Item className={'user-info'}>
  596. <p className={'user-info-nickname'}>{user.nickname}</p>
  597. <p className={'user-info-email'}>{user.email}</p>
  598. </Menu.Item>
  599. <Menu.Item>
  600. <a href='https://www.ioobot.com' onClick={(e) => {
  601. e.preventDefault();
  602. this.props.changeLocale(e)
  603. }}>{this.props.languageButton}</a>
  604. </Menu.Item>
  605. <Menu.Item>
  606. <Link to="/developer/login">
  607. <div onClick={() => this.props.switchMenuLevel('menuLevel1', 'user')}>
  608. <FormattedMessage id="Account center"/>
  609. </div>
  610. </Link>
  611. </Menu.Item>
  612. <Menu.Item>
  613. <Link to="/developer/ticket">
  614. <div onClick={() => this.props.switchMenuLevel('menuLevel1', 'ticket')}>
  615. <FormattedMessage id="Submit Support Ticket"/>
  616. </div>
  617. </Link>
  618. </Menu.Item>
  619. <Menu.Item className={'login-out'}>
  620. <a href='https://www.ioobot.com' onClick={(e) => {
  621. e.preventDefault();
  622. this.logout();
  623. }}><FormattedMessage id="exit"/></a>
  624. </Menu.Item>
  625. </Menu>
  626. );
  627. return (
  628. <div className='login-nickname' onClick={()=>{}}>
  629. <Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
  630. <div>
  631. <span style={{ marginRight: 5 }}>
  632. <Badge dot><Avatar shape="user" icon="user" /></Badge>
  633. </span>
  634. {/*<Icon type="down" />*/}
  635. </div>
  636. </Dropdown>
  637. </div>
  638. )
  639. }
  640. }
  641. }
  642. </Query>
  643. )
  644. }
  645. }