Commit 4f79c3bb authored by nano's avatar nano

update

parent 3d31834a
...@@ -2,11 +2,15 @@ ...@@ -2,11 +2,15 @@
"parser": "babel-eslint", "parser": "babel-eslint",
"extends": "airbnb", "extends": "airbnb",
"rules": { "rules": {
"react/jsx-space-before-closing":[0],
"generator-star-spacing": [0], "generator-star-spacing": [0],
"semi":[0],
"no-console":[0],
"consistent-return": [0], "consistent-return": [0],
"react/forbid-prop-types": [0], "react/forbid-prop-types": [0],
"react/jsx-filename-extension": [1, { "extensions": [".js"] }], "react/jsx-filename-extension": [1, { "extensions": [".js"] }],
"global-require": [1], "global-require": [1],
"react/jsx-closing-bracket-location":[0],
"import/prefer-default-export": [0], "import/prefer-default-export": [0],
"react/jsx-no-bind": [0], "react/jsx-no-bind": [0],
"react/prop-types": [0], "react/prop-types": [0],
...@@ -22,11 +26,16 @@ ...@@ -22,11 +26,16 @@
"no-bitwise": [0], "no-bitwise": [0],
"no-cond-assign": [0], "no-cond-assign": [0],
"import/no-unresolved": [0], "import/no-unresolved": [0],
"require-yield": [1] "require-yield": [1],
"react/react-in-jsx-scope": [0],
"no-extra-semi": [0]
}, },
"parserOptions": { "parserOptions": {
"ecmaFeatures": { "ecmaFeatures": {
"experimentalObjectRestSpread": true "experimentalObjectRestSpread": true
} }
},
"env": {
"browser": true
} }
} }
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/moecube-console-web.iml" filepath="$PROJECT_DIR$/.idea/moecube-console-web.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="false">
<option name="arguments" value="--no-color $FileName$" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="less" />
<option name="immediateSync" value="true" />
<option name="name" value="Less" />
<option name="output" value="$FileNameWithoutExtension$.css" />
<option name="outputFilters">
<array>
<FilterInfo>
<option name="description" value="lessc error format" />
<option name="name" value="lessc" />
<option name="regExp" value="$MESSAGE$\Q in \E$FILE_PATH$\Q on line \E$LINE$\Q, column \E$COLUMN$" />
</FilterInfo>
</array>
</option>
<option name="outputFromStdout" value="true" />
<option name="program" value="" />
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="false" />
<option name="workingDir" value="$FileDir$" />
<envs />
</TaskOptions>
</component>
</project>
\ No newline at end of file
This diff is collapsed.
{ export default {
"entry": "src/index.js", "entry": "src/index.js",
"env": { "env": {
"development": { "development": {
"extraBabelPlugins": [ "extraBabelPlugins": [
"dva-hmr", "dva-hmr",
"transform-runtime", "transform-runtime",
"babel-plugin-transform-decorators-legacy",
["import", { "libraryName": "antd", "style": "css" }] ["import", { "libraryName": "antd", "style": "css" }]
] ]
}, },
"production": { "production": {
"extraBabelPlugins": [ "extraBabelPlugins": [
"transform-runtime", "transform-runtime",
"babel-plugin-transform-decorators-legacy",
["import", { "libraryName": "antd", "style": "css" }] ["import", { "libraryName": "antd", "style": "css" }]
] ]
} }
......
...@@ -3,15 +3,14 @@ ...@@ -3,15 +3,14 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dva Demo</title> <link rel="stylesheet" href="index.css"/>
<link rel="stylesheet" href="index.css" /> <link rel="icon" href="https://moecube.com/favicon.ico">
<base href="/">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script src="index.js"></script> <script src="/index.js"></script>
</body> </body>
</html> </html>
import React from 'react'; import React from 'react';
import styles from './Create.css'; import styles from './Create.css';
import { Button, Modal, Form, Input, Radio, Select, Spin } from 'antd'; import { Button, Modal, Form, Input, Radio, Select, Spin } from 'antd';
import { connect } from 'dva'
const FormItem = Form.Item; const FormItem = Form.Item;
@connect(
(state, props) => {
const { loading } = state
return {
loading
}
}
)
class Create extends React.Component { class Create extends React.Component {
handleSubmit = (e) => { handleSubmit = (e) => {
...@@ -20,11 +29,11 @@ class Create extends React.Component { ...@@ -20,11 +29,11 @@ class Create extends React.Component {
} }
handleSelectChange = (value) => { handleSelectChange = (value) => {
} }
render() { render() {
const { visible, onCancel, form, isLoading } = this.props; const { visible, onCancel, form, loading } = this.props;
const { getFieldDecorator } = form; const { getFieldDecorator } = form;
return ( return (
...@@ -34,7 +43,7 @@ class Create extends React.Component { ...@@ -34,7 +43,7 @@ class Create extends React.Component {
okText="提交" okText="提交"
onCancel={onCancel} onCancel={onCancel}
onOk={this.handleSubmit}> onOk={this.handleSubmit}>
<Spin spinning={isLoading} delay={100} tip="提交中..."> <Spin spinning={loading.global} delay={100} tip="提交中...">
<Form vertical onSubmit={this.handleSubmit}> <Form vertical onSubmit={this.handleSubmit}>
<FormItem label="应用 ID (创建应用后无法修改)"> <FormItem label="应用 ID (创建应用后无法修改)">
...@@ -56,7 +65,7 @@ class Create extends React.Component { ...@@ -56,7 +65,7 @@ class Create extends React.Component {
<FormItem <FormItem
label="主要语言" label="主要语言"
wrapperCol={{ span: 8 }}> wrapperCol={{ span: 8 }}>
{getFieldDecorator('locale', { {getFieldDecorator('locale', {
rules: [{ required: true, message: '请至少选择一门主要语言' }], rules: [{ required: true, message: '请至少选择一门主要语言' }],
onChange: this.handleSelectChange, onChange: this.handleSelectChange,
...@@ -65,13 +74,13 @@ class Create extends React.Component { ...@@ -65,13 +74,13 @@ class Create extends React.Component {
<Select.Option value="zh-CN">zh-CN</Select.Option> <Select.Option value="zh-CN">zh-CN</Select.Option>
<Select.Option value="zh-TW">zh-TW</Select.Option> <Select.Option value="zh-TW">zh-TW</Select.Option>
<Select.Option value="en-US">en-US</Select.Option> <Select.Option value="en-US">en-US</Select.Option>
<Select.Option value="ja-JP">ja-JP</Select.Option> <Select.Option value="ja-JP">ja-JP</Select.Option>
</Select> </Select>
)} )}
</FormItem> </FormItem>
</Form> </Form>
</Spin> </Spin>
</Modal> </Modal>
); );
} }
......
...@@ -2,30 +2,44 @@ import React from 'react'; ...@@ -2,30 +2,44 @@ import React from 'react';
import styles from './Nav.less'; import styles from './Nav.less';
import { Layout, Menu, Icon, Breadcrumb } from 'antd'; import { Layout, Menu, Icon, Breadcrumb } from 'antd';
import {Link} from 'dva/router' import {Link} from 'dva/router'
import { FormattedMessage } from 'react-intl' import {connect} from 'dva'
const { Header, Sider, Content, Footer } = Layout; const { Header, Sider, Content, Footer } = Layout;
const SubMenu = Menu.SubMenu const SubMenu = Menu.SubMenu
function Nav({ collapsed, mode, dispatch }) {
return (
<Sider
collapsible
collapsed={collapsed}
onCollapse={() => dispatch({ type: 'Common/collapsed' })}>
<div className={styles.logo} />
<Menu theme="dark" mode={mode} defaultSelectedKeys={['0']}>
<Menu.Item key="0"> @connect(
<Link to="/apps"> (state, props) => {
<Icon type="windows-o" /> const {
Common: {collapsed, mode}
} = state
return {
collapsed,
mode
}
}
)
export default class Nav extends React.Component {
render(){
const { collapsed, mode, dispatch } = this.props
return (
<Sider
collapsible
collapsed={collapsed}
onCollapse={() => dispatch({ type: 'Common/collapsed' })}>
<div className={styles.logo} />
<Menu theme="dark" mode={mode} defaultSelectedKeys={['0']}>
<Menu.Item key="0">
<Link to="/apps">
<Icon type="windows-o" />
<span className="nav-next"> <span className="nav-next">
<FormattedMessage id="Apps"/> apps
</span> </span>
</Link> </Link>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Sider> </Sider>
); )
}
} }
export default Nav;
import React from 'react';
const Example = () => {
return (
<div>
Example
</div>
);
};
Example.propTypes = {
};
export default Example;
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { browserHistory } from 'dva/router' import {browserHistory} from 'dva/router'
import createLoading from 'dva-loading'
import {message} from 'antd'
import dva from 'dva'; import dva from 'dva';
import './index.less'; import './index.less';
import { IntlProvider, addLocaleData } from 'react-intl';
// 1. Initialize // 1. Initialize
const app = dva({ const app = dva({
history: browserHistory onError: (error) => {
message.destroy();
message.error(error.message);
},
history: browserHistory,
}); });
app.model(require("./models/Apps")); app.model(require("./models/Apps"));
...@@ -24,6 +26,7 @@ app.model(require("./models/App")); ...@@ -24,6 +26,7 @@ app.model(require("./models/App"));
// 2. Plugins // 2. Plugins
// app.use({}); // app.use({});
app.use(createLoading())
// 3. Model // 3. Model
// app.model(require('./models/example')); // app.model(require('./models/example'));
...@@ -31,24 +34,10 @@ app.model(require("./models/App")); ...@@ -31,24 +34,10 @@ app.model(require("./models/App"));
// 4. Router // 4. Router
app.router(require('./router')); app.router(require('./router'));
// 5. Start
import en from 'react-intl/locale-data/en'
import zh from 'react-intl/locale-data/zh'
import localeData from '../i18n.json'
addLocaleData([...en, ...zh])
const language = navigator.language || (navigator.languages && navigator.languages[0]) || navigator.userLanguage;
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
const messages = localeData[languageWithoutRegionCode] || localeData[language] || localeData.zh;
const App = app.start() const App = app.start()
ReactDOM.render( ReactDOM.render(
<IntlProvider locale={ language } messages={ messages }> <App />,
<App />
</IntlProvider>,
document.getElementById("root") document.getElementById("root")
) )
import { create } from '../services/Apps' import {create} from '../services/Apps'
import {message} from 'antd'
export default { export default {
namespace: 'App', namespace: 'App',
...@@ -23,13 +24,6 @@ export default { ...@@ -23,13 +24,6 @@ export default {
isSubmit: true, isSubmit: true,
} }
}, },
SubmitSuccess(state, action){
return {
...state,
isSubmit: false,
isCreate: false
}
},
onCancel(state, action) { onCancel(state, action) {
return { return {
...state, ...state,
...@@ -49,8 +43,8 @@ export default { ...@@ -49,8 +43,8 @@ export default {
} }
}, },
effects: { effects: {
*submit({ payload }, { call, put }){ *submit({payload}, {call, put}){
yield put({ type: 'SubmitRequest', payload }) yield put({type: 'SubmitRequest', payload})
const params = { const params = {
id: payload.id, id: payload.id,
...@@ -59,13 +53,16 @@ export default { ...@@ -59,13 +53,16 @@ export default {
} }
} }
const req = yield call(create, params) try {
if(req.data) { const {data} = yield call(create, params)
yield put({ type: 'SubmitSuccess' }) if (data) {
yield put({ type: 'reset' }) yield put({type: 'SubmitSuccess'})
// TODO: 成功提示 yield put({type: 'reset'})
message.info("i18n 创建成功")
}
} catch (error) {
message.error(error.message)
} }
// TODO: 错误处理
} }
}, },
subscriptions: {}, subscriptions: {},
......
import { fetch, update } from '../services/Apps' import {fetch, update} from '../services/Apps'
import * as crypto from 'crypto' import * as crypto from 'crypto'
import {message} from 'antd'
import config from '../config' import config from '../config'
...@@ -10,47 +11,65 @@ export default { ...@@ -10,47 +11,65 @@ export default {
}, },
reducers: { reducers: {
save(state, action) { save(state, action) {
return { return {
...state, ...state,
...action.payload ...action.payload
} }
}, },
}, },
effects: { effects: {
*fetch({ payload }, { call, put }) { *fetch({payload}, {call, put}) {
const { data } = yield call(fetch, payload)
let apps = {} try {
if(data && data.length > 0) { const {data} = yield call(fetch, payload)
data.map(app => {
apps[app["id"]] = app let apps = {}
}) if (data && data.length > 0) {
data.map(app => {
apps[app["id"]] = app
})
}
yield put({type: 'save', payload: {apps}})
} catch (error) {
message.error(error.message)
} }
yield put({ type: 'save', payload: { apps } })
}, },
*update({ payload }, {call, put}){ *update({payload}, {call, put}){
const { data } = yield call(update, payload) try {
const {data} = yield call(update, payload)
if (data) { if (data) {
yield put({ type: 'success'}) yield put({type: 'success'})
} else { message.info("i18n success")
yield put({ type: 'faile' }) }
} catch (error) {
message.error(error.message)
} }
}, },
*success({ payload }, { call, put}) { *addPackage({payload}, {call, put}){
yield put({ type: 'fetch' }) try {
const {data} = yield call(update, payload)
if (data) {
yield put({type: 'success'})
}
} catch (error) {
message.error(error.message)
}
},
*success({payload}, {call, put}) {
yield put({type: 'fetch'})
} }
}, },
subscriptions: { subscriptions: {
setup({ dispatch, history }) { setup({dispatch, history}) {
return history.listen(({ pathname, query}) => {
dispatch({type: 'fetch'})
if(/^apps/.test(pathname)) { return history.listen(({pathname, query}) => {
dispatch({ type: 'fetch', payload: query}) if (pathname === '/login') {
} else if(pathname === '/login'){
let params = new URLSearchParams() let params = new URLSearchParams()
params.set('return_sso_url', config.returnSSO) params.set('return_sso_url', config.returnSSO)
let payload = Buffer.from(params.toString()).toString('base64') let payload = Buffer.from(params.toString()).toString('base64')
...@@ -59,7 +78,7 @@ export default { ...@@ -59,7 +78,7 @@ export default {
params.set('sso', payload); params.set('sso', payload);
params.set('sig', crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex')) params.set('sig', crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex'))
window.location.href=url window.location.href = url
} }
}) })
} }
......
...@@ -11,11 +11,10 @@ function RouterConfig({ history }) { ...@@ -11,11 +11,10 @@ function RouterConfig({ history }) {
return ( return (
<Router history={history}> <Router history={history}>
<Route path="/" component={Entry} > <Route path="/" component={Entry} >
<Route path="apps" component={Apps} /> <Route path="/apps/:id" component={AppDetail} />
<Route path="apps/:id" component={AppDetail} /> <Route path="/apps" component={Apps} />
</Route> </Route>
<Route path="/loginCallback" component={LoginCallback} /> <Route path="/loginCallback" component={LoginCallback} />
<Route path="*" component={() => <div>404</div>}/>
</Router> </Router>
); );
} }
......
This diff is collapsed.
:global { :global {
.app-detail-nav{
} .ant-upload {
.ant-card { display: flex !important;
margin: 2vh 0; justify-content: center;
} align-items:center;
.app-detail-nav .ant-tabs-nav-scroll { padding: 0 !important;
display: flex; }
justify-content: center;
} .ant-upload-text {
.ant-upload-select-picture-card i { margin-top: 0 !important;
font-size: 28px; }
color: #999;
} .ant-card {
margin: 2vh 0;
.ant-upload-select-picture-card .ant-upload-text { }
margin-top: 8px; .app-detail-nav .ant-tabs-nav-scroll {
font-size: 12px; display: flex;
color: #666; justify-content: center;
} }
} .ant-upload-select-picture-card i {
font-size: 28px;
.wrapSubmit{ color: #999;
display: flex; }
justify-content: center;
} .ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
.form { font-size: 12px;
align-content: center; color: #666;
padding: 0 5vw; }
display: flex; }
justify-content: center;
} .wrapSubmit{
\ No newline at end of file display: flex;
justify-content: center;
}
.form {
align-content: center;
padding: 0 5vw;
display: flex;
justify-content: center;
}
import React from 'react'; import React from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import styles from './Entry.less';
import Nav from '../components/Common/Nav' import Nav from '../components/Common/Nav'
import { Router, Route, browserHistory } from 'dva/router';
import {Layout} from 'antd' import {Layout} from 'antd'
function Entry({ children, collapsed, mode, dispatch }) { function Entry({ children}) {
const NavProps = {
collapsed,
mode,
dispatch
}
return ( return (
<Layout style={{ flexDirection: 'row', minHeight: '100%'}}> <Layout style={{ flexDirection: 'row', minHeight: '100%'}}>
<Nav {...NavProps}/> <Nav />
<Layout style={{ minHeight: '400px'}}> <Layout style={{ minHeight: '400px'}}>
{children} {children}
</Layout> </Layout>
...@@ -26,13 +17,7 @@ function Entry({ children, collapsed, mode, dispatch }) { ...@@ -26,13 +17,7 @@ function Entry({ children, collapsed, mode, dispatch }) {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const { return {}
Common: {collapsed, mode}
} = state
return {
collapsed,
mode
};
} }
export default connect(mapStateToProps)(Entry); export default connect(mapStateToProps)(Entry);
import fetch from 'dva/fetch'; import fetch from 'dva/fetch';
import config from '../config' import config from '../config';
function parseJSON(response) { function parseJSON(response) {
return response.json(); return response.json();
} }
function checkStatus(response) { async function checkStatus(response) {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return response; return response;
} }
const error = new Error(response.statusText); let message;
try {
message = (await response.json()).message;
} catch (error) {
message = response.statusText;
}
const error = new Error(message);
error.response = response; error.response = response;
throw error; throw error;
} }
...@@ -22,17 +29,20 @@ function checkStatus(response) { ...@@ -22,17 +29,20 @@ function checkStatus(response) {
* @param {object} [options] The options we want to pass to "fetch" * @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err" * @return {object} An object containing either "data" or "err"
*/ */
export default function request(url, options) { export default function request(relativeUrl, options) {
url = `${config.apiRoot}${url}` const url = `${config.apiRoot}${relativeUrl}`;
if(options && !options.headers && (options.method == 'POST' || options.method == 'PATCH')) { if (options && !options.headers) {
options.headers = { Object.assign(options, {
"content-type": "application/json" headers: {
} 'content-type': 'application/json',
},
});
} }
console.log(options);
return fetch(url, options) return fetch(url, options)
.then(checkStatus) .then(checkStatus)
.then(parseJSON) .then(parseJSON)
.then(data => ({ data })) .then(data => ({data}));
.catch(err => ({ err })); // .catch(err => ({ err }));
} };
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment