Commit e336c830 authored by shiyunjie's avatar shiyunjie

Merge branch 'shiyunjie'

# Conflicts: # ios/BasicApp.xcodeproj/project.pbxproj
parents 2827e5a2 db9a2334
......@@ -7,7 +7,8 @@
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import {Platform, StyleSheet, View} from 'react-native';
import Text from './src/components/Text';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
......@@ -16,19 +17,6 @@ const instructions = Platform.select({
'Shake or press menu button for dev menu',
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
......@@ -47,3 +35,18 @@ const styles = StyleSheet.create({
marginBottom: 5,
},
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}
# Agera-cli 移动端项目模版
版本: 2019-6-1
最近更新: 2019-7-12
## 前言
开发Agera的目的就是追求"快"。
有人会说复制一个项目,或复制文件、代码也很快,Agera有何不同?
Agera的快是建立在标准化之上的,提前准备好了组件、示例,直接用就好了。修改也会很快,也为远程协作提供便利。
Agera 一期,要建立稳定的演示环境,业务场景(在线投保,培训,绩效查询,行销工具)
## 开发日志
### 已完成
命令行工具
#### git管理模版
创建App
新建页面
#### APP模版
redux-persist
navigation redux
theme
路由跳转传参
屏幕宽度适配
状态栏高度适配
App字体大小控制
热更新升级
组件示例
mock
validate
decorator
HomePage
UserPage
ServicePage
InsureList
Insure config
ProductDetail
### 待开发
ReceiptPage
PaymentPage
OrderList
TrainPage
Achievement
Setting
Login
原生webview
原生启动图
原生相册、拍照
原生ocr
原生人脸识别
原生签名
嵌入太平头条、健康讲堂、祝福卡
导入 邀请有礼、健步行、周周乐
## 依赖库
......@@ -24,7 +118,7 @@
## 初始化
## 如何使用
使用 agera-cli buildApp 初始化项目后,
安装依赖
......@@ -56,7 +150,7 @@ Android studio打开android目录下 build.gradle
路由导航
主题切换
### 原生
### 原生组件
原生组件使用示例:
......@@ -70,7 +164,7 @@ Android studio打开android目录下 build.gradle
启动页
webview
### JS
### JS组件
JS组件使用示例:
按钮
......
......@@ -102,7 +102,7 @@ android {
}
defaultConfig {
applicationId "{{packageName}}"
applicationId "com.basicapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
......@@ -116,11 +116,27 @@ android {
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
releaseConfig {
storeFile file('../keystores/agera-release-key.jks')
keyAlias 'taiping-alias'
keyPassword '123456'
storePassword '123456'
// v1SigningEnabled true
// v2SigningEnabled true
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
signingConfig signingConfigs.releaseConfig
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
debug {
minifyEnabled false
signingConfig signingConfigs.releaseConfig
debuggable true
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
......@@ -138,10 +154,15 @@ android {
}
dependencies {
implementation project(':react-native-reanimated')
implementation project(':@react-native-community_async-storage')
implementation project(':react-native-svg')
implementation project(':react-native-fast-image')
implementation project(':react-native-gesture-handler')
implementation project(':lottie-react-native')
implementation project(':react-native-linear-gradient')
implementation project(':react-native-device-info')
implementation project(':react-native-spring-scrollview')
implementation project(':react-native-gesture-handler')
implementation project(':react-native-vector-icons')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
......
package com.basicapp;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
//导入的包
public class MainActivity extends ReactActivity {
......@@ -12,4 +16,15 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "BasicApp";
}
//方法
// @Override
// protected ReactActivityDelegate createReactActivityDelegate() {
// return new ReactActivityDelegate(this, getMainComponentName()) {
// @Override
// protected ReactRootView createRootView() {
// return new RNGestureHandlerEnabledRootView(MainActivity.this);
// }
// };
// }
}
......@@ -3,6 +3,12 @@ package com.basicapp;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.swmansion.reanimated.ReanimatedPackage;
import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
import com.horcrux.svg.SvgPackage;
import com.dylanvann.fastimage.FastImageViewPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.airbnb.android.react.lottie.LottiePackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.bolan9999.SpringScrollViewPackage;
......@@ -27,6 +33,12 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReanimatedPackage(),
new AsyncStoragePackage(),
new SvgPackage(),
new FastImageViewPackage(),
new RNGestureHandlerPackage(),
new LottiePackage(),
new LinearGradientPackage(),
new RNDeviceInfo(),
new SpringScrollViewPackage(),
......
rootProject.name = 'BasicApp'
include ':react-native-reanimated'
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
include ':@react-native-community_async-storage'
project(':@react-native-community_async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-fast-image'
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':lottie-react-native'
project(':lottie-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/lottie-react-native/src/android')
include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-device-info'
......@@ -7,7 +19,5 @@ include ':react-native-spring-scrollview'
project(':react-native-spring-scrollview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-spring-scrollview/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':app'
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/transform-runtime", {
"helpers": true,
"regenerator": false
}]
]
};
# 投保功能描述
## 简述
投保功能包含三部分
* 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,绑定事件。
This diff is collapsed.
......@@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<string>zh_CN</string>
<key>CFBundleDisplayName</key>
<string>{{appName}}</string>
<string>Agera</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
......@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......@@ -38,7 +38,7 @@
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<string>测试权限</string>
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
......@@ -66,8 +66,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
var hello = require('./node_modules/react-native-update-mutlirn-hg/local-cli')
hello.run()
......@@ -9,21 +9,30 @@
"postinstall-devTools": "remotedev-debugger --hostname localhost --port 5678 --injectserver"
},
"dependencies": {
"@react-native-community/async-storage": "^1.4.0",
"@react-native-community/async-storage": "^1.5.0",
"axios": "^0.18.0",
"lodash": "^4.17.11",
"lottie-react-native": "^2.6.1",
"moment": "^2.24.0",
"react": "16.8.3",
"react-native": "0.59.5",
"react-native-device-info": "^2.1.1",
"react-native": "^0.59.5",
"react-native-device-info": "^2.1.3",
"react-native-elements": "^1.1.0",
"react-native-gesture-handler": "^1.1.0",
"react-native-fast-image": "^6.0.3",
"react-native-gesture-handler": "^1.2.2",
"react-native-image-header-scroll-view": "^0.10.3",
"react-native-largelist-v3": "^3.0.14",
"react-native-linear-gradient": "^2.5.4",
"react-native-modal": "^11.1.0",
"react-native-reanimated": "^1.1.0",
"react-native-root-toast": "^3.1.2",
"react-native-spring-scrollview": "^2.0.22",
"react-native-update-mutlirn-hg": "^3.1.1",
"react-native-super-grid": "^3.0.7",
"react-native-svg": "^9.5.1",
"react-native-swiper": "^1.5.14",
"react-native-tab-view": "^2.7.1",
"react-native-vector-icons": "^6.4.2",
"react-navigation": "^3.9.1",
"react-navigation": "^3.11.0",
"react-navigation-redux-helpers": "^3.0.2",
"react-redux": "^7.0.3",
"redux": "^4.0.1",
......@@ -32,6 +41,7 @@
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/runtime": "^7.4.3",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.7.1",
......
......@@ -7,9 +7,11 @@ export const PUSH = 'push';
export const POP = 'pop';
export const TAB = 'tab';
// action creator
export const push = (pageName: String = '') => ({
export const push = (pageName: String = '', extProps) => ({
type: PUSH,
pageName,
extProps,
message: '暂未开放',
});
export const pop = (pageName: String = '') => ({
type: POP,
......
......@@ -6,7 +6,6 @@ import React, { Component } from 'react';
import {
createReduxContainer,
} from 'react-navigation-redux-helpers';
import { BackHandler } from 'react-native';
import { connect } from 'react-redux';
import NavigationStack from './navigationStack';
......@@ -22,33 +21,12 @@ const mapStateToProps = state => ({
const ReduxNavigation = connect(mapStateToProps)(AppNavigation);
class Navigation extends Component {
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
}
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>
);
}
function Navigation() {
return (
<StoreContext.Provider value={{ theme: global.theme }}>
<ReduxNavigation />
</StoreContext.Provider>
);
}
......
......@@ -2,32 +2,59 @@
* @flow
*/
import {
NativeAppEventEmitter,
Animated,
Easing,
Platform,
} from 'react-native';
import {
createBottomTabNavigator,
createStackNavigator,
createAppContainer,
} from 'react-navigation';
import JSPage from '../jsComponent/JSPage';
import FramePage from '../home/FramePage';
import ThemePage from '../changeThemePage';
import NativeComponentList from '../home/NativeComponentList';
import JSComponentList from '../home/JSComponentList';
import NativePage from '../nativeComponent/NativePage';
import JSPage from '../pages/navigationDemo/JSPage';
import HomePage from '../pages/home/HomePage';
import PolicyPage from '../pages/home/PolicyPage';
import ThemePage from '../pages/changeThemePage';
import ServicePage from '../pages/home/ServicePage';
import UserPage from '../pages/home/UserPage';
import SectionListDemo from '../pages/sectionListDemo';
import NativePage from '../pages/navigationDemo/NativePage';
import InsuranceList from '../pages/InsuranceList';
import ButtonPage from '../pages/buttonDemo';
import WaterList from '../pages/waterListDemo';
import AvatarPage from '../pages/avatarDemo';
import BadgePage from '../pages/badgeDemo';
import CheckBoxPage from '../pages/checkBoxDemo';
import RatingPage from '../pages/ratingDemo';
import SliderPage from '../pages/sliderDemo';
import TooltipPage from '../pages/tooltipDemo';
import InsuranceDetail from '../pages/InsuranceList/InsuranceDetail';
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 { TabOptions } from '../components/TabOptions';
const TabPage = createBottomTabNavigator(
{
HomePage: {
screen: FramePage,
navigationOptions: () => TabOptions('框架', 'Ionicons', 'ios-home'),
screen: HomePage,
navigationOptions: () => TabOptions('首页', 'Ionicons', 'ios-home'),
},
PolicyPage: {
screen: PolicyPage,
navigationOptions: () => TabOptions('保险', 'Ionicons', 'ios-umbrella'),
},
MainPage: {
screen: NativeComponentList,
navigationOptions: () => TabOptions('原生', 'Ionicons', 'ios-bookmarks'),
screen: ServicePage,
navigationOptions: () => TabOptions('服务', 'Ionicons', 'ios-apps'),
},
ServicePage: {
screen: JSComponentList,
navigationOptions: () => TabOptions('JS', 'Ionicons', 'logo-facebook'),
screen: UserPage,
navigationOptions: () => TabOptions('我的', 'Ionicons', 'ios-person', '1'),
},
},
{
......@@ -41,7 +68,8 @@ const TabPage = createBottomTabNavigator(
height: 49,
},
safeAreaInset: {
bottom: 'always',
//bottom: 'always',
bottom: 'never',
top: 'never',
},
showLabel: false,
......@@ -81,9 +109,86 @@ const navigator = createStackNavigator({
ThemePage: {
screen: ThemePage,
},
SectionList: {
screen: SectionListDemo,
},
ButtonPage: {
screen: ButtonPage,
},
WaterList: {
screen: WaterList,
},
AvatarPage: {
screen: AvatarPage,
},
BadgePage: {
screen: BadgePage,
},
CheckBoxPage: {
screen: CheckBoxPage,
},
RatingPage: {
screen: RatingPage,
},
SliderPage: {
screen: SliderPage,
},
TooltipPage: {
screen: TooltipPage,
},
InsuranceList: {
screen: InsuranceList,
},
InsuranceDetail: {
screen: InsuranceDetail,
},
ProductDetailPage: {
screen: ProductDetailPage,
},
FillPolicyInfoPage: {
screen: FillPolicyInfoPage,
},
PaymentPage: {
screen: PaymentPage,
},
PolicyResultPage: {
screen: PolicyResultPage,
},
}, {
initialRouteName,
mode: 'card',
headerMode: 'none',
defaultNavigationOptions: {
gesturesEnabled: false,
gestureDirection: 'default', // inverted
},
disableKeyboardHandling: false,
cardShadowEnabled: true,
cardOverlayEnabled: true,
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;
......
......@@ -5,22 +5,24 @@
import { NavigationActions } from 'react-navigation';
import NavigationStack from './navigationStack';
import { initialRouteName } from '../utils/constants';
import toast from '../components/Toast';
import { PUSH, POP, TAB } from './actions';
const ActionForTab = NavigationStack.router.getActionForPathAndParams(initialRouteName);
const ActionForNativePage = pageName => NavigationStack.router.getActionForPathAndParams(pageName);
const ActionForNativePage = action => NavigationStack.router
.getActionForPathAndParams(action.pageName, action.extProps);
const initialState = NavigationStack.router.getStateForAction(ActionForTab);
const nav = (state = initialState, action) => {
console.log('nav:',action)
switch (action.type) {
case PUSH:
return NavigationStack.router.getStateForAction(ActionForNativePage(action.pageName), state);
return NavigationStack.router.getStateForAction(ActionForNativePage(action), state);
case POP:
if (action.pageName) {
return NavigationStack.router.getStateForAction(ActionForNativePage(action.pageName), state);
return NavigationStack.router.getStateForAction(ActionForNativePage(action), state);
}
return NavigationStack.router.getStateForAction(NavigationActions.back(), state);
case TAB:
......
......@@ -3,12 +3,13 @@
*/
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 { PersistGate } from 'redux-persist/es/integration/react';
import configureStore from './basicStore';
import BasicNavigator from './BasicNavigator';
import StyleSheet from './utils/StyleSheet';
import { pop } from './BasicNavigator/actions';
const styles = StyleSheet.create({
container: {
......@@ -23,12 +24,13 @@ class MainApp extends Component {
//if (AssetsModule.dismisToast) {
// AssetsModule.dismisToast()
//}
BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
AppState.addEventListener('change', this.handleAppChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppChange);
BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
//if (AssetsModule.dismisToast) {
// AssetsModule.dismisToast()
//}
......@@ -38,14 +40,33 @@ class MainApp extends Component {
//console.log('handleAppChange:')
if (currentAppState === 'active') {
//checkForUpdate(true)
console.log('handleAppChange','active');
}
}
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() {
return (
<Provider store={store}>
<PersistGate loading={<Text>Loading...</Text>} persistor={persistor}>
<PersistGate loading={<Text>MyLoading...</Text>} persistor={persistor}>
<SafeAreaView style={styles.container}>
<BasicNavigator param={this.props} />
</SafeAreaView>
......
......@@ -4,6 +4,7 @@
//import _ from 'lodash'
import devTools from 'remote-redux-devtools';
import { Platform, AsyncStorage } from 'react-native';
//import AsyncStorage from '@react-native-community/async-storage';
import {
persistStore,
persistCombineReducers,
......
/**@flow
* Created by shiyunjie on 2018/8/18.
*/
import React, { Component, Element } from 'react';
import {
StyleSheet,
Text,
View,
TouchableWithoutFeedback,
Animated,
Dimensions,
} from 'react-native';
const screenWidth = Dimensions.get('window').width
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
overflow: 'hidden',
marginBottom: 12,
},
titleContainer: {
flexDirection: 'row',
width: screenWidth,
},
button: {
width: 40,
height: 80,
justifyContent: 'center',
paddingRight: 15,
alignItems: 'flex-end',
},
body: {
padding: 10,
paddingTop: 0,
},
orderNum: {
fontSize: 16,
color: '#666',
marginTop: 3,
},
part_text: {
height: 80,
width: screenWidth,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'stretch',
},
})
type Props = {
title : string,
containerHeight : number,
name : string,
index : number,
children? : Element<*>,
amount? : string,
isApprobation? : string,
titleStyle?: Object,
policyTitleColor?: Object,
upIcon:Object,
downIcon:Object,
}
class Panel extends Component {
constructor(props : Props) {
super(props)
this.icons = {
'up': props.upIcon,
'down': props.downIcon,
}
this.state = {
expanded: false,
animation: new Animated.Value((this.props.containerHeight || 80)),
};
}
setMaxHeight = (event) => {
this.setState({
maxHeight: event.nativeEvent.layout.height,
})
}
setMinHeight = (event) => {
this.setState({
minHeight: event.nativeEvent.layout.height,
})
}
toggle = () => {
const initialValue =
this.state.expanded ? this.state.maxHeight + this.state.minHeight : this.state.minHeight
const finalValue =
this.state.expanded ? this.state.minHeight : this.state.maxHeight + this.state.minHeight
this.setState({
expanded: !this.state.expanded,
})
this.state.animation.setValue(initialValue)
Animated.spring(
this.state.animation,
{
toValue: finalValue,
}
).start()
}
render() {
let icon = this.icons.down
if (this.state.expanded) {
icon = this.icons.up;
}
return (
<Animated.View
style={[styles.container, { height: this.state.animation }]}
>
<View
style={styles.titleContainer}
onLayout={this.setMinHeight}
>
<TouchableWithoutFeedback
onPress={this.toggle}
>
<View
style={[styles.part_text, this.props.titleStyle, !!this.props.containerHeight ? {height: this.props.containerHeight}: {}]}
>
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'flex-start',
paddingLeft: (15),
}}
>
<Text style={[styles.orderNum, this.props.policyTitleColor]} >{this.props.title || '标题'}</Text>
</View>
<View style={[styles.button,!!this.props.containerHeight ? {height: this.props.containerHeight}: {}]} >
{icon}
</View>
</View>
</TouchableWithoutFeedback>
</View>
<View style={styles.body} onLayout={this.setMaxHeight} >
{this.props.children}
</View>
</Animated.View>
)
}
}
export default Panel
/**
* @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]} />);
}
}
This diff is collapsed.
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;
......@@ -34,9 +34,10 @@ type BackButtonType = {
onBackPressed: ()=>void,
routes: Array,
imageUrl?: any,
style?: Object,
}
function BackButton({ onBackPressed, imageUrl }: BackButtonType) {
function BackButton({ onBackPressed, imageUrl, style = {} }: BackButtonType) {
return (
<TouchableOpacity
onPress={() => {
......@@ -47,7 +48,7 @@ function BackButton({ onBackPressed, imageUrl }: BackButtonType) {
<View
style={styles.buttonIOSAndroid}
>
<Index suite="Ionicons" name="ios-arrow-back" size={26} color="white" />
<Index suite="Ionicons" name="ios-arrow-back" size={26} color={style.color || 'white'} />
</View>
</TouchableOpacity>
);
......@@ -55,7 +56,6 @@ function BackButton({ onBackPressed, imageUrl }: BackButtonType) {
const actionMapping = (dispatch, props) => ({
onBackPressed: props.onBackPressed || (() => {
console.log('BackButton:', props)
if (props.routes && props.routes.length === 1) {
} else {
......
/**
* @flow
*/
import React from 'react';
import { View, Platform } from 'react-native';
import FastImage from 'react-native-fast-image';
import Text from '../Text';
import { ENV, resourceURLMapping } from '../../utils/constants';
import BasicIcon from '../BasicIcon';
import { iosMarginTop, androidTop } from '../NavigationBar';
export function HomeHeader(props) {
return (
<View style={{
flexDirection: 'row',
alignItems: 'center',
height: 44,
width: global.SCREEN_WIDTH,
marginTop: Platform.OS === 'ios' ? iosMarginTop : androidTop,
}}
>
<FastImage
style={{
width: 78,
height: 30,
marginLeft: 10,
}}
source={{
uri: `${resourceURLMapping[ENV]}logo_login_taiping.png`,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
<View
style={{
flex: 1,
height: 32,
borderRadius: 16,
marginHorizontal: 10,
backgroundColor: '#ECECF0',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
color: '#8CA0BA',
fontSize: 14,
}}
>
{'搜索"保险服务"'}
</Text>
</View>
<View style={{
flexDirection: 'row',
justifyContent: 'flex-end',
marginRight: 10,
}}
>
<FastImage
style={{
width: 24,
height: 24,
marginRight: 10,
}}
source={{
uri: `${resourceURLMapping[ENV]}icon_message77.png`,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
</View>
</View>
);
}
export function UserPageHeader(props) {
return (
<View style={{
flexDirection: 'row',
alignItems: 'center',
height: 44,
width: global.SCREEN_WIDTH,
marginTop: Platform.OS === 'ios' ? iosMarginTop : androidTop,
}}
>
<FastImage
style={{
width: 36,
height: 36,
marginHorizontal: 15,
borderRadius: 18,
}}
source={{
uri: `${resourceURLMapping[ENV]}icon_avatar_default.png`,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
<View
style={{
flex: 1,
height: 36,
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<Text
style={{
color: '#333',
fontSize: 14,
}}
>
{ props.phone }
</Text>
<View
style={{
flex: 1,
height: 18,
flexDirection: 'row',
alignItems: 'stretch',
}}
>
<FastImage
style={{
width: 68,
height: 16,
}}
source={{
uri: `${resourceURLMapping[ENV]}pic_user_dazhong_vip.png`,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
<View style={{ width: 10 }} />
<BasicIcon suite="Foundation" name="laptop" size={16} color={props.color} />
<View style={{ width: 10 }} />
<BasicIcon suite="Ionicons" name="ios-happy" size={16} color={props.color} />
</View>
</View>
<View style={{
flexDirection: 'row',
justifyContent: 'flex-end',
marginRight: 10,
}}
>
<FastImage
style={{
width: 24,
height: 24,
marginRight: 15,
}}
source={{
uri: `${resourceURLMapping[ENV]}icon_message77.png`,
priority: FastImage.priority.normal,
}}
resizeMode={FastImage.resizeMode.stretch}
/>
<BasicIcon suite="Foundation" name="widget" size={24} color="#606273" />
</View>
</View>
);
}
export default {
HomeHeader,
UserPageHeader,
};
......@@ -6,13 +6,13 @@ import React, { Element } from 'react';
import { connect } from 'react-redux';
import {
View,
Text,
Platform,
TouchableOpacity,
} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import LinearGradient from 'react-native-linear-gradient';
import StyleSheet from '../../utils/StyleSheet';
import Text from '../Text';
export const isLessKitKat = Platform.OS === 'android' && Platform.Version > 19;
......@@ -20,10 +20,15 @@ export const androidTop = isLessKitKat ? 0 : 24;
const systemVersion = DeviceInfo.getSystemVersion();
export const iosMarginTop = parseInt(systemVersion, 10) < 11 ? 20 : 0;
export const NavHeight = Platform.OS === 'android' ? 44 + androidTop : 64
const styles = StyleSheet.create({
navBarContainer: {
ios: {
marginTop: parseInt(systemVersion, 10) < 11 ? 20 : 0,
marginTop: iosMarginTop,
height: 64,
},
android: {
......@@ -36,8 +41,6 @@ const styles = StyleSheet.create({
androidPad: {
height: 88,
},
backgroundColor: '#0074E1',
// backgroundColor: 'rgb(42,85,161)',
flexDirection: 'row',
},
left: {
......@@ -56,9 +59,11 @@ const styles = StyleSheet.create({
textAlign: 'center',
phone: {
fontSize: 18,
fontWeight: '400',
},
pad: {
fontSize: 24,
fontWeight: '400',
},
color: 'white',
alignSelf: 'center',
......@@ -79,16 +84,16 @@ export type NavBarPropsType = {
function NavigationBar(props: NavBarPropsType) {
const {
style, titleStyle, left, onPress, textStyle, fontStyle, title, right, theme,
style = {}, titleStyle, left, onPress, textStyle, fontStyle, title, right, theme,
} = props;
if (theme.navEndColor) {
if (theme.ThemeEndColor && !style.backgroundColor) {
return (
<LinearGradient
start={{ x: 0.0, y: 0.25 }}
end={{ x: 0.5, y: 1.0 }}
locations={[0, 1]}
colors={[theme.themeColor, theme.navEndColor]}
style={[styles.navBarContainer, { backgroundColor: theme.themeColor }, style]}
colors={[theme.ThemeColor, theme.ThemeEndColor]}
style={[styles.navBarContainer, { backgroundColor: theme.ThemeColor }, style]}
>
<View style={[styles.left, titleStyle]}>{left}</View>
<TouchableOpacity style={styles.navStyle} activeOpacity={1} onLongPress={onPress}>
......@@ -99,7 +104,7 @@ function NavigationBar(props: NavBarPropsType) {
);
}
return (
<View style={[styles.navBarContainer, { backgroundColor: theme.themeColor }, style]}>
<View style={[styles.navBarContainer, { backgroundColor: theme.ThemeColor }, style]}>
<View style={[styles.left, titleStyle]}>{left}</View>
<TouchableOpacity style={styles.navStyle} activeOpacity={1} onLongPress={onPress}>
<Text style={[styles.title, textStyle, fontStyle]}>{title}</Text>
......
// @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
*/
import React from 'react';
import SwipeView from 'react-native-swiper';
type Props = {
height: number,
width: number
}
function SwiperBanner(props:Props) {
console.log('SwiperBanner',props)
return (
<SwipeView
showsButtons={false}
autoplay
height={props.height}
width={props.width}
autoplayTimeout={5}
paginationStyle={{ bottom: 0 }}
>
{
props.children
}
</SwipeView>
);
}
export default SwiperBanner;
//@flow
import React from 'react';
import { StyleSheet } from 'react-native';
import TabBarIndicator from 'react-native-tab-view/src/TabBarIndicator';
import Animated from 'react-native-reanimated';
import LinearGradient from 'react-native-linear-gradient';
const styles = StyleSheet.create({
indicator: {
position: 'absolute',
left: 0,
bottom: 0,
right: 0,
height: 2,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'stretch',
},
});
export default class TabBarIndicatorLinear extends TabBarIndicator {
render() : any {
const {
width, position, navigationState, style,
} = this.props;
const { routes } = navigationState;
const translateX = this.getTranslateX(position, routes, width);
return (
<Animated.View
style={[
styles.indicator,
{ width: `${100 / routes.length}%` },
// If layout is not available, use `left` property for positioning the indicator
// This avoids rendering delay until we are able to calculate translateX
width
? { transform: [{ translateX }] }
: { left: `${(100 / routes.length) * navigationState.index}%` },
style,
]}
>
<LinearGradient
start={{ x: 0.22, y: 0.5 }}
end={{ x: 1, y: 1 }}
locations={[0, 1]}
colors={['#078CF3', '#01F6C6']}
style={{ width: 36, height: 3 }}
/>
</Animated.View>
);
}
}
/**
* @flow
* Created by Rabbit on 2019-04-30.
* Created by shiyunjie on 2019-04-30.
*/
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text } from 'react-native';
import { View } from 'react-native';
import { Badge } from 'react-native-elements';
import Index from '../BasicIcon';
import Text from '../Text';
type Props = {}
class TabButton extends Component<Props> {
render() {
const { theme, iconSuite, tabBarIconName, tabBarTitle, focused } = this.props
const color = focused ? theme.themeColor : theme.Colors.charcoal;
const {
theme, iconSuite, tabBarIconName, tabBarTitle, focused, badge,
} = this.props;
const color = focused ? theme.ThemeColor : theme.Colors.charcoal;
console.log('badge:', badge);
const hasBadge = !!badge;
return (
<View style={{ alignItems: 'center' }}>
<Index
style={{ margin: 0, padding: 0 }}
suite={iconSuite}
name={tabBarIconName}
size={28}
size={26}
color={color}
/>
<Text
style={{ color }}
style={{ color, fontSize: 14 }}
textAlign="center"
>
{tabBarTitle}
</Text>
{hasBadge
&& (
<Badge
status="error"
value={`${badge}`}
containerStyle={{ position: 'absolute', top: -6, right: -10 }}
/>
)
}
</View>
);
}
......
/**
* @flow
* Created by Rabbit on 2019-04-30.
* Created by shiyunjie on 2019-04-30.
*/
import React from 'react';
......@@ -8,20 +8,22 @@ import React from 'react';
import TabButton from './TabButton';
export function TabOptions(tabBarTitle : string, iconSuite : string, tabBarIconName : string) {
export function TabOptions(
tabBarTitle : string,
iconSuite : string,
tabBarIconName : string,
badge: string,
) {
const title = tabBarTitle;
const tabBarIcon = ({ focused }: { focused : boolean }) => {
return (
<TabButton
iconSuite={iconSuite}
tabBarIconName={tabBarIconName}
tabBarTitle={tabBarTitle}
focused={focused}
/>
);
};
const tabBarIcon = ({ focused }: { focused : boolean }) => (
<TabButton
iconSuite={iconSuite}
tabBarIconName={tabBarIconName}
tabBarTitle={tabBarTitle}
focused={focused}
badge={badge}
/>
);
const tabBarVisible = true;
return { title, tabBarVisible, tabBarIcon };
}
// @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
* Created by Rabbit on 2019-04-30.
* Created by shiyunjie on 2019-04-30.
*/
import React from 'react';
import { connect } from 'react-redux';
import { Button } from 'react-native-elements';
import LinearGradient from 'react-native-linear-gradient';
function ThemeButton(props) {
type Props = {
theme: Object,
buttonStyle: Object,
}
function ThemeButton(props: Props) {
const { theme, buttonStyle } = props;
if (theme.navEndColor) {
if (buttonStyle && buttonStyle.backgroundColor) {
return (
<Button
{...props}
ViewComponent={LinearGradient} // Don't forget this!
buttonStyle={[buttonStyle, { backgroundColor: theme.themeColor }]}
linearGradientProps={{
colors: [theme.themeColor, theme.navEndColor],
start: { x: 0, y: 0.5 },
end: { x: 1, y: 0.5 },
}}
buttonStyle={[{ backgroundColor: theme.ThemeColor }, buttonStyle]}
/>
);
}
return (
<Button
{...props}
buttonStyle={[buttonStyle, { backgroundColor: theme.themeColor }]}
ViewComponent={LinearGradient} // Don't forget this!
buttonStyle={[{ backgroundColor: theme.ThemeColor }, buttonStyle]}
linearGradientProps={{
colors: [theme.ThemeColor, theme.ThemeEndColor],
start: { x: 0, y: 0.5 },
end: { x: 1, y: 0.5 },
}}
/>
);
}
......
/**
* @flow
*/
import Toast from 'react-native-root-toast';
export default function toast(message) {
Toast.show(message, {
duration: Toast.durations.SHORT,
position: Toast.positions.BOTTOM,
shadow: true,
animation: true,
hideOnPress: true,
delay: 0,
onShow: () => {
// calls on toast\`s appear animation start
},
onShown: () => {
// calls on toast\`s appear animation end.
},
onHide: () => {
// calls on toast\`s hide animation start.
},
onHidden: () => {
// calls on toast\`s hide animation end.
},
});
}
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
*/
import React, { Component } from 'react';
import {
Platform, StyleSheet, Text, View, TouchableOpacity,
} from 'react-native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { push,tab } from '../BasicNavigator/actions';
type Props = {};
class JSPage extends Component<Props> {
render() {
console.log('JSPage', this.props);
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => { this.props.push('NativePage'); }}>
<Text style={styles.instructions}>JSPage</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => { this.props.tab('ServicePage'); }}>
<Text style={styles.instructions}>Tab切换</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
function propsMapping(props, extProps) {
const { nav } = props
return {
nav,
};
}
function actionMapping(dispatch) {
return {
push: compose(dispatch, push),
tab: compose(dispatch, tab),
};
}
//const mapDispatchToProps = dispatch => (bindActionCreators({goNext}, dispatch))
export default connect(propsMapping, actionMapping)(JSPage);
/**
* 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
import productDetail from './productDetail';
import fillInfo from './fillInfo';
import healthNotification from './healthNotification';
import payment from './payment';
import receipt from './receipt';
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 './keys';
const initConfig = {
productDetail, // 产品详情页面
fillInfo, // 信息录入
healthNotification, // 健康告知
payment, // 确认支付
receipt, // 签收
};
export const getConfig = (blueID) => {
const config = { ...initConfig };
const premIndex = config.productDetail.keys.findIndex(key => (key === PREM_GROUP));
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;
}
};
// 订单
export type Order = {
oid : string, // 唯一标识
orderno : string, // 订单号
uid : string, // 用户id
memberid : string, //关联关系,
channel : string, // 渠道
company : string, // 承保机构Id
orderstatus : string, //订单状态
ordertype : string, // 订单类型
orderprem : string, // 保费,用户付出的钱 元
orderamnt : string, // 保额,赔付的金额 元,
strmakedate : string, // 创建时间 年 月 日
invitecode : string, // 邀请号,
}
//支付信息
export type Payment = {
bankcode : string, // 银行编号
bankname : string, // 银行名称
name : string, // 开户名
bankaccount : string, // 银行账号
paymode : string, // 支付方式
payprem : string, // 支付金额
appmobile : string, // 联系方式
status : string, // 支付状态,
message : string, // 支付备注
strpaymentdate : string, // 开始支付时间
repaymode : string, //退款方式,
outpayflag : string, // 外部渠道标识,
}
//受益人
export type Bnf = {
bid : string, // 受益人id
bnftype : string, // 受益人类型 0 法定
bnfno : string, // 受益人编号
bnfgrade : string, //受益人级别
relationtoinsured : string, // 与被保人关系
bnflot : string, // 受益份额
sequenceno : string, //显示顺序
name : string, // 受益人名字
gender : string, // 性别
birthday : string, // 生日
idtype : string, // 证件类型
idno : string, // 证件号
isauthentication : string, //是否认证用户,
infrontimg : string, // 正面证件照片
inbackimg : string, // 反面证件照
nativeplace : string, // 籍贯
}
export type PDetail = {
province : string, //省编号
city : string, //市 编号
county : string, //区/县 编号
postaladdress : string, //详细地址
zipcode : string, //邮编
stature : string, //身高
avoirdupois : string, //体重
familyyearincome : string, //家庭收入
salary : string, //工资
mobile : string, //手机
phone : string, //固定电话
}
// 用户
export type Person = {
uid : string, //用户ID
name : string, //用户
gender : string, //性别,
birthday : string, //生日
idvalid : string, //实名认证
cardusefuldate : string, //证件有效期
idtype : string, //证件类型
idno : string, //证件号
nativeplace : string, //籍贯,
ssflag : string, //是否有社保,
infrontimg : string, // 证件照正面
inbackimg : string, // 证件照反面
currenttype : string, // 税收居民类型
detail : PDetail, // 通讯信息
}
//健告输入
export type Customerimpart = {
impartcode : string, // 健康告知项编号
impartver : string, // 健康告知项版本,
impartcontent : string, //告知内容
impartparams : string, // 用户输入信息,
impartexplanation : string, //输入参数,
}
// 投保人
export type Applicant = {
aid : string, // 唯一标识
orderId : Array<string>, // 订单ID集合
ldperson : Person, //用户信息
lccustomerimpart : Array<Customerimpart>, //健康告知
};
export type Insured = {
insureId : string, // 唯一标识
orderUUID : Array<string>, // 订单ID集合
lcinsured : Person, //用户信息
lccustomerimpart : Array<Customerimpart>, //健康告知
bnfs : Array<Bnf>, //受益人
};
//险种计划,计划包含产品
export type Plan = {
pId : string, //唯一标识
prem : string, // 保费 元
amnt : string, // 保额 元,
products : Array<string>, // 保险产品ID
contplancode : string, // 计划编号,
contplanname : string, // 计划名称,
mult : string, // 份数
payintv : string, // 缴费方式 12 年缴,1月缴,0趸缴
insuyear : string, //保障期间 106 终身,
insuyearflag : string, // 保障期间单位 Y 年 , A 岁,
payendyear : string, // 缴费期间
payendyearflag : string, // 缴费期间单位 Y 年 , A 岁,
hospitalbnfflag : string, // 是否有住院津贴 N;不选择Y:选择,
livegetmode : string, //生存金领取方式
deadgetmode : string, //身故金领取方式
bonusgetmode : string, //红利金领取方式
getyear : string, // 领取 年龄,
getyearflag : string, //领取 年龄标记 符合领取年龄 ,
getendperiod : string, // 领取年期,
getendunit : string, //领取年期 单位,
}
// 责任
export type Duty = {
dutycode : string, //责任编码
prem : string, //基本保费 用户付出的
amnt : string, //实际保额 赔付的
getlimit : string, //免赔天数
deductible : string, //免赔金额 元
getrate : string, //免赔比例
}
//保险产品
export type Product = {
productId : string, // 唯一标识
planids : Array<string>, // 关联险种计划ID集合
polno : string, //保单险种号码
riskcode : string, //险种编码
riskname : string, //险种名称
ismainriskflag : string, //主附险标志
payintv : string, //缴费方式 12 年缴,1月缴,0趸缴
payendyearflag : string, //缴费期间
payendyear : string, //缴费期间单位 Y 年 , A 岁
insuyearflag : string, //保险年期单位
insuyear : string, //保险年期
amnt : string, //保额 赔付,
prem : string, //保费, 用户支付
mult : string, //份数,
getyear : string, //领取年龄
getintv : string, //年金领取频次
getendperiod : string, //领取期间,
getendunit : string, //领取期间单位,
livegetmode : string, //生存金领取方式
deadgetmode : string, //身故金领取方式,
bonusgetmode : string, //红利金领取方式
calflg : string, //保额报费计算方式 G保额算保费,P 保费算保额
pubamntflag : string, //是否公共保额 Y--是,N--否
pubamnt : string, //公共保额
dutys : Array<Duty>, // 责任列表
discountsave : string, //折扣率
};
// 保单
export type Policy = {
pid : string, //唯一标识
order : Order, // 订单
payment : Payment, // 支付信息
applicant : Applicant, //投保人
insureds : Array<Insured>, //被保人集合
bnfs : Array<Bnf>, // 受益人
plans : Array<Plan>, // 保险计划
// 目前的产品,products暂时不储存数据,所有产品数据储存在计划中
products : Array<Product>,
}
export default {};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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