Commit 29881cab authored by shiyunjie's avatar shiyunjie

保险产品

parent a38e0b51
...@@ -5,9 +5,14 @@ ...@@ -5,9 +5,14 @@
## 前言 ## 前言
Agera基于客户通项目封装,设计参考交银e保通,交银爱行销 开发Agera的目的就是追求"快"。
Agera一期,要建立稳定的演示环境,演示在线投保(包括空中签名、保单签收),培训,绩效查询,行销工具 有人会说复制一个项目,或复制文件、代码也很快,Agera有何不同?
Agera的快是建立在标准化之上的,提前准备好了组件、示例,直接用就好了。修改也会很快,也为远程协作提供便利。
Agera 一期,要建立稳定的演示环境,业务场景(在线投保,培训,绩效查询,行销工具)
## 开发日志 ## 开发日志
......
# 投保功能描述
## 简述
投保功能包含三部分
* components目录
* config目录
* util目录
components是根据基础组件组装的UI组件,供渲染工具类使用,也可单独导出组件使用。
config是模块配置模版,可直接使用,也可以根据模版的结构自行设计配置。
renderUtil提供一个函数,根据传入的配置渲染组件。
## 配置
### tmpl
BaseTmpl 配置的模版
insuranceProcess 投保流程数组,控制投保流程是否有该页面环节
```
insuranceProcess: [
productDetail, // 产品详情页面
fillInfo, // 信息录入
healthNotification, // 健康告知
payment, // 确认支付
receipt, // 签收
],
```
productDetail 页面对象,控制该页面渲染组件,装填数据
layout 描述页面布局,
key 描述这个组件用于哪个业务场景
type 描述这个组件是哪个UI组件
rules 描述这个组件业务规则,与layout key相关联
```
name: ''
layout: [
{
type: BANNER, // 组件类型
key: BANNER, // 组件的唯一标识,用来串联 layout, rules
uri: '', // banner需要装填数据 uri,展示的图片链接
width: 100, //图片宽度
height: 100, // 图片高度
},
{
type: TITLE,
key: TITLE,
h1: '', // title需要装填数据 主标题
h2: '', // title需要装填数据 副标题
label: [], // 多个展示标签
labelColor: '', //颜色
},
{
type: BUTTON_GROUP,
key: BUTTON_GROUP,
buttons: [],
onSelect: () => {
},
selectIndex: 0,
},
{
type: LIST_ITEM,
key: LIST_ITEM,
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
data: [{ key: '', value: '' }],
},
{
type: CHOOSE,
key: CHOOSE,
title: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
text: '', //选择框显示标题
values: [], //选择框数据集合
source: {}, //修改源对象
setKey: '', //修改源对象内的属性名称
onChange: () => {
}, //选中事件
onWillChange: () => {
}, // 选中前执行事件
},
{
type: PRODUCT,
key: PRODUCT,
uri: '',
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
},
{
type: SERVICE,
key: SERVICE,
uri: '',
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
},
],
rules: [
{
layoutKey: CHOOSE, // 串联 key 为 choose的组件
range: [], // [min , max] [a,b,c,d] 只允许包含在 a\b\c\d
},
],
```
getConfig 做了两件事:
根据ID,组装对应保险产品的配置。
预先设置数据到layout中。
fillData
真正的页面上需要获取服务器数据,再吧这些数据依次填入layout,绑定事件。
...@@ -11458,6 +11458,14 @@ ...@@ -11458,6 +11458,14 @@
} }
} }
}, },
"react-native-animatable": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.2.tgz",
"integrity": "sha512-rmah3KQ63ft8DxkzFUwJSuZeq+oSYwldoGF4DTOR5WM2WR5wiWLgBAtrAHlI3Di3by323uOR21s+MlqPcHz2Kw==",
"requires": {
"prop-types": "^15.5.10"
}
},
"react-native-debugger-open": { "react-native-debugger-open": {
"version": "0.3.19", "version": "0.3.19",
"resolved": "https://registry.npmjs.org/react-native-debugger-open/-/react-native-debugger-open-0.3.19.tgz", "resolved": "https://registry.npmjs.org/react-native-debugger-open/-/react-native-debugger-open-0.3.19.tgz",
...@@ -11544,6 +11552,15 @@ ...@@ -11544,6 +11552,15 @@
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.5.4.tgz", "resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.5.4.tgz",
"integrity": "sha512-FF1NhlerA4uBiS0gFIHa4FBp8/aftv4vPj14Y47lGNkYjSI94tnI6oYO3EfUxXjEyCUPiOVNKZOB9kScyjc5Ew==" "integrity": "sha512-FF1NhlerA4uBiS0gFIHa4FBp8/aftv4vPj14Y47lGNkYjSI94tnI6oYO3EfUxXjEyCUPiOVNKZOB9kScyjc5Ew=="
}, },
"react-native-modal": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-11.1.0.tgz",
"integrity": "sha512-gSD21evs2m+C/vtRS+R/I1ZuIN5td+K+I9Dt04HpBzux3HyA9km2oonQy8t+EKUNN2rFYjFntfUOMmb3vj7i8A==",
"requires": {
"prop-types": "^15.6.2",
"react-native-animatable": "^1.3.1"
}
},
"react-native-ratings": { "react-native-ratings": {
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-6.3.1.tgz", "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-6.3.1.tgz",
......
...@@ -6,7 +6,6 @@ import React, { Component } from 'react'; ...@@ -6,7 +6,6 @@ import React, { Component } from 'react';
import { import {
createReduxContainer, createReduxContainer,
} from 'react-navigation-redux-helpers'; } from 'react-navigation-redux-helpers';
import { BackHandler } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import NavigationStack from './navigationStack'; import NavigationStack from './navigationStack';
...@@ -22,33 +21,12 @@ const mapStateToProps = state => ({ ...@@ -22,33 +21,12 @@ const mapStateToProps = state => ({
const ReduxNavigation = connect(mapStateToProps)(AppNavigation); const ReduxNavigation = connect(mapStateToProps)(AppNavigation);
class Navigation extends Component { function Navigation() {
componentDidMount() { return (
BackHandler.addEventListener('hardwareBackPress', this.onBackPress); <StoreContext.Provider value={{ theme: global.theme }}>
} <ReduxNavigation />
</StoreContext.Provider>
componentWillUnmount() { );
BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
}
onBackPress = () => {
console.log('onBackPress', this.props);
//const { dispatch, navigationState } = this.props;
//if (navigationState.stateForLoggedIn.index <= 1) {
// BackHandler.exitApp();
// return;
//}
//dispatch(NavigationActions.back());
return true;
}
render() {
return (
<StoreContext.Provider value={{ theme: global.theme }}>
<ReduxNavigation />
</StoreContext.Provider>
);
}
} }
......
/** /**
* @flow * @flow
*/ */
import { NativeAppEventEmitter } from 'react-native'; import {
NativeAppEventEmitter,
Animated,
Easing,
Platform,
} from 'react-native';
import { import {
createBottomTabNavigator, createBottomTabNavigator,
createStackNavigator, createStackNavigator,
...@@ -26,6 +31,9 @@ import SliderPage from '../pages/sliderDemo'; ...@@ -26,6 +31,9 @@ import SliderPage from '../pages/sliderDemo';
import TooltipPage from '../pages/tooltipDemo'; import TooltipPage from '../pages/tooltipDemo';
import InsuranceDetail from '../pages/InsuranceList/InsuranceDetail'; import InsuranceDetail from '../pages/InsuranceList/InsuranceDetail';
import ProductDetailPage from '../pages/insurePage/ProductDetailPage'; import ProductDetailPage from '../pages/insurePage/ProductDetailPage';
import FillPolicyInfoPage from '../pages/insurePage/FillPolicyInfoPage';
import PaymentPage from '../pages/insurePage/PaymentPage';
import PolicyResultPage from '../pages/insurePage/PolicyResultPage';
import { initialRouteName, RootPageInitialName } from '../utils/constants'; import { initialRouteName, RootPageInitialName } from '../utils/constants';
import { TabOptions } from '../components/TabOptions'; import { TabOptions } from '../components/TabOptions';
...@@ -137,8 +145,18 @@ const navigator = createStackNavigator({ ...@@ -137,8 +145,18 @@ const navigator = createStackNavigator({
ProductDetailPage: { ProductDetailPage: {
screen: ProductDetailPage, screen: ProductDetailPage,
}, },
FillPolicyInfoPage: {
screen: FillPolicyInfoPage,
},
PaymentPage: {
screen: PaymentPage,
},
PolicyResultPage: {
screen: PolicyResultPage,
},
}, { }, {
initialRouteName, initialRouteName,
mode: 'card',
headerMode: 'none', headerMode: 'none',
defaultNavigationOptions: { defaultNavigationOptions: {
gesturesEnabled: false, gesturesEnabled: false,
...@@ -147,7 +165,30 @@ const navigator = createStackNavigator({ ...@@ -147,7 +165,30 @@ const navigator = createStackNavigator({
disableKeyboardHandling: false, disableKeyboardHandling: false,
cardShadowEnabled: true, cardShadowEnabled: true,
cardOverlayEnabled: true, cardOverlayEnabled: true,
onTransitionEnd: () => NativeAppEventEmitter.emit('react_navigation_onTransitionEnd_call',), transitionConfig: Platform.OS === 'android' ? () => ({
transitionSpec: {
duration: 400,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
},
screenInterpolator: (sceneProps) => {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const Width = layout.initWidth;
//沿X轴平移
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [Width, 0, -(Width - 10)],
});
//透明度
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateX }] };
},
}) : null, //android 设置自定义页面切换动画 左右切换
onTransitionEnd: () => NativeAppEventEmitter.emit('react_navigation_onTransitionEnd_call'),
}); });
//export default navigator; //export default navigator;
......
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
*/ */
import React, { Component } from 'react'; import React, { Component } from 'react';
import { AppState, Text, SafeAreaView } from 'react-native'; import { AppState, Text, SafeAreaView, BackHandler, ToastAndroid } from 'react-native';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/es/integration/react'; import { PersistGate } from 'redux-persist/es/integration/react';
import configureStore from './basicStore'; import configureStore from './basicStore';
import BasicNavigator from './BasicNavigator'; import BasicNavigator from './BasicNavigator';
import StyleSheet from './utils/StyleSheet'; import StyleSheet from './utils/StyleSheet';
import { pop } from './BasicNavigator/actions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
...@@ -23,12 +24,13 @@ class MainApp extends Component { ...@@ -23,12 +24,13 @@ class MainApp extends Component {
//if (AssetsModule.dismisToast) { //if (AssetsModule.dismisToast) {
// AssetsModule.dismisToast() // AssetsModule.dismisToast()
//} //}
BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
AppState.addEventListener('change', this.handleAppChange); AppState.addEventListener('change', this.handleAppChange);
} }
componentWillUnmount() { componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppChange); AppState.removeEventListener('change', this.handleAppChange);
BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
//if (AssetsModule.dismisToast) { //if (AssetsModule.dismisToast) {
// AssetsModule.dismisToast() // AssetsModule.dismisToast()
//} //}
...@@ -42,11 +44,29 @@ class MainApp extends Component { ...@@ -42,11 +44,29 @@ class MainApp extends Component {
} }
} }
onBackPress = () => {
console.log('onBackPress');
//这里的路由信息是你自己项目中的,通过这个原理,我们还是可以提示一些其他信息,比如表单没填写完整等等
const state = store.getState()
if (state.nav.routes.length <= 1) {
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
BackHandler.exitApp();
return false;
}
this.lastBackPressed = Date.now();
ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);
return true;
}
store.dispatch(pop());
return true;
}
render() { render() {
return ( return (
<Provider store={store}> <Provider store={store}>
<PersistGate loading={<Text>Loading...</Text>} persistor={persistor}> <PersistGate loading={<Text>MyLoading...</Text>} persistor={persistor}>
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<BasicNavigator param={this.props} /> <BasicNavigator param={this.props} />
</SafeAreaView> </SafeAreaView>
......
/**
* @flow
*/
import React, { Component } from 'react'
import {
View,
Text,
StatusBar,
TouchableOpacity,
Platform,
Dimensions,
PixelRatio,
StyleSheet,
} from 'react-native';
import BaseDialog from './view/BaseDialog';
import PickerView from './view/PickerView';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-end',
},
title: {
padding: (13),
backgroundColor: '#fff',
textAlign: 'center',
fontSize: (16),
color: '#2F2F2F',
},
contentWrapper: {
backgroundColor: '#F9FAFB',
borderTopColor: '#ddd',
borderBottomColor: '#ddd',
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth,
},
buttonWrapper: {
flexDirection: 'row',
height: (45),
width: Dimensions.get('window').width,
backgroundColor: '#fff',
},
button: {
width: (80),
padding: (13),
},
buttonText: {
color: '#0074E1',
fontWeight: '400',
textAlign: 'center',
fontSize: (18),
},
separator: {
backgroundColor: '#E5E5E5',
width: 1,
},
})
export default class CustomPicker extends BaseDialog {
static defaultProps = {
list: ['item0'],
//list1: ['item10', 'item11', 'item12', 'item13', 'item14', 'item15', 'item16', 'item17', 'item18', 'item19'],
}
constructor(props) {
super(props)
this.state = {
selectIndex: props.defaultIndex ? props.defaultIndex : 0,
}
}
//componentWillReceiveProps(newProps) {
// if (newProps.defaultIndex && newProps.defaultIndex !== this.state.selectIndex) {
// console.log('newProps.defaultIndex:', newProps.defaultIndex)
// this.setState({
// selectIndex: newProps.defaultIndex,
// })
// }
//}
setSelectIndex = (index) => {
this.setState({
selectIndex: index,
});
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' }
}
renderContent() {
return (<View
style={{
width: this.mScreenWidth,
flexDirection: 'column',
alignItems: 'stretch',
}}
>
<View style={[styles.buttonWrapper, { width: this.mScreenWidth }]} >
<TouchableOpacity
style={styles.button}
onPress={() => {
this.props.onCancel()
this.dismiss()
}}
>
<Text
allowFontScaling={false}
style={styles.buttonText}
>
取消
</Text>
</TouchableOpacity>
<View style={styles.container} >
<Text
allowFontScaling={false}
style={styles.title}
>
{this.props.title}
</Text>
</View>
<TouchableOpacity
style={styles.button}
onPress={() => {
this.props.onConfirm(this.state.selectIndex)
this.dismiss()
}}
>
<Text
allowFontScaling={false}
style={styles.buttonText}
>
确认
</Text>
</TouchableOpacity>
</View>
<PickerView
list={this.props.list}
onPickerSelected={(toValue) => {
const selectIndex = this.props.list.findIndex((item) => item === toValue)
this.setState({
selectIndex,
})
}}
selectedIndex={this.state.selectIndex}
fontSize={this.getSize(16)}
itemWidth={this.mScreenWidth}
itemHeight={this.getSize(40)}
/>
{/*<PickerView
list={this.props.list1}
onPickerSelected={(toValue) => {
// console.warn(toValue)
}}
selectedIndex={0}
fontSize={this.getSize(14)}
itemWidth={this.mScreenWidth / 2}
itemHeight={this.getSize(40)}
/>*/}
</View>)
}
}
// @flow
import React, { Component, UIManager } from 'react';
import {
Text,
View,
TouchableOpacity, PixelRatio,
} from 'react-native';
import PickerView from './view/PickerView';
import TimeUtils from './utils/TimeUtils';
class DatePicker extends Component {
static defaultProps = {
removeSubviews: false,
itemTextColor: 0x333333ff,
itemSelectedColor: 0x1097D5ff,
onCancel: null,
onConfirm: null,
unit: ['年', '月', '日'],
selectedValue: [`${new Date().getFullYear()}年`, `${new Date().getMonth() + 1}月`, `${new Date().getDate()}日`],
startYear: 1970,
startMonth: 1,
startDate: 1,
endYear: new Date().getFullYear(),
confirmText: '确定',
confirmTextSize: 18,
confirmTextColor: '#0074E1',
cancelText: '取消',
cancelTextSize: 18,
cancelTextColor: '#0074E1',
itemHeight: 40,
HH: false,
mm: false,
ss: false,
}
constructor(props) {
super(props);
this.state = this.getDateList(props);
}
componentWillReceiveProps(nextProps) {
if (nextProps.startYear !== this.props.startYear
|| nextProps.startMonth !== this.props.startMonth
|| nextProps.startDate !== this.props.startDate) {
//console.log('nextProps:', nextProps.startYear, this.props.startYear)
this.setState({ ...this.getDateList(nextProps) });
}
}
mScreenWidth = global.SCREEN_WIDTH;
mScreenHeight = global.SCREEN_HEIGHT;
//最小显示单位
mOnePixel = (PixelRatio.get() === 3 ? 2 : 1) / PixelRatio.get();
dismiss = () => {
if (this.props.dismiss) {
this.props.dismiss();
}
}
getDateList(nextProps) {
//console.log('getDateList:',this.props)
const { unit } = this.props;
const years = [];
const months = [];
const days = [];
const { startYear } = nextProps;
const { startMonth } = nextProps;
const { startDate } = nextProps;
const { endYear } = this.props;
for (let i = 0; i < (endYear + 1) - startYear; i++) {
years.push(i + startYear + unit[0]);
}
let selectedYear = years[0];
if (this.props.selectedValue) {
selectedYear = this.props.selectedValue[0];
}
selectedYear = selectedYear.substr(0, selectedYear.length - unit[0].length);
//console.log('selectedYear:', selectedYear, new Date().getFullYear(), new Date().getMonth() + 1)
const endMonth = selectedYear === `${new Date().getFullYear()}`
&& endYear === new Date().getFullYear()
&& selectedYear === `${endYear}`
? new Date().getMonth() + 2 : 13;
for (let i = selectedYear === `${startYear}` ? startMonth : 1; i < endMonth; i++) {
months.push(i + unit[1]);
}
let selectedMonth = months[0];
if (this.props.selectedValue) {
selectedMonth = this.props.selectedValue[1];
}
selectedMonth = selectedMonth.substr(0, selectedMonth.length - unit[1].length);
//console.log('selectedMonth:', selectedMonth, new Date().getMonth() + 1, new Date().getDate())
//console.log('selectedYear_selectedMonth:', selectedYear, startYear, selectedMonth, startMonth)
const dayCount = selectedMonth === `${new Date().getMonth() + 1}`
&& endYear === new Date().getFullYear()
&& selectedYear === `${endYear}`
? new Date().getDate() : TimeUtils.getDaysInOneMonth(selectedYear, selectedMonth);
for (let i = selectedYear === `${startYear}` && selectedMonth === `${startMonth}` ? startDate : 1; i <= dayCount; i++) {
days.push(i + unit[2]);
}
let selectedDay = days[0];
if (this.props.selectedValue) {
selectedDay = this.props.selectedValue[2];
}
selectedDay = selectedDay.substr(0, selectedDay.length - unit[2].length);
pickerData = [years, months, days];
selectedIndex = [
years.indexOf(selectedYear + unit[0]) === -1 ? years.length - 1 : years.indexOf(selectedYear + unit[0]),
months.indexOf(selectedMonth + unit[1]),
days.indexOf(selectedDay + unit[2]) === -1 ? days.length - 1 : days.indexOf(selectedDay + unit[2])];
//console.log('checkSelectedMonth:', selectedMonth, months, selectedIndex)
if (selectedIndex[1] === -1) {
selectedIndex[1] = 0;
}
if (selectedIndex[2] === -1) {
selectedIndex[2] = 0;
}
this.props.selectedValue[0] = years[selectedIndex[0]];
this.props.selectedValue[1] = months[selectedIndex[1]];
this.props.selectedValue[2] = days[selectedIndex[2]];
if (this.props.HH) {
const hours = [];
for (let i = 0; i < 24; i++) {
hours.push(`${i + 1}时`);
}
pickerData.push(hours);
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[3] ? parseInt(this.props.selectedValue[3]) : new Date().getHours()) - 1);
} else {
selectedIndex.push((new Date().getHours() - 1));
}
this.props.selectedValue[3] = `${selectedIndex[3] + 1}时`;
if (this.props.mm) {
const minutes = [];
for (let i = 0; i < 60; i++) {
minutes.push(`${i + 1}分`);
}
pickerData.push(minutes);
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[4] ? parseInt(this.props.selectedValue[4]) : new Date().getMinutes()) - 1);
} else {
selectedIndex.push((new Date().getMinutes() - 1));
}
this.props.selectedValue[4] = `${selectedIndex[4] + 1}分`;
if (this.props.ss) {
const seconds = [];
for (let i = 0; i < 60; i++) {
seconds.push(`${i + 1}秒`);
}
pickerData.push(seconds);
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[5] ? parseInt(this.props.selectedValue[5]) : 1) - 1);
} else {
selectedIndex.push(1);
}
this.props.selectedValue[5] = `${selectedIndex[5] + 1}秒`;
}
}
}
const data = {
pickerData,
selectedIndex,
};
return data;
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' };
}
/**
* return 當前分辨率下的數值
* @param {*} size 375标注图下的值
*/
getSize(size) {
return parseInt(this.mScreenWidth * size / 375);
}
renderPicker() {
return this.state.pickerData.map((item, pickerId) => {
if (item) {
return (
<PickerView
key={`picker${pickerId}`}
itemTextColor={this.props.itemTextColor}
itemSelectedColor={this.props.itemSelectedColor}
list={item}
onPickerSelected={(toValue) => {
//是否联动的实现位置
this.props.selectedValue[pickerId] = toValue;
//console.log('====')
this.setState({ ...this.getDateList(this.props) });
}}
selectedIndex={this.state.selectedIndex[pickerId]}
fontSize={this.getSize(14)}
itemWidth={this.mScreenWidth / this.state.pickerData.length}
itemHeight={this.props.itemHeight}
/>
);
}
});
}
render() {
// let data = this.getDateList(this.props);
// this.state.pickerData = data.pickerData;
// this.state.selectedIndex = data.selectedIndex;
return (
<View
style={{
height: this.props.itemHeight * 5 + this.getSize(15) + this.getSize(44), width: this.mScreenWidth,
}}
>
<View style={{
width: this.mScreenWidth, height: this.props.itemHeight * 5 + this.getSize(15), flexDirection: 'row', position: 'absolute', bottom: 0,
}}
>
{this.renderPicker()}
</View>
<View
style={{
width: this.mScreenWidth,
height: this.getSize(44),
backgroundColor: '#ffffff',
flexDirection: 'row',
justifyContent: 'space-between',
position: 'absolute',
top: 0,
}}
>
<TouchableOpacity
onPress={() => {
if (this.props.onCancel) {
this.props.onCancel(this.props.selectedValue);
}
this.dismiss();
}}
style={{
width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center',
}}
>
<Text allowFontScaling={false} style={{ fontSize: this.props.cancelTextSize, fontWeight: '400', color: this.props.cancelTextColor }}>{this.props.cancelText}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
if (this.props.onConfirm) {
this.props.onConfirm(this.props.selectedValue);
}
this.dismiss();
}}
style={{
width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center',
}}
>
<Text allowFontScaling={false} style={{ fontSize: this.props.confirmTextSize, fontWeight: '400', color: this.props.confirmTextColor }}>{this.props.confirmText}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
export default DatePicker;
/**
* @flow
*/
import React, { Component } from 'react'
import {
View,
Text,
StatusBar,
TouchableOpacity,
Platform,
Dimensions,
StyleSheet, PixelRatio,
} from 'react-native'
import PickerView from './view/PickerView'
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-end',
},
title: {
backgroundColor: '#fff',
textAlign: 'center',
fontSize: (16),
color: '#2F2F2F',
},
buttonWrapper: {
flexDirection: 'row',
height: (45),
width: Dimensions.get('window').width,
backgroundColor: '#fff',
},
button: {
width: (80),
padding: (13),
},
buttonText: {
color: '#0074E1',
fontWeight: '400',
textAlign: 'center',
fontSize: (18),
},
})
export default class CustomPicker extends Component {
static defaultProps = {
list: ['item0'],
//list1: ['item10', 'item11', 'item12', 'item13', 'item14', 'item15', 'item16', 'item17', 'item18', 'item19'],
}
constructor(props) {
super(props)
this.state = {
selectIndex: props.defaultIndex ? props.defaultIndex : 0,
}
}
mScreenWidth = global.SCREEN_WIDTH;
mScreenHeight = global.SCREEN_HEIGHT;
//最小显示单位
mOnePixel = (PixelRatio.get() === 3 ? 2 : 1) / PixelRatio.get();
dismiss = () => {
if(this.props.dismiss) {
this.props.dismiss()
}
}
setSelectIndex = (index) => {
this.setState({
selectIndex: index,
})
}
/**
* return 當前分辨率下的數值
* @param {*} size 375标注图下的值
*/
getSize(size) {
return parseInt(this.mScreenWidth * size / 375);
}
render() {
return (<View
style={{
width: this.mScreenWidth,
flexDirection: 'column',
alignItems: 'stretch',
}}
>
<View style={[styles.buttonWrapper, { width: this.mScreenWidth }]} >
<TouchableOpacity
style={styles.button}
onPress={() => {
this.props.onCancel()
this.dismiss()
}}
>
<Text
allowFontScaling={false}
style={styles.buttonText}
>
取消
</Text>
</TouchableOpacity>
<View style={{height: (45),flex: 1,justifyContent: 'center'}} >
<Text
allowFontScaling={false}
style={[styles.buttonText,
{ color: '#2F2F2F',}]}
>
{this.props.title}
</Text>
</View>
<TouchableOpacity
style={styles.button}
onPress={() => {
this.props.onConfirm(this.state.selectIndex)
this.dismiss()
}}
>
<Text
allowFontScaling={false}
style={styles.buttonText}
>
确认
</Text>
</TouchableOpacity>
</View>
<PickerView
list={this.props.list}
onPickerSelected={(toValue) => {
const selectIndex = this.props.list.findIndex((item) => item === toValue)
this.setState({
selectIndex,
})
}}
selectedIndex={this.state.selectIndex}
fontSize={this.getSize(16)}
itemWidth={this.mScreenWidth}
itemHeight={this.getSize(40)}
/>
</View>)
}
}
import AlertDialog from './view/AlertDialog';
import AreaPicker from './view/AreaPicker';
import CustomPicker from './CustomPicker';
import DatePicker from './view/DatePicker';
import InputDialog from './view/InputDialog';
import SimpleChooseDialog from './view/SimpleChooseDialog';
import SimpleItemsDialog from './view/SimpleItemsDialog';
import BaseComponent from './view/BaseComponent';
import BaseDialog from './view/BaseDialog';
import PickerView from './view/PickerView';
import DownloadDialog from './view/DownloadDialog';
import ToastComponent from './view/ToastComponent';
import SinglePicker from './SinglePicker';
export {
BaseComponent,
BaseDialog,
AreaPicker,
CustomPicker,
DatePicker,
InputDialog,
PickerView,
SimpleChooseDialog,
SimpleItemsDialog,
AlertDialog,
DownloadDialog,
ToastComponent,
SinglePicker,
};
function getDaysInOneMonth(year, month) {
var d = new Date(year, month, 0);
return d.getDate();
}
export default module.export = {
getDaysInOneMonth
}
\ No newline at end of file
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity
} from 'react-native';
import BaseDialog from './BaseDialog';
class AlertDialog extends BaseDialog {
static defaultProps = {
messageText: 'Alert Message',
messageTextColor: '#444444',
messageTextSize: 14,
negativeText: 'cancel',
negativeColor: '#666666',
negativeSize: 16,
positiveText: 'ok',
positiveColor: '#1097D5',
positiveSize: 16,
onPress: null
}
constructor(props) {
super(props);
}
_getContentPosition() {
return { justifyContent: 'center', alignItems: 'center' }
}
renderContent() {
return <View style={{
height: this.getSize(150), width: this.getSize(307),
backgroundColor: '#ffffff', borderRadius: this.getSize(6)
}}>
<View style={{
width: this.getSize(307), flex: 1, paddingLeft: this.getSize(15), paddingRight: this.getSize(15),
justifyContent: 'center', alignItems: 'center'
}}>
<Text style={{
fontSize: this.props.messageTextSize, fontWeight: '100', color: this.props.messageTextColor,
lineHeight: this.getSize(20), textAlign: 'center',
}}>{this.props.messageText}</Text>
</View>
<View style={{ width: this.getSize(307), height: 0.5, backgroundColor: '#e6e6e6' }} />
<View style={{
height: this.getSize(45),
width: this.getSize(307),
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
}}>
<TouchableOpacity
onPress={() => {
this.dismiss(() => {
if (this.props.onPress) {
this.props.onPress(true);
}
});
}}
style={{
flex: 1, height: this.getSize(45),
alignItems: 'center', justifyContent: 'center'
}}>
<Text style={{ color: this.props.positiveColor, fontSize: this.props.positiveSize }}>
{this.props.positiveText}
</Text>
</TouchableOpacity>
<View style={{
height: this.getSize(28), width: this.mOnePixel, backgroundColor: '#e6e6e6'
}} />
<TouchableOpacity
onPress={() => {
this.dismiss(() => {
if (this.props.onPress) {
this.props.onPress(false);
}
});
}}
style={{
flex: 1, height: this.getSize(45),
alignItems: 'center', justifyContent: 'center'
}}>
<Text style={{ color: this.props.negativeColor, fontSize: this.props.negativeSize }}>{this.props.negativeText}</Text>
</TouchableOpacity>
</View>
</View >
}
}
export default AlertDialog;
\ No newline at end of file
import React, { Component, UIManager } from 'react';
import {
Text,
View,
Animated,
TouchableOpacity
} from 'react-native';
import PickerView from './PickerView';
import BaseDialog from './BaseDialog';
class AreaPicker extends BaseDialog {
static defaultProps = {
removeSubviews: false,
selectedValue: ['香港', '香港', '中西區'],
areaJson: null,
confirmText: '确定',
confirmTextSize: 14,
confirmTextColor: '#333333',
cancelText: '取消',
cancelTextSize: 14,
cancelTextColor: '#333333',
itemTextColor: 0x333333ff,
itemSelectedColor: 0x1097D5ff,
itemHeight: 40,
onPickerCancel: null,
onPickerConfirm: null
}
constructor(props) {
super(props);
this.state = {
areaData: this.getAreaData(),
path: new Animated.Value(0),
...this.formatPickerData(props.selectedValue)
};
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' }
}
getAreaData() {
let area = this.props.areaJson;
let data = [];
let len = area.length;
for (let i = 0; i < len; i++) {
let city = [];
for (let j = 0, cityLen = area[i]['city'].length; j < cityLen; j++) {
let _city = {};
_city[area[i]['city'][j]['name']] = area[i]['city'][j]['area'];
city.push(_city);
}
let _data = {};
_data[area[i]['name']] = city;
data.push(_data);
}
return data;
}
formatPickerData() {
let province = [];
let city = [];
let county = [];
let firstCity = null;
let firstCountry = null;
let areaData = this.getAreaData();
areaData.map((pitem) => {
for (let pname in pitem) {
province.push(pname)
if (pname == this.props.selectedValue[0]) {
pitem[pname].map(citem => {
for (let cname in citem) {
if (firstCity == null) {
firstCity = cname;
}
city.push(cname);
if (cname == this.props.selectedValue[1]) {
county = citem[cname];
if (firstCountry == null) {
firstCountry = citem[cname][0];
}
}
}
});
}
}
});
if (county.indexOf(this.props.selectedValue[2]) == -1) {
this.props.selectedValue[2] = firstCountry;
}
if (county.length == 0 && firstCity != null) {
this.props.selectedValue[1] = firstCity;
return this.formatPickerData();
}
return {
pickerData: [province, city, county], visible: true
};
}
renderPicker() {
return this.state.pickerData.map((item, pickerId) => {
let selectedIndex = 0;
let length = item.length;
for (let i = 0; i < length; i++) {
if (item[i] == this.props.selectedValue[pickerId]) {
selectedIndex = i;
break;
}
}
if (item && length > 0) {
return <PickerView
itemTextColor={this.props.itemTextColor}
itemSelectedColor={this.props.itemSelectedColor}
key={'picker' + pickerId}
list={item}
onPickerSelected={(toValue) => {
this.props.selectedValue[pickerId] = toValue;
this.setState({ ...this.formatPickerData(this.props.selectedValue) });
}}
selectedIndex={selectedIndex}
fontSize={this.getSize(14)}
itemWidth={this.mScreenWidth / this.state.pickerData.length}
itemHeight={this.props.itemHeight} />
} else {
return null;
}
});
}
renderContent() {
return <View
style={{
height: this.props.itemHeight * 5 + this.getSize(15) + this.getSize(44), width: this.mScreenWidth,
backgroundColor: '#ffffff'
}}>
<View style={{ width: this.mScreenWidth, height: this.props.itemHeight * 5 + this.getSize(15), flexDirection: 'row', position: 'absolute', bottom: 0 }}>
{this.renderPicker()}
</View>
<View style={{
width: this.mScreenWidth, height: this.getSize(44),
backgroundColor: '#ffffff', flexDirection: 'row',
justifyContent: 'space-between', position: 'absolute', top: 0
}}>
<TouchableOpacity
onPress={() => {
this.dismiss(() => {
this.props.onPickerCancel && this.props.onPickerCancel();
});
}}
style={{ width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: this.props.cancelTextSize, fontWeight: '400', color: this.props.cancelTextColor }}>{this.props.cancelText}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.dismiss(() => {
this.props.onPickerConfirm && this.props.onPickerConfirm(this.props.selectedValue);
});
}}
style={{ width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: this.props.confirmTextSize, fontWeight: '400', color: this.props.confirmTextColor }}>{this.props.confirmText}</Text>
</TouchableOpacity>
</View>
</View>
}
}
export default AreaPicker;
\ No newline at end of file
import React, { Component } from 'react';
import {
Dimensions,
PixelRatio
} from 'react-native';
class BaseComponent extends Component {
mScreenWidth = Dimensions.get('window').width;
mScreenHeight = Dimensions.get('window').height;
//最小显示单位
mOnePixel = (PixelRatio.get() == 3 ? 2 : 1) / PixelRatio.get();
constructor(props) {
super(props);
}
/**
* return 當前分辨率下的數值
* @param {*} size 375标注图下的值
*/
getSize(size) {
return parseInt(this.mScreenWidth * size / 375);
}
}
export default BaseComponent;
import React, { Component } from 'react';
import {
Animated,
TouchableOpacity,
Platform
} from 'react-native';
import BaseComponent from './BaseComponent';
export default class BaseDialog extends BaseComponent {
static defaultProps = {
removeSubviews: true, //隐藏时,是否回收前景控件,false 更流畅,true:初始化更快,dismiss后就回收
coverClickable: true,
onCoverPress: null,
showAnimationType: 'spring'
}
_path = new Animated.Value(0);
constructor(props) {
super(props);
this.state = {
_isShow: false
}
}
isShowing() {
return this.state._isShow;
}
show(callback, state = {}) {
this.setState({ _isShow: true, ...state }, () => {
if (!this.props.showAnimationType || this.props.showAnimationType == 'spring') {
Animated.spring(this._path, { toValue: 1 }).start(() => {
callback && callback();
});
} else {
Animated.timing(this._path, { toValue: 1 }).start(() => {
callback && callback();
});
}
});
}
dismiss(callback) {
Animated.timing(this._path, { toValue: 0, duration: 200 }).start(() => {
this.setState({ _isShow: false }, () => {
callback && callback();
});
});
}
/**
* 重写前景动画效果
* @param {*} path
*/
_getContentInterpolate(path) {
return [
{
translateY: path.interpolate(
{
inputRange: [0, 0.5, 1],
outputRange: [this.getSize(200), this.getSize(200), 0]
}
)
}
]
}
/**
* 前景位置
*/
_getContentPosition() {
return { justifyContent: 'center', alignItems: 'center' }
}
/**
* 绘制前景控件
*/
renderContent() {
return null;
}
render() {
if (this.state._isShow || (this.props && this.props.removeSubviews === false)) {
return <Animated.View
style={{
position: 'absolute', left: 0, right: 0, top: 0, bottom: 0,
backgroundColor: 0x00000050, opacity: this._path.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 1, 1]
}), ...this._getContentPosition(),
transform: [
{
translateX: this._path.interpolate(
{
inputRange: [0, 0.01, 1],
outputRange: [-this.mScreenWidth, 0, 0]
}
)
}
]
}}>
<TouchableOpacity
onPress={() => {
if (!this.props || (this.props.coverClickable || this.props.coverClickable == null)) {
this.dismiss(this.props.onCoverPress);
}
}}
style={{ position: 'absolute', width: this.mScreenWidth, height: this.mScreenHeight }} />
<Animated.View style={{
opacity: this._path.interpolate({ inputRange: [0, 0.5, 1], outputRange: [0, 0, 1] }),
transform: this._getContentInterpolate(this._path),
}}>
{this.renderContent()}
</Animated.View>
</Animated.View>
} else {
return null;
}
}
}
import React, { Component, UIManager } from 'react'
import {
Text,
View,
TouchableOpacity,
} from 'react-native'
import PickerView from './PickerView'
import BaseDialog from './BaseDialog'
import TimeUtils from '../utils/TimeUtils'
class DatePicker extends BaseDialog {
static defaultProps = {
removeSubviews: false,
itemTextColor: 0x333333ff,
itemSelectedColor: 0x1097D5ff,
onPickerCancel: null,
onPickerConfirm: null,
unit: ['年', '月', '日'],
selectedValue: [`${new Date().getFullYear()}年`, `${new Date().getMonth() + 1}月`, `${new Date().getDate()}日`],
startYear: 1970,
startMonth: 1,
startDate: 1,
endYear: new Date().getFullYear(),
confirmText: '确定',
confirmTextSize: 18,
confirmTextColor: '#0074E1',
cancelText: '取消',
cancelTextSize: 18,
cancelTextColor: '#0074E1',
itemHeight: 40,
HH: false,
mm: false,
ss: false,
}
constructor(props) {
super(props)
this.state = this.getDateList(props)
}
componentWillReceiveProps(nextProps) {
if (nextProps.startYear !== this.props.startYear ||
nextProps.startMonth !== this.props.startMonth ||
nextProps.startDate !== this.props.startDate) {
//console.log('nextProps:', nextProps.startYear, this.props.startYear)
this.setState({ ...this.getDateList(nextProps) })
}
}
getDateList(nextProps) {
//console.log('getDateList:',this.props)
const unit = this.props.unit
const years = []
const months = []
const days = []
const startYear = nextProps.startYear
const startMonth = nextProps.startMonth
const startDate = nextProps.startDate
const endYear = this.props.endYear
for (let i = 0; i < (endYear + 1) - startYear; i++) {
years.push(i + startYear + unit[0])
}
let selectedYear = years[0]
if (this.props.selectedValue) {
selectedYear = this.props.selectedValue[0]
}
selectedYear = selectedYear.substr(0, selectedYear.length - unit[0].length)
//console.log('selectedYear:', selectedYear, new Date().getFullYear(), new Date().getMonth() + 1)
const endMonth = selectedYear === `${new Date().getFullYear()}`
&& endYear === new Date().getFullYear()
&& selectedYear === `${endYear}`
? new Date().getMonth() + 2 : 13
for (let i = selectedYear === `${startYear}` ? startMonth : 1; i < endMonth; i++) {
months.push(i + unit[1])
}
let selectedMonth = months[0]
if (this.props.selectedValue) {
selectedMonth = this.props.selectedValue[1]
}
selectedMonth = selectedMonth.substr(0, selectedMonth.length - unit[1].length)
//console.log('selectedMonth:', selectedMonth, new Date().getMonth() + 1, new Date().getDate())
//console.log('selectedYear_selectedMonth:', selectedYear, startYear, selectedMonth, startMonth)
const dayCount = selectedMonth === `${new Date().getMonth() + 1}`
&& endYear === new Date().getFullYear()
&& selectedYear === `${endYear}` ?
new Date().getDate() : TimeUtils.getDaysInOneMonth(selectedYear, selectedMonth)
for (let i = selectedYear === `${startYear}` && selectedMonth === `${startMonth}` ? startDate : 1; i <= dayCount; i++) {
days.push(i + unit[2])
}
let selectedDay = days[0]
if (this.props.selectedValue) {
selectedDay = this.props.selectedValue[2]
}
selectedDay = selectedDay.substr(0, selectedDay.length - unit[2].length)
pickerData = [years, months, days]
selectedIndex = [
years.indexOf(selectedYear + unit[0]) === -1 ? years.length - 1 : years.indexOf(selectedYear + unit[0]),
months.indexOf(selectedMonth + unit[1]),
days.indexOf(selectedDay + unit[2]) === -1 ? days.length - 1 : days.indexOf(selectedDay + unit[2])]
//console.log('checkSelectedMonth:', selectedMonth, months, selectedIndex)
if (selectedIndex[1] === -1) {
selectedIndex[1] = 0
}
if (selectedIndex[2] === -1) {
selectedIndex[2] = 0
}
this.props.selectedValue[0] = years[selectedIndex[0]]
this.props.selectedValue[1] = months[selectedIndex[1]]
this.props.selectedValue[2] = days[selectedIndex[2]]
if (this.props.HH) {
const hours = []
for (let i = 0; i < 24; i++) {
hours.push(`${i + 1}时`)
}
pickerData.push(hours)
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[3] ? parseInt(this.props.selectedValue[3]) : new Date().getHours()) - 1)
} else {
selectedIndex.push((new Date().getHours() - 1))
}
this.props.selectedValue[3] = `${selectedIndex[3] + 1}时`
if (this.props.mm) {
const minutes = []
for (let i = 0; i < 60; i++) {
minutes.push(`${i + 1}分`)
}
pickerData.push(minutes)
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[4] ? parseInt(this.props.selectedValue[4]) : new Date().getMinutes()) - 1)
} else {
selectedIndex.push((new Date().getMinutes() - 1))
}
this.props.selectedValue[4] = `${selectedIndex[4] + 1}分`
if (this.props.ss) {
const seconds = []
for (let i = 0; i < 60; i++) {
seconds.push(`${i + 1}秒`)
}
pickerData.push(seconds)
if (this.props.selectedValue) {
selectedIndex.push((this.props.selectedValue[5] ? parseInt(this.props.selectedValue[5]) : 1) - 1)
} else {
selectedIndex.push(1)
}
this.props.selectedValue[5] = `${selectedIndex[5] + 1}秒`
}
}
}
const data = {
pickerData,
selectedIndex,
}
return data
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' }
}
renderPicker() {
return this.state.pickerData.map((item, pickerId) => {
if (item) {
return (<PickerView
key={`picker${pickerId}`}
itemTextColor={this.props.itemTextColor}
itemSelectedColor={this.props.itemSelectedColor}
list={item}
onPickerSelected={(toValue) => {
//是否联动的实现位置
this.props.selectedValue[pickerId] = toValue
//console.log('====')
this.setState({ ...this.getDateList(this.props) })
}}
selectedIndex={this.state.selectedIndex[pickerId]}
fontSize={this.getSize(14)}
itemWidth={this.mScreenWidth / this.state.pickerData.length}
itemHeight={this.props.itemHeight}
/>)
}
})
}
renderContent() {
// let data = this.getDateList(this.props);
// this.state.pickerData = data.pickerData;
// this.state.selectedIndex = data.selectedIndex;
return (<View
style={{
height: this.props.itemHeight * 5 + this.getSize(15) + this.getSize(44), width: this.mScreenWidth,
}}
>
<View style={{ width: this.mScreenWidth, height: this.props.itemHeight * 5 + this.getSize(15), flexDirection: 'row', position: 'absolute', bottom: 0 }}>
{this.renderPicker()}
</View>
<View
style={{
width: this.mScreenWidth,
height: this.getSize(44),
backgroundColor: '#ffffff',
flexDirection: 'row',
justifyContent: 'space-between',
position: 'absolute',
top: 0,
}}
>
<TouchableOpacity
onPress={() => {
this.dismiss(() => this.props.onPickerCancel && this.props.onPickerCancel(this.props.selectedValue))
}}
style={{ width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center' }}
>
<Text allowFontScaling={false} style={{ fontSize: this.props.cancelTextSize, fontWeight: '400', color: this.props.cancelTextColor }}>{this.props.cancelText}</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.dismiss(() => this.props.onPickerConfirm && this.props.onPickerConfirm(this.props.selectedValue))
}}
style={{ width: this.getSize(60), height: this.getSize(44), justifyContent: 'center', alignItems: 'center' }}
>
<Text allowFontScaling={false} style={{ fontSize: this.props.confirmTextSize, fontWeight: '400', color: this.props.confirmTextColor }}>{this.props.confirmText}</Text>
</TouchableOpacity>
</View>
</View>)
}
}
export default DatePicker
import React, { Component } from 'react';
import {
View,
Animated,
Text,
TouchableOpacity,
} from 'react-native';
import BaseDialog from './BaseDialog';
class DownloadDialog extends BaseDialog {
static defaultProps = {
title: '视频下载',
titleColor: '#333333',
titleSize: 14,
active: false,
actionText: '打开',
onAction: null,
totalTextColor: '#666666',
totalTextSize: 12
}
process = new Animated.Value(0);
constructor(props) {
super(props);
this.state = {
}
}
/**
*
* @param {*} process [0, 1]
* @param {*} total
*/
setProcess(process, total) {
if (this.state.total != total) {
this.setState({ total });
}
Animated.spring(this.process, { toValue: process }).start();
}
renderContent() {
return <Animated.View
style={{
width: this.getSize(307),
backgroundColor: '#ffffff', borderRadius: this.getSize(5),
marginBottom: this.getSize(30),
}}>
<Text style={{
marginTop: this.getSize(15), marginLeft: this.getSize(15),
color: this.props.titleColor, fontSize: this.props.titleSize
}}>{this.props.title}</Text>
<View style={{
justifyContent: 'center',
alignItems: 'center', marginTop: this.getSize(25)
}}>
<View style={{ width: this.getSize(280), height: 4, borderRadius: 2, backgroundColor: '#d3d3d3' }} />
<Animated.View style={{
width: this.process.interpolate({
inputRange: [0, 1],
outputRange: [0, this.getSize(280)]
}), height: 4, borderRadius: 2,
backgroundColor: '#1097D5',
position: 'absolute', left: this.getSize((307 - 280) / 2)
}} />
</View>
<Text style={{
paddingRight: this.getSize(12), textAlign: 'right', width: this.getSize(307),
marginTop: this.getSize(5), marginBottom: this.getSize(15),
color: this.props.totalTextColor, height: this.props.totalTextSize,
fontSize: this.props.totalTextSize, lineHeight: this.props.totalTextSize
}}>
{this.state.total}
</Text>
<View style={{ width: this.getSize(307), height: this.mOnePixel, backgroundColor: '#e6e6e6' }} />
<View style={{
width: this.getSize(307), height: this.getSize(40),
justifyContent: 'center', alignItems: 'center',
flexDirection: 'row',
}}>
<TouchableOpacity
onPress={() => {
if (this.props.active) {
this.dismiss(() => {
this.process.setValue(0);
this.props.active = false;
this.props.onAction && this.props.onAction()
});
}
}}
style={{
width: this.getSize(307), height: this.getSize(40),
alignItems: 'center', justifyContent: 'center',
}}>
<Text style={{ color: '#1097D5', fontSize: this.getSize(16), opacity: this.props.active ? 1 : 0.5 }}>{this.props.actionText}</Text>
</TouchableOpacity>
</View>
</Animated.View >
}
}
module.exports = DownloadDialog;
\ No newline at end of file
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity,
TextInput,
Keyboard
} from 'react-native';
import BaseDialog from './BaseDialog';
import KeyboardSpacer from './KeyboardSpacer';
class InputDialog extends BaseDialog {
static defaultProps = {
removeSubviews: false,
title: '我要举报',
titleSize: 16,
titleColor: '#333333',
cancelText: '返回',
cancelSize: 14,
cancelColor: '#333333',
btnText: '提交',
btnTextSize: 12,
btnTextColor: '#ffffff',
btnBgColor: '#1097D5',
placeholder: '请尽量说明问题,我们将尽快处理...',
onSubmit: null
}
constructor(props) {
super(props);
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' }
}
show(text) {
super.show(null, { text: text });
this.textInput.focus();
}
dismiss(callback) {
this.textInput.blur();
super.dismiss(callback);
}
renderContent() {
return <View style={{ width: this.mScreenWidth, backgroundColor: '#f8f8f8' }}>
<View style={{
width: this.mScreenWidth, height: this.getSize(50),
flexDirection: 'row', paddingLeft: this.getSize(10),
justifyContent: 'center', alignItems: 'center'
}}>
<TouchableOpacity
onPress={() => this.dismiss()}
style={{
position: 'absolute', left: this.getSize(10),
height: this.getSize(40), flexDirection: 'row',
justifyContent: 'center', alignItems: 'center', marginLeft: this.getSize(5)
}}>
<Text style={{ fontSize: this.props.cancelSize, color: this.props.cancelColor, marginLeft: this.getSize(5) }}>{this.props.cancelText}</Text>
</TouchableOpacity>
<Text style={{ position: 'absolute', fontSize: this.props.titleSize, color: this.props.titleColor, fontWeight: '600' }}>{this.props.title}</Text>
</View>
<TextInput ref={ref => this.textInput = ref}
style={{
width: this.getSize(345), marginLeft: this.getSize(15),
height: this.getSize(100), color: '#333333', fontSize: this.getSize(14),
borderWidth: 1, borderColor: '#E8EEF0', backgroundColor: '#ffffff', borderRadius: this.getSize(4),
paddingLeft: this.getSize(15), paddingRight: this.getSize(15), paddingTop: this.getSize(10)
}}
numberOfLines={4}
multiline={true}
value={this.state.text}
underlineColorAndroid={'transparent'}
placeholder={this.props.placeholder}
placeholderTextColor='#999999'
onChangeText={(text) => {
this.inputText = text;
}} />
<View style={{
width: this.mScreenWidth, height: this.getSize(48), paddingRight: this.getSize(15),
justifyContent: 'center', alignItems: 'flex-end'
}}>
<TouchableOpacity
onPress={() => this.dismiss(() => {
this.props.onSubmit && this.props.onSubmit(this.inputText);
})}
style={{
width: this.getSize(60), height: this.getSize(28),
justifyContent: 'center', alignItems: 'center',
backgroundColor: this.props.btnBgColor, borderRadius: this.getSize(4)
}}>
<Text style={{ fontSize: this.props.btnTextSize, color: this.props.btnTextColor }}>{this.props.btnText}</Text>
</TouchableOpacity>
</View>
<KeyboardSpacer />
</View >
}
}
export default InputDialog;
\ No newline at end of file
/**
* Created by andrewhurst on 10/5/15.
*/
import React, { Component } from 'react';
import {
Keyboard,
LayoutAnimation,
View,
Dimensions,
Platform,
StyleSheet
} from 'react-native';
const styles = StyleSheet.create({
container: {
left: 0,
right: 0,
bottom: 0,
},
});
// From: https://medium.com/man-moon/writing-modern-react-native-ui-e317ff956f02
const defaultAnimation = {
duration: 500,
create: {
duration: 300,
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.opacity
},
update: {
type: LayoutAnimation.Types.spring,
springDamping: 200
}
};
export default class KeyboardSpacer extends Component {
static defaultProps = {
topSpacing: 0,
onToggle: () => null,
};
constructor(props, context) {
super(props, context);
this.state = {
keyboardSpace: 0,
isKeyboardOpened: false
};
this._listeners = null;
this.updateKeyboardSpace = this.updateKeyboardSpace.bind(this);
this.resetKeyboardSpace = this.resetKeyboardSpace.bind(this);
}
componentDidMount() {
const updateListener = Platform.OS === 'android' ? 'keyboardDidShow' : 'keyboardWillShow';
const resetListener = Platform.OS === 'android' ? 'keyboardDidHide' : 'keyboardWillHide';
this._listeners = [
Keyboard.addListener(updateListener, this.updateKeyboardSpace),
Keyboard.addListener(resetListener, this.resetKeyboardSpace)
];
}
componentWillUnmount() {
this._listeners.forEach(listener => listener.remove());
}
updateKeyboardSpace(event) {
if (!event.endCoordinates) {
return;
}
let animationConfig = defaultAnimation;
if (Platform.OS === 'ios') {
animationConfig = LayoutAnimation.create(
event.duration,
LayoutAnimation.Types[event.easing],
LayoutAnimation.Properties.opacity,
);
}
LayoutAnimation.configureNext(animationConfig);
// get updated on rotation
const screenHeight = Dimensions.get('window').height;
// when external physical keyboard is connected
// event.endCoordinates.height still equals virtual keyboard height
// however only the keyboard toolbar is showing if there should be one
const keyboardSpace = (screenHeight - event.endCoordinates.screenY) + this.props.topSpacing;
this.setState({
keyboardSpace,
isKeyboardOpened: true
}, this.props.onToggle(true, keyboardSpace));
}
resetKeyboardSpace(event) {
let animationConfig = defaultAnimation;
if (Platform.OS === 'ios') {
animationConfig = LayoutAnimation.create(
event.duration,
LayoutAnimation.Types[event.easing],
LayoutAnimation.Properties.opacity,
);
}
LayoutAnimation.configureNext(animationConfig);
this.setState({
keyboardSpace: 0,
isKeyboardOpened: false
}, this.props.onToggle(false, 0));
}
render() {
return (
<View style={[styles.container, { height: this.state.keyboardSpace }, this.props.style]} />);
}
}
import React, { Component } from 'react';
import {
View,
Animated,
PanResponder
} from 'react-native';
import BaseComponent from './BaseComponent';
import Svg, {
LinearGradient,
Rect,
Stop
} from 'react-native-svg';
class PickerView extends BaseComponent {
static defaultProps = {
itemTextColor: 0x333333ff,
itemSelectedColor: 0x1097D5ff,
itemHeight: 40,
onPickerSelected: null,
selectedIndex: 0
}
_previousTop = 0;
lastTop = 0;
constructor(props) {
super(props);
list = ['', ''].concat(props.list).concat(['', '']);
this.colorPath = [];
let length = list.length;
for (let i = 0; i < length; i++) {
this.colorPath.push(new Animated.Value(i == (this.props.selectedIndex + 2) ? 1 : 0));
}
this.path = new Animated.Value(-props.itemHeight * this.props.selectedIndex);
this.state = {
list: list,
selectedIndex: props.selectedIndex,
};
this.maxTop = 0;
this.maxBottom = -props.itemHeight * (list.length - 5);
this.onStartShouldSetPanResponder = this.onStartShouldSetPanResponder.bind(this);
this.onMoveShouldSetPanResponder = this.onMoveShouldSetPanResponder.bind(this);
this.onPanResponderGrant = this.onPanResponderGrant.bind(this);
this.onPanResponderMove = this.onPanResponderMove.bind(this);
this.onPanResponderEnd = this.onPanResponderEnd.bind(this);
//這裏固定在屏幕底部,所以直接寫死touch區域即可。
this.parentTopY = this.mScreenHeight - props.itemHeight * 5 - this.getSize(15);
this.parentBottomY = this.mScreenHeight - this.getSize(15);
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps) {
list = ['', ''].concat(nextProps.list).concat(['', '']);
listChange = JSON.stringify(list) != JSON.stringify(this.state.list);
indexChange = nextProps.selectedIndex != this.state.selectedIndex;
if (listChange || indexChange) {
//console.log('shouldComponentUpdate');
this.path.setValue(-this.props.itemHeight * nextProps.selectedIndex);
if (listChange) {
this.colorPath = [];
let length = list.length;
for (let i = 0; i < length; i++) {
this.colorPath.push(new Animated.Value(i == (nextProps.selectedIndex + 2) ? 1 : 0));
}
}
nextState.list = list;
nextState.selectedIndex = nextProps.selectedIndex;
this.maxTop = 0;
this.maxBottom = -this.props.itemHeight * (list.length - 5);
return true;
}
}
return false;
}
//用户开始触摸屏幕的时候,是否愿意成为响应者;
onStartShouldSetPanResponder(evt, gestureState) {
if (evt.nativeEvent.pageY < this.parentTopY || evt.nativeEvent.pageY > this.parentBottomY) {
return false;
} else {
this.path && this.path.removeAllListeners();
this.path.stopAnimation();
this.keyDown = Date.now();
return true;
}
}
//在每一个触摸点开始移动的时候,再询问一次是否响应触摸交互;
onMoveShouldSetPanResponder(evt, gestureState) {
if (evt.nativeEvent.pageY < this.parentTopY || evt.nativeEvent.pageY > this.parentBottomY) {
return false;
} else {
this.path && this.path.removeAllListeners();
this.path.stopAnimation();
return true;
}
}
// 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
onPanResponderGrant(evt, gestureState) {
this.lastTop = this.path._value;
}
// 最近一次的移动距离为gestureState.move{X,Y}
onPanResponderMove(evt, gestureState) {
if (global.timer != null) {
global.timer.map(item => {
clearTimeout(item);
});
}
this._previousTop = this.lastTop + gestureState.dy;
if (this._previousTop > 0) {
this._previousTop = Math.min(this._previousTop, this.maxTop + this.props.itemHeight);
} else {
this._previousTop = Math.max(this._previousTop, this.maxBottom - this.props.itemHeight);
}
this.path.setValue(this._previousTop);
if (this.previousTop) {
this.velocity = gestureState.dy - this.previousTop;
} else {
this.velocity = 0;
}
this.previousTop = gestureState.dy;
}
lastEvent = null;
lastTwoEvent = null;
onPanResponderEnd(evt, gestureState) {
let actionTime = Date.now() - this.keyDown;
if (actionTime < 300 && Math.abs(gestureState.vy) < 0.1) {
let clickPosition = -(parseInt((gestureState.y0 - this.parentTopY) / this.props.itemHeight) - 2);
let toValue = this.path._value;
let number = Math.round(toValue / this.props.itemHeight);
toValue = this.props.itemHeight * number;
toValue = toValue + (this.props.itemHeight * clickPosition);
if (toValue > 0) {
toValue = Math.min(toValue, this.maxTop);
} else {
toValue = Math.max(toValue, this.maxBottom);
}
if (isNaN(toValue)) {
} else {
//onSeleted
Animated.timing(this.path, { toValue: toValue, duration: 200 }).start(() => {
this.onSeleted(Math.abs(toValue / this.props.itemHeight - 2));
});
}
} else {
this.lastTop = this._previousTop;
let toValue = this._previousTop + gestureState.vy * this.props.itemHeight * 2;
let number = Math.round(toValue / this.props.itemHeight);
toValue = this.props.itemHeight * number;
if (toValue > 0) {
toValue = Math.min(toValue, this.maxTop);
} else {
toValue = Math.max(toValue, this.maxBottom);
}
Animated.decay(this.path, {
velocity: gestureState.vy, //通过手势设置相关速度
deceleration: 0.995,
}).start(() => {
if (this.path._value % this.props.itemHeight == 0) {
this.path.removeListener(this.pathListener);
this.pathListener = null;
} else {
//慣性動畫
if (this.pathListener) {
this.path.removeListener(this.pathListener);
this.pathListener = null;
let toValue = Math.round(this.path._value / this.props.itemHeight) * this.props.itemHeight;
Animated.timing(this.path, {
toValue: toValue,
duration: 50
}).start(() => {
//onSeleted
this.onSeleted(Math.abs(toValue / this.props.itemHeight - 2));
});
}
}
});
//當滾動超出上限或者下限時,接管慣性動畫
this.pathListener = this.path.addListener((listener) => {
if (listener.value < this.maxBottom && this.pathListener) {
this.path.removeListener(this.pathListener);
this.pathListener = null;
Animated.timing(this.path, { toValue: this.maxBottom }).start(() => {
//onSeleted
this.onSeleted(Math.abs(this.maxBottom / this.props.itemHeight - 2));
});
} else if (listener.value > this.maxTop - this.props.itemHeight && this.pathListener) {
this.path.removeListener(this.pathListener);
this.pathListener = null;
Animated.timing(this.path, { toValue: this.maxTop }).start(() => {
//onSeleted
this.onSeleted(Math.abs(this.maxTop / this.props.itemHeight - 2));
});
}
});
}
}
onSeleted(selectedIndex) {
if (global.timer == null) {
global.timer = [];
}
global.timer.push(setTimeout(() => {
this.colorPath.map((item, index) => {
if (item._value == 0 && selectedIndex == index) {
item.setValue(1);
} else if (item._value == 1 && selectedIndex != index) {
item.setValue(0);
}
})
this.props.onPickerSelected && this.props.onPickerSelected(this.state.list[selectedIndex]);
}, 20));
}
componentWillMount(evt, gestureState) {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: this.onStartShouldSetPanResponder,
onMoveShouldSetPanResponder: this.onMoveShouldSetPanResponder,
onPanResponderGrant: this.onPanResponderGrant,
onPanResponderMove: this.onPanResponderMove,
onPanResponderRelease: this.onPanResponderEnd,
onPanResponderTerminate: this.onPanResponderEnd,
});
}
renderList() {
return this.state.list.map((item, index) => {
return this.renderItem(item, index);
});
}
renderItem(item, index) {
return <View
key={index}
style={{
width: this.props.itemWidth, height: this.props.itemHeight,
justifyContent: 'center', alignItems: 'center'
}}>
<Animated.Text style={{
color: this.colorPath[index].interpolate({
inputRange: [0, 1],
outputRange: [this.props.itemTextColor, this.props.itemSelectedColor]
}), fontSize: this.props.fontSize ? this.props.fontSize : this.getSize(20),
backgroundColor: 'transparent', fontWeight: 'normal'
}}>{item}</Animated.Text>
</View >
}
render() {
return <View style={{
width: this.props.itemWidth, height: this.props.itemHeight * 5 + this.getSize(15),
backgroundColor: '#ffffff'
}}>
<View
ref={ref => this.ref = ref}
{...this._panResponder.panHandlers}
style={{
overflow: 'hidden',
width: this.props.itemWidth, height: this.props.itemHeight * 5 + this.getSize(15), backgroundColor: '#ffffff'
}}>
<Animated.View
style={{
transform: [
{
translateY: this.path
}
]
}}
>
{this.renderList()}
</Animated.View>
<View style={{ position: 'absolute', width: this.props.itemWidth, height: this.mOnePixel, top: this.props.itemHeight * 4 / 2, backgroundColor: '#E8EEF0' }} />
<View style={{ position: 'absolute', width: this.props.itemWidth, height: this.mOnePixel, top: this.props.itemHeight * 6 / 2, backgroundColor: '#E8EEF0' }} />
<Svg
onStartShouldSetResponder={() => {
return false;
}}
onResponderStart={() => {
return false;
}}
style={{ position: 'absolute', top: 0 }}
height={this.props.itemHeight * 1}
width={this.props.itemWidth}
>
<LinearGradient id="grad" x1="0" y1={this.props.itemHeight * 1} x2={0} y2="0">
<Stop offset="0" stopColor="#ffffff" stopOpacity="0.2" />
<Stop offset="1" stopColor="#ffffff" stopOpacity="1" />
</LinearGradient>
<Rect
x="0"
y="0"
width={this.props.itemWidth}
height={this.props.itemHeight * 1}
fill="url(#grad)"
clipPath="url(#clip)"
/>
</Svg>
<Svg
onStartShouldSetResponder={() => {
return false;
}}
onResponderStart={() => {
return false;
}}
style={{ position: 'absolute', bottom: this.getSize(15) }}
height={this.props.itemHeight * 1}
width={this.props.itemWidth}
>
<LinearGradient id="grad" x1="0" y1={this.props.itemHeight * 1} x2={0} y2="0">
<Stop offset="0" stopColor="#ffffff" stopOpacity="1" />
<Stop offset="1" stopColor="#ffffff" stopOpacity="0.4" />
</LinearGradient>
<Rect
x="0"
y="0"
width={this.props.itemWidth}
height={this.props.itemHeight * 1}
fill="url(#grad)"
clipPath="url(#clip)"
/>
</Svg>
<View style={{ width: this.mScreenWidth, height: this.getSize(15), bottom: 0, backgroundColor: '#ffffff', position: 'absolute' }} />
</View>
</View>
}
}
export default PickerView;
\ No newline at end of file
import React, { Component } from 'react';
import {
View,
Text,
Animated,
TouchableOpacity,
FlatList
} from 'react-native';
import BaseDialog from './BaseDialog';
/**
* 用于不跳转页面的选择
*/
class SimpleChooseDialog extends BaseDialog {
static defaultProps = {
items: ['a', 'b', 'c'],
itemKey: 'key',
itemStyle: {
fontSize: 14,
fontWeight: '400',
color: '#333333'
},
selectColor: '#1097D5',
normalColor: '#666666',
pointSize: 18,
pointBorderRadius: 9,
confirmText: '确定',
confirmBtnColor: '#1097D5',
confirmTextColor: '#ffffff',
onPress: null,
}
constructor(props) {
super(props);
}
renderItems() {
return this.props.items.map((item, index) => {
return <TouchableOpacity
onPress={() => {
this.setState({ seleted: index })
}}
key={this.key ? item[this.key] : item}
style={{
paddingLeft: this.getSize(20), paddingRight: this.getSize(20),
width: this.getSize(307), height: this.getSize(49),
justifyContent: 'flex-start', alignItems: 'center', flexDirection: 'row'
}}>
<View style={{
width: this.props.pointSize, height: this.props.pointSize,
justifyContent: 'center', alignItems: 'center', marginRight: this.getSize(20),
borderRadius: this.props.pointBorderRadius, borderWidth: 1,
borderColor: this.state.seleted == index ? this.props.selectColor : this.props.normalColor,
}}>
{this.state.seleted == index ? <View style={{
width: this.props.pointSize * 0.6, height: this.props.pointSize * 0.6,
borderRadius: this.props.pointBorderRadius * 0.6,
backgroundColor: this.props.selectColor
}} /> : null}
</View>
<Text style={[this.props.itemStyle, this.state.seleted == index ? { color: this.props.selectColor } : {}]}>{typeof item == 'string' ? item : item[this.props.itemKey]}</Text>
</TouchableOpacity>
})
}
_getContentPosition() {
return { justifyContent: 'center', alignItems: 'center' }
}
renderContent() {
return <View style={{ width: this.getSize(307), backgroundColor: '#ffffff', borderRadius: this.getSize(5), alignItems: 'center', paddingTop: this.getSize(10) }}>
{this.renderItems()}
<TouchableOpacity
onPress={() => {
this.dismiss(() => {
if (this.props.onPress) {
this.props.onPress(this.state.seleted != null ? this.state.seleted : -1);
}
});
}}
style={{
width: this.getSize(200), height: this.getSize(44),
backgroundColor: this.props.confirmBtnColor, borderRadius: this.getSize(5),
justifyContent: 'center', alignItems: 'center', marginTop: this.getSize(13), marginBottom: this.getSize(20)
}}>
<Text style={{ color: this.props.confirmTextColor, fontSize: this.getSize(16), fontWeight: '400' }}>{this.props.confirmText}</Text>
</TouchableOpacity>
</View >
}
}
export default SimpleChooseDialog;
\ No newline at end of file
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet,
TouchableOpacity,
FlatList,
PixelRatio,
} from 'react-native'
import BaseDialog from './BaseDialog'
const px2pt = px => PixelRatio.roundToNearestPixel(px)
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 0,
left: 0,
flex: 1,
justifyContent: 'flex-end',
},
title: {
padding: px2pt(13),
backgroundColor: '#fff',
textAlign: 'center',
fontSize: px2pt(18),
color: '#2F2F2F',
},
contentWrapper: {
backgroundColor: '#F9FAFB',
borderTopColor: '#ddd',
borderBottomColor: '#ddd',
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth,
},
buttonWrapper: {
flexDirection: 'row',
height: px2pt(45),
backgroundColor: '#fff',
},
button: {
width: px2pt(80),
padding: px2pt(13),
},
buttonText: {
color: '#0074E1',
fontWeight: '400',
textAlign: 'center',
fontSize: px2pt(18),
},
separator: {
backgroundColor: '#E5E5E5',
width: 1,
},
})
class SimpleItemsDialog extends BaseDialog {
static defaultProps = {
items: ['a', 'b', 'c'],
itemKey: 'key',
itemStyle: {
fontSize: 14,
fontWeight: '400',
color: '#333333',
},
cancel: false,
cancelText: '取消',
cancelTextStyle: {
fontSize: 14,
fontWeight: '400',
color: '#999999',
},
onPress: null,
}
constructor(props) {
super(props)
}
_getContentPosition() {
return { justifyContent: 'flex-end', alignItems: 'center' }
}
renderItems() {
return this.props.items.map((item, index) => <TouchableOpacity
onPress={() => {
this.dismiss(() => {
if (this.props.onPress) {
this.props.onPress(index)
}
})
}}
key={index}
style={{ width: this.mScreenWidth, height: this.getSize(49), justifyContent: 'center', alignItems: 'center' }}
>
<Text style={this.props.itemStyle} >{typeof item === 'string' ? item : item[this.props.itemKey]}</Text>
<View
style={{
position: 'absolute',
bottom: 0,
width: this.mScreenWidth,
height: this.mOnePixel,
backgroundColor: '#E8EEF0',
}} />
</TouchableOpacity>)
}
renderCancel() {
return (<TouchableOpacity
onPress={() => this.dismiss()}
style={{ width: this.mScreenWidth, height: this.getSize(49), justifyContent: 'center', alignItems: 'center' }}
>
<Text style={this.props.cancelTextStyle} >{this.props.cancelText}</Text>
<View style={{
position: 'absolute',
bottom: 0,
width: this.mScreenWidth,
height: this.mOnePixel,
backgroundColor: '#E8EEF0'
}} />
</TouchableOpacity>)
}
renderContent() {
return (
<View style={{ width: this.mScreenWidth, backgroundColor: '#ffffff' }}>
<View style={styles.buttonWrapper} >
<TouchableOpacity style={styles.button} onPress={this.props.cancel} >
<Text style={styles.buttonText} >取消</Text>
</TouchableOpacity>
<View style={styles.container} >
<Text style={styles.title} >{this.props.title}</Text>
</View>
<TouchableOpacity style={styles.button} onPress={this.props.onConfirm} >
<Text style={styles.buttonText} >确认</Text>
</TouchableOpacity>
</View>
<View style={styles.contentWrapper} >
{this.renderItems()}
{this.props.cancel ? this.renderCancel() : null}
</View>
</View>
)
}
}
export default SimpleItemsDialog
import React, { Component } from 'react';
import {
View,
Text,
Animated
} from 'react-native';
import BaseComponent from '../view/BaseComponent';
export default class ToastComponent extends BaseComponent {
static defaultProps = {
duration: 1500,
textColor: '#ffffff',
fontSize: 14,
lineHeight: 20,
paddingH: 10,
paddingV: 5,
borderRadius: 5,
backgroundColor: 0x00000099,
}
opacity = new Animated.Value(0);
leftPath = new Animated.Value(0);
constructor(props) {
super(props);
this.state = {
toastVisiable: false,
toastText: ''
}
}
show(message) {
this.timeoutId && clearTimeout(this.timeoutId);
this.opacity.setValue(0)
this.setState({ toastText: message, toastVisiable: true });
Animated.timing(this.opacity, { toValue: 1, duration: 200 }).start();
this.timeoutId = setTimeout(() => {
Animated.timing(this.opacity, { toValue: 0, duration: 200 }).start(() => {
this.setState({ toastVisiable: false });
});
}, this.props.duration);
}
componentWillUnmount() {
this.timeoutId && clearTimeout(this.timeoutId);
}
render() {
if (this.state.toastVisiable) {
return <Animated.View
onLayout={(e) => {
//如果依靠父容器来定位Toast居中,
//在配合react-navigation设置全局单例Toast,会导致StackNavigator布局异常
this.leftPath.setValue((this.mScreenWidth - e.nativeEvent.layout.width) / 2);
}}
style={{
opacity: this.opacity, alignItems: 'center',
position: 'absolute',
top: this.mScreenHeight - this.getSize(300),
left: this.leftPath
}}>
<View
style={{
borderRadius: this.props.borderRadius,
backgroundColor: this.props.backgroundColor,
paddingLeft: this.props.paddingH, paddingRight: this.props.paddingH,
paddingTop: this.props.paddingV, paddingBottom: this.props.paddingV
}}>
<Text style={{ color: this.props.textColor, fontSize: this.props.fontSize, lineHeight: this.props.lineHeight }}>{this.state.toastText}</Text>
</View>
</Animated.View>
} else {
return null;
}
}
}
/**
* @flow
* Created by shiyunjie on 2018/8/4.
*/
import React from 'react';
import {
View,
Text,
Image,
} from 'react-native';
type Props = {
index : Number,
data?: Array,
height?: Number,
}
const screenWidth = global.SCREEN_WIDTH;
const Progress = (props : Props) => {
let count = 3;
let iconArr = [
{ title: '测试1' },
{ title: '测试2' },
{ title: '测试3' },
];
if (props.data.length > 1) {
iconArr = [];
props.data.forEach((text) => {
iconArr.push({ title: text });
});
count = props.data.length;
}
const { index: currentIndex } = props;
const width = ((screenWidth / count) - (24)) / 2;
return (
<View
style={{
flexDirection: 'row',
height: props.height || (100),
alignItems: 'center',
backgroundColor: '#fff',
}}
>
{
iconArr.map((item, index) => {
let icon = currentIndex > index
? require('./images/icon_progresbar_complete.png')
: require('./images/icon_progresbar_normal.png');
if (currentIndex === index) {
icon = require('./images/icon_progresbar_active.png');
}
const titleStyle = currentIndex >= index ? { color: '#0E6DCF' }
: { color: '#999999' };
return (
<View
key={`item${index}`}
style={{
width: (screenWidth / count),
flexDirection: 'column',
alignItems: 'center',
}}
>
<Image
style={{
width: (24),
height: (24),
}}
source={icon}
/>
<Text style={[{ fontSize: (12), marginTop: (4) }, titleStyle]}>
{item.title}
</Text>
{ index < iconArr.length - 1
&& (
<Image
style={{
width,
height: (24),
position: 'absolute',
top: 0,
left: (24) + width,
}}
source={require('./images/pic_progressbar_line.png')}
/>
)
}
{ index <= iconArr.length - 1 && index > 0
&& (
<Image
style={{
width,
height: (24),
position: 'absolute',
top: 0,
left: 0,
}}
source={require('./images/pic_progressbar_line.png')}
/>
)
}
</View>
);
})
}
</View>
);
};
export default Progress;
...@@ -56,7 +56,6 @@ function BackButton({ onBackPressed, imageUrl, style = {} }: BackButtonType) { ...@@ -56,7 +56,6 @@ function BackButton({ onBackPressed, imageUrl, style = {} }: BackButtonType) {
const actionMapping = (dispatch, props) => ({ const actionMapping = (dispatch, props) => ({
onBackPressed: props.onBackPressed || (() => { onBackPressed: props.onBackPressed || (() => {
console.log('BackButton:', props)
if (props.routes && props.routes.length === 1) { if (props.routes && props.routes.length === 1) {
} else { } else {
......
// @flow
import React, { useState } from 'react';
import _ from 'lodash';
import {
Text, TouchableOpacity, View, TouchableWithoutFeedback,
} from 'react-native';
import Modal from 'react-native-modal';
import ListItem from '../../pages/Agera-App-Insure/components/ListItem';
import { SinglePicker } from '../Agera-App-Base-Pickers';
const time = 315;
export default function Picker(props) {
const {
text, value, picker, beforeValue, afterValue,
} = props;
const data = ['测试1', '测试2', '测试3', '测试4', '测试5'];
const [isShow, setIsShow] = useState(false);
const [showValue, setShowValue] = useState(value || '请选择');
let CustomPickerRef = null;
const PickerView = picker || SinglePicker;
return (
<View>
<ListItem
style={[{
marginTop: 10,
borderBottomColor: '#ccc',
}, props.style]}
text={text}
>
<TouchableOpacity
onPress={() => {
setIsShow(true);
setTimeout(() => {
if (CustomPickerRef) {
CustomPickerRef.show();
}
});
}}
>
<View style={{ flexDirection: 'row', alignItems: 'stretch' }}>
{beforeValue}
<Text style={{ color:'#999', fontSize: 14}}>{showValue}</Text>
{afterValue}
</View>
</TouchableOpacity>
</ListItem>
<Modal
style={{
justifyContent: 'flex-end',
margin: 0,
padding: 0,
}}
isVisible={isShow}
animationIn="slideInUp"
animationOut="slideOutDown"
animationOutTiming={time}
animationInTiming={time}
backdropTransitionInTiming={time}
backdropTransitionOutTiming={time}
onModalHide={() => setIsShow(false)}
>
<TouchableWithoutFeedback
onPress={() => {
setIsShow(false);
}}
>
<View style={{ flex: 1, backgroundColor: 'rgb(0,0,0,0.5)' }} />
</TouchableWithoutFeedback>
<PickerView
ref={(ref) => {
CustomPickerRef = ref;
}}
list={data}
title={text}
onCancel={() => {
setIsShow(false);
}}
onConfirm={(index) => {
setIsShow(false);
console.log('PickerView', index);
if (_.isNumber(index)) {
setShowValue(data[index]);
} else if (_.isArray(index)) {
setShowValue(index.toString().replace(/,/g, ''));
}
}}
/>
</Modal>
</View>
);
}
//@flow //@flow
import React from 'react'; import React from 'react';
import { StyleSheet, } from 'react-native'; import { StyleSheet } from 'react-native';
import TabBarIndicator from 'react-native-tab-view/src/TabBarIndicator'; import TabBarIndicator from 'react-native-tab-view/src/TabBarIndicator';
import Animated from "react-native-reanimated"; import Animated from 'react-native-reanimated';
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from 'react-native-linear-gradient';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
indicator: { indicator: {
...@@ -20,7 +20,9 @@ const styles = StyleSheet.create({ ...@@ -20,7 +20,9 @@ const styles = StyleSheet.create({
export default class TabBarIndicatorLinear extends TabBarIndicator { export default class TabBarIndicatorLinear extends TabBarIndicator {
render() : any { render() : any {
const { width, position, navigationState, style } = this.props; const {
width, position, navigationState, style,
} = this.props;
const { routes } = navigationState; const { routes } = navigationState;
const translateX = this.getTranslateX(position, routes, width); const translateX = this.getTranslateX(position, routes, width);
...@@ -32,7 +34,7 @@ export default class TabBarIndicatorLinear extends TabBarIndicator { ...@@ -32,7 +34,7 @@ export default class TabBarIndicatorLinear extends TabBarIndicator {
// If layout is not available, use `left` property for positioning the indicator // If layout is not available, use `left` property for positioning the indicator
// This avoids rendering delay until we are able to calculate translateX // This avoids rendering delay until we are able to calculate translateX
width width
? { transform: [{ translateX }]} ? { transform: [{ translateX }] }
: { left: `${(100 / routes.length) * navigationState.index}%` }, : { left: `${(100 / routes.length) * navigationState.index}%` },
style, style,
]} ]}
...@@ -47,5 +49,4 @@ export default class TabBarIndicatorLinear extends TabBarIndicator { ...@@ -47,5 +49,4 @@ export default class TabBarIndicatorLinear extends TabBarIndicator {
</Animated.View> </Animated.View>
); );
} }
} }
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { Component } from 'react';
import {
View,
} from 'react-native';
import { SpringScrollView } from 'react-native-spring-scrollview';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import Picker from '../../components/Picker';
import Styles from '../../theme/Styles';
import { trailPolicyAPI } from '../InsuranceList/InsuranceAPI';
import {
BANNER,
PROCESS,
PAGE_TITLE,
BUTTON_GROUP,
LIST_ITEM,
CELL,
CHECKBOX,
PICKER,
DATE_PICKER,
IMAGE,
TAB,
MULTINPUT,
TEXTAREA,
PRODUCT_TITLE,
PRODUCT_BANNER,
PLAN_INFO,
PREM_GROUP,
PRODUCT_IMAGE,
SERVICE_IMAGE,
NOTICE_IMAGE,
INSURE_YEAR,
MULT,
JIACHENG,
AIFEIXIANG,
XUESHENGYIWAI,
} from './config/keys';
import {
getConfig,
} from './config';
import { renderPageByConfig } from '.';
import Button from '../../components/ThemeButton';
import SubmitButton from './components/SubmitButton';
type Props = {
routes : Array,
popTo : () => void,
blueId : string,
pageName : string,
};
const pageName = 'productDetail';
class BaseContainer extends Component {
constructor(props : Props) {
super(props);
console.log('props', props);
const productDetail = getConfig(props.blueId)[props.pageName];
const { params } = props;
const { keys, layout } = productDetail;
this.productDetail = productDetail;
this.policy = {
mult: '',
prem: 99,
premTotal: 99,
};
this.state = {
params,
config: {},
keys,
};
console.log('ProductDetailPage', this.state);
}
componentWillMount() : void {
const config = {};
const { params } = this.state;
const { keys, layout } = this.productDetail;
keys.forEach((key) => {
const data = this.fillData(key, this.productDetail[key], params);
console.log('productDetail:', this.productDetail, layout[key], key);
config[key] = { ...layout[key], ...data };
});
this.setState({
config,
});
}
fillData = (key, lay, params) => {
const {
name, description, tagNameList, bannerIconUrl, guaranteeContent,
productIntroductionUrl, serviceProcessUrl,
} = params.detail;
console.log('productIntroductionUrl', productIntroductionUrl);
switch (key) {
case PRODUCT_BANNER:
return {
uri: bannerIconUrl.url,
width: global.SCREEN_WIDTH,
height: global.SCREEN_WIDTH * bannerIconUrl.height / bannerIconUrl.width,
};
case PREM_GROUP:
return {
onSelect: (index) => {
console.log('BUTTON_GROUP,onSelect', index);
switch (index) {
case 0:
this.policy.prem = 99;
break;
case 1:
this.policy.prem = 199;
break;
default:
break;
}
},
selectIndex: 0,
};
case PRODUCT_TITLE:
return {
h1: name, // title需要装填数据 主标题
h2: description, // title需要装填数据 副标题
label: tagNameList, // 多个展示标签
labelColor: '#cf4100', //颜色
};
case PLAN_INFO:
return {
label: {
icon: '', //左侧icon
h1: '保障计划', //主标题
rightIcon: '', //右侧icon
rightText: '查看保障详情', //右侧文字
onPress: () => {
}, //点击事件
},
data: JSON.parse(guaranteeContent),
};
case INSURE_YEAR:
return {};
case MULT:
return {
onChangeText: (value) => {
console.log('ProductDetailPage_onChangeText', value);
this.policy.mult = value;
console.log(this.policy.mult);
},
onEndEditing: () => {
},
value: this.state.mult,
};
case NOTICE_IMAGE:
return {};
case PRODUCT_IMAGE:
return {
uri: productIntroductionUrl.url,
width: global.SCREEN_WIDTH,
height: global.SCREEN_WIDTH * productIntroductionUrl.height / productIntroductionUrl.width,
label: {
icon: '', //左侧icon
h1: '特色产品', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
};
case SERVICE_IMAGE:
return {
uri: serviceProcessUrl.url,
width: global.SCREEN_WIDTH,
height: global.SCREEN_WIDTH * serviceProcessUrl.height / serviceProcessUrl.width,
label: {
icon: '', //左侧icon
h1: '理赔流程', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
};
default:
return {};
}
}
translateDataToRules = (key, rule, params) => {
switch (key) {
case BANNER:
return {};
case PAGE_TITLE:
return {};
case LIST_ITEM:
return {};
case PICKER:
return {};
default:
return {};
}
}
renderExtraViews = (viewType) => {
switch (viewType) {
case PICKER:
return this.renderPicker;
case 'submit':
return this.renderSubmit;
default:
return () => {};
}
}
renderSubmit = () => (
<SubmitButton />
)
renderPicker = (data) => {
console.log('renderPicker:', data);
return (
<Picker {...data} />
);
}
renderInsureViews = (config, key, type) => {
renderPageByConfig(
config[key][type], this.renderExtraViews,
)(config[key]); // 第二个参数可以传入自己的方法根据自定义类型渲染组件
}
render() {
const { config, keys } = this.state;
console.log('config:', config, keys);
const type = 'type';
return (
<SpringScrollView
ref={(com) => {
this.scrollView = com;
}}
onScroll={({ nativeEvent: { contentOffset: { x, y } } }) => {
//console.log('offset : x=', x, 'y=', y);
}}
styles={Styles.container}
inverted={false}
//onRefresh={this._onRefresh}
//onLoading={this._onLoading}
// allLoaded={this.state.allLoaded}
refreshHeader={CommonLottieHeader}
//loadingFooter={CommonLottieFooter}
>
<View>
{keys.map((key, index) => (
this.renderInsureViews(config, key, type)
))}
</View>
</SpringScrollView>
);
}
}
export default BaseContainer;
/**
* @flow
*/
import _ from 'lodash';
import { PRODUCT_DETAIL } from './type';
import productDetailConfig from './productDetailConfig';
import policy from './policy';
export { PRODUCT_DETAIL };
function reset() {
return [productDetailConfig({})];
}
export default {
productDetailConfig,
reset,
};
export const emptyPolicy = {
policyid: '',
applicationnum: '',
agentcode: '',
prtno: '',
applicant: initApplicant,
insureds: [initInsured],
beneficiaries: [initBeneficiary],
products: [initProduct],
bankinfo: initBankInfo,
policyplan: initPolicyPlan,
policyproperties: initPolicyProperties,
rangeValue: initLimitValue,
limitValue: initLimitValue,
}
/**
* @flow
*/
import {
PRODUCT_DETAIL,
} from './type';
function ProductDetailConfig(payload:Object) {
return {
type: PRODUCT_DETAIL,
payload,
};
}
export default ProductDetailConfig;
/**
* @flow
*/
// action type
export const PRODUCT_DETAIL = 'insure/productDetail';
export default {};
//@flow
import React, { useState } from 'react';
import { ButtonGroup } from 'react-native-elements';
export default function renderButtonGroup(props) {
const {
buttons,
onSelect,
selectIndex,
containerStyle,
selectedButtonStyle,
selectedTextStyle,
textStyle,
} = props;
const [selected, setSelected] = useState(selectIndex);
return (
<ButtonGroup
onPress={(index) => {
setSelected(index);
onSelect(index);
}}
selectedIndex={selected}
buttons={buttons}
containerStyle={[{ height: 50 },containerStyle]}
containerBorderRadius={8}
selectedButtonStyle={selectedButtonStyle}
selectedTextStyle={selectedTextStyle}
textStyle={textStyle}
/>
);
}
// @flow
import React, { useState } from 'react';
import { View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { CheckBox } from 'react-native-elements';
import ListItem from '../ListItem';
import Text from '../Text';
export default function CheckBoxGroup(props) {
const [select, setSelect] = useState(props.selectIndex);
return (
<View>
{props.data.map((item, index) => (
<ListItem
style={{
borderBottomColor: '#ccc',
height: 74,
}}
leftElement={() => (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<FastImage
style={{
width: 44,
height: 44,
margin: 15,
marginLeft: 0,
}}
source={{
uri: item.imgUrl,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
<View>
<Text>{item.title}</Text>
<Text>{item.detail}</Text>
</View>
</View>
)}
>
<CheckBox
checked={index === select}
onPress={() => {
if (props.onSelect) {
props.onSelect(index);
}
setSelect(index);
}}
/>
</ListItem>
))
}
</View>
);
}
// @flow
import React from 'react';
import { View } from 'react-native';
import Text from '../Text';
export default function (props) {
const { text, style, leftElement } = props;
return (
<View>
<View
style={{
backgroundColor: '#fff',
flexDirection: 'row',
alignItems: 'center',
height: 50,
paddingLeft: 15,
...style,
}}
>
{ leftElement ? leftElement() : (
<View style={{ justifyContent: 'flex-start' }}>
<Text style={{ fontSize: 14, color: '#666' }}>
{text}
</Text>
</View>
)
}
<View style={{
flex: 1,
alignItems: 'flex-end',
paddingRight: 15,
}}
>
{props.children}
</View>
</View>
<View style={{
borderBottomColor: '#ccc',
borderBottomWidth: 0.3,
marginLeft: 15,
}}
/>
</View>
);
}
//@flow
import React, { Component } from 'react';
import TextInput from '../TextInput';
import ListItem from '../ListItem';
export default class Mult extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value,
};
}
componentWillReceiveProps(nextProps : Readonly<P>, nextContext : any) : void {
console.log('componentWillReceiveProps', nextProps.value);
if (nextProps.value !== this.state.value) {
this.setState({
value: nextProps.value,
});
}
}
render() {
const {
maxLength,
keyboardType,
onChangeText,
placeholder,
text,
onEndEditing,
} = this.props;
return (
<ListItem
style={{
borderBottomColor: '#ccc',
}}
text={text}
>
<TextInput
numberOfLines={1}
underlineColorAndroid="transparent"
maxLength={maxLength}
value={this.state.value}
keyboardType={keyboardType}
onChangeText={(value) => {
this.setState({
value,
});
onChangeText(value);
}}
placeholder={placeholder}
onEndEditing={onEndEditing}
/>
</ListItem>
);
}
}
//@flow
import React, { Component } from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-elements';
import Text from '../Text';
import ListItem from '../ListItem';
export default class SubmitButton extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value,
};
}
setPrem = (value) => {
console.log('setPrem', value);
this.setState({
value,
});
}
render() {
return (
<View style={{ flexDirection: 'row', height: 44, alignItems: 'stretch' }}>
<View style={{
width: global.SCREEN_WIDTH * 150 / 375,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
}}
>
<Text style={{ color: '#666', fontSize: 16 }}>
金额
</Text>
<Text style={{ color: '#FF4656', paddingLeft: 10 }}>{this.state.value}</Text>
</View>
<Button
title="立即投保"
containerStyle={{ width: global.SCREEN_WIDTH * 225 / 375 }}
onPress={this.props.onPress}
/>
</View>
);
}
}
// @flow
import React from 'react';
import { Text } from 'react-native';
export default function (props) {
return (
<Text
allowFontScaling={false}
{...props}
>
{props.children}
</Text>
);
}
// @flow
import React from 'react';
import { TextInput } from 'react-native';
export default function (props) {
return (
<TextInput
allowFontScaling={false}
{...props}
/>
);
}
// @flow
import {
PROGRESS,
POLICY_PROGRESS,
INSURE_YEAR_TITLE,
DATE_RANGE,
INSURE_YEAR,
GRAY_LABEL,
TEXT_INPUT,
APPLICANT_TITLE,
APPLICANT_NAME,
APPLICANT_ID_TYPE,
APPLICANT_ID,
APPLICANT_BIRTHDAY,
APPLICANT_GENDER,
APPLICANT_MOBILE,
APPLICANT_EMAIL,
INSURED_TITLE,
INSURED_NAME,
INSURED_ID_TYPE,
} from './keys';
export default {
layout: {
[POLICY_PROGRESS]: {// 组件的唯一标识,用来串联 layout, rules
type: PROGRESS, // 组件类型
data: [],
index: 0,
},
[APPLICANT_TITLE]: {// 组件的唯一标识,用来串联 layout, rules
type: GRAY_LABEL, // 组件类型
text: '投保人信息', // 标题
},
[APPLICANT_NAME]: {
type: TEXT_INPUT,
text: '姓名', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_ID_TYPE]: {
type: TEXT_INPUT,
text: '证件类型', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_ID]: {
type: TEXT_INPUT,
text: '证件号码', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_BIRTHDAY]: {
type: TEXT_INPUT,
text: '出生日期', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_GENDER]: {
type: TEXT_INPUT,
text: '性别', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_MOBILE]: {
type: TEXT_INPUT,
text: '手机号码', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[APPLICANT_EMAIL]: {
type: TEXT_INPUT,
text: '电子邮箱', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[INSURED_TITLE]: {
type: GRAY_LABEL, // 组件类型
text: '被保人信息', //标题
},
[INSURED_NAME]: {
type: TEXT_INPUT,
text: '姓名', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[INSURED_ID_TYPE]: {
type: TEXT_INPUT,
text: '证件类型', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[INSURE_YEAR_TITLE]: {
type: GRAY_LABEL,
text: '承保信息',
},
[INSURE_YEAR]: {
type: DATE_RANGE,
text: '保险期限',
begin: '',
end: '',
},
},
rules: {
[INSURE_YEAR]: {
range: [],
},
},
keys: [POLICY_PROGRESS, APPLICANT_TITLE, APPLICANT_NAME, APPLICANT_ID_TYPE, APPLICANT_ID,
APPLICANT_BIRTHDAY, APPLICANT_GENDER, APPLICANT_MOBILE, APPLICANT_EMAIL, INSURED_TITLE,
INSURED_NAME, INSURED_ID_TYPE, INSURE_YEAR_TITLE, INSURE_YEAR],
};
// @flow // @flow
export const productDetail = 'productDetail'; import productDetail from './productDetail';
export const fillInfo = 'fillInfo'; import fillInfo from './fillInfo';
export const healthNotification = 'healthNotification'; import healthNotification from './healthNotification';
export const payment = 'payment'; import payment from './payment';
export const receipt = 'receipt'; import receipt from './receipt';
// layout type
export const BANNER = 'banner'; import {
export const TITLE = 'title'; BANNER,
export const LIST_ITEM = 'listItem'; PROCESS,
export const CHOOSE = 'choose'; PAGE_TITLE,
export const PRODUCT = 'product'; BUTTON_GROUP,
export const SERVICE = 'service'; LIST_ITEM,
CELL,
CHECKBOX,
PICKER,
DATE_PICKER,
IMAGE,
TAB,
MULTINPUT,
TEXTAREA,
PRODUCT_TITLE,
PRODUCT_BANNER,
PLAN_INFO,
PREM_GROUP,
PRODUCT_IMAGE,
SERVICE_IMAGE,
NOTICE_IMAGE,
INSURE_YEAR,
MULT,
JIACHENG,
AIFEIXIANG,
XUESHENGYIWAI,
} from './keys';
const initConfig = { const initConfig = {
insuranceProcess: [ productDetail, // 产品详情页面
productDetail, fillInfo, // 信息录入
fillInfo, healthNotification, // 健康告知
healthNotification, payment, // 确认支付
payment, receipt, // 签收
receipt, };
],
layout: [
{
type: BANNER, // 组件类型
key: BANNER, // 组件的唯一标识,用来串联 layout, rules
uri: '', // banner需要装填数据 uri,展示的图片链接
width: 100, //图片宽度
height: 100, // 图片高度
},
{
type: TITLE,
key: TITLE,
h1: '', // title需要装填数据 主标题
h2: '', // title需要装填数据 副标题
label: [], // 多个展示标签
labelColor: '', //颜色
},
{
type: LIST_ITEM,
key: LIST_ITEM,
title: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
data: [{ key: '', value: '' }],
},
{
type: CHOOSE,
key: CHOOSE,
title: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
text: '', // 选择框显示标题
values: [], // 选择框数据集合
source: {}, //修改源对象
setKey: '', //修改源对象内的属性名称
onChange: () => {
}, //选中事件
onWillChange: () => {
}, // 选中前执行事件
},
{
type: PRODUCT,
key: PRODUCT,
uri: '',
},
],
rules: [
{
layoutKey: CHOOSE, // 串联 key 为 choose的组件
range: [], // [min , max] [a,b,c,d] 只允许包含在 a\b\c\d内
},
],
}
export const BaseConfig = { export const getConfig = (blueID) => {
'200000156': initConfig, const config = { ...initConfig };
'200000155': initConfig, const premIndex = config.productDetail.keys.findIndex(key => (key === PREM_GROUP));
'200000152': initConfig, switch (blueID) {
case JIACHENG:
// 驾乘
config.productDetail.layout[PREM_GROUP] = {
type: BUTTON_GROUP,
buttons: ['99元基础', '199元加强'],
onSelect: () => {},
mask: false,
editable: true,
selectIndex: 0,
};
return config;
case AIFEIXIANG:
// 爱飞翔
delete config.productDetail[PREM_GROUP];
config.productDetail.keys.splice(premIndex, 1);
return config;
case XUESHENGYIWAI:
//学生意外
delete config.productDetail[PREM_GROUP];
config.productDetail.keys.splice(premIndex, 1);
return config;
default:
return config;
}
}; };
// 订单 // 订单
......
//@flow
// layout type 渲染UI组件
export const BANNER = 'banner';
export const PROCESS = 'process';
export const PAGE_TITLE = 'title';
export const BUTTON_GROUP = 'buttonGroup';
export const LIST_ITEM = 'listItem';
export const CELL = 'cell';
export const CHECKBOX = 'checkbox';
export const PICKER = 'picker';
export const DATE_PICKER = 'datePicker';
export const IMAGE = 'listImage';
export const TAB = 'tab';
export const MULTINPUT = 'multInput';
export const TEXT_INPUT = 'textInput';
export const TEXTAREA = 'textArea';
export const PROGRESS = 'progress';
export const GRAY_LABEL = 'grayLabel';
export const DATE_RANGE = 'dateRange';
export const NOTICE = 'notice';
// layout key 组件作用于哪个业务,是装填数据,绑定事件的依据
export const PRODUCT_TITLE = 'productTitle';
export const PRODUCT_BANNER = 'productBanner';
export const PLAN_INFO = 'planInfo';
export const PREM_GROUP = 'premGroup';
export const PRODUCT_IMAGE = 'productImage';
export const SERVICE_IMAGE = 'serviceImage';
export const NOTICE_IMAGE = 'noticeImage';
export const INSURE_YEAR = 'insureYear';
export const MULT = 'mult';
export const APPLICANT_TITLE = 'applicantTitle';
export const APPLICANT_NAME = 'applicantName';
export const APPLICANT_ID_TYPE = 'applicantIDType';
export const APPLICANT_ID = 'applicantID';
export const APPLICANT_BIRTHDAY = 'applicantBirthday';
export const APPLICANT_GENDER = 'applicantGender';
export const APPLICANT_MOBILE = 'applicantMobile';
export const APPLICANT_EMAIL = 'applicantEmail';
export const INSURED_TITLE = 'insuredTitle';
export const INSURED_NAME = 'insuredName';
export const INSURED_ID_TYPE = 'insuredIDType';
export const POLICY_PROGRESS = 'policy_progress';
export const INSURE_YEAR_TITLE = 'insureYearTitle';
export const INSURED_PREM = 'insurePrem';
export const INSURED_PRODUCT = 'insureProduct';
export const PAYMENT_TITLE = 'paymentTitle';
export const PAYMENT_NOTICE = 'paymentNotice';
export const PAYMENT_TYPE = 'paymentType';
// 保险ID
export const JIACHENG = '200000156';
export const AIFEIXIANG = '200000155';
export const XUESHENGYIWAI = '200000152';
//@flow
import {
BANNER, BUTTON_GROUP, IMAGE, LIST_ITEM, MULTINPUT, PAGE_TITLE, PICKER,
PRODUCT_BANNER, PRODUCT_TITLE, PREM_GROUP, PLAN_INFO, INSURE_YEAR, MULT, PRODUCT_IMAGE,
SERVICE_IMAGE, NOTICE, POLICY_PROGRESS, PROGRESS, INSURED_TITLE, GRAY_LABEL, INSURED_NAME, TEXT_INPUT,
INSURED_PREM, INSURED_PRODUCT, PAYMENT_TITLE, PAYMENT_NOTICE, CHECKBOX, PAYMENT_TYPE,
} from './keys';
export default {
layout: {
[POLICY_PROGRESS]: {// 组件的唯一标识,用来串联 layout, rules
type: PROGRESS, // 组件类型
data: [],
index: 0,
},
[PAYMENT_NOTICE]: {
type: NOTICE, // 组件类型
text: '订单已生成,请尽快完成支付,以免因超时导致支付失败。', //标题
},
[INSURED_TITLE]: {
type: GRAY_LABEL, // 组件类型
text: '被保人信息', //标题
},
[INSURED_PRODUCT]: {
type: TEXT_INPUT,
text: '产品名称', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[INSURED_PREM]: {
type: TEXT_INPUT,
text: '保费', //标题
value: '',
onEndEditing: () => {
}, //编辑完成
mask: false,
editable: true,
},
[PAYMENT_TITLE]: {
type: GRAY_LABEL, // 组件类型
text: '产品信息', //标题
},
[PAYMENT_TYPE]: {
type: CHECKBOX, // 组件类型
data:[{
title: '',
detail: '',
imgUrl: '',
}], //选项
onSelect: () => {},
selectIndex: -1,
},
},
rules: {},
keys: [POLICY_PROGRESS, INSURED_TITLE, INSURED_PRODUCT, INSURED_PREM, PAYMENT_TITLE, PAYMENT_TYPE],
};
//@flow
import {
BANNER, BUTTON_GROUP, IMAGE, LIST_ITEM, MULTINPUT, PAGE_TITLE, PICKER,
PRODUCT_BANNER, PRODUCT_TITLE, PREM_GROUP, PLAN_INFO, INSURE_YEAR, MULT, PRODUCT_IMAGE,
SERVICE_IMAGE, NOTICE_IMAGE,
} from './keys';
export default {
layout: {
[PRODUCT_BANNER]: {// 组件的唯一标识,用来串联 layout, rules
type: BANNER, // 组件类型
uri: '', // banner需要装填数据 uri,展示的图片链接
width: 100, //图片宽度
height: 100, // 图片高度
},
[PRODUCT_TITLE]: {
type: PAGE_TITLE,
h1: '', // title需要装填数据 主标题
h2: '', // title需要装填数据 副标题
label: [], // 多个展示标签
labelColor: '', //颜色
mask: false,
},
[PREM_GROUP]: {
type: BUTTON_GROUP,
buttons: ['1', '2'],
onSelect: () => {
},
mask: false,
editable: true,
selectIndex: 0,
},
[PLAN_INFO]: {
type: LIST_ITEM,
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
mask: false,
editable: true,
data: [{ key: '', value: '' }],
},
[INSURE_YEAR]: {
type: PICKER,
icon: '', //左侧icon
text: '保障期限', //标题
value: '', //右侧显示值
source: {}, //修改源对象
setKey: '', //修改源对象内的属性名称
onSelect: () => {
}, //点击事件
mask: false,
editable: true,
selectIndex: -1,
},
[MULT]: {
type: MULTINPUT,
text: '承保份数', //标题
value: '', //右侧显示值
source: {}, //修改源对象
setKey: '', //修改源对象内的属性名称
onChangeText: () => {
}, // 编辑文本
onEndEditing: () => {}, //输入完毕
mask: false,
editable: true,
selectIndex: 0,
},
[PRODUCT_IMAGE]: {
type: IMAGE,
uri: '',
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
},
[SERVICE_IMAGE]: {
type: IMAGE,
uri: '',
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
},
[NOTICE_IMAGE]: {
type: IMAGE,
uri: '',
label: {
icon: '', //左侧icon
h1: '', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
},
},
rules: {
[INSURE_YEAR]: { // 串联 key 为 INSURE_YEAR的组件
range: [], // [min , max] [a,b,c,d] 只允许包含在 a\b\c\d内
},
},
keys: [PRODUCT_BANNER, PRODUCT_TITLE, PREM_GROUP, PLAN_INFO, INSURE_YEAR, MULT, PRODUCT_IMAGE,
SERVICE_IMAGE, NOTICE_IMAGE],
};
...@@ -2,14 +2,42 @@ ...@@ -2,14 +2,42 @@
import React from 'react'; import React from 'react';
import { import {
View, View,
TouchableOpacity,
} from 'react-native'; } from 'react-native';
import FastImage from 'react-native-fast-image'; import FastImage from 'react-native-fast-image';
import { ListItem } from 'react-native-elements'; import Text from './components/Text';
import Text from '../../components/Text'; import ListItem from './components/ListItem';
import Mult from './components/Mult';
import CheckBoxGroup from './components/CheckBoxGroup';
import ButtonGroup from './components/ButtonGroup';
import Progress from '../../components/Agera-App-Base-Progress';
import { import {
BANNER, TITLE, LIST_ITEM, CHOOSE, PRODUCT, SERVICE, BANNER,
} from './config'; PROCESS,
import BasicIcon from '../../components/BasicIcon'; PAGE_TITLE,
BUTTON_GROUP,
LIST_ITEM,
CELL,
CHECKBOX,
PICKER,
DATE_PICKER,
IMAGE,
TAB,
MULTINPUT,
TEXTAREA,
PRODUCT_TITLE,
PRODUCT_BANNER,
PLAN_INFO,
PREM_GROUP,
PRODUCT_IMAGE,
SERVICE_IMAGE,
NOTICE_IMAGE,
INSURE_YEAR,
MULT,
JIACHENG,
AIFEIXIANG,
XUESHENGYIWAI, GRAY_LABEL, DATE_RANGE, TEXT_INPUT, PROGRESS,NOTICE
} from './config/keys';
function renderBanner(props) { function renderBanner(props) {
console.log('renderBanner:', props); console.log('renderBanner:', props);
...@@ -26,7 +54,6 @@ function renderBanner(props) { ...@@ -26,7 +54,6 @@ function renderBanner(props) {
} }
function renderTitle(props) { function renderTitle(props) {
console.log('renderTitle:', props);
return ( return (
<View <View
style={{ style={{
...@@ -79,41 +106,79 @@ function renderTitle(props) { ...@@ -79,41 +106,79 @@ function renderTitle(props) {
); );
} }
function f() { function renderButtonGroup(props) {
const { onSelect, buttons, selectIndex } = props;
if (buttons.length > 0) {
return (
<ButtonGroup
onSelect={onSelect}
buttons={buttons}
selectIndex={selectIndex}
containerStyle={{
marginHorizontal: 15, marginBottom: 20, borderWidth: 1, borderColor: '#0E6DCF',
}}
selectedButtonStyle={{ backgroundColor: '#0E6DCF' }}
textStyle={{ color: '#0E6DCF' }}
selectedTextStyle={{ color: '#fff' }}
/>
);
}
return null;
} }
function renderItemText(props) { function renderItemText(props) {
console.log('props:', props);
return ( return (
<View> <View>
{props.text.map((text, index) => ( {props.text.map((text, index) => (
<ListItem <ListItem
key={`ListItem${index}`} key={`ListItem${index}`}
containerStyle={{ style={{
borderBottomWidth: index < props.text.length - 1 ? 0.3 : 0,
borderBottomColor: '#ccc', borderBottomColor: '#ccc',
}} }}
title={text.name} text={text.name}
titleStyle={{ color: '#666', fontSize: 14 }} >
leftElement={( <Text>{text.value}</Text>
<View /> </ListItem>
)}
rightElement={(
<Text>{text.value}</Text>
)}
/>
)) ))
} }
</View> </View>
); );
} }
function renderLabel(props) {
const { label } = props;
return (
<View style={{ height: 42, flexDirection: 'row', alignItems: 'center' }}>
{label.icon || (
<View style={{
width: 4,
height: 16,
backgroundColor: '#0E6DCF',
borderBottomRightRadius: 2,
borderTopRightRadius: 2,
}}
/>
)}
<Text style={{ color: '#20253C', fontSize: 16, marginLeft: 10 }}>{label.h1}</Text>
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'flex-end' }}>
<TouchableOpacity
onPress={() => {
label.onPress();
}}
>
<Text style={{ color: '#0E6DCF', paddingRight: 15 }}>{label.rightText}</Text>
</TouchableOpacity>
</View>
</View>
);
}
function renderListItem(props) { function renderListItem(props) {
const array = props.data; const array = props.data;
console.log('array:', array); console.log('renderListItem:', array);
return ( return (
<View> <View style={{ backgroundColor: '#fff', marginTop: 10 }}>
{renderLabel(props)}
{array.map((item, index) => ( {array.map((item, index) => (
renderItemText(item) renderItemText(item)
)) ))
...@@ -123,43 +188,129 @@ function renderListItem(props) { ...@@ -123,43 +188,129 @@ function renderListItem(props) {
} }
function renderImage(props) { function renderImage(props) {
console.log('props:',props) console.log('props:', props);
return (
<View style={{ backgroundColor: '#fff', marginTop: 10 }}>
{renderLabel(props)}
<FastImage
style={{ width: props.width, height: props.height }}
source={{
uri: props.uri,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.contain}
/>
</View>
);
}
function renderMult(props) {
return (
<Mult {...props} />
);
}
function renderGrayTitle(props) {
return (
<View
style={{
height: props.height || 50,
backgroundColor: '#F4F5F5',
paddingLeft: 15,
}}
>
<Text style={{
color: '#666',
fontSize: 14,
marginTop: 15,
}}
>
{props.text}
</Text>
</View>
);
}
function renderTextInput(props) {
return( return(
<FastImage <ListItem
style={{ width: props.width, height: props.width * 1200 / 375 }} style={{
source={{ borderBottomColor: '#ccc',
uri: props.uri,
priority: FastImage.priority.normal,
}} }}
resizeMode={FastImage.resizeMode.contain} text={props.text}
>
<Text>{props.value}</Text>
</ListItem>
);
}
function renderProgress(props) {
return(
<Progress
index={props.index}
data={props.data}
/> />
) )
} }
function renderNotice(props) {
return(<View
style={{
height: props.height || 50,
backgroundColor: '#FFF8EC',
alignItems: 'center'
}}
>
<Text style={{
color: '#F5591C',
fontSize: 14,
marginTop: 15,
}}
>
{props.text}
</Text>
</View>)
}
function renderCheckBox(props){
return(
<CheckBoxGroup
{...props}
/>
);
}
export function renderPageByConfig(config, renderCustomView?) { export function renderPageByConfig(type, renderCustomView?) {
switch (config.type) { console.log('renderPageByConfig:', type);
switch (type) {
case BANNER: case BANNER:
return renderBanner; return renderBanner;
case TITLE: case PAGE_TITLE:
return renderTitle; return renderTitle;
case LIST_ITEM: case LIST_ITEM:
return renderListItem; return renderListItem;
case CHOOSE: case MULTINPUT:
return ()=>{ return renderMult;
case BUTTON_GROUP:
} return renderButtonGroup;
case PRODUCT: case IMAGE:
return renderImage; return renderImage;
case SERVICE: case TEXT_INPUT:
return renderImage; return renderTextInput;
case GRAY_LABEL:
return renderGrayTitle;
case NOTICE:
return renderNotice;
case PROGRESS:
return renderProgress;
case CHECKBOX:
return renderCheckBox;
default: default:
return () => { // 有自定义方法执行传入的渲染方法
// 有自定义方法执行传入的渲染方法 if (renderCustomView) {
if (renderCustomView) { return renderCustomView(type);
renderCustomView(config); }
} return () => {};
};
} }
} }
......
...@@ -8,20 +8,30 @@ type RiskTakerParams = { ...@@ -8,20 +8,30 @@ type RiskTakerParams = {
uid : string, uid : string,
} }
export async function queryInsuranceListByUid(params : RiskTakerParams) { export async function queryInsuranceListByUidAPI(params : RiskTakerParams) {
console.log('queryInsuranceListByUid', params); console.log('queryInsuranceListByUidAPI', params);
const body = await fetchCoreAPI('001001', params); const body = await fetchCoreAPI('001001', params);
console.log('fetchCoreAPI:', body); console.log('queryInsuranceListByUidAPI_body:', body);
if (_.get(body, 'code') === '0000' && _.get(body, 'data')) { if (_.get(body, 'code') === '0000' && _.get(body, 'data')) {
return _.get(body, 'data'); return _.get(body, 'data');
} }
return []; return [];
} }
export async function queryInsuranceProducts(params : RiskTakerParams) { export async function queryInsuranceProductsAPI(params) {
console.log('queryInsuranceProducts', params); console.log('queryInsuranceProductsAPI', params);
const body = await fetchCoreAPI('001002', params); const body = await fetchCoreAPI('001002', params);
console.log('fetchCoreAPI:', body); console.log('queryInsuranceProductsAPI_body:', body);
if (_.get(body, 'code') === '0000' && _.get(body, 'data')) {
return _.get(body, 'data');
}
return [];
}
export async function trailPolicyAPI(params) {
console.log('trailPolicyAPI', params);
const body = await fetchCoreAPI('001003', {params:params});
console.log('trailPolicyAPI_body:', body);
if (_.get(body, 'code') === '0000' && _.get(body, 'data')) { if (_.get(body, 'code') === '0000' && _.get(body, 'data')) {
return _.get(body, 'data'); return _.get(body, 'data');
} }
......
...@@ -7,7 +7,7 @@ import { ...@@ -7,7 +7,7 @@ import {
Text, Text,
View, View,
} from 'react-native'; } from 'react-native';
import Panel from '../../components/Agera-App-Base-panel'; import Panel from '../../components/Agera-App-Base-Panel';
import BasicIcon from '../../components/BasicIcon'; import BasicIcon from '../../components/BasicIcon';
......
...@@ -22,7 +22,7 @@ import LinearGradient from 'react-native-linear-gradient'; ...@@ -22,7 +22,7 @@ import LinearGradient from 'react-native-linear-gradient';
import { push, tab } from '../../BasicNavigator/actions'; import { push, tab } from '../../BasicNavigator/actions';
import NavigationBar, { NavHeight } from '../../components/NavigationBar'; import NavigationBar, { NavHeight } from '../../components/NavigationBar';
import BackButton from '../../components/BackButton'; import BackButton from '../../components/BackButton';
import { queryInsuranceListByUid } from './InsuranceAPI'; import { queryInsuranceListByUidAPI } from './InsuranceAPI';
import Styles from '../../theme/Styles'; import Styles from '../../theme/Styles';
import ListItem from './ListItem'; import ListItem from './ListItem';
import decorator from '../../utils/decorator'; import decorator from '../../utils/decorator';
...@@ -96,7 +96,7 @@ function InsuranceListPage(props : Props) { ...@@ -96,7 +96,7 @@ function InsuranceListPage(props : Props) {
); );
}; };
const queryList = async () => { const queryList = async () => {
const list = await queryInsuranceListByUid({ params: { uid: 'u0000001' } }); const list = await queryInsuranceListByUidAPI({ params: { uid: 'u0000001' } });
console.log('list:', list); console.log('list:', list);
if (list) { if (list) {
......
...@@ -10,12 +10,13 @@ ...@@ -10,12 +10,13 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { SpringScrollView } from 'react-native-spring-scrollview'; import { SpringScrollView } from 'react-native-spring-scrollview';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader'; import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import CommonLottieFooter from 'react-native-spring-scrollview/Customize/CommonLottieFooter'; //import CommonLottieFooter from 'react-native-spring-scrollview/Customize/CommonLottieFooter';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose } from 'redux'; import { compose } from 'redux';
import { import {
NativeAppEventEmitter, NativeAppEventEmitter,
StyleSheet, StyleSheet,
BackHandler,
View, View,
} from 'react-native'; } from 'react-native';
...@@ -44,17 +45,19 @@ class HomePage extends Component<Props> { ...@@ -44,17 +45,19 @@ class HomePage extends Component<Props> {
}; };
this.scrollView = null; this.scrollView = null;
} }
componentDidMount() : void { componentDidMount() : void {
this.listener =NativeAppEventEmitter.addListener('react_navigation_onTransitionEnd_call',() => console.log('onTransitionEnd_call')) this.listener =NativeAppEventEmitter.addListener('react_navigation_onTransitionEnd_call',() => console.log('onTransitionEnd_call'))
} }
componentWillUnmount() : void { componentWillUnmount() : void {
if(this.listener){ if(this.listener){
this.listener.remove(); this.listener.remove();
} }
} }
_onRefresh = () => { _onRefresh = () => {
setTimeout(() => { setTimeout(() => {
this.scrollView.endRefresh(); this.scrollView.endRefresh();
...@@ -106,9 +109,10 @@ function actionMapping(dispatch) { ...@@ -106,9 +109,10 @@ function actionMapping(dispatch) {
pushTo: compose(dispatch, push), pushTo: compose(dispatch, push),
}; };
} }
function propsMapping({ theme }) { function propsMapping({ theme, nav }) {
return { return {
theme, theme,
nav,
}; };
} }
......
/**
* @flow
*/
import React, { Component } from 'react';
import { View, TouchableOpacity } from 'react-native';
import FastImage from 'react-native-fast-image';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import { LargeList } from 'react-native-largelist-v3';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { headerHeight, tabHeight } from './policyRenderUtil';
import { queryInsuranceProductsAPI } from '../InsuranceList/InsuranceAPI';
import { push } from '../../BasicNavigator/actions';
const listItemHeight = (global.SCREEN_WIDTH - 10) * 255 / 365;
const listItemWidth = global.SCREEN_WIDTH - 10;
class PolicyList extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{
items: [],
},
],
};
}
componentDidMount() : void {
this.queryList();
}
renderIndexPath = (item, row ) => {
const { theme, pushTo } = this.props;
return (
<TouchableOpacity
onPress={() => pushTo('ProductDetailPage', item)}
>
<FastImage
style={{
width: listItemWidth,
height: listItemHeight,
}}
source={{
uri: item.iconUrl,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
</TouchableOpacity>
);
};
queryList = async () => {
const list = await queryInsuranceProductsAPI({});
console.log('queryInsuranceProductsAPI:', list);
if (list) {
this.setState({
data: [
{
items: list,
},
],
});
}
};
onRefresh = async () => {
await this.queryList();
this.scrollView.endRefresh();
};
endRefresh = () => {
};
/* <LargeList
ref={(com) => {
this.scrollView = com;
}}
style={{
backgroundColor: '#F4F5F5',
height: global.SCREEN_HEIGHT - headerHeight - tabHeight - 49,
}}
contentStyle={[
{ backgroundColor: '#F4F5F5' },
]}
data={this.state.data}
onRefresh={this.onRefresh}
endRefresh={this.endRefresh}
refreshHeader={CommonLottieHeader}
heightForIndexPath={() => listItemHeight}
heightForSection={() => 0}
renderIndexPath={this.renderIndexPath}
/>*/
render() {
return (
<View>
{this.state.data[0].items.map(this.renderIndexPath)}
</View>
);
}
}
function actionMapping(dispatch) {
return {
pushTo: compose(dispatch, push),
};
}
function propsMapping({ theme }) {
return {
theme,
};
}
export default connect(propsMapping, actionMapping)(PolicyList);
...@@ -16,24 +16,27 @@ import { ...@@ -16,24 +16,27 @@ import {
} from 'react-native'; } from 'react-native';
import FastImage from 'react-native-fast-image'; import FastImage from 'react-native-fast-image';
import { TabBar, TabView } from 'react-native-tab-view'; import { TabBar, TabView, SceneMap } from 'react-native-tab-view';
import { LargeList } from 'react-native-largelist-v3'; import { LargeList } from 'react-native-largelist-v3';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader'; import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import { push } from '../../BasicNavigator/actions'; import { push } from '../../BasicNavigator/actions';
import decorator from '../../utils/decorator'; import decorator from '../../utils/decorator';
import { queryInsuranceProducts } from '../InsuranceList/InsuranceAPI'; import { queryInsuranceProductsAPI } from '../InsuranceList/InsuranceAPI';
import TabBarIndicatorLinear from '../../components/TabBarIndicator'; import TabBarIndicatorLinear from '../../components/TabBarIndicator';
import PolicyList from './PolicyList';
import { import {
renderPolicyHeader, renderPolicyHeader,
renderPolicyTab, renderPolicyTab,
headerHeight, headerHeight,
tabHeight, tabHeight,
} from './policyRenderUtil'; } from './policyRenderUtil';
import Styles from "../../theme/Styles";
import { SpringScrollView } from "react-native-spring-scrollview";
import NavigationBar from "../../components/NavigationBar";
type Props = {}; type Props = {};
const tabWidth = global.SCREEN_WIDTH / 4; const tabWidth = global.SCREEN_WIDTH / 4;
const listItemHeight = (global.SCREEN_WIDTH - 10) * 255 / 365
const listItemWidth = global.SCREEN_WIDTH - 10;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
...@@ -62,6 +65,9 @@ const typeData = { ...@@ -62,6 +65,9 @@ const typeData = {
routes: typeArr, routes: typeArr,
}; };
const PolicyListView = () => (<PolicyList style={{flex:1}} />);
@decorator @decorator
class PolicyPage extends Component<Props> { class PolicyPage extends Component<Props> {
constructor(props) { constructor(props) {
...@@ -72,11 +78,9 @@ class PolicyPage extends Component<Props> { ...@@ -72,11 +78,9 @@ class PolicyPage extends Component<Props> {
}; };
} }
componentDidMount() : void {
this.queryList();
}
_handleIndexChange = (index) => { _handleIndexChange = (index) => {
console.log('_handleIndexChange:',index)
const type = { const type = {
index, index,
routes: typeArr, routes: typeArr,
...@@ -84,72 +88,27 @@ class PolicyPage extends Component<Props> { ...@@ -84,72 +88,27 @@ class PolicyPage extends Component<Props> {
this.setState({ type }); this.setState({ type });
}; };
renderIndexPath = ({ section, row }) => {
const { theme, pushTo } = this.props;
const item = this.state.data[section].items[row];
console.log('renderIndexPath:', item)
return (
<TouchableOpacity
onPress={() => pushTo('ProductDetailPage', item)}
>
<FastImage
style={{
width: listItemWidth,
height: listItemHeight,
}}
source={{
uri: item.iconUrl,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
</TouchableOpacity>
);
};
queryList = async () => { //queryList = async () => {
const list = await queryInsuranceProducts({}); // const list = await queryInsuranceProductsAPI({});
console.log('queryInsuranceProducts:', list); // console.log('queryInsuranceProductsAPI:', list);
if (list) { // if (list) {
this.setState({ // this.setState({
data: [ // data: [
{ // {
items: list, // items: list,
}, // },
], // ],
}); // });
} // }
}; //};
onRefresh = async () => {
await this.queryList();
this.scrollView.endRefresh();
};
endRefresh = () => {
};
_renderScene = ({ route, jumpTo }) => ( //_renderScene = () => {
<LargeList // console.log('_renderScene:');
ref={(com) => { // return (
this.scrollView = com; // );
}} //};
style={{
backgroundColor: '#F4F5F5',
}}
contentStyle={[
{ height: global.SCREEN_HEIGHT - headerHeight - tabHeight, backgroundColor: '#F4F5F5' },
]}
data={this.state.data}
onRefresh={this.onRefresh}
endRefresh={this.endRefresh}
refreshHeader={CommonLottieHeader}
heightForIndexPath={() => listItemHeight}
heightForSection={() => 0}
renderIndexPath={this.renderIndexPath}
/>
);
_renderTabBar = item => ( _renderTabBar = item => (
<TabBar <TabBar
...@@ -161,29 +120,54 @@ class PolicyPage extends Component<Props> { ...@@ -161,29 +120,54 @@ class PolicyPage extends Component<Props> {
labelStyle={[styles.label]} labelStyle={[styles.label]}
activeColor={this.props.theme.ThemeColor} activeColor={this.props.theme.ThemeColor}
inactiveColor="#666" inactiveColor="#666"
renderIndicator={(props) => ( renderIndicator={props => (
<TabBarIndicatorLinear {...props} /> <TabBarIndicatorLinear {...props} />
)} )}
/> />
) )
_onRefresh = () => {
//await this.queryList();
this.scrollView.endRefresh();
};
render() { render() {
return ( return (
<View style={[styles.container, { backgroundColor: '#fff' }]}> <View style={[styles.container, { backgroundColor: '#fff' }]}>
<NavigationBar
style={{zIndex:4,backgroundColor: '#fff'}}
textStyle={{color:'#333'}}
title="保险"
/>
<SpringScrollView
ref={(com) => {
this.scrollView = com;
}}
onScroll={({ nativeEvent: { contentOffset: { x, y } } }) => {
//console.log('offset : x=', x, 'y=', y);
}}
styles={Styles.container}
inverted={false}
onRefresh={this._onRefresh}
//onLoading={this._onLoading}
// allLoaded={this.state.allLoaded}
refreshHeader={CommonLottieHeader}
>
{renderPolicyHeader(this.props)} {renderPolicyHeader(this.props)}
{renderPolicyTab(this.props)} {renderPolicyTab(this.props)}
<View style={{ height: 43 }} /> <View style={{ height: 43 }} />
<TabView <TabView
navigationState={this.state.type} navigationState={this.state.type}
renderScene={this._renderScene} renderScene={()=>null}
renderTabBar={this._renderTabBar} renderTabBar={this._renderTabBar}
onIndexChange={this._handleIndexChange} onIndexChange={this._handleIndexChange}
keyboardDismissMode="none"
lazy lazy
swipeEnabled={false} swipeEnabled={false}
swipeDistanceThreshold={global.SCREEN_WIDTH / 10} swipeDistanceThreshold={global.SCREEN_WIDTH / 10}
initialLayout={{ width: global.SCREEN_WIDTH }} initialLayout={{ width: global.SCREEN_WIDTH }}
/> />
<PolicyList style={{flex:1}} />
</SpringScrollView>
</View> </View>
); );
} }
......
...@@ -21,14 +21,10 @@ export function renderPolicyHeader(props) { ...@@ -21,14 +21,10 @@ export function renderPolicyHeader(props) {
locations={[0, 1]} locations={[0, 1]}
colors={['#08BFF9', '#4A98E0']} colors={['#08BFF9', '#4A98E0']}
style={[{ style={[{
height: headerHeight, height: headerHeight - tabHeight,
}]} }]}
> >
<View> <View>
<NavigationBar
style={{ backgroundColor: '#00000000' }}
title="保险"
/>
<View <View
style={{ style={{
height: 25, height: 25,
...@@ -70,10 +66,10 @@ export function renderPolicyTab(props) { ...@@ -70,10 +66,10 @@ export function renderPolicyTab(props) {
borderRadius: 10, borderRadius: 10,
position: 'absolute', position: 'absolute',
zIndex: 3, zIndex: 3,
top: headerHeight - tabHeight, top: headerHeight - tabHeight - tabHeight,
left: 15, left: 15,
right: 15, right: 15,
bottom: global.SCREEN_HEIGHT - headerHeight - tabHeight, bottom: global.SCREEN_HEIGHT - headerHeight,
elevation: 20, elevation: 20,
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowColor: '#D0DBFF', shadowColor: '#D0DBFF',
......
...@@ -105,7 +105,7 @@ export function renderGrid(props) { ...@@ -105,7 +105,7 @@ export function renderGrid(props) {
width: gridWidth - 1, width: gridWidth - 1,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginVertical: 25, marginVertical: 15,
}} }}
> >
<FastImage <FastImage
......
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { Component } from 'react';
import {
View,
} from 'react-native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { SpringScrollView } from 'react-native-spring-scrollview';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import { pop, push } from '../../BasicNavigator/actions';
import NavigationBar, { NavHeight } from '../../components/NavigationBar';
import BackButton from '../../components/BackButton';
import Picker from '../../components/Picker';
import Styles from '../../theme/Styles';
import decorator from '../../utils/decorator';
import DatePicker from '../../components/Agera-App-Base-Pickers/DatePicker';
import BasicIcon from '../../components/BasicIcon';
import Text from '../../components/Text';
import {
APPLICANT_NAME,
APPLICANT_ID_TYPE,
APPLICANT_ID,
APPLICANT_BIRTHDAY,
APPLICANT_GENDER,
APPLICANT_MOBILE,
APPLICANT_EMAIL,
INSURED_TITLE,
INSURED_NAME,
INSURED_ID_TYPE,
INSURE_YEAR_TITLE,
PICKER,
DATE_RANGE,
} from '../Agera-App-Insure/config/keys';
import {
getConfig,
} from '../Agera-App-Insure/config';
import { renderPageByConfig } from '../Agera-App-Insure';
import Button from '../../components/ThemeButton';
import SubmitButton from '../Agera-App-Insure/components/SubmitButton';
import BaseContainer from '../Agera-App-Insure/BaseContainer';
import localData from '../../utils/api/localData';
import ListItem from '../Agera-App-Insure/components/ListItem';
type Props = {
routes : Array,
popTo : () => void,
pushTo : () => void,
blueId : string,
};
function dateView(props) {
return (
<View style={{ flexDirection: 'row', paddingLeft: 10, alignItems: 'center'}}>
<BasicIcon suite="Ionicons" name="ios-calendar" size={16} color="#666" />
<Text style={{
color: '#666',
fontSize: 14,
paddingLeft: 10,
}}
>
{props.value}
</Text>
</View>
);
}
@decorator
class FillPolicyInfoPage extends Component {
constructor(props : Props) {
super(props);
this.policy = props.navigation.state.params.policy;
const { fillInfo } = getConfig(this.policy.blueId);
console.log('FillPolicyInfoPage_fillInfo', fillInfo);
const { keys, layout } = fillInfo;
this.fillInfo = fillInfo;
this.state = {
params: {
user: localData.user,
},
config: {},
keys,
};
console.log('FillPolicyInfoPage', this.state);
}
componentWillMount() : void {
const config = {};
const { params } = this.state;
const { keys, layout } = this.fillInfo;
keys.forEach((key) => {
const data = this.fillData(key, this.fillInfo[key], params);
console.log('FillPolicyInfoPage:', this.fillInfo, layout[key], key);
config[key] = { ...layout[key], ...data };
});
this.setState({
config,
});
}
fillData = (key, lay, params) => {
const User = params.user;
switch (key) {
case APPLICANT_NAME:
case INSURED_NAME:
return {
value: User.name,
};
case APPLICANT_ID_TYPE:
case INSURED_ID_TYPE:
return {
value: User.idType,
};
case APPLICANT_ID:
return {
value: User.idNo,
};
case APPLICANT_GENDER:
return {
value: User.gender,
};
case APPLICANT_BIRTHDAY:
return {
value: User.birthday,
};
case APPLICANT_MOBILE:
return {
value: User.mobile,
};
case APPLICANT_EMAIL:
return {
value: '-',
};
default:
return {};
}
}
translateDataToRules = (key, rul, params) => ({})
renderPicker = (data) => {
console.log('renderPicker:', data);
return (
<View>
<Picker
{...data}
picker={DatePicker}
value="请选择启始时间"
afterValue={dateView({ value: '0时止' })}
/>
<Picker
style={{
margin: 0,
}}
{...data}
text=""
value="请选择结束时间"
picker={DatePicker}
beforeValue={(
<View style={{justifyContent: 'center'}}>
<Text style={{
marginRight: 10,
color: '#666',
fontSize: 14,
}}
>
</Text>
</View>
)}
afterValue={dateView({ value: '24时止' })}
/>
</View>
);
}
render() {
const { config, keys } = this.state;
const { pushTo } = this.props;
console.log('config:', config, keys);
const type = 'type';
return (
<View style={Styles.container}>
<NavigationBar
left={<BackButton />}
title="太平保险"
/>
<SpringScrollView
ref={(com) => {
this.scrollView = com;
}}
onScroll={({ nativeEvent: { contentOffset: { x, y } } }) => {
//console.log('offset : x=', x, 'y=', y);
}}
styles={Styles.container}
inverted={false}
//onRefresh={this._onRefresh}
//onLoading={this._onLoading}
// allLoaded={this.state.allLoaded}
refreshHeader={CommonLottieHeader}
>
<View>
{keys.map((key, index) => (
renderPageByConfig(config[key][type], (viewType) => {
switch (viewType) {
case DATE_RANGE:
return this.renderPicker;
default:
return () => {};
}
})(config[key]) // 第二个参数可以传入自己的switch 方法根据自定义类型渲染组件
))}
</View>
<Button
title="下一步"
containerStyle={{ margin: 15 }}
onPress={() => pushTo('PaymentPage', { policy: this.policy })}
/>
</SpringScrollView>
</View>
);
}
}
function propsMapping(props) {
const { nav } = props;
return {
routes: nav ? nav.routes : [],
};
}
function actionMapping(dispatch) {
return {
popTo: compose(dispatch, pop),
pushTo: compose(dispatch, push),
};
}
export default connect(propsMapping, actionMapping)(FillPolicyInfoPage);
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { Component } from 'react';
import {
View,
} from 'react-native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { SpringScrollView } from 'react-native-spring-scrollview';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import { pop, push } from '../../BasicNavigator/actions';
import NavigationBar, { NavHeight } from '../../components/NavigationBar';
import BackButton from '../../components/BackButton';
import Picker from '../../components/Picker';
import Styles from '../../theme/Styles';
import decorator from '../../utils/decorator';
import DatePicker from '../../components/Agera-App-Base-Pickers/DatePicker';
import BasicIcon from '../../components/BasicIcon';
import Text from '../../components/Text';
import {
APPLICANT_NAME,
APPLICANT_ID_TYPE,
APPLICANT_ID,
APPLICANT_BIRTHDAY,
APPLICANT_GENDER,
APPLICANT_MOBILE,
APPLICANT_EMAIL,
INSURED_TITLE,
INSURED_NAME,
INSURED_ID_TYPE,
INSURE_YEAR_TITLE,
PICKER,
DATE_RANGE,
INSURED_PREM,
INSURED_PRODUCT,
POLICY_PROGRESS,
PAYMENT_TYPE,
} from '../Agera-App-Insure/config/keys';
import {
getConfig,
} from '../Agera-App-Insure/config';
import { renderPageByConfig } from '../Agera-App-Insure';
import Button from '../../components/ThemeButton';
import SubmitButton from '../Agera-App-Insure/components/SubmitButton';
import BaseContainer from '../Agera-App-Insure/BaseContainer';
import localData from '../../utils/api/localData';
import ListItem from '../Agera-App-Insure/components/ListItem';
import { ENV, resourceURLMapping } from "../../utils/constants";
type Props = {
routes : Array,
popTo : () => void,
pushTo : () => void,
blueId : string,
};
@decorator
class PaymentPage extends Component {
constructor(props : Props) {
super(props);
this.policy = props.navigation.state.params.policy;
const { payment } = getConfig(this.policy.blueId);
console.log('paymentPage_payment', payment);
const { keys, layout } = payment;
this.payment = payment;
this.state = {
params: {
payment: {
prem: this.policy.premTotal,
name: this.policy.productName,
},
},
config: {},
keys,
};
console.log('paymentPage', this.state);
}
componentWillMount() : void {
const config = {};
const { params } = this.state;
const { keys, layout } = this.payment;
keys.forEach((key) => {
const data = this.fillData(key, this.payment[key], params);
console.log('paymentPage:', this.payment, layout[key], key);
config[key] = { ...layout[key], ...data };
});
this.setState({
config,
});
}
fillData = (key, lay, params) => {
const Payment = params.payment;
switch (key) {
case POLICY_PROGRESS:
return {
index: 1,
};
case INSURED_PRODUCT:
return {
value: Payment.name,
};
case INSURED_PREM:
return {
value: Payment.prem,
};
case PAYMENT_TYPE:
return {
data: [{
title: '微信支付',
detail: '支持微信账号支付',
imgUrl: `${resourceURLMapping[ENV]}wxpay.png`,
},
{
title: '支付宝',
detail: '支持支付宝支付',
imgUrl: `${resourceURLMapping[ENV]}alipay.jpeg`,
}], //选项
onSelect: () => {},
selectIndex: -1,
};
default:
return {};
}
}
translateDataToRules = (key, rul, params) => ({})
render() {
const { config, keys } = this.state;
const { pushTo } = this.props;
console.log('config:', config, keys);
const type = 'type';
return (
<View style={Styles.container}>
<NavigationBar
left={<BackButton />}
title="太平保险"
/>
<SpringScrollView
ref={(com) => {
this.scrollView = com;
}}
onScroll={({ nativeEvent: { contentOffset: { x, y } } }) => {
//console.log('offset : x=', x, 'y=', y);
}}
styles={Styles.container}
inverted={false}
//onRefresh={this._onRefresh}
//onLoading={this._onLoading}
// allLoaded={this.state.allLoaded}
refreshHeader={CommonLottieHeader}
>
<View>
{keys.map((key, index) => (
renderPageByConfig(config[key][type])(config[key]) // 第二个参数可以传入自己的switch 方法根据自定义类型渲染组件
))}
</View>
<Button
title="确认支付"
containerStyle={{ margin: 15 }}
onPress={() => {pushTo('PolicyResultPage')}}
/>
</SpringScrollView>
</View>
);
}
}
function propsMapping(props) {
const { nav } = props;
return {
routes: nav ? nav.routes : [],
};
}
function actionMapping(dispatch) {
return {
popTo: compose(dispatch, pop),
pushTo: compose(dispatch, push),
};
}
export default connect(propsMapping, actionMapping)(PaymentPage);
/**
* @flow
* Created by shiyunjie on 2018/8/4.
*/
import React, { Component } from 'react';
import {
View,
Text,
Image,
BackHandler,
} from 'react-native';
import { connect } from 'react-redux';
import { compose } from 'redux';
import Button from '../../components/ThemeButton';
import NavigationBar, { NavHeight } from '../../components/NavigationBar';
import { pop, push } from '../../BasicNavigator/actions';
import StyleSheet from '../../utils/StyleSheet';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
text: { fontSize: 16, color: '#666' },
});
type Props = {
pushTo : () => void,
pop : () => void,
activeFirst : () => void,
routes : Array,
success : boolean,
flag : string,
desc : string,
reportNo : string,
popAppointPage? : () => void
}
type State = {
date : string,
showModal : boolean,
address : string,
area : string,
nature : string,
hospitals : Array,
}
class ResultPage extends Component {
state : State
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.onBackPressed);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.onBackPressed);
}
onBackPressed = () => {
this.popToHome()
return true;
}
popToHome = () => {
const { popTo } = this.props;
popTo('TabPage')
}
render() {
return (
<View style={styles.container}>
<NavigationBar
title="太平保险"
left={<View />}
/>
<View
style={{
flex: 1,
flexDirection: 'column',
alignItems: 'center',
}}
>
<View
style={{
width: 84,
height: 84,
justifyContent: 'center',
alignItems: 'center',
marginTop: 32,
}}
>
<Image source={require('./img/success.png')} />
</View>
<Text
style={[
styles.text,
{
fontSize: 18,
marginTop: 24,
fontWeight: 'bold',
}]}
>
支付成功
</Text>
<View style={{
height: 94,
backgroundColor: '#F9FAFB',
marginHorizontal: 15,
justifyContent:'center',
alignItems:'center',
paddingHorizontal: 70,
}}>
<Text
style={{
textAlign:'center',
alignItems:'center',
color:'#666',
fontSize: 14,
lineHeight:19,
}}
>
您的保单已经发送到邮箱:
1211447311@163.com,请注意查收
</Text>
</View>
<Button
title="返回"
containerStyle={{ margin: 15, width: global.SCREEN_WIDTH - 30, height: 50 }}
onPress={this.popToHome}
/>
</View>
</View>
);
}
}
function propsMapping(props) {
const { nav } = props;
return {
routes: nav ? nav.routes : [],
};
}
function actionMapping(dispatch) {
return {
popTo: compose(dispatch, pop),
pushTo: compose(dispatch, push),
};
}
export default connect(propsMapping, actionMapping)(ResultPage);
...@@ -14,126 +14,228 @@ import { compose } from 'redux'; ...@@ -14,126 +14,228 @@ import { compose } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SpringScrollView } from 'react-native-spring-scrollview'; import { SpringScrollView } from 'react-native-spring-scrollview';
import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader'; import { CommonLottieHeader } from 'react-native-spring-scrollview/Customize/CommonLottieHeader';
import { pop } from '../../BasicNavigator/actions'; import { pop, push } from '../../BasicNavigator/actions';
import NavigationBar, { NavHeight } from '../../components/NavigationBar'; import NavigationBar, { NavHeight } from '../../components/NavigationBar';
import BackButton from '../../components/BackButton'; import BackButton from '../../components/BackButton';
import Picker from '../../components/Picker';
import Styles from '../../theme/Styles'; import Styles from '../../theme/Styles';
import decorator from '../../utils/decorator'; import decorator from '../../utils/decorator';
import Text from '../../components/Text' import { trailPolicyAPI } from '../InsuranceList/InsuranceAPI';
import Text from '../../components/Text';
import { import {
BANNER, CHOOSE, BaseConfig, LIST_ITEM, TITLE, PRODUCT, SERVICE, BANNER,
PROCESS,
PAGE_TITLE,
BUTTON_GROUP,
LIST_ITEM,
CELL,
CHECKBOX,
PICKER,
DATE_PICKER,
IMAGE,
TAB,
MULTINPUT,
TEXTAREA,
PRODUCT_TITLE,
PRODUCT_BANNER,
PLAN_INFO,
PREM_GROUP,
PRODUCT_IMAGE,
SERVICE_IMAGE,
NOTICE_IMAGE,
INSURE_YEAR,
MULT,
JIACHENG,
AIFEIXIANG,
XUESHENGYIWAI,
} from '../Agera-App-Insure/config/keys';
import {
getConfig,
} from '../Agera-App-Insure/config'; } from '../Agera-App-Insure/config';
import { renderPageByConfig } from '../Agera-App-Insure'; import { renderPageByConfig } from '../Agera-App-Insure';
import Button from '../../components/ThemeButton'; import SubmitButton from '../Agera-App-Insure/components/SubmitButton';
type Props = { type Props = {
routes: Array, routes : Array,
popTo: () => void, popTo : () => void,
blueId: string, pushTo : () => void,
blueId : string,
}; };
@decorator @decorator
class ProductDetailPage extends Component { class ProductDetailPage extends Component {
constructor(props:Props) { constructor(props : Props) {
super(props); super(props);
/* { this.blueId = props.navigation.state.params.blueId;
blueId: '200000156', const { productDetail } = getConfig(this.blueId);
company: 'PROPERTY',
iconUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/03/1babf50701374e79b2d0e1758d7e6d42.png',
detail: {
name: '“驾乘至尊宝”驾乘人员人身意外综合保险',
description: '不限被保险人、最高50万保障',
detailsPageWxshareUrl: '',
guaranteeContent: '[{"text":[{"name":"驾乘意外身故、残疾","value":"25万元/份"},{"name":"驾乘意外伤害医疗","value":"1万元/份"}]},{"text":[{"name":"驾乘意外身故、残疾","value":"50万元/份"},{"name":"驾乘意外伤害医疗","value":"3万元/份"}]}]',
bannerIconUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201901/01/dbf8648cdca04b20af889f07e3938b5f.png',
productIntroductionUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/1755eb7f49934157b6fc65f09ef9015a.png',
serviceProcessUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/2c0eb0145bf1498bb677897a8b03b179.png',
importantNotificationUrl: '',
tagNameList: [
'1-75周岁',
],
},
}*/
const pageConfig = BaseConfig[props.navigation.state.params.blueId];
const { params } = props.navigation.state; const { params } = props.navigation.state;
pageConfig.layout.forEach((lay, index) => { const { keys, layout } = productDetail;
console.log(lay); this.productDetail = productDetail;
const data = this.translateDataToConfig(lay, params); this.policy = {
pageConfig.layout[index] = { ...lay, ...data }; mult: '',
}); prem: 99,
premTotal: 99,
blueId: this.blueId,
productName: params.detail.name,
};
this.state = { this.state = {
params, params,
config: pageConfig, config: {},
keys,
}; };
console.log('ProductDetailPage', this.state); console.log('ProductDetailPage', this.state);
} }
translateDataToConfig = (lay, params) => { componentWillMount() : void {
const config = {};
const { params } = this.state;
const { keys, layout } = this.productDetail;
keys.forEach((key) => {
const data = this.fillData(key, this.productDetail[key], params);
console.log('productDetail:', this.productDetail, layout[key], key);
config[key] = { ...layout[key], ...data };
});
this.setState({
config,
});
}
fillData = (key, lay, params) => {
const { const {
name, description, tagNameList, bannerIconUrl, guaranteeContent, name, description, tagNameList, bannerIconUrl, guaranteeContent,
productIntroductionUrl, serviceProcessUrl } = params.detail; productIntroductionUrl, serviceProcessUrl,
console.log('guaranteeContent',guaranteeContent) } = params.detail;
switch (lay.type) { console.log('productIntroductionUrl', productIntroductionUrl);
case BANNER: switch (key) {
case PRODUCT_BANNER:
return { return {
uri: bannerIconUrl, uri: bannerIconUrl.url,
width: global.SCREEN_WIDTH, width: global.SCREEN_WIDTH,
height: global.SCREEN_WIDTH * 180 / 375, height: global.SCREEN_WIDTH * bannerIconUrl.height / bannerIconUrl.width,
}; };
case TITLE: case PREM_GROUP:
return {
onSelect: (index) => {
console.log('BUTTON_GROUP,onSelect', index);
switch (index) {
case 0:
this.policy.prem = 99;
break;
case 1:
this.policy.prem = 199;
break;
default:
break;
}
this.trialPolicy();
},
selectIndex: 0,
};
case PRODUCT_TITLE:
return { return {
h1: name, // title需要装填数据 主标题 h1: name, // title需要装填数据 主标题
h2: description, // title需要装填数据 副标题 h2: description, // title需要装填数据 副标题
label: tagNameList, // 多个展示标签 label: tagNameList, // 多个展示标签
labelColor: '#cf4100', //颜色 labelColor: '#cf4100', //颜色
}; };
case LIST_ITEM: case PLAN_INFO:
return { return {
title: { label: {
icon: '', //左侧icon icon: '', //左侧icon
h1: '', //主标题 h1: '保障计划', //主标题
rightIcon: '', //右侧icon rightIcon: '', //右侧icon
rightText: '', //右侧文字 rightText: '查看保障详情', //右侧文字
onPress: () => {}, //点击事件 onPress: () => {
}, //点击事件
}, },
data: JSON.parse(guaranteeContent), data: JSON.parse(guaranteeContent),
}; };
case CHOOSE: case INSURE_YEAR:
return {};
case MULT:
return {
onChangeText: (value) => {
console.log('ProductDetailPage_onChangeText', value);
this.policy.mult = value;
console.log(this.policy.mult);
},
onEndEditing: () => {
this.trialPolicy();
},
value: this.state.mult,
};
case NOTICE_IMAGE:
return {}; return {};
case PRODUCT: case PRODUCT_IMAGE:
return { return {
uri: productIntroductionUrl, uri: productIntroductionUrl.url,
width: global.SCREEN_WIDTH, width: global.SCREEN_WIDTH,
}; height: global.SCREEN_WIDTH * productIntroductionUrl.height / productIntroductionUrl.width,
case SERVICE: label: {
return { icon: '', //左侧icon
uri: serviceProcessUrl, h1: '特色产品', //主标题
width: global.SCREEN_WIDTH, rightIcon: '', //右侧icon
}; rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
};
case SERVICE_IMAGE:
return {
uri: serviceProcessUrl.url,
width: global.SCREEN_WIDTH,
height: global.SCREEN_WIDTH * serviceProcessUrl.height / serviceProcessUrl.width,
label: {
icon: '', //左侧icon
h1: '理赔流程', //主标题
rightIcon: '', //右侧icon
rightText: '', //右侧文字
onPress: () => {
}, //点击事件
},
};
default: default:
return {}; return {};
} }
} }
translateDataToRules = (lay, rul, params) => { translateDataToRules = (key, rul, params) => {
switch (lay.type) { switch (key) {
case BANNER: case BANNER:
return {}; return {};
case TITLE: case PAGE_TITLE:
return {}; return {};
case LIST_ITEM: case LIST_ITEM:
return {}; return {};
case CHOOSE: case PICKER:
return {}; return {};
default: default:
return {}; return {};
} }
} }
trialPolicy = async () => {
const prem = await trailPolicyAPI(this.policy);
console.log('trialPolicy:', prem);
this.policy.premTotal = prem;
this.submit.setPrem(prem);
console.log('this.policy:', this.policy);
}
renderPicker = (data) => {
console.log('renderPicker:', data);
return (
<Picker {...data} />
);
}
render() { render() {
const { config } = this.state; const { config, keys } = this.state;
const { pushTo } = this.props;
console.log('config:', config, keys);
const type = 'type';
return ( return (
<View style={Styles.container}> <View style={Styles.container}>
<NavigationBar <NavigationBar
...@@ -141,7 +243,12 @@ class ProductDetailPage extends Component { ...@@ -141,7 +243,12 @@ class ProductDetailPage extends Component {
title="太平保险" title="太平保险"
/> />
<SpringScrollView <SpringScrollView
ref={com => (this.scrollView = com)} ref={(com) => {
this.scrollView = com;
}}
onScroll={({ nativeEvent: { contentOffset: { x, y } } }) => {
//console.log('offset : x=', x, 'y=', y);
}}
styles={Styles.container} styles={Styles.container}
inverted={false} inverted={false}
//onRefresh={this._onRefresh} //onRefresh={this._onRefresh}
...@@ -150,21 +257,25 @@ class ProductDetailPage extends Component { ...@@ -150,21 +257,25 @@ class ProductDetailPage extends Component {
refreshHeader={CommonLottieHeader} refreshHeader={CommonLottieHeader}
//loadingFooter={CommonLottieFooter} //loadingFooter={CommonLottieFooter}
> >
{config.layout.map((lay, index) => ( <View>
renderPageByConfig(lay)(lay) {keys.map((key, index) => (
))} renderPageByConfig(config[key][type], (viewType) => {
<View style={{flexDirection:'row',height:44,alignItems: 'stretch'}}> switch (viewType) {
<View style={{width:global.SCREEN_WIDTH * 150 / 375,justifyContent:'center',alignItems:'center',}}> case PICKER:
<Text style={{color:'#666', fontSize: 16}}> return this.renderPicker;
金额 default:
<Text style={{color:'#FF4656'}}> 125</Text> return () => {};
</Text> }
</View> })(config[key]) // 第二个参数可以传入自己的switch 方法根据自定义类型渲染组件
<Button ))}
title="立即投保"
containerStyle={{ width:global.SCREEN_WIDTH * 225 /375 }}
/>
</View> </View>
<SubmitButton
ref={(com) => {
this.submit = com;
}}
value={this.policy.premTotal}
onPress={() => pushTo('FillPolicyInfoPage', { policy: this.policy })}
/>
</SpringScrollView> </SpringScrollView>
</View> </View>
...@@ -178,9 +289,11 @@ function propsMapping(props) { ...@@ -178,9 +289,11 @@ function propsMapping(props) {
routes: nav ? nav.routes : [], routes: nav ? nav.routes : [],
}; };
} }
function actionMapping(dispatch) { function actionMapping(dispatch) {
return { return {
popTo: compose(dispatch, pop), popTo: compose(dispatch, pop),
pushTo: compose(dispatch, push),
}; };
} }
......
...@@ -15,6 +15,7 @@ export const APICodeToUrl = { ...@@ -15,6 +15,7 @@ export const APICodeToUrl = {
'001001': '/sysapp/getPolicyList', // 获取我的保单 '001001': '/sysapp/getPolicyList', // 获取我的保单
'001002': '/sysapp/getProductList', // 获取保险产品 '001002': '/sysapp/getProductList', // 获取保险产品
'001003': '/sysapp/trailPolicy', // 试算
//实时贷请求 //实时贷请求
'06001': '/lifems/loan/checkLoanLimit', // 获取保单列表,额度 '06001': '/lifems/loan/checkLoanLimit', // 获取保单列表,额度
......
// @flow // @flow
export default { export default {
// 本地假数据 // 本地假数据
user: [{ user: {
uid: 'u0000001', uid: 'u0000001',
name: '江渔剑', name: '江渔剑',
userName: '18588880005', userName: '18588880005',
pwd: '123456', pwd: '123456',
}], idType: '身份证',
idNo: '301980********5316',
gender: '男',
birthday: '1979-01-01',
mobile: '17900698956',
},
policy: [ policy: [
{ {
uid: 'u0000001', uid: 'u0000001',
...@@ -64,10 +69,22 @@ export default { ...@@ -64,10 +69,22 @@ export default {
description: '不限被保险人、最高50万保障', description: '不限被保险人、最高50万保障',
detailsPageWxshareUrl: '', detailsPageWxshareUrl: '',
guaranteeContent: '[{"text":[{"name":"驾乘意外身故、残疾","value":"25万元/份"},{"name":"驾乘意外伤害医疗","value":"1万元/份"}]},{"text":[{"name":"驾乘意外身故、残疾","value":"50万元/份"},{"name":"驾乘意外伤害医疗","value":"3万元/份"}]}]', guaranteeContent: '[{"text":[{"name":"驾乘意外身故、残疾","value":"25万元/份"},{"name":"驾乘意外伤害医疗","value":"1万元/份"}]},{"text":[{"name":"驾乘意外身故、残疾","value":"50万元/份"},{"name":"驾乘意外伤害医疗","value":"3万元/份"}]}]',
bannerIconUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201901/01/dbf8648cdca04b20af889f07e3938b5f.png', bannerIconUrl: {
productIntroductionUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/1755eb7f49934157b6fc65f09ef9015a.png', height: '180',
serviceProcessUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/2c0eb0145bf1498bb677897a8b03b179.png', width: '375',
importantNotificationUrl: '', url: 'https://ecustomer.tp95589.com/static/insurance_img/201901/01/dbf8648cdca04b20af889f07e3938b5f.png',
},
productIntroductionUrl: {
width: '204',
height: '667',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/1755eb7f49934157b6fc65f09ef9015a.png',
},
serviceProcessUrl: {
width: '377',
height: '539',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/11/2c0eb0145bf1498bb677897a8b03b179.png',
},
importantNotificationUrl: {},
tagNameList: [ tagNameList: [
'1-75周岁', '1-75周岁',
], ],
...@@ -82,10 +99,26 @@ export default { ...@@ -82,10 +99,26 @@ export default {
description: '3.99元震撼低价、全年100万元保障', description: '3.99元震撼低价、全年100万元保障',
detailsPageWxshareUrl: '', detailsPageWxshareUrl: '',
guaranteeContent: '[{"text":[{"name":"航空意外身故、伤残","value":"100万元/份"}]}]', guaranteeContent: '[{"text":[{"name":"航空意外身故、伤残","value":"100万元/份"}]}]',
bannerIconUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201901/01/340dc870750b41e492ac9ee88b5aae2a.png', bannerIconUrl: {
productIntroductionUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/3d96dd2f2a6d4e579a53d4ee4bdd73cb.png', height: '180',
serviceProcessUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/be5dfbf9afc74ef99fdc39bef66ef292.png', width: '375',
importantNotificationUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/438000682508403caf62a15878c9416d.png', url: 'https://ecustomer.tp95589.com/static/insurance_img/201901/01/340dc870750b41e492ac9ee88b5aae2a.png',
},
productIntroductionUrl: {
width: '310',
height: '667',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/3d96dd2f2a6d4e579a53d4ee4bdd73cb.png',
},
serviceProcessUrl: {
width: '750',
height: '755',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/be5dfbf9afc74ef99fdc39bef66ef292.png',
},
importantNotificationUrl: {
width: '374',
height: '553',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/438000682508403caf62a15878c9416d.png',
},
tagNameList: [ tagNameList: [
'超低价', '超低价',
'0-80周岁', '0-80周岁',
...@@ -101,10 +134,27 @@ export default { ...@@ -101,10 +134,27 @@ export default {
description: '疾病住院、意外医疗多重保障,最高享6万元住院医疗', description: '疾病住院、意外医疗多重保障,最高享6万元住院医疗',
detailsPageWxshareUrl: '', detailsPageWxshareUrl: '',
guaranteeContent: '[{"text": [{"name":"住院医疗", "value":"6万元/份"},{"name":"意外医疗","value":"5000元/份"},{"name":"意外身故","value":"5万元/份"},{"name":"意外残疾","value":"5万元/份"}]}]', guaranteeContent: '[{"text": [{"name":"住院医疗", "value":"6万元/份"},{"name":"意外医疗","value":"5000元/份"},{"name":"意外身故","value":"5万元/份"},{"name":"意外残疾","value":"5万元/份"}]}]',
bannerIconUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/8cffbf808b584ff6b0a4fa3082c151d2.png', bannerIconUrl: {
productIntroductionUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/500622b8641c46eeadf0a8cf6460ba04.png', height: '180',
serviceProcessUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/b51ca9e95aab49cc8bb44fe8eda16583.png', width: '375',
importantNotificationUrl: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/97de2b11f574471facbe4577b97c3879.png', url: 'https://ecustomer.tp95589.com/static/insurance_img/201906/12/8cffbf808b584ff6b0a4fa3082c151d2.png',
},
productIntroductionUrl: {
width: '374',
height: '1138',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/500622b8641c46eeadf0a8cf6460ba04.png',
},
serviceProcessUrl:
{
width: '300',
height: '580',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/b51ca9e95aab49cc8bb44fe8eda16583.png',
},
importantNotificationUrl: {
width: '375',
height: '600',
url: 'https://ecustomer.tp95589.com/static/insurance_img/201905/06/97de2b11f574471facbe4577b97c3879.png',
},
tagNameList: [ tagNameList: [
'5-16周岁', '5-16周岁',
'学生专享', '学生专享',
......
...@@ -6,12 +6,11 @@ import localData from './localData'; ...@@ -6,12 +6,11 @@ import localData from './localData';
// 获取用户保单列表 // 获取用户保单列表
function get001001(data) { function get001001(param) {
const { policy } = localData; const { policy } = localData;
const param = JSON.parse(data);
const result = []; const result = [];
console.log('policy:',policy) console.log('policy:', policy);
console.log('data:',param) console.log('data:', param);
policy.forEach((item) => { policy.forEach((item) => {
if (item.uid === param.uid) { if (item.uid === param.uid) {
result.push(item); result.push(item);
...@@ -26,24 +25,36 @@ function get001002() { ...@@ -26,24 +25,36 @@ function get001002() {
return product; return product;
} }
function get001003(param) {
const { mult, prem } = param;
console.log('get001003:', mult, prem, param)
const reg = /^[1-9]\d*$|^0$/
let premTotal = Number(prem);
if (reg.test(mult)) {
premTotal = Number(prem) * mult;
}
return premTotal;
}
const mockMap = { const mockMap = {
'001001': get001001, '001001': get001001,
'001002': get001002, '001002': get001002,
'001003': get001003,
}; };
export default function getMockData(url, config, callback) { export default function getMockData(url, config, callback) {
console.log('getMockData:', url, config); console.log('getMockData:', url, config);
const time = RandomNumBoth(1, 3); const time = RandomNumBoth(0, 2);
// 随机暂停 1 - 3 // 随机暂停 0.5 - 2.5
console.log('模拟请求:', time, '秒'); console.log('模拟请求:', time, '秒');
setTimeout( setTimeout(
() => { () => {
const result = mockMap[config.APICode](config.data) const result = mockMap[config.APICode](JSON.parse(config.data || JSON.stringify({})));
console.log('result:',result) console.log('result:', result);
callback(result); callback(result);
}, },
time * 1000, (time + 0.5) * 1000,
); );
} }
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