提交 49685f61 authored 作者: zhouhuan's avatar zhouhuan

new commit by wq

上级 a60986e3
const proxy = { const proxy = {
"target": "http://192.168.1.200:8090", "target": "http://192.168.1.22:8082",
"changeOrigin": true, "changeOrigin": true,
"pathRewrite": { "pathRewrite": {
"^/api": "/big-machine/restful-services" "^/api": "/app/restful-services"
} }
}; };
const resource_proxy = { const resource_proxy = {
"target": "http://192.168.1.200:8090", "target": "http://192.168.1.22:8082",
"changeOrigin": true, "changeOrigin": true,
"pathRewrite": { "pathRewrite": {
"^/": "/big-machine/" "^/": "/app/"
} }
}; };
...@@ -32,6 +32,7 @@ module.exports = { ...@@ -32,6 +32,7 @@ module.exports = {
["import", { "libraryName": "antd", "style": true }] ["import", { "libraryName": "antd", "style": true }]
], ],
"proxy": { "proxy": {
"/api/app": proxy,
"/api/auth": proxy, "/api/auth": proxy,
"/api/user": proxy, "/api/user": proxy,
"/api/domain": proxy, "/api/domain": proxy,
...@@ -44,10 +45,10 @@ module.exports = { ...@@ -44,10 +45,10 @@ module.exports = {
} }
}, },
"production": { "production": {
publicPath: "/bm/", publicPath: "/app/console/",
define: { define: {
contextPath: "/bm", contextPath: "/app/console",
apiContextPath: "/bm" apiContextPath: "/app"
}, },
"extraBabelPlugins": [ "extraBabelPlugins": [
"transform-runtime", "transform-runtime",
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"moment": "^2.18.1", "moment": "^2.18.1",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qrcode": "^1.0.0",
"react": "^15.6.1", "react": "^15.6.1",
"react-dom": "^15.6.1", "react-dom": "^15.6.1",
"react-json-view": "^1.11.4", "react-json-view": "^1.11.4",
......
import { push } from '../../../../services/route';
import { getAppInfoes, addApp, editApp, removeApp, deployApp, undeployApp, editDeployment } from '../../../../services/app';
import { getToken } from '../../../../utils/auth';
export default {
namespace: 'appInfo',
state: {
allAppInfo: [],
token: '',
uploadURL: '',
record: {},
},
subscriptions: {
setup({ dispatch }) { // eslint-disable-line
},
},
effects: {
*getTokens(action, { put, call }) {
const token = yield call(getToken);
yield put({ type: 'queryToken', payload: token });
},
*getAppInfo({ payload: { name } }, { put, call }) {
const info = yield call(getAppInfoes, name);
yield put({ type: 'queryAppInfo', payload: info });
},
*addAppInfo({ payload: { values } }, { call }) {
yield call(addApp, values.name, values.packageName, values.description);
push('../list');
},
*editAppInfo({ payload: { values } }, { call }) {
yield call(editApp, values.name, values.newName, values.packageName, values.description);
push('../list');
},
*delAppInfo({ payload: { name } }, { call, put }) {
yield call(removeApp, name);
yield put({ type: 'getAppInfo', payload: { } });
push('../list');
},
*uploadAPK({ payload: { response } }, { put }) {
if (response.errorCode !== 0) {
throw new Error(response.message);
}
const uploadURL = response.data[0];
console.log(uploadURL);
yield put({ type: 'queryUploadURL', payload: uploadURL });
},
*addDeployApp({ payload: { values } }, { call, put }) {
console.log(values);
yield call(deployApp, values.idOrName, values.uri, values.description, values.release);
const name = values.idOrName;
yield put({ type: 'getAppInfo', payload: { name } });
},
*removeDeployApp({ payload: { id, name } }, { call, put }) {
yield call(undeployApp, id);
yield put({ type: 'getAppInfo', payload: { name } });
},
*editSaveDeploy({ payload: { values, name } }, { call, put }) {
console.log(values);
yield call(editDeployment, values.id, values.description, values.status);
yield put({ type: 'getAppInfo', payload: { name } });
},
},
reducers: {
queryUploadURL(state, { payload: uploadURL }) {
return {
...state,
uploadURL,
};
},
queryAppInfo(state, { payload: allAppInfo }) {
return {
...state,
allAppInfo,
};
},
queryToken(state, { payload: token }) {
return {
...state,
token,
};
},
saveRecord(state, { payload: record }) {
return {
...state,
record,
};
},
},
};
/**
* Created by zhouhuan on 2017/11/20.
*/
export default [
{
name: 'appManagementInfo',
showName: 'App管理',
modules: [
{
name: 'appInfo',
showName: 'app信息',
layout: {
route: 'appManagement',
},
},
],
},
];
import React from 'react';
import { Button, Input, Form } from 'antd';
import { connect } from 'dva';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
class Add extends React.Component {
componentDidMount() {
}
onCancel = () => {
thisPush(this, { pathname: '../list' });
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.dispatch({ type: 'appInfo/addAppInfo', payload: { values } });
}
});
};
render() {
// console.log(this.props.appInfo);
const { getFieldDecorator } = this.props.form;
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('name', {
rules: [{ required: true, message: 'Please input your name!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="包名"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('packageName', {
rules: [{ required: true, message: 'Please input your packageName!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
})(
<Input />,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(Form.create()(Add));
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 18px;
}
.container {
margin-top: 10px;
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
.divBtn{
margin-top: 10px;
Button{
margin-left: 10px;
}
}
.divRow{
margin-bottom: 10px;
width: 300px;
}
.divCode{
border: 1px solid;
border-color:#C4C4C4;
padding: 20px;
border-radius: 10px;
}
/**
* Created by zhouhuan on 2017/11/21.
*/
import React from 'react';
import { uniqBy } from 'lodash';
import { Button, Input, Upload, Form, message, Icon, Modal, Radio } from 'antd';
import { connect } from 'dva';
import { encrypt } from '../../../../utils/helper';
import config from '../../../../utils/config';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const { TextArea } = Input;
const RadioGroup = Radio.Group;
class AddDeploy extends React.Component {
state = {
descriptionInfo: '',
radioValue: false,
}
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getTokens' });
}
onChangeInfo = (e) => {
this.setState({ descriptionInfo: e.target.value });
}
onCancel = () => {
const { value } = this.props.location.state;
console.log(value);
thisPush(this, { pathname: '../deploy', state: { value } });
}
onChange = (e) => {
this.setState({
radioValue: e.target.value,
});
}
onSubmit = () => {
const { descriptionInfo, radioValue } = this.state;
console.log(radioValue);
const { value } = this.props.location.state;
const { uploadURL } = this.props.appInfo;
const name = value.name;
if (uploadURL === '') {
Modal.error({
title: '请先上传Apk!',
});
} else {
const values = { idOrName: name, uri: uploadURL, description: descriptionInfo, release: radioValue };
this.props.dispatch({ type: 'appInfo/addDeployApp', payload: { values } });
thisPush(this, { pathname: '../deploy', state: { value } });
}
};
render() {
const { token, uploadURL } = this.props.appInfo;
// console.log(token);
// console.log(uploadURL);
const tokens = encrypt(token);
// let URL;
// if (uploadURL !== '') {
// URL = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uploadURL)}`;
// }
let action;
if (uploadURL) {
action = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uploadURL)}`;
} else {
action = `${config.apiContextPath}/resource/${tokens}`;
}
const props = {
name: 'test',
accept: '.apk',
action,
headers: {
authorization: 'authorization-text',
},
fileList: this.state.fileList,
data: (file) => {
return file;
},
onChange: (info) => {
if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
const response = info.file.response;
this.props.dispatch({ type: 'appInfo/uploadAPK', payload: { response } });
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
let fileList = info.fileList;
fileList = fileList.slice(-3);
fileList = fileList.filter((file) => {
if (file.response) {
return file.response.errorCode === 0;
}
return true;
});
fileList = fileList.map((file) => {
if (file.response) {
// Component will show file.url as link
// eslint-disable-next-line
file.url = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(file.response.data[0])}`;
}
return file;
});
fileList = uniqBy(fileList, (file) => {
const url = file.url;
return url ? url.slice(url.lastIndexOf('/')) : null;
});
this.setState({
fileList,
});
},
};
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divRow}>
<TextArea rows={5} placeholder="description" onChange={this.onChangeInfo} />
</div>
<div className={styles.divRow}>
<RadioGroup onChange={this.onChange} value={this.state.radioValue}>
<Radio value={false}>development</Radio>
<Radio value>release</Radio>
</RadioGroup>
</div>
<div className={styles.divBtn}>
<Button type="primary" icon="check" onClick={this.onSubmit}>提交</Button>
<Button type="primary" icon="close" onClick={this.onCancel}>取消</Button>
<Upload {...props}>
<Button type="primary" >
<Icon type="upload" /> 选择上传
</Button>
</Upload>
</div>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(Form.create()(AddDeploy));
/* eslint-disable no-undef,jsx-a11y/img-has-alt */
/**
* Created by zhouhuan on 2017/11/21.
*/
import React from 'react';
import { connect } from 'dva';
import QRcode from 'qrcode';
import { Button, Table, Popconfirm, Modal } from 'antd';
import config from '../../../../utils/config';
import { encrypt } from '../../../../utils/helper';
import styles from './list.less';
import { thisPush } from '../../../../services/route';
class Deploy extends React.Component {
state = {
values: '',
urls: '',
visible: false,
completeUrl: '',
}
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getTokens' });
const { value } = this.props.location.state;
// console.log(value);
this.props.dispatch({ type: 'appInfo/getAppInfo', payload: { name: value.name } });
}
onDelete = (record) => {
console.log(record);
const id = record.id;
const { value } = this.props.location.state;
this.props.dispatch({ type: 'appInfo/removeDeployApp', payload: { id, name: value.name } });
};
onEdit = (record) => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../editDeploy', state: { record, value } });
};
onQrcode = (record) => {
const { token } = this.props.appInfo;
const tokens = encrypt(token);
const uri = record.uri;
const URL = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uri)}`;
// console.log(document.location.href);
const browerUrl = (document.location.href).split(`${config.contextPath}/main`)[0];
const completeUrl = browerUrl + URL;
this.setState({ completeUrl });
// console.log(completeUrl);
QRcode.toDataURL(completeUrl, (err, url) => {
const qrcodeUrl = url;
this.setState({ urls: qrcodeUrl, visible: true });
});
// const w = window.open('about:blank');
// w.location.href = URL;
};
onClick = () => {
const { value } = this.props.location.state;
console.log(value);
thisPush(this, { pathname: '../addDeploy', state: { value } });
};
handleOk = () => {
this.setState({
visible: false,
});
}
handleCancel = () => {
this.setState({
visible: false,
});
}
makeColumns() {
const columns = [{
title: 'ID',
dataIndex: 'id',
}, {
title: '更新时间',
dataIndex: 'updateTime',
}, {
title: '状态',
dataIndex: 'status',
}, {
title: '版本号',
dataIndex: 'versionNumber',
}, {
title: '描述',
dataIndex: 'description',
}, {
title: 'uri',
dataIndex: 'uri',
}, {
title: '操作',
dataIndex: 'operation',
render: (text, record, index) => (
<span>
<span className="ant-divider" />
<a onClick={() => this.onEdit(record, index)}>Edit</a>
<span className="ant-divider" />
<Popconfirm title="确定删除?" okText="Yes" cancelText="No"onConfirm={() => this.onDelete(record, index)}>
<a>Delete</a>
</Popconfirm>
<span className="ant-divider" />
<a onClick={() => this.onQrcode(record, index)}>qrcode</a>
</span>
),
}];
return columns;
}
render() {
const { allAppInfo } = this.props.appInfo;
const { urls, visible, completeUrl } = this.state;
let data = [];
if (allAppInfo.length !== 0) {
allAppInfo.map(({ history }) => {
data = history.map(({ id, updateTime, status, versionNumber, description, uri }) => {
const date = (new Date(updateTime)).toLocaleString();
const info = {
key: updateTime,
id,
updateTime: date,
status,
versionNumber,
description,
uri,
};
return info;
});
return data;
});
}
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divBtn}>
<Button type="primary" icon="plus" onClick={this.onClick}>新增</Button>
</div>
<div className={styles.divTable}>
<Table columns={this.makeColumns()} dataSource={data} />
</div>
<div>
<Modal
title="二维码"
visible={visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={null}
>
<div style={{ textAlign: 'center' }}>
<img src={urls} />
<div>
<a href={completeUrl} className={styles.a}>{completeUrl}</a>
</div>
</div>
</Modal>
</div>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(Deploy);
import React from 'react';
import { Button, Input, Form } from 'antd';
import { connect } from 'dva';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
class Edit extends React.Component {
onCancel = () => {
thisPush(this, { pathname: '../list' });
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.dispatch({ type: 'appInfo/editAppInfo', payload: { values } });
}
});
}
render() {
const { value } = this.props.location.state;
const { getFieldDecorator } = this.props.form;
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('name', {
rules: [{ required: true, message: 'Please input your name!' }],
initialValue: value.name,
})(
<Input disabled />,
)}
</FormItem>
<FormItem
label="新名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('newName', {
rules: [{ required: true, message: 'Please input your newName!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="包名"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('packageName', {
rules: [{ required: true, message: 'Please input your packageName!' }],
initialValue: value.packageName,
})(
<Input />,
)}
</FormItem>
<FormItem
label="描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
initialValue: value.description,
})(
<Input />,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(Form.create()(Edit));
/**
* Created by zhouhuan on 2017/11/24.
*/
import React from 'react';
import { Button, Input, Select, Form } from 'antd';
import { connect } from 'dva';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
const { TextArea } = Input;
const Option = Select.Option;
class EditDeploy extends React.Component {
state = {
descriptionInfo: '',
}
componentDidMount() {
// this.props.dispatch({ type: 'appInfo/getTokens' });
}
onChangeInfo = (e) => {
this.setState({ descriptionInfo: e.target.value });
}
onCancel = () => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../deploy', state: { value } });
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
const { value } = this.props.location.state;
this.props.dispatch({ type: 'appInfo/editSaveDeploy', payload: { values, name: value.name } });
thisPush(this, { pathname: '../deploy', state: { value } });
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
const { record } = this.props.location.state;
const selects = (
<Select>
<Option key="release">公开版本</Option>
<Option key="development">测试版</Option>
<Option key="broken">重大bug版本</Option>
</Select>
);
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="ID"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('id', {
rules: [{ required: true, message: 'Please input your name!' }],
initialValue: record.id,
})(
<Input disabled />,
)}
</FormItem>
<FormItem
label="部署描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
initialValue: record.description,
})(
<TextArea rows={5} />,
)}
</FormItem>
<FormItem
label="部署状态"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('status', {
initialValue: record.status,
})(
selects,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(Form.create()(EditDeploy));
/**
* Created by zhouhuan on 2017/11/20.
*/
import { connect } from 'dva';
import { withRouter } from 'dva/router';
import List from './list';
import Add from './add';
import Edit from './edit';
import Deploy from './deploy';
import AddDeploy from './addDeploy';
import EditDeploy from './editDeploy';
import route from '../../../../components/hoc/routes';
export default connect(({ appManagement }) => ({ appManagement }))(route({
childRoutes: [
{
path: 'list',
name: '列表',
component: withRouter(List, { withRef: true }),
},
{
path: 'add',
name: '新增',
component: withRouter(Add, { withRef: true }),
},
{
path: 'edit',
name: '新增',
component: withRouter(Edit, { withRef: true }),
},
{
path: 'deploy',
name: '部署',
component: withRouter(Deploy, { withRef: true }),
},
{
path: 'addDeploy',
name: '新增部署信息',
component: withRouter(AddDeploy, { withRef: true }),
},
{
path: 'editDeploy',
name: '编辑部署信息',
component: withRouter(EditDeploy, { withRef: true }),
},
],
}));
import React from 'react';
import { connect } from 'dva';
import { Button, Table, Popconfirm } from 'antd';
import styles from './list.less';
import { thisPush } from '../../../../services/route';
class List extends React.Component {
state = {
values: '',
}
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getAppInfo', payload: { name: '' } });
}
onDelete = (record) => {
const name = record.name;
this.props.dispatch({ type: 'appInfo/delAppInfo', payload: { name } });
};
onEdit = (record) => {
console.log(record);
thisPush(this, { pathname: '../edit', state: { value: record } });
};
onDeploy = (record) => {
thisPush(this, { pathname: '../deploy', state: { value: record } });
this.props.dispatch({ type: 'appInfo/saveRecord', payload: { record } });
};
onClick = () => {
thisPush(this, { pathname: '../add', state: { value: '' } });
};
makeColumns() {
const columns = [{
title: '创建时间',
dataIndex: 'createTime',
}, {
title: '名称',
dataIndex: 'name',
}, {
title: '包名',
dataIndex: 'packageName',
}, {
title: '描述',
dataIndex: 'description',
}, {
title: '操作',
dataIndex: 'operation',
render: (text, record, index) => (
<span>
<span className="ant-divider" />
<a onClick={() => this.onEdit(record, index)}>Edit</a>
<span className="ant-divider" />
<Popconfirm title="确定删除?" okText="Yes" cancelText="No"onConfirm={() => this.onDelete(record, index)}>
<a>Delete</a>
</Popconfirm>
<span className="ant-divider" />
<a onClick={() => this.onDeploy(record, index)}>deploy</a>
</span>
),
}];
return columns;
}
render() {
const { allAppInfo } = this.props.appInfo;
const data = allAppInfo.map(({ createTime, name, packageName, description }) => {
const date = (new Date(createTime)).toLocaleString();
const info = {
key: createTime,
createTime: date,
name,
packageName,
description,
};
return info;
});
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divBtn}>
<Button type="primary" icon="plus" onClick={this.onClick}>添加</Button>
</div>
<div className={styles.divTable}>
<Table columns={this.makeColumns()} dataSource={data} />
</div>
</div>
</div>
);
}
}
export default connect(({ appInfo, loading }) => ({ appInfo, loading }))(List);
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 18px;
}
.container {
margin-top: 10px;
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
.divBtn{
float: left;
}
.divSelect{
float: right;
}
.divTable{
margin-top: 60px;
border: 1px solid;
border-color:#C4C4C4;
padding: 20px;
border-radius: 10px;
}
.a {
word-wrap:break-word;
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论