Commit 15c4b1c3 authored by nano's avatar nano

awesome

parent 4b82338e
...@@ -12,9 +12,13 @@ ...@@ -12,9 +12,13 @@
"dependencies": { "dependencies": {
"antd": "^2.7.4", "antd": "^2.7.4",
"babel-runtime": "^6.9.2", "babel-runtime": "^6.9.2",
"crypto": "^0.0.3",
"dva": "^1.2.1", "dva": "^1.2.1",
"qs": "^6.4.0",
"react": "^15.4.0", "react": "^15.4.0",
"react-dom": "^15.4.0" "react-dom": "^15.4.0",
"react-intl": "^2.2.3",
"uuid": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^7.1.1", "babel-eslint": "^7.1.1",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dva Demo</title> <title>Dva Demo</title>
<link rel="stylesheet" href="index.css" /> <link rel="stylesheet" href="index.css" />
<base href="/">
</head> </head>
<body> <body>
......
...@@ -20,7 +20,7 @@ class Create extends React.Component { ...@@ -20,7 +20,7 @@ class Create extends React.Component {
} }
handleSelectChange = (value) => { handleSelectChange = (value) => {
} }
render() { render() {
......
import React from 'react';
import styles from './Items.css';
function Items() {
return (
<div className={styles.normal}>
Component: Items
</div>
);
}
export default Items;
import { Table, Input, Popconfirm } from 'antd';
class EditableCell extends React.Component {
state = {
value: this.props.value,
editable: this.props.editable || false,
}
componentWillReceiveProps(nextProps) {
if (nextProps.editable !== this.state.editable) {
this.setState({ editable: nextProps.editable });
if (nextProps.editable) {
this.cacheValue = this.state.value;
}
}
if (nextProps.status && nextProps.status !== this.props.status) {
if (nextProps.status === 'save') {
this.props.onChange(this.state.value);
} else if (nextProps.status === 'cancel') {
this.setState({ value: this.cacheValue });
this.props.onChange(this.cacheValue);
}
}
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.editable !== this.state.editable ||
nextState.value !== this.state.value;
}
handleChange(e) {
const value = e.target.value;
this.setState({ value });
}
render() {
const { value, editable } = this.state;
return (
<div>
{
editable ?
<div>
<Input
value={value}
onChange={e => this.handleChange(e)}
/>
</div>
:
<div className="editable-row-text">
{value.toString() || ' '}
</div>
}
</div>
);
}
}
export default EditableCell
\ No newline at end of file
export default { export default {
apiRoot: 'http://localhost:8001' apiRoot: 'http://localhost:8001',
imgRoot: 'http://node:7888',
returnSSO: 'http://localhost:8000/loginCallback',
SSOProvider: 'https://ygobbs.com/session/sso_provider'
// SSOProvider: 'http://localhost:8081'
} }
\ No newline at end of file
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { browserHistory } from 'dva/router'
import dva from 'dva'; import dva from 'dva';
import './index.css'; import './index.less';
import { IntlProvider, addLocaleData } from 'react-intl'; import { IntlProvider, addLocaleData } from 'react-intl';
// 1. Initialize // 1. Initialize
const app = dva(); const app = dva({
history: browserHistory
});
app.model(require("./models/Apps")); app.model(require("./models/Apps"));
app.model(require("./models/user"));
app.model(require("./models/Common")); app.model(require("./models/Common"));
app.model(require("./models/App")); app.model(require("./models/App"));
......
html, body, :global(#root) { html, body, :global(#root) {
height: 100%; height: 100%;
} }
\ No newline at end of file
import { fetch } from '../services/Apps' import { fetch, update } from '../services/Apps'
import * as crypto from 'crypto'
import config from '../config'
export default { export default {
namespace: 'Apps', namespace: 'Apps',
state: { state: {
list: [], apps: {},
}, },
reducers: { reducers: {
save(state, action) { save(state, action) {
...@@ -16,14 +19,44 @@ export default { ...@@ -16,14 +19,44 @@ export default {
effects: { effects: {
*fetch({ payload }, { call, put }) { *fetch({ payload }, { call, put }) {
const { data } = yield call(fetch, payload) const { data } = yield call(fetch, payload)
yield put({ type: 'save', payload: { list: data }})
let apps = {}
if(data && data.length > 0) {
data.map(app => {
apps[app["id"]] = app
})
}
yield put({ type: 'save', payload: { apps } })
},
*update({ payload }, {call, put}){
const { data } = yield call(update, payload)
if (data) {
yield put({ type: 'success'})
} else {
yield put({ type: 'faile' })
}
} }
}, },
subscriptions: { subscriptions: {
setup({ dispatch, history }) { setup({ dispatch, history }) {
return history.listen(({ pathname, query}) => { return history.listen(({ pathname, query}) => {
if(pathname === '/apps') {
if(/^apps/.test(pathname)) {
dispatch({ type: 'fetch', payload: query}) dispatch({ type: 'fetch', payload: query})
} else if(pathname === '/login'){
let params = new URLSearchParams()
params.set('return_sso_url', config.returnSSO)
let payload = Buffer.from(params.toString()).toString('base64')
let url = new URL(config.SSOProvider)
params = url['searchParams'];
params.set('sso', payload);
params.set('sig', crypto.createHmac('sha256', 'zsZv6LXHDwwtUAGa').update(payload).digest('hex'))
window.location.href=url
} }
}) })
} }
......
export default {
namespace: 'user',
state: {
},
reducers: {
change(state, action){
return {
...state, ...action.payload
}
}
},
effects: {},
subscriptions: {},
};
...@@ -4,14 +4,19 @@ import IndexPage from './routes/IndexPage'; ...@@ -4,14 +4,19 @@ import IndexPage from './routes/IndexPage';
import Apps from "./routes/Apps.js"; import Apps from "./routes/Apps.js";
import AppDetail from './routes/AppDetail.js' import AppDetail from './routes/AppDetail.js'
import Entry from "./routes/Entry.js"; import Entry from "./routes/Entry.js";
import LoginCallback from "./routes/LoginCallback.js";
function RouterConfig({ history }) { 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" component={Apps} />
<Route path="apps/:id" component={AppDetail} /> <Route path="apps/:id" component={AppDetail} />
</Route> </Route>
<Route path="/loginCallback" component={LoginCallback} />
<Route path="*" component={() => <div>404</div>}/>
</Router> </Router>
); );
} }
......
import React from 'react'; import React from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import styles from './AppDetail.less'; import styles from './AppDetail.less';
import { Form, Input, Icon, Radio, Tag, Tooltip, Button, Select, Tabs } from 'antd' import config from '../config'
import { Form, Input, Icon, Radio, Tag, Tooltip, Button, Select, Tabs, Upload, Modal, Table, Popconfirm, Row, Col, Card } from 'antd'
const FormItem = Form.Item; const FormItem = Form.Item;
const RadioButton = Radio.Button; const RadioButton = Radio.Button;
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
const Option = Select.Option; const Option = Select.Option;
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
const Dragger = Upload.Dragger;
function AppDetail({ form }) {
const { getFieldDecorator } = form
const formItemLayout = {
labelCol: { span: 5 }, const defCategory = ["game", "runtime", "emulator", "module"]
wrapperCol: { span: 24 }, const defLocales = ["zh-CN", "zh-TW", "en-US", "ja-JP"]
const defPlatform = ["win32","darwin"]
let defDevelopers = {}
let defPublishers = {}
let defNews = {}
defLocales.forEach(locale => {
defDevelopers[locale] = []
defPublishers[locale] = []
defNews[locale] = []
})
const defPackage = {
id: '',
version: '',
platforms: [],
locales: []
}
const defPackages = {
main: []
}
const formItemLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 24 },
}
const uploadProps = {
name: 'file',
multiple: false,
showUploadList: false,
action: '//jsonplaceholder.typicode.com/posts/',
onChange(info) {
const status = info.file.status;
if (status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (status === 'done') {
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
}
class AppDetail extends React.Component {
state = {
previewVisible: false,
previewImage: '',
icoList: [],
coverList: [],
backgroundList: [],
isCreateNews: false,
developers: {},
publishers: {},
news: {},
packages: {},
isDanger: false
};
componentWillReceiveProps(nextProps){
const { App:{ developers, publishers, news, packages = {} } } = nextProps
this.setState({
developers: {...defDevelopers, ...developers},
publishers: {...defPublishers, ...publishers},
packages: {...defPackages, ...packages},
news: {...defNews, ...news},
})
}
handleCancel = () => this.setState({ previewVisible: false })
handlePreview = (file) => {
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
}
handleChangeIco = ({ fileList }) => {
this.setState({ icoList: fileList })
this.handleUpdateImg({ fileList, field: 'icon'})
}
handleChangeCover = ({ fileList }) => {
this.setState({ coverList: fileList })
this.handleUpdateImg({ fileList, field: 'cover'})
}
handleChangeBackground = ({ fileList }) => {
this.setState({ backgroundList: fileList })
this.handleUpdateImg({ fileList, field: 'background'})
} }
return ( handleUpdateImg = ({ field, fileList }) => {
const { form, dispatch, params: { id }} = this.props
const [img] = fileList
if(img.status === 'done') {
const [res] = img.response
dispatch({type: "Apps/update", payload: {id, [field]: res.fileName }})
}
}
onSubmitBase = (e) => {
const { form, dispatch, params: { id }} = this.props
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
const {category, homepage, conference, tags, locales} = values
dispatch({type: "Apps/update", payload: {id, category, homepage, conference, tags, locales}})
}
});
}
onSubmitIntro = (e) => {
const { form, dispatch, params: { id }} = this.props
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
const {description, name, developers, publishers, news} = values
dispatch({type: "Apps/update", payload: {id, description, name, developers, publishers, news}})
}
});
}
onSubmitManage = (e) => {
const { form, dispatch, params: { id }} = this.props
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
const {version, actions, references, dependencies} = values
Object.keys(actions).forEach((platform) => {
actions[platform] = JSON.parse(actions[platform])
})
dispatch({type: "Apps/update", payload: {id, version, actions, references, dependencies }})
}
});
}
onSubmitUpload = (e) => {
const { form, dispatch, params: { id }} = this.props
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
let {packages} = values
// dispatch({type: "Apps/update", payload: {id, packages}})
}
});
}
onEdit = (targetKey, action) => {
console.log(targetKey, action)
this[action](targetKey);
}
add = (targetKey) => {
let packages = this.state.packages
packages["main"].push(defPackage)
this.setState({
packages
})
}
remove = (targetKey) => {
if(!this.props.isDanger) {
let {packages} = this.state
packages["main"].splice(targetKey,1)
this.setState({
packages
})
}
}
onAddField = ({locale, field, data}) => {
let newField = Object.assign({}, this.state[field])
newField[locale].push(data)
this.setState({
[field]: newField
})
}
onDeleteField = ({locale, field, index}) => {
let newField = Object.assign({}, this.state[field])
newField[locale].splice(index,1)
this.setState({
[field]: newField
})
}
render() {
console.log(this.props, this.state)
const { form, App} = this.props
const { getFieldDecorator } = form
const { id, author, homepage, references={}, dependencies={}, description={}, actions={}, version={}, name={}, category={}, tags=[], locales=[], conference, icon, cover, background, } = App
const { publishers, developers, previewVisible, previewImage, icoList, coverList, backgroundList, isCreateNews, news, packages } = this.state
return (
<Tabs defaultActiveKey="1" className="app-detail-nav"> <Tabs defaultActiveKey="1" className="app-detail-nav">
<TabPane tab={<span><Icon type="setting" /> 基本信息 </span>} key="1"> <TabPane tab={<span><Icon type="setting" /> 基本信息 </span>} key="1">
<div className={styles.form}> <div className={styles.form}>
<Form> <Form onSubmit={this.onSubmitBase}>
<FormItem {...formItemLayout} label="游戏类别">
{getFieldDecorator('category')(
<RadioGroup>
<RadioButton value="game">game</RadioButton>
<RadioButton value="runtime">runtime</RadioButton>
<RadioButton value="emulator">emulator</RadioButton>
<RadioButton value="module">module</RadioButton>
</RadioGroup>
)}
</FormItem>
<FormItem {...formItemLayout}> <div style={{ display: 'flex' }}>
{getFieldDecorator('author', {
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
<FormItem {...formItemLayout}>
{getFieldDecorator('icon', {
})(
<div className="clearfix">
<Upload
multiple={false}
action={`${config.apiRoot}/upload/image`}
listType="picture-card"
className="upload-icon"
fileList={icoList}
onChange={this.handleChangeIco}
onPreview={this.handlePreview}
>
{icoList.length >= 1 ? null :
<div>
<Icon type="plus" />
<div className="ant-upload-text">Icon</div>
</div>
}
</Upload>
</div>
)}
</FormItem>
<FormItem {...formItemLayout}>
{getFieldDecorator('cover', {
})(
<div className="clearfix">
<Upload
multiple={false}
action={`${config.apiRoot}/upload/image`}
listType="picture-card"
className="upload-icon"
fileList={coverList}
onChange={this.handleChangeCover}
onPreview={this.handlePreview}
>
{coverList.length >= 1 ? null :
<div>
<Icon type="plus" />
<div className="ant-upload-text">Cover</div>
</div>
}
</Upload>
</div>
)}
</FormItem>
<FormItem {...formItemLayout}>
{getFieldDecorator('background', {
})(
<div className="clearfix">
<Upload
multiple={false}
action={`${config.apiRoot}/upload/image`}
listType="picture-card"
className="upload-icon"
fileList={backgroundList}
onChange={this.handleChangeBackground}
onPreview={this.handlePreview}
>
{backgroundList.length >= 1 ? null :
<div>
<Icon type="plus" />
<div className="ant-upload-text">Background</div>
</div>
}
</Upload>
</div>
)}
</FormItem>
</div>
<FormItem {...formItemLayout} label="游戏类别" >
{getFieldDecorator('category', {
initialValue: category
})( })(
<Input addonBefore={<Icon type="user" />} placeholder="开发者" /> <RadioGroup>
{defCategory.map((c,i) => {
return <RadioButton value={c} key={i}>{c}</RadioButton>
})}
</RadioGroup>
)} )}
</FormItem> </FormItem>
<FormItem <FormItem
{...formItemLayout} {...formItemLayout}
help="以 http:// 或 https:// 开头,没有可留空"> help="以 http:// 或 https:// 开头,没有可留空">
{getFieldDecorator('homepage', { {getFieldDecorator('homepage', {
initialValue: homepage
})( })(
<Input addonBefore={<Icon type="home" />} placeholder="网站" /> <Input addonBefore={<Icon type="home" />} placeholder="网站" />
)} )}
...@@ -53,6 +355,7 @@ function AppDetail({ form }) { ...@@ -53,6 +355,7 @@ function AppDetail({ form }) {
{...formItemLayout} {...formItemLayout}
help="游戏类应用通常与 ID 相同,其他类型的应用留空"> help="游戏类应用通常与 ID 相同,其他类型的应用留空">
{getFieldDecorator('conference', { {getFieldDecorator('conference', {
initialValue: conference
})( })(
<Input addonBefore={<Icon type="message" />} placeholder="聊天室" /> <Input addonBefore={<Icon type="message" />} placeholder="聊天室" />
)} )}
...@@ -63,6 +366,7 @@ function AppDetail({ form }) { ...@@ -63,6 +366,7 @@ function AppDetail({ form }) {
help="JSON string[],不会写请联系 zh99998@gmail.com" help="JSON string[],不会写请联系 zh99998@gmail.com"
> >
{getFieldDecorator('tags', { {getFieldDecorator('tags', {
initialValue: tags
})( })(
<Select tags style={{ width: '100%' }} placeholder="标签"> <Select tags style={{ width: '100%' }} placeholder="标签">
</Select> </Select>
...@@ -71,16 +375,15 @@ function AppDetail({ form }) { ...@@ -71,16 +375,15 @@ function AppDetail({ form }) {
<FormItem {...formItemLayout}> <FormItem {...formItemLayout}>
{getFieldDecorator('locales', { {getFieldDecorator('locales', {
initialValue: locales
})( })(
<Select <Select
multiple tags
style={{ width: '100%' }} style={{ width: '100%' }}
placeholder="游戏支持的语言" placeholder="游戏支持的语言">
defaultValue={['a10', 'c12']}> {defLocales.map((locale,i) => {
<Select.Option value="zh-CN">zh-CN</Select.Option> return <Select.Option key={i} value={locale}>{locale}</Select.Option>
<Select.Option value="zh-TW">zh-TW</Select.Option> })}
<Select.Option value="en-US">en-US</Select.Option>
<Select.Option value="ja-JP">ja-JP</Select.Option>
</Select> </Select>
)} )}
</FormItem> </FormItem>
...@@ -98,64 +401,282 @@ function AppDetail({ form }) { ...@@ -98,64 +401,282 @@ function AppDetail({ form }) {
<TabPane tab={<span><Icon type="solution" />应用介绍</span>} key="2"> <TabPane tab={<span><Icon type="solution" />应用介绍</span>} key="2">
<div className={styles.form}> <div className={styles.form}>
<Tabs type="card" className="app-detail-nav"> <Tabs type="card" className="app-detail-nav">
<TabPane tab="zh-CN" key="1">
<Form> {defLocales.map((locale,i) => {
<FormItem {...formItemLayout}>
{getFieldDecorator('locale', { return (
})( <TabPane tab={locale} key={i}>
<Input addonBefore={<Icon type="info" />} placeholder="应用名称" /> <Form onSubmit={this.onSubmitIntro}>
)} <FormItem {...formItemLayout}>
</FormItem> {getFieldDecorator(`name[${locale}]`, {
initialValue: name[locale]
<FormItem {...formItemLayout}> })(
{getFieldDecorator('description', { <Input addonBefore={<Icon type="edit" />} placeholder="应用名称" />
})( )}
<Input type="textarea" placeholder="应用介绍" autosize={{ minRows: 2}}/> </FormItem>
)}
</FormItem> <FormItem {...formItemLayout} >
{getFieldDecorator(`description[${locale}]`, {
<FormItem {...formItemLayout}> initialValue: description[locale]
{getFieldDecorator('news', { })(
})( <Input type="textarea" placeholder="应用介绍" autosize={{ minRows: 2}}/>
<Input type="textarea" placeholder="新闻" autosize={{ minRows: 2}} /> )}
)} </FormItem>
</FormItem>
<Card title="开发者" extra={<a onClick={ () => this.onAddField({locale, field:'developers', data: { url:'', name: ''}})}>Add </a>}>
<FormItem {...formItemLayout} > {developers[locale] && developers[locale].length > 0 && developers[locale].map((developer,index) => {
<div className={styles.wrapSubmit}> return (
<Button type="primary" htmlType="submit" size="large">提交</Button> <Form key={index}>
</div> <FormItem {...formItemLayout}>
</FormItem> <Row gutter={24}>
<Col span={10}>
</Form> {getFieldDecorator(`developers[${locale}][${index}]["name"]`, {
</TabPane> initialValue: developer["name"]
})(
<TabPane tab="zh-TW" key="2"> <Input addonBefore={<Icon type="user" />} placeholder="name" />
<Form> )}
<FormItem {...formItemLayout}> </Col>
{getFieldDecorator('locale', { <Col span={10}>
})( {getFieldDecorator(`developers[${locale}][${index}]["url"]`, {
<Input addonBefore={<Icon type="info" />} placeholder="应用名称" /> initialValue: developer["url"]
)} })(
</FormItem> <Input addonBefore={<Icon type="link" />} placeholder="url" />
</Form> )}
</TabPane> </Col>
</Tabs> <Col span={4}>
<a onClick={() => this.onDeleteField({locale, index, field: 'developers'})}>Delete</a>
</Col>
</Row>
</FormItem>
</Form>
)
})}
</Card>
<Card title="发行者" extra={<a onClick={ () => this.onAddField({locale, field:'publishers', data: { url:'', name: ''}})}>Add </a>}>
{publishers[locale] && publishers[locale].length > 0 && publishers[locale].map((publisher,index) => {
return (
<Form key={index}>
<FormItem {...formItemLayout}>
<Row gutter={24}>
<Col span={10}>
{getFieldDecorator(`publishers[${locale}][${index}]["name"]`, {
initialValue: publisher["name"]
})(
<Input addonBefore={<Icon type="user" />} placeholder="name" />
)}
</Col>
<Col span={10}>
{getFieldDecorator(`publishers[${locale}][${index}]["url"]`, {
initialValue: publisher["url"]
})(
<Input addonBefore={<Icon type="link" />} placeholder="url" />
)}
</Col>
<Col span={4}>
<a onClick={() => this.onDeleteField({locale, index, field: 'publishers'})}>Delete</a>
</Col>
</Row>
</FormItem>
</Form>
)
})}
</Card>
<Card title="新闻" extra={<a onClick={ () => this.onAddField({locale, field:'news', data: { url:'', title: '', text: ""}})}>Add </a>}>
{news[locale] && news[locale].length > 0 && news[locale].map((item,index) => {
return (
<Form key={index}>
<FormItem {...formItemLayout} >
{getFieldDecorator(`news[${locale}][${index}]["url"]`, {
initialValue: item["url"]
})(
<Input prefix={<Icon type="link" />} placeholder="url" />
)}
{getFieldDecorator(`news[${locale}][${index}]["title"]`, {
initialValue: item["title"]
})(
<Input prefix={<Icon type="edit" />} placeholder="title" />
)}
{getFieldDecorator(`news[${locale}][${index}]["text"]`, {
initialValue: item["text"]
})(
<Input type="textarea" prefix={<Icon type="edit" />} placeholder="text" />
)}
<a onClick={() => this.onDeleteField({locale, index, field: 'news'})}>Delete</a>
</FormItem>
</Form>
)
})}
</Card>
<FormItem {...formItemLayout} >
<div className={styles.wrapSubmit}>
<Button type="primary" htmlType="submit" size="large">提交</Button>
</div>
</FormItem>
</Form>
</TabPane>
)
})}
</Tabs>
</div> </div>
</TabPane> </TabPane>
<TabPane tab={<span><Icon type="save" />应用管理</span>} key="3"> <TabPane tab={<span><Icon type="save" />应用管理</span>} key="3">
Tab 2 <div className={styles.form}>
<Tabs type="card" className="app-detail-nav">
{defPlatform.map((platform,i) =>
<TabPane tab={platform} key={i}>
<Form onSubmit={this.onSubmitManage}>
<FormItem {...formItemLayout} help="version">
{getFieldDecorator(`version[${platform}]`, {
initialValue: version[platform]
})(
<Input addonBefore={<Icon type="info-circle-o" />} placeholder="版本号" />
)}
</FormItem>
<FormItem {...formItemLayout} help="actions">
{getFieldDecorator(`actions[${platform}]`, {
initialValue: actions[platform] && JSON.stringify(actions[platform], null, '\t')
})(
<Input type="textarea" addonBefore={<Icon type="info-circle-o" />} placeholder="actions" autosize/>
)}
</FormItem>
<FormItem {...formItemLayout} help="dependencies">
{getFieldDecorator(`dependencies[${platform}]`, {
initialValue: dependencies[platform]
})(
<Select tags style={{ width: '100%' }} placeholder="dependencies">
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} help="references">
{getFieldDecorator(`references[${platform}]`, {
initialValue: references[platform]
})(
<Select tags style={{ width: '100%' }} placeholder="references">
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} >
<div className={styles.wrapSubmit}>
<Button type="primary" htmlType="submit" size="large">提交</Button>
</div>
</FormItem>
</Form>
</TabPane>
)}
</Tabs>
</div>
</TabPane>
<TabPane tab={<span><Icon type="save" />应用上传</span>} key="4">
<div className={styles.form}>
<Tabs type="editable-card" className="app-detail-nav" onEdit={this.onEdit}>
{packages["main"] && packages["main"].map((pack,i) => {
return (
<TabPane tab={pack.id || "New"} key={i} closable={true}>
<Form onSubmit={this.onSubmitUpload}>
<FormItem {...formItemLayout} help="id">
{getFieldDecorator(`packages["main"][${i}]["id"]`, {
initialValue: pack["id"]
})(
<Input addonBefore={<Icon type="info-circle-o" />} placeholder="id" />
)}
</FormItem>
<FormItem {...formItemLayout} help="version">
{getFieldDecorator(`packages["main"][${i}]["version"]`, {
initialValue: pack["version"]
})(
<Input addonBefore={<Icon type="info-circle-o" />} placeholder="版本号" />
)}
</FormItem>
<FormItem {...formItemLayout} help="platforms">
{getFieldDecorator(`packages["main"][${i}]["platforms"]`, {
initialValue: pack["platforms"]
})(
<Select multiple style={{ width: '100%' }} placeholder="platforms">
{defPlatform.map((_platform,i) => {
return <Select.Option key={i} value={_platform}>{_platform}</Select.Option>
})}
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} help="locales">
{getFieldDecorator(`packages["main"][${i}]["locales"]`, {
initialValue: pack["locales"]
})(
<Select multiple style={{ width: '100%' }} placeholder="locales">
{defLocales.map((_locales,i) => {
return <Select.Option key={i} value={_locales}>{_locales}</Select.Option>
})}
</Select>
)}
</FormItem>
<FormItem {...formItemLayout}>
{getFieldDecorator(`packages["main"][${i}]["upload"]`, {
})(
<Dragger {...uploadProps}>
<p className="ant-upload-drag-icon">
<Icon type="inbox" />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</p>
</Dragger>
)}
</FormItem>
<FormItem {...formItemLayout} >
<div className={styles.wrapSubmit}>
<Button type="primary" htmlType="submit" size="large">提交</Button>
</div>
</FormItem>
</Form>
</TabPane>
)
})}
</Tabs>
</div>
</TabPane> </TabPane>
</Tabs> </Tabs>
); );
}
} }
function mapStateToProps() { function mapStateToProps(state, props) {
return {}; const {params: { id }} = props
const {
Apps: { apps }
} = state
const App = apps[id] || {}
return {
App
};
} }
const WrapperAppDetail = Form.create()(AppDetail) const WrapperAppDetail = Form.create()(AppDetail)
......
:global { :global {
.app-detail-nav{ .app-detail-nav{
width: 100%; }
.ant-card {
margin: 2vh 0;
} }
.app-detail-nav .ant-tabs-nav-scroll { .app-detail-nav .ant-tabs-nav-scroll {
widows: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.ant-upload-select-picture-card i {
font-size: 28px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
font-size: 12px;
color: #666;
}
} }
.wrapSubmit{ .wrapSubmit{
width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.form { .form {
flex: 1; align-content: center;
padding: 0 5vw;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import styles from './Apps.less'; import styles from './Apps.less';
import { Link } from 'dva/router'; import { Link } from 'dva/router';
import { Button, Affix, Icon, Table } from 'antd' import { Button, Affix, Icon, Table, Tag } from 'antd'
import Create from '../components/App/Create' import Create from '../components/App/Create'
...@@ -17,15 +17,28 @@ const columns = [ ...@@ -17,15 +17,28 @@ const columns = [
}, { }, {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
render: name => `${Object.values(name)[0]}`, render: name => name && name["zh-CN"],
width: '10%', width: '10%',
key: 'name' key: 'name'
},{ },{
title: "Created_At", title: "author",
dataIndex: 'created_at', dataIndex: 'author',
width: '10%',
},{
title: "category",
dataIndex: 'category',
width: '10%',
},{
title: "homepage",
dataIndex: 'homepage',
render: homepage => <a href={homepage} target="_blank">{homepage}</a>,
width: '10%',
},{
title: "released_at",
dataIndex: 'released_at',
sorter: true, sorter: true,
width: '10%', width: '10%',
key:'created_at' key:'released_at'
}, { }, {
title: "Updated_at", title: "Updated_at",
dataIndex: 'updated_at', dataIndex: 'updated_at',
...@@ -35,7 +48,7 @@ const columns = [ ...@@ -35,7 +48,7 @@ const columns = [
}, },
] ]
function Apps({children, dispatch, isCreate, isSubmit, list}) { function Apps({children, dispatch, isCreate, isSubmit, apps}) {
const CreateProps = { const CreateProps = {
visible: isCreate, visible: isCreate,
...@@ -55,11 +68,10 @@ function Apps({children, dispatch, isCreate, isSubmit, list}) { ...@@ -55,11 +68,10 @@ function Apps({children, dispatch, isCreate, isSubmit, list}) {
const TableProps = { const TableProps = {
columns, columns,
dataSource: list dataSource: Object.values(apps)
} }
return ( return (
<div className={styles.normal}> <div className={styles.normal}>
...@@ -78,11 +90,11 @@ function Apps({children, dispatch, isCreate, isSubmit, list}) { ...@@ -78,11 +90,11 @@ function Apps({children, dispatch, isCreate, isSubmit, list}) {
function mapStateToProps(state) { function mapStateToProps(state) {
const { const {
Apps: { list }, Apps: { apps },
App: { isCreate, isSubmit } App: { isCreate, isSubmit }
} = state } = state
return { return {
list, apps,
isCreate, isCreate,
isSubmit isSubmit
}; };
......
...@@ -2,6 +2,8 @@ import React from 'react'; ...@@ -2,6 +2,8 @@ import React from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import styles from './Entry.less'; 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'
...@@ -14,12 +16,12 @@ function Entry({ children, collapsed, mode, dispatch }) { ...@@ -14,12 +16,12 @@ function Entry({ children, collapsed, mode, dispatch }) {
} }
return ( return (
<Layout style={{ flexDirection: 'row', height: '100%'}}> <Layout style={{ flexDirection: 'row', minHeight: '100%'}}>
<Nav {...NavProps}/> <Nav {...NavProps}/>
<Layout style={{ minHeight: '400px'}}> <Layout style={{ minHeight: '400px'}}>
{children} {children}
</Layout>
</Layout> </Layout>
</Layout>
); );
} }
......
import React from 'react';
import { connect } from 'dva';
import styles from './LoginCallback.css';
class LoginCallback extends React.Component{
componentDidMount() {
const { location: { query: { sso }}, dispatch } = this.props
console.log(this.props)
const data = toObject(new URLSearchParams(Buffer.from(sso, 'base64').toString()))
if (data) {
dispatch({type: "user/change", payload:{ data }})
localStorage.setItem("user", JSON.stringify(data))
}
}
render(){
const {dispatch, location} = this.props
return (
<div className={styles.normal}>
</div>
)
}
}
function LoginCallback({ }) {
return (
<div className={styles.normal}>
{JSON.stringify(data)}
</div>
);
}
function mapStateToProps({ }) {
return {
};
}
function toObject(entries){
let result = {};
for (let [key, value] of entries) {
result[key] = value;
}
return result;
}
export default connect(mapStateToProps)(LoginCallback);
...@@ -6,7 +6,14 @@ export async function fetch() { ...@@ -6,7 +6,14 @@ export async function fetch() {
export async function create(params) { export async function create(params) {
return request(`/apps/${params.id}`, { return request(`/apps/${params.id}`, {
method: 'post', method: 'POST',
body: JSON.stringify(params)
})
}
export async function update(params) {
return request(`/apps/${params.id}`, {
method: 'PATCH',
body: JSON.stringify(params) body: JSON.stringify(params)
}) })
} }
\ No newline at end of file
...@@ -24,7 +24,7 @@ function checkStatus(response) { ...@@ -24,7 +24,7 @@ function checkStatus(response) {
*/ */
export default function request(url, options) { export default function request(url, options) {
url = `${config.apiRoot}${url}` url = `${config.apiRoot}${url}`
if(options && !options.headers && options.method == 'post') { if(options && !options.headers && (options.method == 'POST' || options.method == 'PATCH')) {
options.headers = { options.headers = {
"content-type": "application/json" "content-type": "application/json"
} }
......
...@@ -1444,6 +1444,10 @@ crypto-browserify@3.3.0: ...@@ -1444,6 +1444,10 @@ crypto-browserify@3.3.0:
ripemd160 "0.2.0" ripemd160 "0.2.0"
sha.js "2.2.6" sha.js "2.2.6"
crypto@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/crypto/-/crypto-0.0.3.tgz#470a81b86be4c5ee17acc8207a1f5315ae20dbb0"
css-animation@1.x, css-animation@^1.2.5, css-animation@^1.3.0: css-animation@1.x, css-animation@^1.2.5, css-animation@^1.3.0:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.3.2.tgz#df515820ef5903733ad2db0999403b3037b8b880" resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.3.2.tgz#df515820ef5903733ad2db0999403b3037b8b880"
...@@ -2701,7 +2705,27 @@ interpret@^1.0.0: ...@@ -2701,7 +2705,27 @@ interpret@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1: intl-format-cache@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.0.5.tgz#b484cefcb9353f374f25de389a3ceea1af18d7c9"
intl-messageformat-parser@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.2.0.tgz#5906b7f953ab7470e0dc8549097b648b991892ff"
intl-messageformat@1.3.0, intl-messageformat@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-1.3.0.tgz#f7d926aded7a3ab19b2dc601efd54e99a4bd4eae"
dependencies:
intl-messageformat-parser "1.2.0"
intl-relativeformat@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-1.3.0.tgz#893dc7076fccd380cf091a2300c380fa57ace45b"
dependencies:
intl-messageformat "1.3.0"
invariant@^2.0.0, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
dependencies: dependencies:
...@@ -4102,6 +4126,10 @@ qs@6.3.1, qs@~6.3.0: ...@@ -4102,6 +4126,10 @@ qs@6.3.1, qs@~6.3.0:
version "6.3.1" version "6.3.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.1.tgz#918c0b3bcd36679772baf135b1acb4c1651ed79d" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.1.tgz#918c0b3bcd36679772baf135b1acb4c1651ed79d"
qs@^6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
query-string@^3.0.0: query-string@^3.0.0:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638" resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638"
...@@ -4459,6 +4487,15 @@ react-hammerjs@~0.5.0: ...@@ -4459,6 +4487,15 @@ react-hammerjs@~0.5.0:
dependencies: dependencies:
hammerjs "^2.0.8" hammerjs "^2.0.8"
react-intl@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.2.3.tgz#8eebb03cddc38b337ed22fab78037ab53a594270"
dependencies:
intl-format-cache "^2.0.5"
intl-messageformat "^1.3.0"
intl-relativeformat "^1.3.0"
invariant "^2.1.1"
react-lazy-load@^3.0.10: react-lazy-load@^3.0.10:
version "3.0.11" version "3.0.11"
resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.0.11.tgz#1d27e4e1f6dc77daaf9236db707aaf2bbbfabafe" resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.0.11.tgz#1d27e4e1f6dc77daaf9236db707aaf2bbbfabafe"
...@@ -5371,7 +5408,7 @@ uuid@^2.0.2: ...@@ -5371,7 +5408,7 @@ uuid@^2.0.2:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
uuid@^3.0.0: uuid@^3.0.0, uuid@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
......
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