Ant Design Mobile¶
统计信息:字数 9573 阅读20分钟
蚂蚁金服移动端组件(蚂蚁金服、阿里巴巴)
https://mobile.ant.design/docs/react/introduce-cn
WingBlank 两翼留白¶
import { WingBlank, WhiteSpace } from 'antd-mobile';
const PlaceHolder = ({ className = '', ...restProps }) => (
<div className={`${className} placeholder`} {...restProps}>Block</div>
);
const WingBlankExample = () => (
<div style={{ padding: '15px 0' }}>
<WingBlank><PlaceHolder /></WingBlank>
<WhiteSpace size="lg" />
<WingBlank size="md"><PlaceHolder /></WingBlank>
<WhiteSpace size="lg" />
<WingBlank size="sm"><PlaceHolder /></WingBlank>
</div>
);
ReactDOM.render(<WingBlankExample />, mountNode);
Modal 对话框¶
这里的对话框和PC端的对话框不同,适合很少内容(确定-取消),移动端不适合很多内容,不利于选择
import { Modal, List, Button, WhiteSpace, WingBlank, Icon } from 'antd-mobile';
function closest(el, selector) {
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
while (el) {
if (matchesSelector.call(el, selector)) {
return el;
}
el = el.parentElement;
}
return null;
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modal1: false,
modal2: false,
};
}
showModal = key => (e) => {
e.preventDefault(); // 修复 Android 上点击穿透
this.setState({
[key]: true,
});
}
onClose = key => () => {
this.setState({
[key]: false,
});
}
onWrapTouchStart = (e) => {
// fix touch to scroll background page on iOS
if (!/iPhone|iPod|iPad/i.test(navigator.userAgent)) {
return;
}
const pNode = closest(e.target, '.am-modal-content');
if (!pNode) {
e.preventDefault();
}
}
render() {
return (
<WingBlank>
<Button onClick={this.showModal('modal1')}><Icon type="up" />basic<Icon type="up" /></Button>
<WhiteSpace />
<Modal
visible={this.state.modal1}
transparent
maskClosable={false}
onClose={this.onClose('modal1')}
title="Title"
footer={[{ text: 'Ok', onPress: () => { console.log('ok'); this.onClose('modal1')(); } }]}
wrapProps={{ onTouchStart: this.onWrapTouchStart }}
afterClose={() => { alert('afterClose'); }}
>
<div style={{ height: 100, overflow: 'scroll' }}>
scoll content...<br />
scoll content...<br />
scoll content...<br />
scoll content...<br />
scoll content...<br />
scoll content...<br />
</div>
</Modal>
<Button onClick={this.showModal('modal2')}>popup</Button>
<WhiteSpace />
<Modal
popup
visible={this.state.modal2}
onClose={this.onClose('modal2')}
animationType="slide-up"
afterClose={() => { alert('afterClose'); }}
>
<List renderHeader={() => <div>委托买入</div>} className="popup-list">
{['股票名称', '股票代码', '买入价格'].map((i, index) => (
<List.Item key={index}>{i}</List.Item>
))}
<List.Item>
<Button type="primary" onClick={this.onClose('modal2')}>买入</Button>
</List.Item>
</List>
</Modal>
</WingBlank>
);
}
}
ReactDOM.render(<App />, mountNode);
ListView 长列表¶
适用于显示同类的长列表数据类型,对渲染性能有一定的优化效果。
/* eslint no-dupe-keys: 0 */
import { ListView } from 'antd-mobile';
const data = [
{
img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
title: 'Meet hotel',
des: '不是所有的兼职汪都需要风吹日晒',
},
];
const NUM_ROWS = 20;
let pageIndex = 0;
function genData(pIndex = 0) {
const dataBlob = {};
for (let i = 0; i < NUM_ROWS; i++) {
const ii = (pIndex * NUM_ROWS) + i;
dataBlob[`${ii}`] = `row - ${ii}`;
}
return dataBlob;
}
class Demo extends React.Component {
constructor(props) {
super(props);
const dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
dataSource,
isLoading: true,
};
}
componentDidMount() {
// you can scroll to the specified position
// setTimeout(() => this.lv.scrollTo(0, 120), 800);
// simulate initial Ajax
setTimeout(() => {
this.rData = genData();
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.rData),
isLoading: false,
});
}, 600);
}
render() {
const separator = (sectionID, rowID) => (
<div
key={`${sectionID}-${rowID}`}
style={{
backgroundColor: '#F5F5F9',
height: 8,
borderTop: '1px solid #ECECED',
borderBottom: '1px solid #ECECED',
}}
/>
);
let index = data.length - 1;
const row = (rowData, sectionID, rowID) => {
if (index < 0) {
index = data.length - 1;
}
const obj = data[index--];
return (
<div key={rowID} style={{ padding: '0 15px' }}>
<div
style={{
lineHeight: '50px',
color: '#888',
fontSize: 18,
borderBottom: '1px solid #F6F6F6',
}}
>{obj.title}</div>
<div style={{ display: '-webkit-box', display: 'flex', padding: '15px 0' }}>
<img style={{ height: '64px', marginRight: '15px' }} src={obj.img} alt="" />
<div style={{ lineHeight: 1 }}>
<div style={{ marginBottom: '8px', fontWeight: 'bold' }}>{obj.des}</div>
<div><span style={{ fontSize: '30px', color: '#FF6E27' }}>{rowID}</span>¥</div>
</div>
</div>
</div>
);
};
return (
<ListView
ref={el => this.lv = el}
dataSource={this.state.dataSource}
renderHeader={() => <span>header</span>}
renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
{this.state.isLoading ? 'Loading...' : 'Loaded'}
</div>)}
renderRow={row}
renderSeparator={separator}
className="am-list"
pageSize={4}
useBodyScroll
onScroll={() => { console.log('scroll'); }}
scrollRenderAheadDistance={500}
// onEndReached={this.onEndReached}
onEndReachedThreshold={10}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
单选¶
import { List, Radio, Flex, WhiteSpace } from 'antd-mobile';
const RadioItem = Radio.RadioItem;
class Test extends React.Component {
state = {
value: 0,
value2: 0,
value3: 0,
value4: 0,
};
onChange = (value) => {
console.log('checkbox');
this.setState({
value,
});
};
onChange2 = (value) => {
console.log('checkbox');
this.setState({
value2: value,
});
};
render() {
const { value, value2, value3, value4 } = this.state;
const data = [
{ value: 0, label: 'doctor' },
{ value: 1, label: 'bachelor' },
];
const data2 = [
{ value: 0, label: 'basketball', extra: 'details' },
{ value: 1, label: 'football', extra: 'details' },
];
return (<div>
<List renderHeader={() => 'RadioItem demo'}>
{data.map(i => (
<RadioItem key={i.value} checked={value === i.value} onChange={() => this.onChange(i.value)}>
{i.label}
</RadioItem>
))}
</List>
<WhiteSpace size="lg" />
<List>
{data2.map(i => (
<RadioItem key={i.value} checked={value2 === i.value} onChange={() => this.onChange2(i.value)}>
{i.label}<List.Item.Brief>{i.extra}</List.Item.Brief>
</RadioItem>
))}
</List>
<List renderHeader={() => 'Disabled'}>
{data.map(i => (
<RadioItem key={i.value} checked={value3 === i.value} onChange={() => this.onChange3(i.value)} disabled>
{i.label}
</RadioItem>
))}
</List>
<WhiteSpace size="lg" />
<List>
{data2.map(i => (
<RadioItem key={i.value} checked={value4 === i.value} onChange={() => this.onChange4(i.value)} disabled>
{i.label}<List.Item.Brief>{i.extra}</List.Item.Brief>
</RadioItem>
))}
</List>
<Flex style={{ padding: '15px' }}>
<Flex.Item style={{ padding: '15px 0', color: '#888', flex: 'none' }}>Radio demo(dustomized style)</Flex.Item>
<Flex.Item>
<Radio className="my-radio" onChange={e => console.log('checkbox', e)}>Agree</Radio>
</Flex.Item>
</Flex>
</div>);
}
}
ReactDOM.render(<Test />, mountNode);
导航栏¶
import { NavBar, Icon } from 'antd-mobile';
ReactDOM.render(
<div>
<NavBar
mode="light"
icon={<Icon type="left" />}
onLeftClick={() => console.log('onLeftClick')}
rightContent={[
<Icon key="0" type="search" style={{ marginRight: '16px' }} />,
<Icon key="1" type="ellipsis" />,
]}
>NavBar</NavBar>
<NavBar
mode="dark"
leftContent="Back"
rightContent={[
<Icon key="0" type="search" style={{ marginRight: '16px' }} />,
<Icon key="1" type="ellipsis" />,
]}
>NavBar</NavBar>
</div>
, mountNode);
Last update:
November 9, 2024