Skip to content

Commit ee70460

Browse files
dongruisunnywx
authored andcommitted
feat: Add audit record page (#534)
* refactor: Center layout page style * fix: Image Component load logic
1 parent f827371 commit ee70460

14 files changed

Lines changed: 523 additions & 12 deletions

File tree

src/components/AppImages/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import React, { Component } from 'react';
22
import PropTypes from 'prop-types';
33
import { Link } from 'react-router-dom';
44
import { translate } from 'react-i18next';
5+
import { inject } from 'mobx-react';
56

67
import { Image } from 'components/Base';
78

89
import styles from './index.scss';
9-
import { inject } from 'mobx-react/index';
1010

1111
@translate()
1212
@inject(({ rootStore }) => ({

src/components/Base/Image/index.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,19 @@ export default class Image extends React.Component {
2525
};
2626

2727
shouldComponentUpdate(nextProps, nextState) {
28-
return nextProps.src !== this.props.src || Boolean(nextState.failed);
28+
return (
29+
nextProps.src !== this.props.src || Boolean(nextState.failed) || Boolean(nextProps.iconLetter)
30+
);
2931
}
3032

3133
componentDidMount() {
3234
this.img.onerror = () => {
3335
this.setState({ failed: true });
3436
};
37+
38+
this.img.onload = () => {
39+
this.setState({ failed: false });
40+
};
3541
}
3642

3743
componentWillUnmount() {
@@ -45,7 +51,6 @@ export default class Image extends React.Component {
4551
const { failed } = this.state;
4652

4753
if (failed) {
48-
const nonIcon = '/none.svg';
4954
const style = {
5055
width: iconSize + 'px',
5156
height: iconSize + 'px'
@@ -55,6 +60,7 @@ export default class Image extends React.Component {
5560
if (letter) {
5661
style.fontSize = iconSize / 2 + 'px';
5762
style.padding = iconSize / 4 - 1 + 'px';
63+
5864
return (
5965
<span className={classnames(styles.letter, className)} style={style} {...rest}>
6066
{letter}
@@ -64,7 +70,7 @@ export default class Image extends React.Component {
6470

6571
return (
6672
<img
67-
src={nonIcon}
73+
src="/none.svg"
6874
data-origin-url={src}
6975
style={style}
7076
className={classnames(styles.img, className)}

src/components/Layout/BreadCrumb/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33
import { Link } from 'react-router-dom';
44
import { observer } from 'mobx-react';
55
import { translate } from 'react-i18next';
6-
import { inject } from 'mobx-react/index';
6+
import { inject } from 'mobx-react';
77

88
import pathLink from './path-link';
99
import styles from './index.scss';

src/components/Layout/Card/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ $card-padding: 24px;
22

33
.card {
44
position: relative;
5+
box-sizing: border-box;
56
padding: $card-padding;
67
margin-bottom: 24px;
78

src/components/Layout/SideNav/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Fragment } from 'react';
22
import PropTypes from 'prop-types';
33
import { Link, NavLink, withRouter } from 'react-router-dom';
4-
import { inject, observer } from 'mobx-react/index';
4+
import { inject, observer } from 'mobx-react';
55
import { translate } from 'react-i18next';
66
import classnames from 'classnames';
77
import _ from 'lodash';

src/components/Layout/index.jsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import classnames from 'classnames';
55
import { inject } from 'mobx-react';
66
import { noop, clone, isEmpty, get } from 'lodash';
77

8-
import { Notification } from 'components/Base';
8+
import { Notification, Icon } from 'components/Base';
99
import Loading from 'components/Loading';
1010
import TitleBanner from './TitleBanner';
1111
import SideNav from './SideNav';
@@ -27,6 +27,7 @@ class Layout extends Component {
2727
loadClass: PropTypes.string,
2828
listenToJob: PropTypes.func,
2929
title: PropTypes.string,
30+
pageTitle: PropTypes.string,
3031
hasSearch: PropTypes.bool,
3132
isHome: PropTypes.bool
3233
};
@@ -36,6 +37,7 @@ class Layout extends Component {
3637
backBtn: null,
3738
listenToJob: noop,
3839
title: '',
40+
pageTitle: '',
3941
hasSearch: false,
4042
isHome: false
4143
};
@@ -77,6 +79,11 @@ class Layout extends Component {
7779
scrollTop > 0 ? this.setState({ isScroll: true }) : this.setState({ isScroll: false });
7880
};
7981

82+
goBack = () => {
83+
const { history } = this.props;
84+
history.goBack();
85+
};
86+
8087
render() {
8188
const {
8289
className,
@@ -88,14 +95,16 @@ class Layout extends Component {
8895
hasSearch,
8996
title,
9097
isHome,
91-
match
98+
match,
99+
pageTitle
92100
} = this.props;
93101

94102
const { isNormal, isDev, isAdmin } = this.props.user;
95103
const hasMenu = (isDev || isAdmin) && !isHome;
96104
const { isScroll } = this.state;
97105
const paths = ['/dashboard', '/profile', '/ssh_keys', '/dev/apps'];
98-
const hasSubNav = hasMenu && !paths.includes(match.path);
106+
const isCenterPage = Boolean(pageTitle); // detail page, only one level menu
107+
const hasSubNav = hasMenu && !isCenterPage && !paths.includes(match.path);
99108

100109
return (
101110
<div
@@ -104,7 +113,8 @@ class Layout extends Component {
104113
className,
105114
{ [styles.hasMenu]: hasSubNav },
106115
{ [styles.hasNav]: hasMenu && !hasSubNav },
107-
{ [styles.hasBack]: Boolean(backBtn) }
116+
{ [styles.hasBack]: Boolean(backBtn) },
117+
{ [styles.detailPage]: isCenterPage }
108118
)}
109119
>
110120
{noNotification ? null : <Notification />}
@@ -113,8 +123,23 @@ class Layout extends Component {
113123
{hasMenu && <SideNav isScroll={isScroll} hasSubNav={hasSubNav} />}
114124
{isNormal && !isHome && <TitleBanner title={title} hasSearch={hasSearch} />}
115125

126+
{isCenterPage && (
127+
<div className={styles.pageTitle}>
128+
<div className={styles.title}>
129+
<Icon
130+
onClick={this.goBack}
131+
name="previous"
132+
size={20}
133+
type="dark"
134+
className={styles.icon}
135+
/>
136+
{pageTitle}
137+
</div>
138+
</div>
139+
)}
140+
116141
<Loading isLoading={isLoading} className={styles[loadClass]}>
117-
{children}
142+
{isCenterPage ? <div className={styles.centerPage}>{children}</div> : children}
118143
</Loading>
119144
</div>
120145
);

src/components/Layout/index.scss

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
margin-left: $menu-nav-width + 20px;
2929
}
3030

31+
&.detailPage {
32+
width: calc(100% - #{$menu-nav-width});
33+
margin-left: $menu-nav-width;
34+
margin-top: 88px;
35+
}
36+
3137
.backBtn {
3238
//width: $content-width;
3339
margin: 0 auto 20px;
@@ -159,3 +165,38 @@
159165
}
160166
}
161167
}
168+
169+
.pageTitle {
170+
position: fixed;
171+
left: $menu-nav-width;
172+
top: 0;
173+
z-index: 2;
174+
width: 100%;
175+
height: 64px;
176+
box-shadow: inset 0 -1px 0 0 $N10;
177+
background-color: $N0;
178+
179+
.title {
180+
margin: 0 auto;
181+
width: calc(100% - #{$min-center-page-margin}*2);
182+
max-width: $max-center-page-width;
183+
line-height: 64px;
184+
font-size: 16px;
185+
font-weight: 500;
186+
color: $N500;
187+
}
188+
189+
.icon {
190+
float: left;
191+
margin-top: 22px;
192+
margin-left: -40px;
193+
line-height: 20px;
194+
cursor: pointer;
195+
}
196+
}
197+
198+
.centerPage {
199+
margin: 0 auto;
200+
width: calc(100% - #{$min-center-page-margin}*2);
201+
max-width: $max-center-page-width;
202+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React, { Component, Fragment } from 'react';
2+
import { observer, inject } from 'mobx-react';
3+
import { Link } from 'react-router-dom';
4+
import { translate } from 'react-i18next';
5+
import classnames from 'classnames';
6+
7+
import { Icon, Button, Table, Popover, Select, Modal, Image } from 'components/Base';
8+
import Layout, { Dialog, Grid, Row, Section, Card } from 'components/Layout';
9+
import { getObjName, mappingStatus, getFilterObj } from 'utils';
10+
11+
import { records } from './record';
12+
import styles from './index.scss';
13+
14+
@translate()
15+
@inject(({ rootStore }) => ({
16+
rootStore,
17+
appVersionStore: rootStore.appVersionStore,
18+
appStore: rootStore.appStore
19+
}))
20+
@observer
21+
export default class AuditRecord extends Component {
22+
async componentDidMount() {
23+
const { appStore, match } = this.props;
24+
const { appId } = match.params;
25+
26+
appStore.fetch(appId);
27+
}
28+
29+
componentWillUnmount() {
30+
const { appVersionStore } = this.props;
31+
appVersionStore.reset();
32+
}
33+
34+
render() {
35+
const { appVersionStore, appStore, t } = this.props;
36+
const { appDetail } = appStore;
37+
const statusMap = {
38+
notStarted: '未开始',
39+
passed: '已通过',
40+
processing: '进行中',
41+
notPassed: '未通过'
42+
};
43+
44+
return (
45+
<Layout className={styles.auditRecord} pageTitle="审核记录详情">
46+
<Row>
47+
<Card className={styles.baseInfo}>
48+
<div className={styles.main}>
49+
<span className={styles.image}>
50+
<Image iconLetter={appDetail.name} iconSize={48} src={appDetail.icon} />
51+
</span>
52+
<div className={styles.title}>{appDetail.name}</div>
53+
<div className={styles.word}>
54+
交付方式:<label className={styles.value}>Helm</label>
55+
版本:<label className={styles.value}>0.0.1</label>
56+
</div>
57+
</div>
58+
59+
<div className={styles.secondary}>
60+
<p>申请编号:201810039282092</p>
61+
<p>提交时间:2018年10月23日 12:22:02</p>
62+
</div>
63+
</Card>
64+
</Row>
65+
66+
<Row>
67+
<Grid>
68+
<Section size={12} className={styles.moduleOuter}>
69+
{records.map(record => (
70+
<div
71+
key={record.title}
72+
className={classnames(styles.module, [styles[record.status]])}
73+
>
74+
<div className={styles.status}>
75+
<label>{statusMap[record.status]}</label>
76+
</div>
77+
<div className={styles.title}>{record.title}</div>
78+
79+
<div className={styles.description}>{record.description}</div>
80+
<ul className={styles.steps}>
81+
{record.process.map(item => (
82+
<li key={item.name} className={classnames(styles[item.status])}>
83+
<label className={styles.dot} />
84+
{item.name}
85+
<span className={styles.arrow}>&nbsp;→</span>
86+
</li>
87+
))}
88+
</ul>
89+
<div className={styles.info}>
90+
<p>审核人员:{record.auditor}</p>
91+
<p>通过时间:{record.passTime}</p>
92+
</div>
93+
</div>
94+
))}
95+
96+
<div className={styles.module}>
97+
<div className={styles.result}>
98+
<Icon name="error" size={36} type="dark" />
99+
<div className={styles.title}>审核未通过</div>
100+
<div className={styles.reason}>
101+
严格测试应用的功能性、稳定性,以及应用的各个信息。
102+
</div>
103+
<Link to="#">
104+
<Button className={styles.view}>查看版本</Button>
105+
</Link>
106+
</div>
107+
</div>
108+
</Section>
109+
</Grid>
110+
</Row>
111+
</Layout>
112+
);
113+
}
114+
}

0 commit comments

Comments
 (0)