├── .watchmanconfig
├── src
├── services
│ ├── index.js
│ ├── api
│ │ └── index.js
│ └── leanclound
│ │ ├── index.js
│ │ ├── common.js
│ │ ├── groupOrder.js
│ │ ├── resumeOrder.js
│ │ ├── warOrder.js
│ │ ├── recruitOrder.js
│ │ └── user.js
├── components
│ ├── Container
│ │ ├── index.js
│ │ ├── styles.js
│ │ └── Container.js
│ ├── CustomStyles
│ │ ├── index.js
│ │ ├── Card.js
│ │ └── ImagePicker.js
│ ├── index.js
│ ├── HomeTeamList
│ │ └── index.js
│ ├── HomeWarOrderList
│ │ └── index.js
│ ├── HomeUserInfoList
│ │ └── index.js
│ ├── HomeGroupOrderList
│ │ └── index.js
│ ├── HomeRecruitOrderList
│ │ └── index.js
│ ├── HomeResumeOrderList
│ │ └── index.js
│ ├── AccountWarOrderList
│ │ └── index.js
│ ├── AccountGroupOrderList
│ │ └── index.js
│ ├── AccountResumeOrderList
│ │ └── index.js
│ ├── HomeResumeCard
│ │ └── index.js
│ ├── HomeGroupCard
│ │ └── index.js
│ ├── HomeWarCard
│ │ └── index.js
│ ├── AccountRecruitOrderList
│ │ └── index.js
│ ├── HomeRecruitCard
│ │ └── index.js
│ ├── HomeTeamCard
│ │ └── index.js
│ ├── HomeUserInfoCard
│ │ └── index.js
│ ├── AccountResumeCard
│ │ └── index.js
│ ├── AccountGroupCard
│ │ └── index.js
│ ├── AccountWarCard
│ │ └── index.js
│ └── AccountRecruitCard
│ │ └── index.js
├── store
│ ├── index.js
│ ├── configureStore.prod.js
│ └── configureStore.dev.js
├── config
│ └── index.js
├── screens
│ ├── Welcome
│ │ └── index.js
│ ├── Home
│ │ ├── Teams
│ │ │ ├── index.js
│ │ │ └── Detail
│ │ │ │ └── index.js
│ │ ├── UserInfos
│ │ │ ├── index.js
│ │ │ └── Detail
│ │ │ │ └── index.js
│ │ ├── WarOrders
│ │ │ └── index.js
│ │ ├── ResumeOrders
│ │ │ └── index.js
│ │ ├── GroupOrders
│ │ │ └── index.js
│ │ ├── RecruitOrders
│ │ │ └── index.js
│ │ └── index.js
│ ├── Account
│ │ ├── WarOrders
│ │ │ └── index.js
│ │ ├── GroupOrders
│ │ │ └── index.js
│ │ ├── ResumeOrders
│ │ │ └── index.js
│ │ ├── RecruitOrders
│ │ │ └── index.js
│ │ ├── EmaiVerify
│ │ │ └── index.js
│ │ └── Teams
│ │ │ └── index.js
│ ├── SignUp
│ │ └── index.js
│ └── SignIn
│ │ └── index.js
├── reducers
│ ├── nav.js
│ ├── index.js
│ ├── app.js
│ ├── team.js
│ └── user.js
├── utils
│ ├── http.js
│ └── utils.js
├── sagas
│ ├── commonSaga.js
│ ├── index.js
│ ├── groupOrderSaga.js
│ ├── resumeOrderSaga.js
│ ├── warOrderSaga.js
│ ├── recruitOrderSaga.js
│ ├── userSaga.js
│ └── teamsSaga.js
├── index.js
└── constants
│ └── index.js
├── .gitignore
├── assets
├── icon.png
├── splash.png
├── images
│ ├── home.png
│ ├── logo.png
│ ├── home@2x.png
│ ├── home@3x.png
│ ├── home_icon.png
│ ├── avatar_logo.png
│ ├── home_icon@2x.png
│ ├── home_icon@3x.png
│ ├── ios_arrow_back.png
│ ├── android_arrow_back.png
│ ├── avatar_logo@2x.png.png
│ ├── avatar_logo@3x.png.png
│ ├── ios_arrow_back@2x.png
│ ├── ios_arrow_back@3x.png
│ ├── android_arrow_back@2x.png
│ └── android_arrow_back@3x.png
└── notification_icon.png
├── .babelrc
├── App.js
├── package.json
├── app.json
└── README.md
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/src/services/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/services/api/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/assets/images/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home.png
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/home@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home@2x.png
--------------------------------------------------------------------------------
/assets/images/home@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home@3x.png
--------------------------------------------------------------------------------
/assets/images/home_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home_icon.png
--------------------------------------------------------------------------------
/assets/notification_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/notification_icon.png
--------------------------------------------------------------------------------
/assets/images/avatar_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/avatar_logo.png
--------------------------------------------------------------------------------
/assets/images/home_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home_icon@2x.png
--------------------------------------------------------------------------------
/assets/images/home_icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/home_icon@3x.png
--------------------------------------------------------------------------------
/assets/images/ios_arrow_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/ios_arrow_back.png
--------------------------------------------------------------------------------
/src/components/Container/index.js:
--------------------------------------------------------------------------------
1 | import Container from './Container'
2 | import styles from './styles'
3 |
4 | export { Container, styles }
5 |
--------------------------------------------------------------------------------
/assets/images/android_arrow_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/android_arrow_back.png
--------------------------------------------------------------------------------
/assets/images/avatar_logo@2x.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/avatar_logo@2x.png.png
--------------------------------------------------------------------------------
/assets/images/avatar_logo@3x.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/avatar_logo@3x.png.png
--------------------------------------------------------------------------------
/assets/images/ios_arrow_back@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/ios_arrow_back@2x.png
--------------------------------------------------------------------------------
/assets/images/ios_arrow_back@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/ios_arrow_back@3x.png
--------------------------------------------------------------------------------
/assets/images/android_arrow_back@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/android_arrow_back@2x.png
--------------------------------------------------------------------------------
/assets/images/android_arrow_back@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxj963577494/OverWatchTeams-React-Native-Expo/HEAD/assets/images/android_arrow_back@3x.png
--------------------------------------------------------------------------------
/src/components/CustomStyles/index.js:
--------------------------------------------------------------------------------
1 | import CardStyle from './Card'
2 | import ImagePickerStyle from './ImagePicker'
3 |
4 | export { CardStyle, ImagePickerStyle }
5 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./configureStore.prod')
3 | } else {
4 | module.exports = require('./configureStore.dev')
5 | }
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "plugins": [["import", { "libraryName": "antd-mobile" }]],
4 | "env": {
5 | "development": {
6 | "plugins": ["transform-react-jsx-source"]
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | BASE_PIC_URL: 'http://p0bl3nkmx.bkt.clouddn.com',
3 | BASE_DEFAULT_PIC_URL: 'http://p0bl3nkmx.bkt.clouddn.com/logo.png',
4 | TEAM_DEFAULT_AVATAR: 'http://p0bl3nkmx.bkt.clouddn.com/avatar.png',
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/Container/styles.js:
--------------------------------------------------------------------------------
1 | import EStyleSheet from 'react-native-extended-stylesheet'
2 |
3 | export default EStyleSheet.create({
4 | container: {
5 | flex: 1,
6 | alignItems: 'center',
7 | justifyContent: 'center'
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/src/screens/Welcome/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | export default class SignUp extends Component {
6 | render() {
7 | return SignUp
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import moment from 'moment'
3 | import app from './src'
4 |
5 | require('moment/locale/zh-cn')
6 | moment.locale('zh-cn')
7 |
8 | const appId = 'Vvtn3QVyWcN9eVbuAT3wjMfG-9Nh9j0Va'
9 | const appKey = 'P59gxu0DMT7GkFeP1VlJoVmp'
10 | AV.init({ appId, appKey })
11 |
12 | export default app
13 |
--------------------------------------------------------------------------------
/src/components/Container/Container.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import { View } from 'react-native'
4 |
5 | import styles from './styles'
6 |
7 | const Container = ({ children }) => (
8 | {children}
9 | )
10 |
11 | Container.propTypes = {
12 | children: PropTypes.any
13 | }
14 |
15 | export default Container
16 |
--------------------------------------------------------------------------------
/src/components/CustomStyles/Card.js:
--------------------------------------------------------------------------------
1 | import CardStyle from 'antd-mobile/lib/card/style/index.native'
2 |
3 | export default {
4 | ...CardStyle,
5 | headerTitle: {
6 | flex: 3,
7 | flexDirection: 'row',
8 | alignItems: 'center'
9 | },
10 | headerExtra: {
11 | flex: 1,
12 | },
13 | footerContent: {
14 | flex: 3
15 | },
16 | footerExtra: {
17 | flex: 1,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/reducers/nav.js:
--------------------------------------------------------------------------------
1 | import { NavigationActions } from 'react-navigation'
2 | import AppNavigator from '../routes'
3 |
4 | const initialNavState = AppNavigator.router.getStateForAction(
5 | NavigationActions.init()
6 | )
7 |
8 | const navReducer = (state = initialNavState, action) => {
9 | const nextState = AppNavigator.router.getStateForAction(action, state)
10 | return nextState || state
11 | }
12 |
13 | export { navReducer }
14 |
--------------------------------------------------------------------------------
/src/services/leanclound/index.js:
--------------------------------------------------------------------------------
1 | import * as commonService from './common'
2 | import * as userService from './user'
3 | import * as teamsService from './teams'
4 | import * as recruitOrderService from './recruitOrder'
5 | import * as groupOrderService from './groupOrder'
6 | import * as warOrderService from './warOrder'
7 | import * as resumeOrderService from './resumeOrder'
8 |
9 | export {
10 | commonService,
11 | userService,
12 | teamsService,
13 | recruitOrderService,
14 | groupOrderService,
15 | warOrderService,
16 | resumeOrderService
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/CustomStyles/ImagePicker.js:
--------------------------------------------------------------------------------
1 | import ImagePickerStyle from 'antd-mobile/lib/image-picker/style/index.native'
2 |
3 | export default {
4 | ...ImagePickerStyle,
5 | container: {
6 | flexWrap: 'nowrap',
7 | flexDirection: 'row',
8 | justifyContent: 'center'
9 | },
10 | size: {
11 | width: 120,
12 | height: 120
13 | },
14 | closeWrap: {
15 | width: 24,
16 | height: 24,
17 | backgroundColor: '#999',
18 | borderRadius: 12,
19 | position: 'absolute',
20 | top: 6,
21 | right: 6,
22 | justifyContent: 'center',
23 | alignItems: 'center',
24 | overflow: 'hidden'
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { userReducer } from './user'
2 | import { appReducer } from './app'
3 | import { navReducer } from './nav'
4 | import { teamReducer } from './team'
5 | import { recruitOrderReducer } from './recruitOrder'
6 | import { groupOrderReducer } from './groupOrder'
7 | import { warOrderReducer } from './warOrder'
8 | import { resumeOrderReducer } from './resumeOrder'
9 |
10 | export default {
11 | app: appReducer,
12 | nav: navReducer,
13 | user: userReducer,
14 | team: teamReducer,
15 | recruitOrder: recruitOrderReducer,
16 | groupOrder: groupOrderReducer,
17 | warOrder: warOrderReducer,
18 | resumeOrder: resumeOrderReducer
19 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "dependencies": {
5 | "antd-mobile": "^2.1.3",
6 | "expo": "^24.0.0",
7 | "leancloud-storage": "^3.4.2",
8 | "moment": "^2.20.1",
9 | "rc-form": "^2.1.6",
10 | "react": "16.0.0",
11 | "react-native": "https://github.com/expo/react-native/archive/sdk-24.0.0.tar.gz",
12 | "react-native-keyboard-aware-scroll-view": "^0.4.1",
13 | "react-native-timeago": "^0.4.0",
14 | "react-navigation": "^1.0.0-beta.22",
15 | "react-redux": "^5.0.6",
16 | "redux": "^3.7.2",
17 | "redux-logger": "^3.0.6",
18 | "redux-persist": "^5.4.0",
19 | "redux-saga": "^0.16.0"
20 | },
21 | "devDependencies": {
22 | "babel-plugin-import": "^1.6.3"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/store/configureStore.prod.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux'
2 | import { persistStore, persistCombineReducers } from 'redux-persist'
3 | import storage from 'redux-persist/es/storage'
4 | import CreateSagaMiddleware, { END } from 'redux-saga'
5 | import reducers from '../reducers'
6 |
7 | const config = {
8 | key: 'root',
9 | storage
10 | }
11 |
12 | const reducer = persistCombineReducers(config, reducers)
13 |
14 | export default function configureStore(initialState) {
15 | const sagaMiddleware = CreateSagaMiddleware()
16 | const store = createStore(
17 | reducer,
18 | initialState,
19 | applyMiddleware(sagaMiddleware)
20 | )
21 | store.runSaga = sagaMiddleware.run
22 | store.close = () => store.dispatch(END)
23 | const persistor = persistStore(store)
24 | return { persistor, store }
25 | }
26 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "OW Teams",
4 | "description": "OverWatch Teams",
5 | "slug": "OverWatchTeams",
6 | "privacy": "public",
7 | "sdkVersion": "24.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.2",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "githubUrl": "https://github.com/zxj963577494/OverWatchTeams-React-Native",
13 | "notification": {
14 | "icon": "./assets/notification_icon.png"
15 | },
16 | "splash": {
17 | "image": "./assets/splash.png",
18 | "resizeMode": "contain",
19 | "backgroundColor": "#ffffff"
20 | },
21 | "ios": {
22 | "supportsTablet": true,
23 | "bundleIdentifier": "com.zhengxujiang.overwatchteams",
24 | "buildNumber": "1.0.2"
25 | },
26 | "android": {
27 | "package": "com.zhengxujiang.overwatchteams",
28 | "versionCode": 3,
29 | "permissions": []
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/utils/http.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | const headers = {
3 | Accept: 'application/json',
4 | };
5 |
6 | function request(method, url, axiosConfig = {}) {
7 | const options = Object.assign(
8 | {},
9 | {
10 | method,
11 | url,
12 | headers,
13 | responseType: 'json',
14 | },
15 | headers,
16 | axiosConfig,
17 | );
18 | return axios(options);
19 | }
20 |
21 | export function post(url, { data = {}, params = {}, transformResponse = [] }) {
22 | return request('post', url, { data, params, transformResponse });
23 | }
24 |
25 | export function put(url, { data = {}, params = {}, transformResponse = [] }) {
26 | return request('put', url, { data, params, transformResponse });
27 | }
28 |
29 | export function get(url, { params = {}, transformResponse = [] }) {
30 | return request('get', url, { transformResponse, params });
31 | }
32 |
33 | export function destroy(url) { // delete
34 | return request('delete', url, {});
35 | }
--------------------------------------------------------------------------------
/src/store/configureStore.dev.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import { persistStore, persistCombineReducers } from 'redux-persist'
3 | import storage from 'redux-persist/es/storage'
4 | import CreateSagaMiddleware, { END } from 'redux-saga'
5 | import { createLogger } from 'redux-logger'
6 | import reducers from '../reducers'
7 |
8 | const config = {
9 | key: 'root',
10 | storage,
11 | debug: true
12 | }
13 |
14 | const reducer = persistCombineReducers(config, reducers)
15 |
16 | export default function configureStore(initialState) {
17 | const sagaMiddleware = CreateSagaMiddleware()
18 | const loggerMiddleware = createLogger()
19 | // 启动loggerMiddleware可能会导致XDE假死,高性能机器使用
20 | // const middlewares = [sagaMiddleware, loggerMiddleware]
21 | const middlewares = [sagaMiddleware]
22 | const enhancers = compose(applyMiddleware(...middlewares))
23 | const store = createStore(reducer, initialState, enhancers)
24 | store.runSaga = sagaMiddleware.run
25 | store.close = () => store.dispatch(END)
26 | const persistor = persistStore(store)
27 | return { persistor, store }
28 | }
29 |
--------------------------------------------------------------------------------
/src/services/leanclound/common.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 |
3 | // 发送验证码
4 | export function requestSmsCode(payload) {
5 | const { phone } = payload
6 | return AV.Cloud.requestSmsCode(phone).then(function(result) {
7 | return result.toJSON()
8 | })
9 | }
10 |
11 | // 验证邮箱
12 | export function requestEmailVerify(payload) {
13 | const { email } = payload
14 | return AV.User.requestEmailVerify(email).then(function(result) {
15 | return result
16 | })
17 | }
18 |
19 | // 邮箱重置密码
20 | export function requestPasswordReset(payload) {
21 | const { email } = payload
22 | return AV.User.requestPasswordReset(email).then(function(result) {
23 | return result
24 | })
25 | }
26 |
27 | // 手机号码重置密码, 发送验证码
28 | export function requestPasswordResetBySmsCode(payload) {
29 | const { phone } = payload
30 | return AV.User.requestPasswordResetBySmsCode(phone)
31 | }
32 |
33 | // 手机号码重置密码
34 | export function resetPasswordBySmsCode(payload) {
35 | const { code, password } = payload
36 | return AV.User.resetPasswordBySmsCode(code, password)
37 | }
38 |
39 | // 上传图片
40 | export function uploadPic(payload) {
41 | const { image, name } = payload
42 | const file = new AV.File(name, { blob: image })
43 | return file.save().then(function(result) {
44 | return result.toJSON()
45 | })
46 | }
47 |
--------------------------------------------------------------------------------
/src/screens/Home/Teams/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeTeamListRequest } from '../../../actions'
7 | import { HomeTeamList } from '../../../components'
8 |
9 | class HomeTeams extends Component {
10 | componentDidMount() {
11 | if (this.props.team.list.length === 0) {
12 | this.props.getHomeTeamList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { team, navigateTo, getHomeTeamList } = this.props
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
30 | const mapStateToProps = (state, ownProps) => {
31 | return {
32 | team: state.team.home.team
33 | }
34 | }
35 |
36 | const mapDispatchToProps = (dispatch, ownProps) => {
37 | return {
38 | getHomeTeamList: payload => {
39 | dispatch(getHomeTeamListRequest(payload))
40 | },
41 | navigateTo: (path, params) => {
42 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
43 | }
44 | }
45 | }
46 |
47 | HomeTeams.propTypes = {
48 | team: PropTypes.object,
49 | getHomeTeamList: PropTypes.func,
50 | navigateTo: PropTypes.func
51 | }
52 |
53 | export default connect(mapStateToProps, mapDispatchToProps)(HomeTeams)
54 |
--------------------------------------------------------------------------------
/src/screens/Home/UserInfos/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeUserInfoListRequest } from '../../../actions'
7 | import { HomeUserInfoList } from '../../../components'
8 |
9 | class HomeUserInfos extends Component {
10 | componentDidMount() {
11 | if (this.props.userinfo.list.length === 0) {
12 | this.props.getHomeUserInfoList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { userinfo, navigateTo, getHomeUserInfoList } = this.props
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
30 | const mapStateToProps = (state, ownProps) => {
31 | return {
32 | userinfo: state.user.home.userinfo
33 | }
34 | }
35 |
36 | const mapDispatchToProps = (dispatch, ownProps) => {
37 | return {
38 | getHomeUserInfoList: payload => {
39 | dispatch(getHomeUserInfoListRequest(payload))
40 | },
41 | navigateTo: (path, params) => {
42 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
43 | }
44 | }
45 | }
46 |
47 | HomeUserInfos.propTypes = {
48 | userinfo: PropTypes.object,
49 | getHomeUserInfoList: PropTypes.func,
50 | navigateTo: PropTypes.func
51 | }
52 |
53 | export default connect(mapStateToProps, mapDispatchToProps)(HomeUserInfos)
54 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import HomeGroupCard from './HomeGroupCard'
2 | import HomeRecruitCard from './HomeRecruitCard'
3 | import HomeResumeCard from './HomeResumeCard'
4 | import HomeWarCard from './HomeWarCard'
5 | import HomeTeamCard from './HomeTeamCard'
6 | import AccountGroupCard from './AccountGroupCard'
7 | import AccountRecruitCard from './AccountRecruitCard'
8 | import AccountResumeCard from './AccountResumeCard'
9 | import AccountWarCard from './AccountWarCard'
10 | import HomeUserInfoCard from './HomeUserInfoCard'
11 | import HomeGroupOrderList from './HomeGroupOrderList'
12 | import HomeRecruitOrderList from './HomeRecruitOrderList'
13 | import HomeResumeOrderList from './HomeResumeOrderList'
14 | import HomeWarOrderList from './HomeWarOrderList'
15 | import HomeTeamList from './HomeTeamList'
16 | import HomeUserInfoList from './HomeUserInfoList'
17 | import AccountRecruitOrderList from './AccountRecruitOrderList'
18 | import AccountGroupOrderList from './AccountGroupOrderList'
19 | import AccountResumeOrderList from './AccountResumeOrderList'
20 | import AccountWarOrderList from './AccountWarOrderList'
21 |
22 | export {
23 | HomeGroupCard,
24 | HomeRecruitCard,
25 | HomeResumeCard,
26 | HomeWarCard,
27 | HomeTeamCard,
28 | HomeUserInfoCard,
29 | AccountRecruitCard,
30 | AccountGroupCard,
31 | AccountResumeCard,
32 | AccountWarCard,
33 | HomeGroupOrderList,
34 | HomeRecruitOrderList,
35 | HomeResumeOrderList,
36 | HomeWarOrderList,
37 | HomeTeamList,
38 | HomeUserInfoList,
39 | AccountRecruitOrderList,
40 | AccountGroupOrderList,
41 | AccountResumeOrderList,
42 | AccountWarOrderList
43 | }
44 |
--------------------------------------------------------------------------------
/src/screens/Home/WarOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeWarOrderListRequest } from '../../../actions'
7 | import { HomeWarOrderList } from '../../../components'
8 |
9 | class HomeWarOrders extends Component {
10 | componentDidMount() {
11 | if (this.props.warOrder.list.length === 0) {
12 | this.props.getHomeWarOrderList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { warOrder, navigateTo, getHomeWarOrderList } = this.props
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
30 | const mapStateToProps = (state, ownProps) => {
31 | return {
32 | warOrder: state.warOrder.home.warOrder
33 | }
34 | }
35 |
36 | const mapDispatchToProps = (dispatch, ownProps) => {
37 | return {
38 | getHomeWarOrderList: payload => {
39 | dispatch(getHomeWarOrderListRequest(payload))
40 | },
41 | navigateTo: (path, params) => {
42 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
43 | }
44 | }
45 | }
46 |
47 | HomeWarOrders.propTypes = {
48 | warOrder: PropTypes.object,
49 | getHomeWarOrderList: PropTypes.func,
50 | navigateTo: PropTypes.func
51 | }
52 |
53 | export default connect(mapStateToProps, mapDispatchToProps)(HomeWarOrders)
54 |
--------------------------------------------------------------------------------
/src/screens/Home/ResumeOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeResumeOrderListRequest } from '../../../actions'
7 | import { HomeResumeOrderList } from '../../../components'
8 |
9 | class HomeResumeOrders extends Component {
10 | componentDidMount() {
11 | if (this.props.resumeOrder.list.length === 0) {
12 | this.props.getHomeResumeOrderList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { resumeOrder, navigateTo, getHomeResumeOrderList } = this.props
18 | return (
19 |
24 | )
25 | }
26 | }
27 |
28 | const mapStateToProps = (state, ownProps) => {
29 | return {
30 | resumeOrder: state.resumeOrder.home.resumeOrder
31 | }
32 | }
33 |
34 | const mapDispatchToProps = (dispatch, ownProps) => {
35 | return {
36 | getHomeResumeOrderList: payload => {
37 | dispatch(getHomeResumeOrderListRequest(payload))
38 | },
39 | navigateTo: (path, params) => {
40 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
41 | }
42 | }
43 | }
44 |
45 | HomeResumeOrders.propTypes = {
46 | resumeOrder: PropTypes.object,
47 | getHomeResumeOrderList: PropTypes.func,
48 | navigateTo: PropTypes.func
49 | }
50 |
51 | export default connect(mapStateToProps, mapDispatchToProps)(HomeResumeOrders)
52 |
--------------------------------------------------------------------------------
/src/screens/Home/GroupOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeGroupOrderListRequest } from '../../../actions'
7 | import { HomeGroupOrderList } from '../../../components'
8 |
9 | class HomeGroupOrders extends Component {
10 | componentDidMount() {
11 | if (this.props.groupOrder.list.length === 0) {
12 | this.props.getHomeGroupOrderList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { groupOrder, navigateTo, getHomeGroupOrderList } = this.props
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
30 | const mapStateToProps = (state, ownProps) => {
31 | return {
32 | groupOrder: state.groupOrder.home.groupOrder
33 | }
34 | }
35 |
36 | const mapDispatchToProps = (dispatch, ownProps) => {
37 | return {
38 | getHomeGroupOrderList: payload => {
39 | dispatch(getHomeGroupOrderListRequest(payload))
40 | },
41 | navigateTo: (path, params) => {
42 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
43 | }
44 | }
45 | }
46 |
47 | HomeGroupOrders.propTypes = {
48 | groupOrder: PropTypes.object,
49 | getHomeGroupOrderList: PropTypes.func,
50 | navigateTo: PropTypes.func
51 | }
52 |
53 | export default connect(mapStateToProps, mapDispatchToProps)(HomeGroupOrders)
54 |
--------------------------------------------------------------------------------
/src/reducers/app.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_REQUEST,
3 | FETCH_SUCCESS,
4 | FETCH_FAILED,
5 | POST_UPLOAD_REQUEST,
6 | POST_UPLOAD_SUCCESS,
7 | POST_UPLOAD_FAILED,
8 | SEND_EMAIL_REQUEST,
9 | SEND_EMAIL_SUCCESS,
10 | SEND_EMAIL_FAILED,
11 | SEND_PASSWORD_RESET_REQUEST,
12 | SEND_PASSWORD_RESET_SUCCESS,
13 | SEND_PASSWORD_RESET_FAILED
14 | } from '../constants/actionTypes'
15 |
16 | const initialAppState = {
17 | isFetching: false,
18 | text: '',
19 | file: {},
20 | emailError: ''
21 | }
22 |
23 | function appReducer(state = initialAppState, action) {
24 | switch (action.type) {
25 | case FETCH_REQUEST:
26 | return {
27 | ...state,
28 | isFetching: true,
29 | text: action.payload.text
30 | }
31 | case FETCH_SUCCESS:
32 | return {
33 | ...state,
34 | isFetching: false
35 | }
36 | case FETCH_FAILED:
37 | return {
38 | ...state,
39 | isFetching: false
40 | }
41 | case POST_UPLOAD_REQUEST:
42 | return state
43 | case POST_UPLOAD_SUCCESS:
44 | return { ...state, file: action.payload }
45 | case POST_UPLOAD_FAILED:
46 | return state
47 | case SEND_EMAIL_REQUEST:
48 | return { ...state, emailError: '正在发送...' }
49 | case SEND_EMAIL_SUCCESS:
50 | return { ...state, emailError: '发送成功' }
51 | case SEND_EMAIL_FAILED:
52 | return { ...state, emailError: '发送失败' }
53 | case SEND_PASSWORD_RESET_REQUEST:
54 | return state
55 | case SEND_PASSWORD_RESET_SUCCESS:
56 | return state
57 | case SEND_PASSWORD_RESET_FAILED:
58 | return state
59 | default:
60 | return state
61 | }
62 | }
63 |
64 | export { appReducer }
65 |
--------------------------------------------------------------------------------
/src/screens/Home/RecruitOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { NavigationActions } from 'react-navigation'
5 | import { connect } from 'react-redux'
6 | import { getHomeRecruitOrderListRequest } from '../../../actions'
7 | import { HomeRecruitOrderList } from '../../../components'
8 |
9 | class HomeRecruitOrders extends Component {
10 | componentDidMount() {
11 | if (this.props.recruitOrder.list.length === 0) {
12 | this.props.getHomeRecruitOrderList({ page: 1 })
13 | }
14 | }
15 |
16 | render() {
17 | const { recruitOrder, navigateTo, getHomeRecruitOrderList } = this.props
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
30 | const mapStateToProps = (state, ownProps) => {
31 | return {
32 | recruitOrder: state.recruitOrder.home.recruitOrder
33 | }
34 | }
35 |
36 | const mapDispatchToProps = (dispatch, ownProps) => {
37 | return {
38 | getHomeRecruitOrderList: payload => {
39 | dispatch(getHomeRecruitOrderListRequest(payload))
40 | },
41 | navigateTo: (path, params) => {
42 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
43 | }
44 | }
45 | }
46 |
47 | HomeRecruitOrders.propTypes = {
48 | recruitOrder: PropTypes.object,
49 | getHomeRecruitOrderList: PropTypes.func,
50 | navigateTo: PropTypes.func
51 | }
52 |
53 | export default connect(mapStateToProps, mapDispatchToProps)(HomeRecruitOrders)
54 |
--------------------------------------------------------------------------------
/src/screens/Account/WarOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | getAccountWarOrderListRequest,
7 | deleteWarOrderRequest
8 | } from '../../../actions'
9 | import { AccountWarOrderList } from '../../../components'
10 |
11 | class AccountWarOrders extends Component {
12 | componentDidMount() {
13 | if (this.props.warOrder.list.length === 0) {
14 | this.props.getAccountWarOrderList({ page: 1 })
15 | }
16 | }
17 |
18 | render() {
19 | const {
20 | warOrder,
21 | navigateTo,
22 | getAccountWarOrderList,
23 | deleteWarOrder
24 | } = this.props
25 | return (
26 |
32 | )
33 | }
34 | }
35 |
36 | const mapStateToProps = (state, ownProps) => {
37 | return {
38 | warOrder: state.warOrder.account.warOrder
39 | }
40 | }
41 |
42 | const mapDispatchToProps = (dispatch, ownProps) => {
43 | return {
44 | getAccountWarOrderList: payload => {
45 | dispatch(getAccountWarOrderListRequest(payload))
46 | },
47 | deleteWarOrder: payload => {
48 | dispatch(deleteWarOrderRequest(payload))
49 | },
50 | navigateTo: (path, params) => {
51 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
52 | }
53 | }
54 | }
55 |
56 | AccountWarOrders.propTypes = {
57 | warOrder: PropTypes.object,
58 | getAccountWarOrderList: PropTypes.func,
59 | navigateTo: PropTypes.func,
60 | deleteWarOrder: PropTypes.func
61 | }
62 |
63 | export default connect(mapStateToProps, mapDispatchToProps)(
64 | AccountWarOrders
65 | )
66 |
--------------------------------------------------------------------------------
/src/screens/Account/GroupOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | getAccountGroupOrderListRequest,
7 | deleteGroupOrderRequest
8 | } from '../../../actions'
9 | import { AccountGroupOrderList } from '../../../components'
10 |
11 | class AccountGroupOrders extends Component {
12 | componentDidMount() {
13 | if (this.props.groupOrder.list.length === 0) {
14 | this.props.getAccountGroupOrderList({ page: 1 })
15 | }
16 | }
17 |
18 | render() {
19 | const {
20 | groupOrder,
21 | navigateTo,
22 | getAccountGroupOrderList,
23 | deleteGroupOrder
24 | } = this.props
25 | return (
26 |
32 | )
33 | }
34 | }
35 |
36 | const mapStateToProps = (state, ownProps) => {
37 | return {
38 | groupOrder: state.groupOrder.account.groupOrder
39 | }
40 | }
41 |
42 | const mapDispatchToProps = (dispatch, ownProps) => {
43 | return {
44 | getAccountGroupOrderList: payload => {
45 | dispatch(getAccountGroupOrderListRequest(payload))
46 | },
47 | deleteGroupOrder: payload => {
48 | dispatch(deleteGroupOrderRequest(payload))
49 | },
50 | navigateTo: (path, params) => {
51 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
52 | }
53 | }
54 | }
55 |
56 | AccountGroupOrders.propTypes = {
57 | groupOrder: PropTypes.object,
58 | getAccountGroupOrderList: PropTypes.func,
59 | navigateTo: PropTypes.func,
60 | deleteGroupOrder: PropTypes.func
61 | }
62 |
63 | export default connect(mapStateToProps, mapDispatchToProps)(
64 | AccountGroupOrders
65 | )
66 |
--------------------------------------------------------------------------------
/src/screens/Account/ResumeOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | getAccountResumeOrderListRequest,
7 | deleteResumeOrderRequest
8 | } from '../../../actions'
9 | import { AccountResumeOrderList } from '../../../components'
10 |
11 | class AccountResumeOrders extends Component {
12 | componentDidMount() {
13 | if (this.props.resumeOrder.list.length === 0) {
14 | this.props.getAccountResumeOrderList({ page: 1 })
15 | }
16 | }
17 |
18 | render() {
19 | const {
20 | resumeOrder,
21 | navigateTo,
22 | getAccountResumeOrderList,
23 | deleteResumeOrder
24 | } = this.props
25 | return (
26 |
32 | )
33 | }
34 | }
35 |
36 | const mapStateToProps = (state, ownProps) => {
37 | return {
38 | resumeOrder: state.resumeOrder.account.resumeOrder
39 | }
40 | }
41 |
42 | const mapDispatchToProps = (dispatch, ownProps) => {
43 | return {
44 | getAccountResumeOrderList: payload => {
45 | dispatch(getAccountResumeOrderListRequest(payload))
46 | },
47 | deleteResumeOrder: payload => {
48 | dispatch(deleteResumeOrderRequest(payload))
49 | },
50 | navigateTo: (path, params) => {
51 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
52 | }
53 | }
54 | }
55 |
56 | AccountResumeOrders.propTypes = {
57 | resumeOrder: PropTypes.object,
58 | getAccountResumeOrderList: PropTypes.func,
59 | navigateTo: PropTypes.func,
60 | deleteResumeOrder: PropTypes.func
61 | }
62 |
63 | export default connect(mapStateToProps, mapDispatchToProps)(
64 | AccountResumeOrders
65 | )
66 |
--------------------------------------------------------------------------------
/src/screens/Account/RecruitOrders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { connect } from 'react-redux'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | getAccountRecruitOrderListRequest,
7 | deleteRecruitOrderRequest
8 | } from '../../../actions'
9 | import { AccountRecruitOrderList } from '../../../components'
10 |
11 | class AccountRecruitOrders extends Component {
12 | componentDidMount() {
13 | if (this.props.recruitOrder.list.length === 0) {
14 | this.props.getAccountRecruitOrderList({ page: 1 })
15 | }
16 | }
17 |
18 | render() {
19 | const {
20 | recruitOrder,
21 | navigateTo,
22 | getAccountRecruitOrderList,
23 | deleteRecruitOrder
24 | } = this.props
25 | return (
26 |
32 | )
33 | }
34 | }
35 |
36 | const mapStateToProps = (state, ownProps) => {
37 | return {
38 | recruitOrder: state.recruitOrder.account.recruitOrder
39 | }
40 | }
41 |
42 | const mapDispatchToProps = (dispatch, ownProps) => {
43 | return {
44 | getAccountRecruitOrderList: payload => {
45 | dispatch(getAccountRecruitOrderListRequest(payload))
46 | },
47 | deleteRecruitOrder: payload => {
48 | dispatch(deleteRecruitOrderRequest(payload))
49 | },
50 | navigateTo: (path, params) => {
51 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
52 | }
53 | }
54 | }
55 |
56 | AccountRecruitOrders.propTypes = {
57 | recruitOrder: PropTypes.object,
58 | getAccountRecruitOrderList: PropTypes.func,
59 | navigateTo: PropTypes.func,
60 | deleteRecruitOrder: PropTypes.func
61 | }
62 |
63 | export default connect(mapStateToProps, mapDispatchToProps)(
64 | AccountRecruitOrders
65 | )
66 |
--------------------------------------------------------------------------------
/src/sagas/commonSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { Toast } from 'antd-mobile'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | POST_UPLOAD_REQUEST,
7 | SEND_EMAIL_REQUEST,
8 | SEND_PASSWORD_RESET_REQUEST
9 | } from '../constants/actionTypes'
10 | import * as action from '../actions'
11 | import { commonService, userService } from '../services/leanclound'
12 |
13 | function* postUploadWorker(payload) {
14 | try {
15 | Toast.loading('上传中')
16 | const response = yield call(commonService.uploadPic, payload)
17 | yield put(action.postUploadSuccess(response))
18 | Toast.success('上传成功', 1)
19 | } catch (error) {
20 | yield put(action.postUploadFailed(error))
21 | Toast.fail('上传失败', 1)
22 | }
23 | }
24 |
25 | function* sendEmailWorker(payload) {
26 | try {
27 | Toast.loading('邮件发送中')
28 | const response = yield call(commonService.requestEmailVerify, payload)
29 | yield put(action.sendEmailSuccess(response))
30 | Toast.success('发送成功', 1)
31 | } catch (error) {
32 | yield put(action.sendEmailFailed(error))
33 | Toast.fail('发送失败', 1)
34 | }
35 | }
36 |
37 | function* sendPasswordResetWorker(payload) {
38 | try {
39 | Toast.loading('上传中')
40 | const response = yield call(commonService.requestPasswordReset, payload)
41 | yield put(action.sendPasswordResetSuccess(response))
42 | yield call(userService.logOut)
43 | yield put(NavigationActions.navigate('Home'))
44 | Toast.success('重置密码的邮件已发送', 1.5)
45 | } catch (error) {
46 | yield put(action.sendPasswordResetFailed(error))
47 | Toast.fail('重置密码的邮件发送失败', 1.5)
48 | }
49 | }
50 |
51 | function* watchUpload() {
52 | while (true) {
53 | const { payload } = yield take(POST_UPLOAD_REQUEST)
54 | yield fork(postUploadWorker, payload)
55 | }
56 | }
57 |
58 | function* watchSendEmail() {
59 | while (true) {
60 | const { payload } = yield take(SEND_EMAIL_REQUEST)
61 | yield fork(sendEmailWorker, payload)
62 | }
63 | }
64 |
65 | function* watchSendPasswordReset() {
66 | while (true) {
67 | const { payload } = yield take(SEND_PASSWORD_RESET_REQUEST)
68 | yield fork(sendPasswordResetWorker, payload)
69 | }
70 | }
71 |
72 | export { watchUpload, watchSendEmail, watchSendPasswordReset }
73 |
--------------------------------------------------------------------------------
/src/components/HomeTeamList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeTeamCard from '../HomeTeamCard'
6 |
7 | export default class HomeTeamList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.team.isFetching || !this.props.team.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.team.page + 1
22 | this.props.getHomeTeamList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getHomeTeamList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.team.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return
41 | }
42 |
43 | _renderSeparator() {
44 | return
45 | }
46 |
47 | _getItemLayout(data, index) {
48 | let [length, separator, header] = [200, 3, 0]
49 | return { length, offset: (length + separator) * index + header, index }
50 | }
51 |
52 | _keyExtractor = (item, index) => item.objectId
53 |
54 | render() {
55 | const { list, isRefreshing, fetchingText, isFetching } = this.props.team
56 | return (
57 |
71 | )
72 | }
73 | }
74 |
75 | HomeTeamList.propTypes = {
76 | team: PropTypes.object,
77 | navigateTo: PropTypes.func,
78 | getHomeTeamList: PropTypes.func
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/HomeWarOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeWarCard from '../HomeWarCard'
6 |
7 | export default class HomeWarOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.warOrder.isFetching || !this.props.warOrder.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.warOrder.page + 1
22 | this.props.getHomeWarOrderList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getHomeWarOrderList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.warOrder.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return
41 | }
42 |
43 | _renderSeparator() {
44 | return
45 | }
46 |
47 | _getItemLayout(data, index) {
48 | let [length, separator, header] = [200, 3, 0]
49 | return { length, offset: (length + separator) * index + header, index }
50 | }
51 |
52 | _keyExtractor = (item, index) => item.objectId
53 |
54 | render() {
55 | const { list, isRefreshing, fetchingText, isFetching } = this.props.warOrder
56 | return (
57 |
71 | )
72 | }
73 | }
74 |
75 | HomeWarOrderList.propTypes = {
76 | warOrder: PropTypes.object,
77 | navigateTo: PropTypes.func,
78 | getHomeWarOrderList: PropTypes.func
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/HomeUserInfoList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeUserInfoCard from '../HomeUserInfoCard'
6 |
7 | export default class HomeUserInfoList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.userinfo.isFetching || !this.props.userinfo.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.userinfo.page + 1
22 | this.props.getHomeUserInfoList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getHomeUserInfoList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.userinfo.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return
41 | }
42 |
43 | _renderSeparator() {
44 | return
45 | }
46 |
47 | _getItemLayout(data, index) {
48 | let [length, separator, header] = [200, 3, 0]
49 | return { length, offset: (length + separator) * index + header, index }
50 | }
51 |
52 | _keyExtractor = (item, index) => item.objectId
53 |
54 | render() {
55 | const { list, isRefreshing, fetchingText, isFetching } = this.props.userinfo
56 | return (
57 |
71 | )
72 | }
73 | }
74 |
75 | HomeUserInfoList.propTypes = {
76 | userinfo: PropTypes.object,
77 | navigateTo: PropTypes.func,
78 | getHomeUserInfoList: PropTypes.func
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/HomeGroupOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeGroupCard from '../HomeGroupCard'
6 |
7 | export default class HomeGroupOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.groupOrder.isFetching || !this.props.groupOrder.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.groupOrder.page + 1
22 | this.props.getHomeGroupOrderList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getHomeGroupOrderList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.groupOrder.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return
41 | }
42 |
43 | _renderSeparator() {
44 | return
45 | }
46 |
47 | _getItemLayout(data, index) {
48 | let [length, separator, header] = [197, 3, 0]
49 | return { length, offset: (length + separator) * index + header, index }
50 | }
51 |
52 | _keyExtractor = (item, index) => item.objectId
53 |
54 | render() {
55 | const {
56 | list,
57 | isRefreshing,
58 | fetchingText,
59 | isFetching
60 | } = this.props.groupOrder
61 | return (
62 |
77 | )
78 | }
79 | }
80 |
81 | HomeGroupOrderList.propTypes = {
82 | groupOrder: PropTypes.object,
83 | navigateTo: PropTypes.func,
84 | getHomeGroupOrderList: PropTypes.func
85 | }
86 |
--------------------------------------------------------------------------------
/src/components/HomeRecruitOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeRecruitCard from '../HomeRecruitCard'
6 |
7 | export default class HomeRecruitOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (
19 | this.props.recruitOrder.isFetching ||
20 | !this.props.recruitOrder.isLoadMore
21 | ) {
22 | return
23 | }
24 | const page = this.props.recruitOrder.page + 1
25 | this.props.getHomeRecruitOrderList({ page: page })
26 | }
27 |
28 | _onRefresh() {
29 | this.props.getHomeRecruitOrderList({ isRefreshing: true })
30 | }
31 |
32 | _renderFonter() {
33 | return (
34 |
35 |
36 | {this.props.recruitOrder.isFetching ? '' : '到底了'}
37 |
38 |
39 | )
40 | }
41 |
42 | _renderItem({ item }) {
43 | return
44 | }
45 |
46 | _renderSeparator() {
47 | return
48 | }
49 |
50 | _getItemLayout(data, index) {
51 | let [length, separator, header] = [197, 3, 0]
52 | return { length, offset: (length + separator) * index + header, index }
53 | }
54 |
55 | _keyExtractor = (item, index) => item.objectId
56 |
57 | render() {
58 | const {
59 | list,
60 | isRefreshing,
61 | fetchingText,
62 | isFetching
63 | } = this.props.recruitOrder
64 | return (
65 |
79 | )
80 | }
81 | }
82 |
83 | HomeRecruitOrderList.propTypes = {
84 | recruitOrder: PropTypes.object,
85 | navigateTo: PropTypes.func,
86 | getHomeRecruitOrderList: PropTypes.func
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/HomeResumeOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import HomeResumeCard from '../HomeResumeCard'
6 |
7 | export default class HomeResumeOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (
19 | this.props.resumeOrder.isFetching ||
20 | !this.props.resumeOrder.isLoadMore
21 | ) {
22 | return
23 | }
24 | const page = this.props.resumeOrder.page + 1
25 | this.props.getHomeResumeOrderList({ page: page })
26 | }
27 |
28 | _onRefresh() {
29 | this.props.getHomeResumeOrderList({ isRefreshing: true })
30 | }
31 |
32 | _renderFonter() {
33 | return (
34 |
35 |
36 | {this.props.resumeOrder.isFetching ? '' : '到底了'}
37 |
38 |
39 | )
40 | }
41 |
42 | _renderItem({ item }) {
43 | return
44 | }
45 |
46 | _renderSeparator() {
47 | return
48 | }
49 |
50 | _getItemLayout(data, index) {
51 | let [length, separator, header] = [197, 3, 0]
52 | return { length, offset: (length + separator) * index + header, index }
53 | }
54 |
55 | _keyExtractor = (item, index) => item.objectId
56 |
57 | render() {
58 | const {
59 | list,
60 | isRefreshing,
61 | fetchingText,
62 | isFetching
63 | } = this.props.resumeOrder
64 | return (
65 |
80 | )
81 | }
82 | }
83 |
84 | HomeResumeOrderList.propTypes = {
85 | resumeOrder: PropTypes.object,
86 | navigateTo: PropTypes.func,
87 | getHomeResumeOrderList: PropTypes.func
88 | }
89 |
--------------------------------------------------------------------------------
/src/components/AccountWarOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import AccountWarCard from '../AccountWarCard'
6 |
7 | export default class AccountWarOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.warOrder.isFetching || !this.props.warOrder.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.warOrder.page + 1
22 | this.props.getAccountWarOrderList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getAccountWarOrderList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.warOrder.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return (
41 |
46 | )
47 | }
48 |
49 | _renderSeparator() {
50 | return
51 | }
52 |
53 | _getItemLayout(data, index) {
54 | let [length, separator, header] = [197, 3, 0]
55 | return { length, offset: (length + separator) * index + header, index }
56 | }
57 |
58 | _keyExtractor = (item, index) => item.objectId
59 |
60 | render() {
61 | const { list, isRefreshing, fetchingText, isFetching } = this.props.warOrder
62 | return (
63 |
78 | )
79 | }
80 | }
81 |
82 | AccountWarOrderList.propTypes = {
83 | warOrder: PropTypes.object,
84 | navigateTo: PropTypes.func,
85 | getAccountWarOrderList: PropTypes.func,
86 | deleteWarOrder: PropTypes.func
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/AccountGroupOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import AccountGroupCard from '../AccountGroupCard'
6 |
7 | export default class AccountGroupOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (this.props.groupOrder.isFetching || !this.props.groupOrder.isLoadMore) {
19 | return
20 | }
21 | const page = this.props.groupOrder.page + 1
22 | this.props.getAccountGroupOrderList({ page: page })
23 | }
24 |
25 | _onRefresh() {
26 | this.props.getAccountGroupOrderList({ isRefreshing: true })
27 | }
28 |
29 | _renderFonter() {
30 | return (
31 |
32 |
33 | {this.props.groupOrder.isFetching ? '' : '到底了'}
34 |
35 |
36 | )
37 | }
38 |
39 | _renderItem({ item }) {
40 | return (
41 |
46 | )
47 | }
48 |
49 | _renderSeparator() {
50 | return
51 | }
52 |
53 | _getItemLayout(data, index) {
54 | let [length, separator, header] = [197, 3, 0]
55 | return { length, offset: (length + separator) * index + header, index }
56 | }
57 |
58 | _keyExtractor = (item, index) => item.objectId
59 |
60 | render() {
61 | const {
62 | list,
63 | isRefreshing,
64 | fetchingText,
65 | isFetching
66 | } = this.props.groupOrder
67 | return (
68 |
82 | )
83 | }
84 | }
85 |
86 | AccountGroupOrderList.propTypes = {
87 | groupOrder: PropTypes.object,
88 | navigateTo: PropTypes.func,
89 | getAccountGroupOrderList: PropTypes.func,
90 | deleteGroupOrder: PropTypes.func
91 | }
92 |
--------------------------------------------------------------------------------
/src/components/AccountResumeOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import AccountResumeCard from '../AccountResumeCard'
6 |
7 | export default class AccountResumeOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (
19 | this.props.resumeOrder.isFetching ||
20 | !this.props.resumeOrder.isLoadMore
21 | ) {
22 | return
23 | }
24 | const page = this.props.resumeOrder.page + 1
25 | this.props.getAccountResumeOrderList({ page: page })
26 | }
27 |
28 | _onRefresh() {
29 | this.props.getAccountResumeOrderList({ isRefreshing: true })
30 | }
31 |
32 | _renderFonter() {
33 | return (
34 |
35 |
36 | {this.props.resumeOrder.isFetching ? '' : '到底了'}
37 |
38 |
39 | )
40 | }
41 |
42 | _renderItem({ item }) {
43 | return (
44 |
49 | )
50 | }
51 |
52 | _renderSeparator() {
53 | return
54 | }
55 |
56 | _getItemLayout(data, index) {
57 | let [length, separator, header] = [197, 3, 0]
58 | return { length, offset: (length + separator) * index + header, index }
59 | }
60 |
61 | _keyExtractor = (item, index) => item.objectId
62 |
63 | render() {
64 | const {
65 | list,
66 | isRefreshing,
67 | fetchingText,
68 | isFetching
69 | } = this.props.resumeOrder
70 | return (
71 |
85 | )
86 | }
87 | }
88 |
89 | AccountResumeOrderList.propTypes = {
90 | resumeOrder: PropTypes.object,
91 | navigateTo: PropTypes.func,
92 | getAccountResumeOrderList: PropTypes.func,
93 | deleteResumeOrder: PropTypes.func
94 | }
95 |
--------------------------------------------------------------------------------
/src/components/HomeResumeCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 |
7 | import { cutstr } from '../../utils/utils'
8 |
9 | export default class HomeResumeCard extends PureComponent {
10 |
11 | render() {
12 | const { item, navigateTo } = this.props
13 | return (
14 |
16 | navigateTo('HomeUserInfoDetail', {
17 | objectId: item.user.userinfo.objectId
18 | })
19 | }
20 | >
21 |
22 |
23 |
33 |
34 |
35 |
36 |
37 | {item.user.userinfo.nickname}
38 |
39 |
40 |
41 |
42 | {item.contact}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
55 | {cutstr(item.description, 60, 0)}
56 |
57 |
58 |
59 |
60 |
67 | 发布时间:
68 |
69 | }
70 | extra={
71 |
72 | 有效日期:
73 |
74 | }
75 | style={{ paddingBottom: 5 }}
76 | />
77 |
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | HomeResumeCard.propTypes = {
85 | navigateTo: PropTypes.func,
86 | item: PropTypes.object
87 | }
88 |
--------------------------------------------------------------------------------
/src/sagas/index.js:
--------------------------------------------------------------------------------
1 | import { fork, all } from 'redux-saga/effects'
2 | import {
3 | watchSignUp,
4 | watchLogin,
5 | watchLogout,
6 | watchPutUserInfo,
7 | watchGetUserInfo,
8 | watchGetHomeUserList,
9 | watchGetHomeUserDetail,
10 | watchGetCurrentUser
11 | } from './userSaga'
12 | import {
13 | watchPostTeams,
14 | watchPutTeams,
15 | watchDeleteTeamMember,
16 | watchDeleteTeam,
17 | watchGetHomeTeamList,
18 | watchGetHomeTeamDetail,
19 | watchGetMyTeams,
20 | watchGetInTeams
21 | } from './teamsSaga'
22 | import {
23 | watchUpload,
24 | watchSendEmail,
25 | watchSendPasswordReset
26 | } from './commonSaga'
27 | import {
28 | watchGetAccountRecruitOrderList,
29 | watchGetHomeRecruitOrderList,
30 | watchPostRecruitOrder,
31 | watchPutRecruitOrder,
32 | watchDeleteRecruitOrder
33 | } from './recruitOrderSaga'
34 | import {
35 | watchGetAccountGroupOrderList,
36 | watchGetHomeGroupOrderList,
37 | watchPostGroupOrder,
38 | watchPutGroupOrder,
39 | watchDeleteGroupOrder
40 | } from './groupOrderSaga'
41 | import {
42 | watchGetAccountWarOrderList,
43 | watchGetHomeWarOrderList,
44 | watchPostWarOrder,
45 | watchPutWarOrder,
46 | watchDeleteWarOrder
47 | } from './warOrderSaga'
48 | import {
49 | watchGetAccountResumeOrderList,
50 | watchGetHomeResumeOrderList,
51 | watchPostResumeOrder,
52 | watchPutResumeOrder,
53 | watchDeleteResumeOrder
54 | } from './resumeOrderSaga'
55 |
56 | export default function* rootSaga() {
57 | yield all([
58 | fork(watchGetCurrentUser),
59 | fork(watchLogin),
60 | fork(watchLogout),
61 | fork(watchSignUp),
62 | fork(watchUpload),
63 | fork(watchPutUserInfo),
64 | fork(watchGetUserInfo),
65 | fork(watchPostTeams),
66 | fork(watchPutTeams),
67 | fork(watchDeleteTeamMember),
68 | fork(watchDeleteTeam),
69 | fork(watchGetHomeUserList),
70 | fork(watchGetHomeUserDetail),
71 | fork(watchGetHomeTeamList),
72 | fork(watchGetHomeTeamDetail),
73 | fork(watchGetMyTeams),
74 | fork(watchGetInTeams),
75 | fork(watchGetAccountRecruitOrderList),
76 | fork(watchGetHomeRecruitOrderList),
77 | fork(watchPostRecruitOrder),
78 | fork(watchPutRecruitOrder),
79 | fork(watchDeleteRecruitOrder),
80 | fork(watchGetAccountGroupOrderList),
81 | fork(watchGetHomeGroupOrderList),
82 | fork(watchPostGroupOrder),
83 | fork(watchPutGroupOrder),
84 | fork(watchDeleteGroupOrder),
85 | fork(watchGetAccountWarOrderList),
86 | fork(watchGetHomeWarOrderList),
87 | fork(watchPostWarOrder),
88 | fork(watchPutWarOrder),
89 | fork(watchDeleteWarOrder),
90 | fork(watchGetAccountResumeOrderList),
91 | fork(watchGetHomeResumeOrderList),
92 | fork(watchPostResumeOrder),
93 | fork(watchPutResumeOrder),
94 | fork(watchDeleteResumeOrder),
95 | fork(watchSendEmail),
96 | fork(watchSendPasswordReset),
97 | ])
98 | }
99 |
--------------------------------------------------------------------------------
/src/components/HomeGroupCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 |
7 | import { cutstr } from '../../utils/utils'
8 |
9 | export default class HomeGroupCard extends PureComponent {
10 | render() {
11 | const { item, navigateTo } = this.props
12 | return (
13 |
15 | navigateTo('HomeUserInfoDetail', {
16 | objectId: item.user.userinfo.objectId
17 | })
18 | }
19 | >
20 |
21 |
22 |
32 |
33 |
34 |
35 |
36 | {item.user.userinfo.nickname}
37 |
38 |
39 |
40 |
41 | {item.contact}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
54 | {cutstr(item.description, 60, 0)}
55 |
56 |
57 |
58 |
59 |
66 | 发布时间:
67 |
68 | }
69 | extra={
70 |
71 | 有效日期:
72 |
73 | }
74 | style={{ paddingBottom: 5 }}
75 | />
76 |
77 |
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | HomeGroupCard.propTypes = {
85 | navigateTo: PropTypes.func,
86 | item: PropTypes.object
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/HomeWarCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 |
7 | export default class HomeWarCard extends PureComponent {
8 | render() {
9 | const { item, navigateTo } = this.props
10 | return (
11 |
13 | navigateTo('HomeTeamDetail', {
14 | objectId: item.team.objectId
15 | })
16 | }
17 | >
18 |
19 |
20 |
30 |
31 |
32 |
33 |
34 | {item.team.englishFullName ||
35 | item.team.chineseFullName ||
36 | item.team.englishName ||
37 | item.team.chineseName}
38 |
39 |
40 |
41 |
42 | {item.contact}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
55 | {item.description}
56 |
57 |
58 |
59 |
60 |
67 | 发布时间:
68 |
69 | }
70 | extra={
71 |
72 | 有效日期:
73 |
74 | }
75 | style={{ paddingBottom: 5 }}
76 | />
77 |
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | HomeWarCard.propTypes = {
85 | navigateTo: PropTypes.func,
86 | item: PropTypes.object
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/AccountRecruitOrderList/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { FlatList, View, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { WhiteSpace } from 'antd-mobile'
5 | import AccountRecruitCard from '../AccountRecruitCard'
6 |
7 | export default class AccountRecruitOrderList extends PureComponent {
8 | constructor(props) {
9 | super(props)
10 | this._onEndReached = this._onEndReached.bind(this)
11 | this._onRefresh = this._onRefresh.bind(this)
12 | this._renderFonter = this._renderFonter.bind(this)
13 | this._renderSeparator = this._renderSeparator.bind(this)
14 | this._renderItem = this._renderItem.bind(this)
15 | }
16 |
17 | _onEndReached() {
18 | if (
19 | this.props.recruitOrder.isFetching ||
20 | !this.props.recruitOrder.isLoadMore
21 | ) {
22 | return
23 | }
24 | const page = this.props.recruitOrder.page + 1
25 | this.props.getAccountRecruitOrderList({ page: page })
26 | }
27 |
28 | _onRefresh() {
29 | this.props.getAccountRecruitOrderList({ isRefreshing: true })
30 | }
31 |
32 | _renderFonter() {
33 | return (
34 |
35 |
36 | {this.props.recruitOrder.isFetching ? '' : '到底了'}
37 |
38 |
39 | )
40 | }
41 |
42 | _renderItem({ item }) {
43 | return (
44 |
49 | )
50 | }
51 |
52 | _renderSeparator() {
53 | return
54 | }
55 |
56 | _getItemLayout(data, index) {
57 | let [length, separator, header] = [197, 3, 0]
58 | return { length, offset: (length + separator) * index + header, index }
59 | }
60 |
61 | _keyExtractor = (item, index) => item.objectId
62 |
63 | render() {
64 | const {
65 | list,
66 | isRefreshing,
67 | fetchingText,
68 | isFetching
69 | } = this.props.recruitOrder
70 | return (
71 |
86 | )
87 | }
88 | }
89 |
90 | AccountRecruitOrderList.propTypes = {
91 | recruitOrder: PropTypes.object,
92 | navigateTo: PropTypes.func,
93 | getAccountRecruitOrderList: PropTypes.func,
94 | deleteRecruitOrder: PropTypes.func
95 | }
96 |
--------------------------------------------------------------------------------
/src/components/HomeRecruitCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 |
7 | import { cutstr } from '../../utils/utils'
8 |
9 | export default class HomeRecruitCard extends PureComponent {
10 | render() {
11 | const { item, navigateTo } = this.props
12 | return (
13 |
15 | navigateTo('HomeTeamDetail', {
16 | objectId: item.team.objectId
17 | })
18 | }
19 | >
20 |
21 |
22 |
32 |
33 |
34 |
35 |
36 | {item.team.englishFullName ||
37 | item.team.chineseFullName ||
38 | item.team.englishName ||
39 | item.team.chineseName}
40 |
41 |
42 |
43 |
44 | {item.contact}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
56 | {item.description}
57 |
58 |
59 |
60 |
61 |
69 | 发布时间:
70 |
71 | }
72 | extra={
73 |
74 | 有效日期:
75 |
76 | }
77 | style={{ paddingBottom: 5 }}
78 | />
79 |
80 |
81 |
82 |
83 | )
84 | }
85 | }
86 |
87 | HomeRecruitCard.propTypes = {
88 | navigateTo: PropTypes.func,
89 | item: PropTypes.object
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/HomeTeamCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 |
7 | import { CardStyle } from '../../components/CustomStyles'
8 | import { cutstr } from '../../utils/utils'
9 |
10 | export default class HomeTeamCard extends PureComponent {
11 | render() {
12 | const { item, navigateTo } = this.props
13 | return (
14 |
16 | navigateTo('HomeTeamDetail', { objectId: item.objectId })
17 | }
18 | >
19 |
20 |
21 |
36 |
37 |
38 |
39 |
40 | {cutstr(item.introduction, 400, 0)}
41 |
42 |
43 |
44 | {item.isRecruit ? (
45 |
46 |
47 |
48 | {item.isRecruit ? (
49 |
50 | 正在招募
51 |
52 | ) : (
53 | 暂无招募
54 | )}
55 |
56 |
57 |
58 |
59 |
60 |
61 | 联系方式:{item.contact ? item.contact : '暂无'}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 战队地点:{item.createCity ? item.createCity : '暂无'}
70 |
71 |
72 |
73 |
74 | ) : null}
75 |
76 |
77 |
78 |
79 | )
80 | }
81 | }
82 |
83 | HomeTeamCard.propTypes = {
84 | navigateTo: PropTypes.func,
85 | item: PropTypes.object
86 | }
87 |
--------------------------------------------------------------------------------
/src/components/HomeUserInfoCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View, TouchableWithoutFeedback, Image } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace, Grid } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 | import { RANKS, TEAMPOSITIONS } from '../../constants'
7 |
8 | export default class HomeUserInfoCard extends PureComponent {
9 | layout = e => {
10 | console.log(e)
11 | }
12 |
13 | render() {
14 | const { item, navigateTo } = this.props
15 | return (
16 |
18 | navigateTo('HomeUserDetail', {
19 | objectId: item.objectId
20 | })
21 | }
22 | >
23 | this.layout(e)}>
24 |
25 |
35 |
36 |
37 |
38 |
39 | 天梯:{item.rankscore ? item.rankscore + '分' : '未知'}
40 |
41 |
42 |
43 |
44 | 段位:{item.rank
45 | ? RANKS.filter(x => x.value === item.rank)[0].label
46 | : '未知'}
47 |
48 |
49 |
50 |
51 | 位置:{item.position
52 | ? TEAMPOSITIONS.filter(x => x.value === item.position)[0]
53 | .label
54 | : '未知'}
55 |
56 |
57 |
58 |
59 | {item.heros ? (
60 | (
65 |
66 |
70 |
71 | )}
72 | />
73 | ) : (
74 |
75 | )}
76 |
77 |
78 |
79 | {item.introduction}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | )
88 | }
89 | }
90 |
91 | HomeUserInfoCard.propTypes = {
92 | navigateTo: PropTypes.func,
93 | item: PropTypes.object
94 | }
95 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Provider, connect } from 'react-redux'
3 | import { PersistGate } from 'redux-persist/es/integration/react'
4 | import { BackHandler, Platform, StatusBar, View } from 'react-native'
5 | import { addNavigationHelpers, NavigationActions } from 'react-navigation'
6 | import { AppLoading, Asset, Font } from 'expo'
7 | import { Ionicons } from '@expo/vector-icons'
8 | import { Toast } from 'antd-mobile'
9 | import Navigator from './routes'
10 | import configureStore from './store'
11 | import rootSaga from './sagas'
12 |
13 | function cacheImages(images) {
14 | return images.map(image => {
15 | if (typeof image === 'string') {
16 | return Image.prefetch(image)
17 | } else {
18 | return Asset.fromModule(image).downloadAsync()
19 | }
20 | })
21 | }
22 |
23 | function cacheFonts(fonts) {
24 | return fonts.map(font => Font.loadAsync(font))
25 | }
26 |
27 | class App extends Component {
28 | state = {
29 | isReady: false
30 | }
31 | async _loadAssetsAsync() {
32 | const imageAssets = cacheImages([
33 | require('../assets/images/home.png'),
34 | require('../assets/images/home_icon.png'),
35 | require('../assets/images/avatar_logo.png')
36 | ])
37 | const fontAssets = cacheFonts([Ionicons.font])
38 | await Promise.all([...imageAssets, ...fontAssets])
39 | }
40 |
41 | onBackPress = () => {
42 | const { dispatch, nav } = this.props
43 | if (nav.routes[0].index === 0) {
44 | if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
45 | BackHandler.exitApp()
46 | return true
47 | }
48 | this.lastBackPressed = Date.now()
49 | Toast.show('再按一次退出应用')
50 | return true
51 | } else {
52 | dispatch(NavigationActions.back())
53 | return true
54 | }
55 | }
56 |
57 | componentWillUnmount() {
58 | BackHandler.removeEventListener('hardwareBackPress', this.onBackPress)
59 | }
60 |
61 | componentDidMount() {
62 | if (Platform.OS === 'android') {
63 | BackHandler.addEventListener('hardwareBackPress', this.onBackPress)
64 | }
65 | }
66 |
67 | render() {
68 | const { dispatch, nav } = this.props
69 | if (!this.state.isReady) {
70 | return (
71 | {
74 | this.setState({ isReady: true })
75 | }}
76 | onError={console.warn}
77 | />
78 | )
79 | }
80 | return (
81 |
82 |
88 |
89 | )
90 | }
91 | }
92 |
93 | const mapStateToProps = state => ({
94 | nav: state.nav
95 | })
96 |
97 | const AppWithNavigator = connect(mapStateToProps)(App)
98 |
99 | export default () => {
100 | const { persistor, store } = configureStore({})
101 |
102 | store.runSaga(rootSaga)
103 |
104 | return (
105 |
106 |
107 |
108 |
109 |
110 | )
111 | }
112 |
--------------------------------------------------------------------------------
/src/services/leanclound/groupOrder.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import { getCurrentUserAsync } from './user'
3 | import { getDayStart, getDayEnd } from '../../utils/utils'
4 |
5 | // 创建组队帖
6 | export function cerateGroupOrder(payload, userinfo, currentUser) {
7 | const groupOrders = new AV.Object('GroupOrders')
8 | groupOrders.set('title', payload.title)
9 | groupOrders.set('description', payload.description)
10 | groupOrders.set('contact', payload.contact)
11 | const endDate = new Date(payload.endDate)
12 | groupOrders.set('endDate', endDate)
13 | groupOrders.set('user', currentUser)
14 | groupOrders.set('stick', 0)
15 | groupOrders.set('show', 1)
16 |
17 | var acl = new AV.ACL()
18 | acl.setPublicReadAccess(true)
19 | acl.setWriteAccess(currentUser, true)
20 | groupOrders.setACL(acl)
21 |
22 | return groupOrders.save().then(function(result) {
23 | return { ...result.toJSON(), userinfo }
24 | })
25 | }
26 |
27 | export function updateGroupOrder(payload) {
28 | const groupOrders = AV.Object.createWithoutData(
29 | 'GroupOrders',
30 | payload.objectId
31 | )
32 | groupOrders.set('title', payload.title)
33 | groupOrders.set('description', payload.description)
34 | groupOrders.set('contact', payload.contact)
35 | const endDate = new Date(payload.endDate)
36 | groupOrders.set('endDate', endDate)
37 |
38 | return groupOrders.save().then(function(result) {
39 | return {
40 | ...result.toJSON()
41 | }
42 | })
43 | }
44 |
45 | export function removeGroupOrder(payload) {
46 | var groupOrders = AV.Object.createWithoutData(
47 | 'GroupOrders',
48 | payload.objectId
49 | )
50 | return groupOrders.destroy().then(function(success) {
51 | return success.toJSON()
52 | })
53 | }
54 |
55 | export function getAccountGroupOrderList(payload, currentUser) {
56 | let list = []
57 | let { page, pagesize } = payload
58 | pagesize = pagesize || 20
59 | const query = new AV.Query('GroupOrders')
60 | query.equalTo('user', currentUser)
61 | query.greaterThanOrEqualTo('endDate', new Date())
62 | query.descending('updatedAt')
63 | query.limit(pagesize)
64 | query.skip(pagesize * (page - 1))
65 | query.include('user.userinfo')
66 | return query.find().then(function(result) {
67 | result.forEach(item => {
68 | list.push(item.toJSON())
69 | })
70 | return list
71 | })
72 | }
73 |
74 | export function getHomeGroupOrderList(payload) {
75 | let list = []
76 | let { page, pagesize } = payload
77 | pagesize = pagesize || 20
78 | const query = new AV.Query('GroupOrders')
79 | query.equalTo('show', 1)
80 | query.greaterThanOrEqualTo('endDate', new Date())
81 | query.descending('stick')
82 | query.descending('createdAt')
83 | query.limit(pagesize)
84 | query.skip(pagesize * (page - 1))
85 | query.include('user.userinfo')
86 | return query.find().then(function(result) {
87 | result.forEach(item => {
88 | list.push(item.toJSON())
89 | })
90 | return list
91 | })
92 | }
93 |
94 | export function getGroupOrderCountOfToday(currentUser) {
95 | const query = new AV.Query('GroupOrders')
96 | query.equalTo('user', currentUser)
97 | query.lessThanOrEqualTo('createdAt', getDayEnd())
98 | query.greaterThanOrEqualTo('createdAt', getDayStart())
99 | return query.count().then(function(result) {
100 | return result
101 | })
102 | }
103 |
--------------------------------------------------------------------------------
/src/services/leanclound/resumeOrder.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import { getCurrentUserAsync } from './user'
3 | import { getDayStart, getDayEnd } from '../../utils/utils'
4 |
5 | export function cerateResumeOrder(payload, userinfo, currentUser) {
6 | const resumeOrders = new AV.Object('ResumeOrders')
7 | resumeOrders.set('title', payload.title)
8 | resumeOrders.set('description', payload.description)
9 | resumeOrders.set('contact', payload.contact)
10 | const endDate = new Date(payload.endDate)
11 | resumeOrders.set('endDate', endDate)
12 | resumeOrders.set('user', currentUser)
13 | resumeOrders.set('stick', 0)
14 | resumeOrders.set('show', 1)
15 |
16 | var acl = new AV.ACL()
17 | acl.setPublicReadAccess(true)
18 | acl.setWriteAccess(currentUser, true)
19 |
20 | resumeOrders.setACL(acl)
21 |
22 | return resumeOrders.save().then(function(result) {
23 | return { ...result.toJSON(), userinfo }
24 | })
25 | }
26 |
27 | export function updateResumeOrder(payload) {
28 | const resumeOrders = AV.Object.createWithoutData(
29 | 'ResumeOrders',
30 | payload.objectId
31 | )
32 | resumeOrders.set('title', payload.title)
33 | resumeOrders.set('description', payload.description)
34 | resumeOrders.set('contact', payload.contact)
35 | const endDate = new Date(payload.endDate)
36 | resumeOrders.set('endDate', endDate)
37 |
38 | return resumeOrders.save().then(function(result) {
39 | return {
40 | ...result.toJSON()
41 | }
42 | })
43 | }
44 |
45 | export function removeResumeOrder(payload) {
46 | var resumeOrders = AV.Object.createWithoutData(
47 | 'ResumeOrders',
48 | payload.objectId
49 | )
50 | return resumeOrders.destroy().then(function(success) {
51 | return success.toJSON()
52 | })
53 | }
54 |
55 | export function getAccountResumeOrderList(payload, currentUser) {
56 | let list = []
57 | let { page, pagesize } = payload
58 | pagesize = pagesize || 20
59 | const query = new AV.Query('ResumeOrders')
60 | query.descending('updatedAt')
61 | query.limit(pagesize)
62 | query.skip(pagesize * (page - 1))
63 | query.equalTo('user', currentUser)
64 | query.greaterThanOrEqualTo('endDate', new Date())
65 | query.include('user.userinfo')
66 | return query.find().then(function(result) {
67 | result.forEach(item => {
68 | list.push(item.toJSON())
69 | })
70 | return list
71 | })
72 | }
73 |
74 | export function getHomeResumeOrderList(payload) {
75 | let list = []
76 | let { page, pagesize } = payload
77 | pagesize = pagesize || 20
78 | const query = new AV.Query('ResumeOrders')
79 | query.equalTo('show', 1)
80 | query.greaterThanOrEqualTo('endDate', new Date())
81 | query.descending('stick')
82 | query.descending('createdAt')
83 | query.limit(pagesize)
84 | query.skip(pagesize * (page - 1))
85 | query.include('user.userinfo')
86 | return query.find().then(function(result) {
87 | result.forEach(item => {
88 | list.push(item.toJSON())
89 | })
90 | return list
91 | })
92 | }
93 |
94 | export function getResumeOrderCountOfToday(currentUser) {
95 | const query = new AV.Query('ResumeOrders')
96 | query.equalTo('user', currentUser)
97 | query.lessThanOrEqualTo('createdAt', getDayEnd())
98 | query.greaterThanOrEqualTo('createdAt', getDayStart())
99 | return query.count().then(function(result) {
100 | return result
101 | })
102 | }
103 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OverWatchTeams-React-Native-Expo
2 |
3 | ## iOS演示
4 |
5 | 
6 |
7 | ## 开源地址
8 |
9 | [GitHub](https://github.com/zxj963577494/OverWatchTeams-React-Native-Expo)
10 |
11 | [Gitee](https://gitee.com/zhengxujiang/OverWatchTeams-React-Redux-Native)
12 |
13 | ## 项目地址
14 |
15 | ### Android 版
16 |
17 | 
18 |
19 | [Android](https://fir.im/yg3w)
20 |
21 | ### Expo 版(需 Expo 移动端([Android](https://play.google.com/store/apps/details?id=host.exp.exponent)/[iOS](https://itunes.com/apps/exponent)))
22 |
23 | 
24 |
25 | [Expo](https://exp.host/@linq/OverWatchTeams)
26 |
27 | ## 简介
28 |
29 | 项目基于[Expo](https://expo.io/)的 React Native 构建技术。
30 |
31 | Expo 是一个围绕 React Native 构建的免费开源工具链,可帮助您使用 JavaScript 和 React 构建本地 iOS 和 Android 项目
32 |
33 | [Expo 文档](https://docs.expo.io/versions/latest/index.html)
34 |
35 | ## 技术栈
36 |
37 | * Expo
38 | * React-Native
39 | * React-Navigation
40 | * Redux
41 | * Redux-Saga
42 | * LeanCloud
43 | * Antd-Mobile
44 |
45 | ## 项目结构
46 |
47 | 
48 |
49 | ## 准备工具
50 |
51 | 1. 获取 Expo 构建桌面客户端(XDE)
52 |
53 | * [macOS](https://xde-updates.exponentjs.com/download/mac)
54 |
55 | * [Windows64-bit](https://xde-updates.exponentjs.com/download/win32)
56 |
57 | * [Linux](https://xde-updates.exponentjs.com/download/linux-x86_64)
58 |
59 | 2. 获取 Expo 预览 iOS 或 Android 客户端
60 |
61 | * [Android](https://play.google.com/store/apps/details?id=host.exp.exponent)
62 |
63 | 
64 |
65 | * [iOS](https://itunes.com/apps/exponent)
66 |
67 | 
68 |
69 | 3. [参考资料](https://docs.expo.io/versions/latest/introduction/installation.html)
70 |
71 | ## 使用方式
72 |
73 | 1. git clone https://github.com/zxj963577494/OverWatchTeams-React-Native-Expo.git
74 |
75 | 2. 打开桌面客户端(XDE)加载本项目,启动本项目,点击 Share 获取二维码
76 |
77 | 3. 打开 iOS 或 Android 客户端,扫描 XDE 客户端二维码
78 |
79 | 4. [参考资料](https://docs.expo.io/versions/latest/introduction/xde-tour.html)
80 |
81 | ## 项目生成
82 |
83 | 1. yarn global add exp
84 |
85 | 2. exp build:ios / build:android
86 |
87 | 3. exp build:status
88 |
89 | 4. [参考资料](https://docs.expo.io/versions/latest/guides/exp-cli.html)
90 |
91 | ## 项目发布
92 |
93 | 1. yarn global add exp
94 |
95 | 2. exp publish
96 |
97 | ## 一些说明
98 |
99 | `exp build:ios/build:android`生成的是 JS Bundle 文件,Expo 会将该文件上传到 Expo 云端,由 Expo 构建 APP,使用`exp build:status`可以得到 APP 在云端构建的进度,构建完成后,会返回 APP 地址
100 |
101 | 使用 `exp build:ios` 时必须有$99 的开发者账户(我没有,所以没构建 iOS 版本),如果 apple id 开启了两步验证,需要加`--local-auth`
102 |
103 | `exp publish`用于发布 JS Bundle 文件,更改 app.json 文件的版本号,icon 之类,用户下载的 App 会自动同步发布时版本所改动的内容,类似于 CodePush 热更新。[参考资料 1](https://docs.expo.io/versions/latest/guides/publishing.html) [参考资料 2](https://docs.expo.io/versions/latest/guides/offline-support.html)
104 |
105 | Expo 无法像原生 React Native 一样,可以对本地模块进行操作,当然`react-native link` 命令也是无法使用的,但你可以使用 Expo 开放的操作本地功能的 API,[SDK API 参考](https://docs.expo.io/versions/latest/sdk/index.html)
106 |
107 | ## 开源协议
108 |
109 | [GPL-2.0](./LICENSE)
110 |
111 | ## 免责声明
112 |
113 | 网站数据均为测试数据,不作为商业用途使用
114 |
115 | ## Web版
116 |
117 | [https://github.com/zxj963577494/OverWatchTeams](https://github.com/zxj963577494/OverWatchTeams)
--------------------------------------------------------------------------------
/src/services/leanclound/warOrder.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import { getCurrentUserAsync } from './user'
3 | import { getDayStart, getDayEnd } from '../../utils/utils'
4 |
5 | // 创建战队训练赛约战
6 | export function cerateWarOrder(payload, team, currentUser) {
7 | const teamData = AV.Object.createWithoutData('Teams', payload.teamid)
8 | const warOrders = new AV.Object('WarOrders')
9 | warOrders.set('title', payload.title)
10 | warOrders.set('description', payload.description)
11 | warOrders.set('contact', payload.contact)
12 | warOrders.set('stick', 0)
13 | warOrders.set('show', 1)
14 | const endDate = new Date(payload.endDate)
15 | warOrders.set('endDate', endDate)
16 | warOrders.set('user', currentUser)
17 | warOrders.set('team', teamData)
18 |
19 | var acl = new AV.ACL()
20 | acl.setPublicReadAccess(true)
21 | acl.setWriteAccess(currentUser, true)
22 | warOrders.setACL(acl)
23 |
24 | return warOrders.save().then(function(result) {
25 | return {...result.toJSON(), team}
26 | })
27 | }
28 |
29 | export function updateWarOrder(payload, team) {
30 | const warOrders = AV.Object.createWithoutData(
31 | 'WarOrders',
32 | payload.objectId
33 | )
34 | warOrders.set('title', payload.title)
35 | warOrders.set('description', payload.description)
36 | warOrders.set('contact', payload.contact)
37 | const endDate = new Date(payload.endDate)
38 | warOrders.set('endDate', endDate)
39 | warOrders.set('team', team)
40 |
41 | return warOrders.save().then(function(result) {
42 | return {
43 | ...result.toJSON(),
44 | team: team.toJSON()
45 | }
46 | })
47 | }
48 |
49 | export function removeWarOrder(payload) {
50 | var warOrders = AV.Object.createWithoutData('WarOrders', payload.objectId)
51 | return warOrders.destroy().then(function(success) {
52 | return success.toJSON()
53 | })
54 | }
55 |
56 | export function getAccountWarOrderList(payload, currentUser) {
57 | let list = []
58 | let { page, pagesize } = payload
59 | pagesize = pagesize || 20
60 | const query = new AV.Query('WarOrders')
61 | query.equalTo('user', currentUser)
62 | query.greaterThanOrEqualTo('endDate', new Date())
63 | query.descending('updatedAt')
64 | query.limit(pagesize)
65 | query.skip(pagesize * (page - 1))
66 | query.include('team')
67 | return query.find().then(function(result) {
68 | result.forEach(item => {
69 | const team = item.get('team').toJSON()
70 | const res = { ...item.toJSON(), team }
71 | list.push(res)
72 | })
73 | return list
74 | })
75 | }
76 |
77 | export function getHomeWarOrderList(payload) {
78 | let list = []
79 | let { page, pagesize } = payload
80 | pagesize = pagesize || 20
81 | const query = new AV.Query('WarOrders')
82 | query.equalTo('show', 1)
83 | query.greaterThanOrEqualTo('endDate', new Date())
84 | query.descending('stick')
85 | query.descending('createdAt')
86 | query.limit(pagesize)
87 | query.skip(pagesize * (page - 1))
88 | query.include('team')
89 | return query.find().then(function(result) {
90 | result.forEach(item => {
91 | const team = item.get('team').toJSON()
92 | const res = { ...item.toJSON(), team }
93 | list.push(res)
94 | })
95 | return list
96 | })
97 | }
98 |
99 | export function getWarOrderCountOfToday(currentUser) {
100 | const query = new AV.Query('WarOrders')
101 | query.equalTo('user', currentUser)
102 | query.lessThanOrEqualTo('createdAt', getDayEnd())
103 | query.greaterThanOrEqualTo('createdAt', getDayStart())
104 | return query.count().then(function(result) {
105 | return result
106 | })
107 | }
--------------------------------------------------------------------------------
/src/services/leanclound/recruitOrder.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import { getCurrentUserAsync } from './user'
3 | import { getDayStart, getDayEnd } from '../../utils/utils'
4 |
5 | // 创建招募令
6 | export function cerateRecruitOrder(payload, team, currentUser) {
7 | const teamData = AV.Object.createWithoutData('Teams', payload.teamid)
8 | const recruitOrders = new AV.Object('RecruitOrders')
9 | recruitOrders.set('title', payload.title)
10 | recruitOrders.set('description', payload.description)
11 | recruitOrders.set('contact', payload.contact)
12 | recruitOrders.set('stick', 0)
13 | recruitOrders.set('show', 1)
14 | const endDate = new Date(payload.endDate)
15 | recruitOrders.set('endDate', endDate)
16 | recruitOrders.set('user', currentUser)
17 | recruitOrders.set('team', teamData)
18 |
19 | var acl = new AV.ACL()
20 | acl.setPublicReadAccess(true)
21 | acl.setWriteAccess(currentUser, true)
22 | recruitOrders.setACL(acl)
23 |
24 | return recruitOrders.save().then(function(result) {
25 | return { ...result.toJSON(), team }
26 | })
27 | }
28 |
29 | export function updateRecruitOrder(payload, team) {
30 | const recruitOrders = AV.Object.createWithoutData(
31 | 'RecruitOrders',
32 | payload.objectId
33 | )
34 | recruitOrders.set('title', payload.title)
35 | recruitOrders.set('description', payload.description)
36 | recruitOrders.set('contact', payload.contact)
37 | const endDate = new Date(payload.endDate)
38 | recruitOrders.set('endDate', endDate)
39 |
40 | return recruitOrders.save().then(function(result) {
41 | return {
42 | ...result.toJSON(),
43 | team: team.toJSON()
44 | }
45 | })
46 | }
47 |
48 | export function removeRecruitOrder(payload) {
49 | var recruitOrders = AV.Object.createWithoutData(
50 | 'RecruitOrders',
51 | payload.objectId
52 | )
53 | return recruitOrders.destroy().then(function(success) {
54 | return success.toJSON()
55 | })
56 | }
57 |
58 | export function getAccountRecruitOrderList(payload, currentUser) {
59 | let list = []
60 | let { page, pagesize } = payload
61 | pagesize = pagesize || 20
62 | const query = new AV.Query('RecruitOrders')
63 | query.equalTo('user', currentUser)
64 | query.greaterThanOrEqualTo('endDate', new Date())
65 | query.descending('updatedAt')
66 | query.limit(pagesize)
67 | query.skip(pagesize * (page - 1))
68 | query.include('team')
69 | return query.find().then(function(result) {
70 | result.forEach(item => {
71 | const team = item.get('team').toJSON()
72 | const res = { ...item.toJSON(), team }
73 | list.push(res)
74 | })
75 | return list
76 | })
77 | }
78 |
79 | export function getHomeRecruitOrderList(payload) {
80 | let list = []
81 | let { page, pagesize } = payload
82 | pagesize = pagesize || 20
83 | const query = new AV.Query('RecruitOrders')
84 | query.equalTo('show', 1)
85 | query.greaterThanOrEqualTo('endDate', new Date())
86 | query.descending('stick')
87 | query.descending('createdAt')
88 | query.limit(pagesize)
89 | query.skip(pagesize * (page - 1))
90 | query.include('team')
91 | return query.find().then(function(result) {
92 | result.forEach(item => {
93 | const team = item.get('team').toJSON()
94 | const res = { ...item.toJSON(), team }
95 | list.push(res)
96 | })
97 | return list
98 | })
99 | }
100 |
101 | export function getRecruitOrderCountOfToday(currentUser) {
102 | const query = new AV.Query('RecruitOrders')
103 | query.equalTo('user', currentUser)
104 | console.log(getDayEnd())
105 | console.log(getDayStart())
106 | query.lessThanOrEqualTo('createdAt', getDayEnd())
107 | query.greaterThanOrEqualTo('createdAt', getDayStart())
108 | return query.count().then(function(result) {
109 | return result
110 | })
111 | }
112 |
--------------------------------------------------------------------------------
/src/components/AccountResumeCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace, Button, Modal } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 | import { cutstr } from '../../utils/utils'
7 | import { CardStyle } from '../CustomStyles'
8 |
9 | export default class AccountResumeCard extends PureComponent {
10 | onRemove = objectId => e => {
11 | Modal.alert('警告', '是否删除该自荐帖?', [
12 | { text: '取消', onPress: () => console.log('cancel') },
13 | {
14 | text: '确定',
15 | onPress: () => this.props.deleteResumeOrder({ objectId: objectId })
16 | }
17 | ])
18 | }
19 |
20 | render() {
21 | const { item, navigateTo } = this.props
22 | return (
23 |
24 |
25 |
36 |
49 |
50 |
59 |
60 | }
61 | />
62 |
63 |
64 |
65 |
66 | {item.user.userinfo.nickname}
67 |
68 |
69 |
70 |
71 | {item.contact}
72 |
73 |
74 |
75 |
76 |
77 |
78 |
83 | {item.description}
84 |
85 |
86 |
87 |
88 |
96 | 发布时间:
97 |
98 | }
99 | extra={
100 |
101 | 有效日期:
102 |
103 | }
104 | style={{ paddingBottom: 5 }}
105 | />
106 |
107 |
108 | )
109 | }
110 | }
111 |
112 | AccountResumeCard.propTypes = {
113 | navigateTo: PropTypes.func,
114 | item: PropTypes.object,
115 | deleteResumeOrder: PropTypes.func
116 | }
117 |
--------------------------------------------------------------------------------
/src/components/AccountGroupCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace, Button, Modal } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 | import { cutstr } from '../../utils/utils'
7 | import { CardStyle } from '../CustomStyles'
8 |
9 | export default class AccountGroupCard extends PureComponent {
10 | onRemove = objectId => e => {
11 | Modal.alert('警告', '是否删除该组队帖?', [
12 | { text: '取消', onPress: () => console.log('cancel') },
13 | {
14 | text: '确定',
15 | onPress: () => this.props.deleteGroupOrder({ objectId: objectId })
16 | }
17 | ])
18 | }
19 |
20 | render() {
21 | const { item, navigateTo } = this.props
22 | return (
23 |
24 |
25 |
36 |
49 |
50 |
59 |
60 | }
61 | />
62 |
63 |
64 |
65 |
66 | {item.user.userinfo.nickname}
67 |
68 |
69 |
70 |
71 | {item.contact}
72 |
73 |
74 |
75 |
76 |
77 |
78 |
83 | {item.description}
84 |
85 |
86 |
87 |
88 |
96 | 发布时间:
97 |
98 | }
99 | extra={
100 |
101 | 有效日期:
102 |
103 | }
104 | style={{ paddingBottom: 5 }}
105 | />
106 |
107 |
108 | )
109 | }
110 | }
111 |
112 | AccountGroupCard.propTypes = {
113 | navigateTo: PropTypes.func,
114 | item: PropTypes.object,
115 | deleteGroupOrder: PropTypes.func
116 | }
117 |
--------------------------------------------------------------------------------
/src/components/AccountWarCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace, Button, Modal } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 | import { cutstr } from '../../utils/utils'
7 | import { CardStyle } from '../CustomStyles'
8 |
9 | export default class AccountWarCard extends PureComponent {
10 | onRemove = objectId => e => {
11 | Modal.alert('警告', '是否删除该约战帖?', [
12 | { text: '取消', onPress: () => console.log('cancel') },
13 | {
14 | text: '确定',
15 | onPress: () => this.props.deleteWarOrder({ objectId: objectId })
16 | }
17 | ])
18 | }
19 |
20 | render() {
21 | const { item, navigateTo } = this.props
22 | return (
23 |
24 |
25 |
36 |
49 |
50 |
59 |
60 | }
61 | />
62 |
63 |
64 |
65 |
66 | {item.team.englishFullName ||
67 | item.team.chineseFullName ||
68 | item.team.englishName ||
69 | item.team.chineseName}
70 |
71 |
72 |
73 |
74 | {item.contact}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
86 | {item.description}
87 |
88 |
89 |
90 |
91 |
99 | 发布时间:
100 |
101 | }
102 | extra={
103 |
104 | 有效日期:
105 |
106 | }
107 | style={{ paddingBottom: 5 }}
108 | />
109 |
110 |
111 | )
112 | }
113 | }
114 |
115 | AccountWarCard.propTypes = {
116 | navigateTo: PropTypes.func,
117 | item: PropTypes.object,
118 | deleteWarOrder: PropTypes.func
119 | }
120 |
--------------------------------------------------------------------------------
/src/components/AccountRecruitCard/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import { Text, View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { Card, Flex, WhiteSpace, Button, Modal } from 'antd-mobile'
5 | import TimeAgo from 'react-native-timeago'
6 | import { cutstr } from '../../utils/utils'
7 | import { CardStyle } from '../CustomStyles'
8 |
9 | export default class AccountRecruitCard extends PureComponent {
10 | onRemove = objectId => e => {
11 | Modal.alert('警告', '是否删除该招募令?', [
12 | { text: '取消', onPress: () => console.log('cancel') },
13 | {
14 | text: '确定',
15 | onPress: () => this.props.deleteRecruitOrder({ objectId: objectId })
16 | }
17 | ])
18 | }
19 |
20 | render() {
21 | const { item, navigateTo } = this.props
22 | return (
23 |
24 |
25 |
36 |
49 |
50 |
59 |
60 | }
61 | />
62 |
63 |
64 |
65 |
66 | {item.team.englishFullName ||
67 | item.team.chineseFullName ||
68 | item.team.englishName ||
69 | item.team.chineseName}
70 |
71 |
72 |
73 |
74 | {item.contact}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
86 | {item.description}
87 |
88 |
89 |
90 |
91 |
99 | 发布时间:
100 |
101 | }
102 | extra={
103 |
104 | 有效日期:
105 |
106 | }
107 | style={{ paddingBottom: 5 }}
108 | />
109 |
110 |
111 | )
112 | }
113 | }
114 |
115 | AccountRecruitCard.propTypes = {
116 | navigateTo: PropTypes.func,
117 | item: PropTypes.object,
118 | deleteRecruitOrder: PropTypes.func
119 | }
120 |
--------------------------------------------------------------------------------
/src/screens/Account/EmaiVerify/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, Text, StyleSheet } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import { createForm } from 'rc-form'
6 | import {
7 | Button,
8 | InputItem,
9 | WhiteSpace,
10 | Flex,
11 | WingBlank,
12 | List,
13 | Toast
14 | } from 'antd-mobile'
15 | import _ from 'lodash'
16 | import { setNavBar, sendEmailRequest } from '../../../actions'
17 | import { userService } from '../../../services/leanclound'
18 |
19 | class AccountEmaiVerify extends Component {
20 | constructor(props) {
21 | super(props)
22 | this.state = {
23 | isVerify: props.user.emailVerified,
24 | email: props.user.email,
25 | pending: props.app.isFetching
26 | }
27 | this.onEmailChange = this.onEmailChange.bind(this)
28 | this.onSubmit = this.onSubmit.bind(this)
29 | }
30 |
31 | onEmailChange(value) {
32 | this.setState({
33 | email: value
34 | })
35 | }
36 |
37 | onSubmit() {
38 | const { sendEmail, form } = this.props
39 | form.validateFields((error, value) => {
40 | if (!error) {
41 | sendEmail({
42 | email: this.state.email
43 | })
44 | } else {
45 | Toast.fail('格式错误,请检查后提交', 1.5)
46 | }
47 | })
48 | }
49 |
50 | componentDidMount() {}
51 |
52 | render() {
53 | const { getFieldProps, getFieldError } = this.props.form
54 | const { app, pending, user } = this.props
55 | const { email, isVerify } = this.state
56 | const emailErrors = getFieldError('email')
57 | const VerifyMessage = user.emailVerified ? '已验证' : '未验证'
58 | return (
59 |
60 | (
62 |
70 | 邮箱地址[{VerifyMessage}]
71 |
72 | )}
73 | >
74 |
91 |
92 |
93 |
94 |
95 | {emailErrors ? emailErrors.join(',') : null}
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
110 |
111 |
112 | {app.emailError}
113 |
114 |
115 |
116 |
117 | )
118 | }
119 | }
120 |
121 | const mapStateToProps = (state, ownProps) => {
122 | return {
123 | app: state.app,
124 | user: state.user.account.user
125 | }
126 | }
127 |
128 | const mapDispatchToProps = (dispatch, ownProps) => {
129 | return {
130 | sendEmail: payload => {
131 | dispatch(sendEmailRequest(payload))
132 | }
133 | }
134 | }
135 |
136 | AccountEmaiVerify.propTypes = {
137 | app: PropTypes.object,
138 | user: PropTypes.object,
139 | sendEmail: PropTypes.func,
140 | form: PropTypes.object
141 | }
142 |
143 | const styles = StyleSheet.create({
144 | error: {
145 | color: 'red',
146 | textAlign: 'center'
147 | }
148 | })
149 |
150 | export default connect(mapStateToProps, mapDispatchToProps)(
151 | createForm()(AccountEmaiVerify)
152 | )
153 |
--------------------------------------------------------------------------------
/src/screens/Home/Teams/Detail/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Text, ScrollView, View, Image } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import { List, Card, WhiteSpace } from 'antd-mobile'
6 | import { setNavBar, getHomeTeamDetailRequest } from '../../../../actions'
7 | import { RANKS } from '../../../../constants'
8 |
9 | class HomeTeamDetail extends Component {
10 | constructor(props) {
11 | super(props)
12 | this.state = {
13 | isGetMember: false
14 | }
15 | this.getTeamById = this.getTeamById.bind(this)
16 | }
17 |
18 | componentDidMount() {
19 | if (!this.props.team) {
20 | const id = this.props.navigation.state.params.objectId
21 | this.props.getTeamById({ objectId: id })
22 | }
23 | }
24 |
25 | getTeamById() {
26 | this.setState({
27 | isGetMember: true
28 | })
29 | const id = this.props.navigation.state.params.objectId
30 | this.props.getTeamById({ objectId: id })
31 | }
32 |
33 | render() {
34 | let { team, current } = this.props
35 | if ((team == null && current != null) || this.state.isGetMember) {
36 | team = current
37 | }
38 | if (!team) {
39 | return null
40 | }
41 | return (
42 |
43 |
44 |
45 |
59 | }
60 | />
61 |
62 |
63 |
64 | 英文简称
65 |
66 |
67 | 英文全称
68 |
69 |
70 | 中文简称
71 |
72 |
73 | 中文全称
74 |
75 |
76 | 联系方式
77 |
78 |
79 | 所在地点
80 |
81 |
82 | 是否正在招募
83 |
84 | x.value === team.rank)[0].label
88 | : '未知'
89 | }
90 | >
91 | 平均段位
92 |
93 |
94 | '战队介绍'}>
95 |
96 | {team.introduction}
97 |
98 |
99 | '比赛经历'}>
100 |
101 | {team.match}
102 |
103 |
104 | '主要荣耀'}>
105 |
106 | {team.honor}
107 |
108 |
109 |
110 |
111 |
112 | )
113 | }
114 | }
115 |
116 | const mapStateToProps = (state, ownProps) => {
117 | return {
118 | current: state.team.home.team.current,
119 | team: state.team.home.team.list.filter(
120 | x => x.objectId === ownProps.navigation.state.params.objectId
121 | )[0]
122 | }
123 | }
124 |
125 | const mapDispatchToProps = (dispatch, ownProps) => {
126 | return {
127 | getTeamById: payload => {
128 | dispatch(getHomeTeamDetailRequest(payload))
129 | }
130 | }
131 | }
132 |
133 | HomeTeamDetail.propTypes = {
134 | current: PropTypes.object,
135 | team: PropTypes.object
136 | }
137 |
138 | export default connect(mapStateToProps, mapDispatchToProps)(HomeTeamDetail)
139 |
--------------------------------------------------------------------------------
/src/services/leanclound/user.js:
--------------------------------------------------------------------------------
1 | import AV from 'leancloud-storage'
2 | import { getAvatar, getNickName } from '../../utils/utils'
3 |
4 | // 用户名和密码注册
5 | export function signUp(payload) {
6 | const { username, password, email } = payload
7 | const avatar = getAvatar()
8 | const nickname = getNickName()
9 | const user = new AV.User()
10 | user.setUsername(username)
11 | user.setPassword(password)
12 | user.setEmail(email)
13 | return user.signUp().then(function(loginedUser) {
14 | if (!loginedUser.get('userinfo')) {
15 | const Userinfo = AV.Object.extend('UserInfo')
16 | const userinfo = new Userinfo()
17 | userinfo.set('nickname', nickname)
18 | userinfo.set('contact', email)
19 | userinfo.set('avatar', avatar)
20 | userinfo.set('avatar', avatar)
21 | userinfo.set('isPublic', true)
22 | userinfo.set('show', 1)
23 | userinfo.set('stick', 0)
24 | userinfo.set('teamLimit', 1)
25 | userinfo.set('groupOrderLimit', 1)
26 | userinfo.set('recruitOrderLimit', 1)
27 | userinfo.set('resumeOrderLimit', 1)
28 | userinfo.set('warOrderLimit', 1)
29 | userinfo.set('introduction', '这个世界需要更多的英雄')
30 | loginedUser.set('userinfo', userinfo)
31 | return loginedUser.save().then(function(result) {
32 | return result.toJSON()
33 | })
34 | }
35 | })
36 | }
37 |
38 | // 手机号码注册
39 | export function signUpBySmsCode(payload) {
40 | const { phone, code } = payload
41 | return AV.User.signUpOrlogInWithMobilePhone(phone, code)
42 | }
43 |
44 | // 第三方账号登录
45 | export function signUpOrlogInWithAuthData(payload) {}
46 |
47 | // 用户名和密码登录
48 | export function logIn(payload) {
49 | const { username, password } = payload
50 | return AV.User.logIn(username, password).then(function(result) {
51 | return result.toJSON()
52 | })
53 | }
54 |
55 | // 手机号和密码登录
56 | export function logInWithMobilePhone(payload) {
57 | const { phone, password } = payload
58 | return AV.User.logInWithMobilePhone(phone, password)
59 | }
60 |
61 | // 手机号和验证码登录
62 | export function requestLoginSmsCode(payload) {
63 | const { phone, code } = payload
64 | return AV.User.logInWithMobilePhoneSmsCode(phone, code)
65 | }
66 |
67 | // 当前用户
68 | export function getCurrentUserAsync() {
69 | console.log('service getCurrentUserAsync');
70 | return AV.User.currentAsync()
71 | }
72 |
73 | // 验证 SessionToken 是否在有效期内
74 | export function isAuthenticated() {
75 | // var currentUser = AV.User.currentAsync()
76 | // return currentUser.isAuthenticated()
77 | }
78 |
79 | // 使用 SessionToken 登录
80 | // 登录后可以调用 user.getSessionToken() 方法得到当前登录用户的 sessionToken
81 | export function logInWithSessionToken(payload) {
82 | const { sessionToken } = payload
83 | return AV.User.become(sessionToken)
84 | }
85 |
86 | // 登出
87 | export function logOut() {
88 | return AV.User.logOut()
89 | // 现在的 currentUser 是 null 了
90 | // var currentUser = AV.User.currentAsync()
91 | }
92 |
93 | export function putUserInfo(payload) {
94 | const userinfo = AV.Object.createWithoutData('UserInfo', payload.objectId)
95 | for (let key of Object.keys(payload)) {
96 | if (key !== 'objectId') {
97 | userinfo.set(key, payload[key])
98 | }
99 | }
100 | return userinfo.save().then(function(result) {
101 | return result.toJSON()
102 | })
103 | }
104 |
105 | export function getUserInfoToJson(currentUser) {
106 | const user = new AV.Query('_User')
107 | user.equalTo('objectId', currentUser.id)
108 | user.include('userinfo')
109 | return user.first().then(function(result) {
110 | return result.get('userinfo').toJSON()
111 | })
112 | }
113 |
114 | export function getHomeUserInfoList(payload) {
115 | let list = []
116 | let { page, pagesize } = payload
117 | pagesize = pagesize || 20
118 | const query = new AV.Query('UserInfo')
119 | query.equalTo('isPublic', true)
120 | query.equalTo('show', 1)
121 | query.descending('stick')
122 | query.descending('createdAt')
123 | query.limit(pagesize)
124 | query.skip(pagesize * (page - 1))
125 | return query.find().then(function(result) {
126 | result.forEach(item => {
127 | list.push(item.toJSON())
128 | })
129 | return list
130 | })
131 | }
132 |
133 | export function getHomeUserInfoDetail(payload) {
134 | const { objectId } = payload
135 | const query = new AV.Query('UserInfo')
136 | query.equalTo('objectId', objectId)
137 | return query.first().then(function(result) {
138 | return result.toJSON()
139 | })
140 | }
141 |
--------------------------------------------------------------------------------
/src/sagas/groupOrderSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { Toast } from 'antd-mobile'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | GET_HOME_GROUP_ORDER_LIST_REQUEST,
7 | GET_ACCOUNT_GROUP_ORDER_LIST_REQUEST,
8 | POST_GROUP_ORDER_REQUEST,
9 | PUT_GROUP_ORDER_REQUEST,
10 | DELETE_GROUP_ORDER_REQUEST
11 | } from '../constants/actionTypes'
12 | import * as action from '../actions'
13 | import { groupOrderService, userService } from '../services/leanclound'
14 |
15 | function* postGroupOrderWorker(payload) {
16 | try {
17 | Toast.loading('提交中')
18 | const currentUser = yield call(userService.getCurrentUserAsync)
19 | const userinfo = yield call(userService.getUserInfoToJson, currentUser)
20 | const count = yield call(
21 | groupOrderService.getGroupOrderCountOfToday,
22 | currentUser
23 | )
24 | const groupOrderLimit = userinfo.groupOrderLimit
25 | if (count < groupOrderLimit) {
26 | const response = yield call(
27 | groupOrderService.cerateGroupOrder,
28 | payload,
29 | userinfo,
30 | currentUser
31 | )
32 | yield put(action.postGroupOrderSuccess(response))
33 | Toast.success('提交成功', 1)
34 | yield delay(1000)
35 | yield put(NavigationActions.back())
36 | } else {
37 | yield put(action.putGroupOrderFailed())
38 | Toast.fail(`1天最多发布${groupOrderLimit}条组队上分帖`, 2)
39 | yield delay(2000)
40 | yield put(NavigationActions.back())
41 | }
42 | } catch (error) {
43 | yield put(action.postGroupOrderFailed(error))
44 | Toast.fail('提交失败', 1)
45 | }
46 | }
47 |
48 | function* putGroupOrderWorker(payload) {
49 | try {
50 | Toast.loading('提交中')
51 | const response = yield call(
52 | groupOrderService.updateGroupOrder,
53 | payload,
54 | )
55 | yield put(action.putGroupOrderSuccess(response))
56 | Toast.success('提交成功', 1)
57 | yield delay(1000)
58 | yield put(NavigationActions.back())
59 | } catch (error) {
60 | yield put(action.putGroupOrderFailed(error))
61 | Toast.fail('提交失败', 1)
62 | }
63 | }
64 |
65 | function* deleteGroupOrderWorker(payload) {
66 | try {
67 | Toast.loading('提交中')
68 | const response = yield call(groupOrderService.removeGroupOrder, payload)
69 | yield put(action.deleteGroupOrderSuccess(response))
70 | Toast.success('删除成功', 1)
71 | } catch (error) {
72 | yield put(action.deleteGroupOrderFailed(error))
73 | Toast.fail('删除失败', 1)
74 | }
75 | }
76 |
77 | function* getAccountGroupOrderListWorker(payload) {
78 | try {
79 | Toast.loading('加载中')
80 | const currentUser = yield call(userService.getCurrentUserAsync)
81 | const response = yield call(
82 | groupOrderService.getAccountGroupOrderList,
83 | payload,
84 | currentUser
85 | )
86 | yield put(action.getAccountGroupOrderListSuccess(response))
87 | Toast.hide()
88 | } catch (error) {
89 | yield put(action.getAccountGroupOrderListFailed(error))
90 | Toast.hide()
91 | }
92 | }
93 |
94 | function* getHomeGroupOrderListWorker(payload) {
95 | try {
96 | Toast.loading('加载中')
97 | const response = yield call(
98 | groupOrderService.getHomeGroupOrderList,
99 | payload
100 | )
101 | yield put(action.getHomeGroupOrderListSuccess(response))
102 | Toast.hide()
103 | } catch (error) {
104 | yield put(action.getHomeGroupOrderListFailed(error))
105 | Toast.hide()
106 | }
107 | }
108 |
109 | function* watchPostGroupOrder() {
110 | while (true) {
111 | const { payload } = yield take(POST_GROUP_ORDER_REQUEST)
112 | yield fork(postGroupOrderWorker, payload)
113 | }
114 | }
115 |
116 | function* watchGetAccountGroupOrderList() {
117 | while (true) {
118 | const { payload } = yield take(GET_ACCOUNT_GROUP_ORDER_LIST_REQUEST)
119 | yield fork(getAccountGroupOrderListWorker, payload)
120 | }
121 | }
122 |
123 | function* watchGetHomeGroupOrderList() {
124 | while (true) {
125 | const { payload } = yield take(GET_HOME_GROUP_ORDER_LIST_REQUEST)
126 | yield fork(getHomeGroupOrderListWorker, payload)
127 | }
128 | }
129 |
130 | function* watchPutGroupOrder() {
131 | while (true) {
132 | const { payload } = yield take(PUT_GROUP_ORDER_REQUEST)
133 | yield fork(putGroupOrderWorker, payload)
134 | }
135 | }
136 |
137 | function* watchDeleteGroupOrder() {
138 | while (true) {
139 | const { payload } = yield take(DELETE_GROUP_ORDER_REQUEST)
140 | yield fork(deleteGroupOrderWorker, payload)
141 | }
142 | }
143 |
144 | export {
145 | watchGetHomeGroupOrderList,
146 | watchPostGroupOrder,
147 | watchGetAccountGroupOrderList,
148 | watchPutGroupOrder,
149 | watchDeleteGroupOrder
150 | }
151 |
--------------------------------------------------------------------------------
/src/sagas/resumeOrderSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { Toast } from 'antd-mobile'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | GET_HOME_RESUME_ORDER_LIST_REQUEST,
7 | GET_ACCOUNT_RESUME_ORDER_LIST_REQUEST,
8 | POST_RESUME_ORDER_REQUEST,
9 | PUT_RESUME_ORDER_REQUEST,
10 | DELETE_RESUME_ORDER_REQUEST
11 | } from '../constants/actionTypes'
12 | import * as action from '../actions'
13 | import { resumeOrderService, userService } from '../services/leanclound'
14 |
15 | function* postResumeOrderWorker(payload) {
16 | try {
17 | Toast.loading('提交中')
18 | const currentUser = yield call(userService.getCurrentUserAsync)
19 | const userinfo = yield call(userService.getUserInfoToJson, currentUser)
20 | const count = yield call(
21 | resumeOrderService.getResumeOrderCountOfToday,
22 | currentUser
23 | )
24 | const resumeOrderLimit = userinfo.resumeOrderLimit
25 | if (count < resumeOrderLimit) {
26 | const response = yield call(
27 | resumeOrderService.cerateResumeOrder,
28 | payload,
29 | userinfo,
30 | currentUser
31 | )
32 | yield put(action.postResumeOrderSuccess(response))
33 | Toast.success('提交成功', 1)
34 | yield delay(1000)
35 | yield put(NavigationActions.back())
36 | } else {
37 | yield put(action.postResumeOrderFailed())
38 | Toast.fail(`1天最多发布${resumeOrderLimit}条寻找战队帖`, 2)
39 | yield delay(2000)
40 | yield put(NavigationActions.back())
41 | }
42 | } catch (error) {
43 | yield put(action.postResumeOrderFailed(error))
44 | Toast.fail('提交失败', 1)
45 | }
46 | }
47 |
48 | function* putResumeOrderWorker(payload) {
49 | try {
50 | Toast.loading('提交中')
51 | const response = yield call(
52 | resumeOrderService.updateResumeOrder,
53 | payload,
54 | )
55 | yield put(action.putResumeOrderSuccess(response))
56 | Toast.success('提交成功', 1)
57 | yield delay(1000)
58 | yield put(NavigationActions.back())
59 | } catch (error) {
60 | yield put(action.putResumeOrderFailed(error))
61 | Toast.fail('提交失败', 1)
62 | }
63 | }
64 |
65 | function* deleteResumeOrderWorker(payload) {
66 | try {
67 | Toast.loading('提交中')
68 | const response = yield call(resumeOrderService.removeResumeOrder, payload)
69 | yield put(action.deleteResumeOrderSuccess(response))
70 | Toast.success('删除成功', 1)
71 | } catch (error) {
72 | yield put(action.deleteResumeOrderFailed(error))
73 | Toast.fail('删除失败', 1)
74 | }
75 | }
76 |
77 | function* getAccountResumeOrderListWorker(payload) {
78 | try {
79 | Toast.loading('加载中')
80 | const currentUser = yield call(userService.getCurrentUserAsync)
81 | const response = yield call(
82 | resumeOrderService.getAccountResumeOrderList,
83 | payload,
84 | currentUser
85 | )
86 | yield put(action.getAccountResumeOrderListSuccess(response))
87 | Toast.hide()
88 | } catch (error) {
89 | yield put(action.getAccountResumeOrderListFailed(error))
90 | Toast.hide()
91 | }
92 | }
93 |
94 | function* getHomeResumeOrderListWorker(payload) {
95 | try {
96 | Toast.loading('加载中')
97 | const response = yield call(
98 | resumeOrderService.getHomeResumeOrderList,
99 | payload
100 | )
101 | yield put(action.getHomeResumeOrderListSuccess(response))
102 | Toast.hide()
103 | } catch (error) {
104 | yield put(action.getHomeResumeOrderListFailed(error))
105 | Toast.hide()
106 | }
107 | }
108 |
109 | function* watchPostResumeOrder() {
110 | while (true) {
111 | const { payload } = yield take(POST_RESUME_ORDER_REQUEST)
112 | yield fork(postResumeOrderWorker, payload)
113 | }
114 | }
115 |
116 | function* watchGetAccountResumeOrderList() {
117 | while (true) {
118 | const { payload } = yield take(GET_ACCOUNT_RESUME_ORDER_LIST_REQUEST)
119 | yield fork(getAccountResumeOrderListWorker, payload)
120 | }
121 | }
122 |
123 | function* watchGetHomeResumeOrderList() {
124 | while (true) {
125 | const { payload } = yield take(GET_HOME_RESUME_ORDER_LIST_REQUEST)
126 | yield fork(getHomeResumeOrderListWorker, payload)
127 | }
128 | }
129 |
130 | function* watchPutResumeOrder() {
131 | while (true) {
132 | const { payload } = yield take(PUT_RESUME_ORDER_REQUEST)
133 | yield fork(putResumeOrderWorker, payload)
134 | }
135 | }
136 |
137 | function* watchDeleteResumeOrder() {
138 | while (true) {
139 | const { payload } = yield take(DELETE_RESUME_ORDER_REQUEST)
140 | yield fork(deleteResumeOrderWorker, payload)
141 | }
142 | }
143 |
144 | export {
145 | watchGetHomeResumeOrderList,
146 | watchPostResumeOrder,
147 | watchGetAccountResumeOrderList,
148 | watchPutResumeOrder,
149 | watchDeleteResumeOrder
150 | }
151 |
--------------------------------------------------------------------------------
/src/screens/Account/Teams/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { ScrollView, View } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import {
6 | Button,
7 | WhiteSpace,
8 | WingBlank,
9 | Card,
10 | Modal,
11 | List,
12 | } from 'antd-mobile'
13 | import { NavigationActions } from 'react-navigation'
14 | import _ from 'lodash'
15 | import { getMyTeamsRequest, deleteTeamRequest } from '../../../actions'
16 | import { cutstr } from '../../../utils/utils'
17 | import { CardStyle } from '../../../components/CustomStyles'
18 |
19 | class AccountTeams extends Component {
20 | constructor(props) {
21 | super(props)
22 | this.state = {
23 | modal: false,
24 | teamid: ''
25 | }
26 | this.onCreateTeam = this.onCreateTeam.bind(this)
27 | }
28 |
29 | onCreateTeam() {
30 | if (!_.isEmpty(this.props.user)) {
31 | if (this.props.teams.length < this.props.user.teamLimit) {
32 | this.props.navigateTo('AccountTeamsCreate')
33 | } else {
34 | Modal.alert(
35 | '提示',
36 | '每位用户最多可创建一支战队,若想创建多支战队,请联系管理员963577494@qq.com',
37 | [{ text: '确定', onPress: () => console.log('success') }]
38 | )
39 | }
40 | } else {
41 | console.log('登录')
42 | }
43 | }
44 |
45 | onRemoveTeam = id => e => {
46 | Modal.alert('警告', '是否解散该队伍?', [
47 | { text: '取消', onPress: () => null },
48 | { text: '确定', onPress: () => this.props.deleteTeam({ teamid: id }) }
49 | ])
50 | }
51 |
52 | componentDidMount() {
53 | if (this.props.teams.length === 0) {
54 | this.props.getMyTeams()
55 | }
56 | }
57 |
58 | render() {
59 | const { teams, navigateTo } = this.props
60 | return (
61 |
62 | {teams.map((item, index) => (
63 |
64 |
65 |
66 | {
84 | navigateTo('AccountTeamsEdit', {
85 | objectId: item.objectId
86 | })
87 | }}
88 | type="ghost"
89 | size="small"
90 | inline
91 | >
92 | 编辑
93 |
94 | }
95 | />
96 |
97 |
98 |
99 | {cutstr(item.introduction, 200, 0)}
100 |
101 |
102 |
103 |
112 | 解散
113 |
114 | }
115 | />
116 |
117 |
118 | ))}
119 |
120 |
121 |
124 |
125 |
126 | )
127 | }
128 | }
129 |
130 | const mapStateToProps = (state, ownProps) => {
131 | return {
132 | teams: state.team.account.team.myTeams,
133 | user: state.user.account.user
134 | }
135 | }
136 |
137 | const mapDispatchToProps = (dispatch, ownProps) => {
138 | return {
139 | getMyTeams: () => {
140 | dispatch(getMyTeamsRequest())
141 | },
142 | deleteTeam: payload => {
143 | dispatch(deleteTeamRequest(payload))
144 | },
145 | navigateTo: (path, params) => {
146 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
147 | }
148 | }
149 | }
150 |
151 | AccountTeams.propTypes = {
152 | teams: PropTypes.array,
153 | getMyTeams: PropTypes.func,
154 | navigateTo: PropTypes.func
155 | }
156 |
157 | export default connect(mapStateToProps, mapDispatchToProps)(AccountTeams)
158 |
--------------------------------------------------------------------------------
/src/sagas/warOrderSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { Toast } from 'antd-mobile'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | GET_HOME_WAR_ORDER_LIST_REQUEST,
7 | GET_ACCOUNT_WAR_ORDER_LIST_REQUEST,
8 | POST_WAR_ORDER_REQUEST,
9 | PUT_WAR_ORDER_REQUEST,
10 | DELETE_WAR_ORDER_REQUEST
11 | } from '../constants/actionTypes'
12 | import * as action from '../actions'
13 | import {
14 | warOrderService,
15 | teamsService,
16 | userService
17 | } from '../services/leanclound'
18 |
19 | function* postWarOrderWorker(payload) {
20 | try {
21 | Toast.loading('提交中')
22 | const currentUser = yield call(userService.getCurrentUserAsync)
23 | const userinfo = yield call(userService.getUserInfoToJson, currentUser)
24 | const count = yield call(
25 | warOrderService.getWarOrderCountOfToday,
26 | currentUser
27 | )
28 | const warOrderLimit = userinfo.warOrderLimit
29 | if (count < warOrderLimit) {
30 | const team = yield call(teamsService.getTeamToJson, payload)
31 | const response = yield call(
32 | warOrderService.cerateWarOrder,
33 | payload,
34 | team,
35 | currentUser
36 | )
37 | yield put(action.postWarOrderSuccess(response))
38 | Toast.success('提交成功', 1)
39 | yield delay(1000)
40 | yield put(NavigationActions.back())
41 | } else {
42 | yield put(action.postWarOrderFailed())
43 | Toast.fail(`1天最多发布${warOrderLimit}条比赛约战帖`, 2)
44 | yield delay(2000)
45 | yield put(NavigationActions.back())
46 | }
47 | } catch (error) {
48 | yield put(action.postWarOrderFailed(error))
49 | Toast.fail('提交失败', 1)
50 | }
51 | }
52 |
53 | function* putWarOrderWorker(payload) {
54 | try {
55 | Toast.loading('提交中')
56 | const currentUser = yield call(userService.getCurrentUserAsync)
57 | const team = yield call(teamsService.getTeam, payload)
58 | const response = yield call(warOrderService.updateWarOrder, payload, team)
59 | yield put(action.putWarOrderSuccess(response))
60 | Toast.success('提交成功', 1)
61 | yield delay(1000)
62 | yield put(NavigationActions.back())
63 | } catch (error) {
64 | yield put(action.putWarOrderFailed(error))
65 | Toast.fail('提交失败', 1)
66 | }
67 | }
68 |
69 | function* deleteWarOrderWorker(payload) {
70 | try {
71 | Toast.loading('提交中')
72 | const response = yield call(warOrderService.removeWarOrder, payload)
73 | yield put(action.deleteWarOrderSuccess(response))
74 | yield put(action.fetchSuccess())
75 | Toast.success('删除成功', 1)
76 | } catch (error) {
77 | yield put(action.deleteWarOrderFailed(error))
78 | yield put(action.fetchFailed())
79 | Toast.fail('删除失败', 1)
80 | }
81 | }
82 |
83 | function* getAccountWarOrderListWorker(payload) {
84 | try {
85 | Toast.loading('加载中')
86 | const currentUser = yield call(userService.getCurrentUserAsync)
87 | const response = yield call(
88 | warOrderService.getAccountWarOrderList,
89 | payload,
90 | currentUser
91 | )
92 | yield put(action.getAccountWarOrderListSuccess(response))
93 | Toast.hide()
94 | } catch (error) {
95 | yield put(action.getAccountWarOrderListFailed(error))
96 | Toast.hide()
97 | }
98 | }
99 |
100 | function* getHomeWarOrderListWorker(payload) {
101 | try {
102 | Toast.loading('加载中')
103 | const response = yield call(warOrderService.getHomeWarOrderList, payload)
104 | yield put(action.getHomeWarOrderListSuccess(response))
105 | Toast.hide()
106 | } catch (error) {
107 | yield put(action.getHomeWarOrderListFailed(error))
108 | Toast.hide()
109 | }
110 | }
111 |
112 | function* watchPostWarOrder() {
113 | while (true) {
114 | const { payload } = yield take(POST_WAR_ORDER_REQUEST)
115 | yield fork(postWarOrderWorker, payload)
116 | }
117 | }
118 |
119 | function* watchGetAccountWarOrderList() {
120 | while (true) {
121 | const { payload } = yield take(GET_ACCOUNT_WAR_ORDER_LIST_REQUEST)
122 | yield fork(getAccountWarOrderListWorker, payload)
123 | }
124 | }
125 |
126 | function* watchGetHomeWarOrderList() {
127 | while (true) {
128 | const { payload } = yield take(GET_HOME_WAR_ORDER_LIST_REQUEST)
129 | yield fork(getHomeWarOrderListWorker, payload)
130 | }
131 | }
132 |
133 | function* watchPutWarOrder() {
134 | while (true) {
135 | const { payload } = yield take(PUT_WAR_ORDER_REQUEST)
136 | yield fork(putWarOrderWorker, payload)
137 | }
138 | }
139 |
140 | function* watchDeleteWarOrder() {
141 | while (true) {
142 | const { payload } = yield take(DELETE_WAR_ORDER_REQUEST)
143 | yield fork(deleteWarOrderWorker, payload)
144 | }
145 | }
146 |
147 | export {
148 | watchGetHomeWarOrderList,
149 | watchPostWarOrder,
150 | watchGetAccountWarOrderList,
151 | watchPutWarOrder,
152 | watchDeleteWarOrder
153 | }
154 |
--------------------------------------------------------------------------------
/src/screens/Home/UserInfos/Detail/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Text, ScrollView, View, Image } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import { WhiteSpace, List, Result } from 'antd-mobile'
6 | import { RANKS, TEAMPOSITIONS } from '../../../../constants'
7 | import { getHomeUserInfoDetailRequest } from '../../../../actions'
8 | import config from '../../../../config'
9 |
10 | class HomeUserInfoDetail extends Component {
11 | componentDidMount() {
12 | if (!this.props.userinfo) {
13 | const id = this.props.navigation.state.params.objectId
14 | this.props.getUserById({ objectId: id })
15 | }
16 | }
17 |
18 | render() {
19 | let { userinfo, current } = this.props
20 | if (userinfo == null && current != null) {
21 | userinfo = current
22 | }
23 | return (
24 |
25 |
26 |
33 | ) : (
34 |
38 | )
39 | }
40 | title={userinfo.nickname}
41 | message={userinfo.introduction}
42 | />
43 |
44 |
45 |
48 | 天梯分
49 |
50 | x.value === userinfo.rank)[0].label
54 | : '未知'
55 | }
56 | >
57 | 天梯段位
58 |
59 | x.value === userinfo.position)[0]
63 | .label
64 | : '未知'
65 | }
66 | >
67 | 团队定位
68 |
69 |
70 | '擅长英雄'}>
71 |
78 | {userinfo.heros ? (
79 | userinfo.heros.map((item, index) => {
80 | return (
81 |
91 | )
92 | })
93 | ) : (
94 |
103 | )}
104 |
105 |
106 | '个人比赛经历'}>
107 |
108 | {userinfo.match}
109 |
110 |
111 | '其他'}>
112 |
113 | 鼠标
114 |
115 |
116 | 键盘
117 |
118 |
119 | 耳机
120 |
121 |
122 |
123 |
124 | )
125 | }
126 | }
127 |
128 | const mapStateToProps = (state, ownProps) => {
129 | return {
130 | current: state.user.home.userinfo.current,
131 | userinfo: state.user.home.userinfo.list.filter(
132 | x => x.objectId === ownProps.navigation.state.params.objectId
133 | )[0]
134 | }
135 | }
136 |
137 | const mapDispatchToProps = (dispatch, ownProps) => {
138 | return {
139 | getUserById: payload => {
140 | dispatch(getHomeUserInfoDetailRequest(payload))
141 | }
142 | }
143 | }
144 |
145 | HomeUserInfoDetail.propTypes = {
146 | current: PropTypes.object,
147 | user: PropTypes.object,
148 | getUserById: PropTypes.func
149 | }
150 |
151 | export default connect(mapStateToProps, mapDispatchToProps)(HomeUserInfoDetail)
152 |
--------------------------------------------------------------------------------
/src/sagas/recruitOrderSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { Toast } from 'antd-mobile'
4 | import { NavigationActions } from 'react-navigation'
5 | import {
6 | GET_HOME_RECRUIT_ORDER_LIST_REQUEST,
7 | GET_ACCOUNT_RECRUIT_ORDER_LIST_REQUEST,
8 | POST_RECRUIT_ORDER_REQUEST,
9 | PUT_RECRUIT_ORDER_REQUEST,
10 | DELETE_RECRUIT_ORDER_REQUEST
11 | } from '../constants/actionTypes'
12 | import * as action from '../actions'
13 | import {
14 | recruitOrderService,
15 | teamsService,
16 | userService
17 | } from '../services/leanclound'
18 |
19 | function* postRecruitOrderWorker(payload) {
20 | try {
21 | Toast.loading('提交中')
22 | const currentUser = yield call(userService.getCurrentUserAsync)
23 | const userinfo = yield call(userService.getUserInfoToJson, currentUser)
24 | const count = yield call(
25 | recruitOrderService.getRecruitOrderCountOfToday,
26 | currentUser
27 | )
28 | const recruitOrderLimit = userinfo.recruitOrderLimit
29 | if (count < recruitOrderLimit) {
30 | const team = yield call(teamsService.getTeamToJson, payload)
31 | const response = yield call(
32 | recruitOrderService.cerateRecruitOrder,
33 | payload,
34 | team,
35 | currentUser
36 | )
37 | yield put(action.postRecruitOrderSuccess(response))
38 | Toast.success('提交成功', 1)
39 | yield delay(1000)
40 | yield put(NavigationActions.back())
41 | } else {
42 | yield put(action.postRecruitOrderFailed())
43 | Toast.fail(`1天最多发布${recruitOrderLimit}条战队招募令`, 2)
44 | yield delay(2000)
45 | yield put(NavigationActions.back())
46 | }
47 | } catch (error) {
48 | yield put(action.postRecruitOrderFailed(error))
49 | Toast.fail('提交失败', 1)
50 | }
51 | }
52 |
53 | function* putRecruitOrderWorker(payload) {
54 | try {
55 | Toast.loading('提交中')
56 | const currentUser = yield call(userService.getCurrentUserAsync)
57 | const team = yield call(teamsService.getTeam, payload)
58 | const response = yield call(
59 | recruitOrderService.updateRecruitOrder,
60 | payload,
61 | team
62 | )
63 | yield put(action.putRecruitOrderSuccess(response))
64 | Toast.success('提交成功', 1)
65 | yield delay(1000)
66 | yield put(NavigationActions.back())
67 | } catch (error) {
68 | yield put(action.putRecruitOrderFailed(error))
69 | Toast.fail('提交失败', 1)
70 | }
71 | }
72 |
73 | function* deleteRecruitOrderWorker(payload) {
74 | try {
75 | Toast.loading('提交中')
76 | const response = yield call(recruitOrderService.removeRecruitOrder, payload)
77 | yield put(action.deleteRecruitOrderSuccess(response))
78 | Toast.success('删除成功', 1)
79 | } catch (error) {
80 | yield put(action.deleteRecruitOrderFailed(error))
81 | Toast.fail('删除失败', 1)
82 | }
83 | }
84 |
85 | function* getAccountRecruitOrderListWorker(payload) {
86 | try {
87 | Toast.loading('加载中')
88 | const currentUser = yield call(userService.getCurrentUserAsync)
89 | const response = yield call(
90 | recruitOrderService.getAccountRecruitOrderList,
91 | payload,
92 | currentUser
93 | )
94 | yield put(action.getAccountRecruitOrderListSuccess(response))
95 | Toast.hide()
96 | } catch (error) {
97 | yield put(action.getAccountRecruitOrderListFailed(error))
98 | Toast.hide()
99 | }
100 | }
101 |
102 | function* getHomeRecruitOrderListWorker(payload) {
103 | try {
104 | Toast.loading('加载中')
105 | const response = yield call(
106 | recruitOrderService.getHomeRecruitOrderList,
107 | payload
108 | )
109 | yield put(action.getHomeRecruitOrderListSuccess(response))
110 | Toast.hide()
111 | } catch (error) {
112 | yield put(action.getHomeRecruitOrderListFailed(error))
113 | Toast.hide()
114 | }
115 | }
116 |
117 | function* watchPostRecruitOrder() {
118 | while (true) {
119 | const { payload } = yield take(POST_RECRUIT_ORDER_REQUEST)
120 | yield fork(postRecruitOrderWorker, payload)
121 | }
122 | }
123 |
124 | function* watchGetAccountRecruitOrderList() {
125 | while (true) {
126 | const { payload } = yield take(GET_ACCOUNT_RECRUIT_ORDER_LIST_REQUEST)
127 | yield fork(getAccountRecruitOrderListWorker, payload)
128 | }
129 | }
130 |
131 | function* watchGetHomeRecruitOrderList() {
132 | while (true) {
133 | const { payload } = yield take(GET_HOME_RECRUIT_ORDER_LIST_REQUEST)
134 | yield fork(getHomeRecruitOrderListWorker, payload)
135 | }
136 | }
137 |
138 | function* watchPutRecruitOrder() {
139 | while (true) {
140 | const { payload } = yield take(PUT_RECRUIT_ORDER_REQUEST)
141 | yield fork(putRecruitOrderWorker, payload)
142 | }
143 | }
144 |
145 | function* watchDeleteRecruitOrder() {
146 | while (true) {
147 | const { payload } = yield take(DELETE_RECRUIT_ORDER_REQUEST)
148 | yield fork(deleteRecruitOrderWorker, payload)
149 | }
150 | }
151 |
152 | export {
153 | watchGetHomeRecruitOrderList,
154 | watchPostRecruitOrder,
155 | watchGetAccountRecruitOrderList,
156 | watchPutRecruitOrder,
157 | watchDeleteRecruitOrder
158 | }
159 |
--------------------------------------------------------------------------------
/src/screens/Home/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import {
3 | View,
4 | Image,
5 | Text,
6 | StyleSheet,
7 | TouchableWithoutFeedback,
8 | ScrollView,
9 | StatusBar
10 | } from 'react-native'
11 | import PropTypes from 'prop-types'
12 | import { connect } from 'react-redux'
13 | import { Flex, WhiteSpace, Grid, List } from 'antd-mobile'
14 | import { NavigationActions } from 'react-navigation'
15 | import {
16 | getHomeGroupOrderListRequest,
17 | getHomeRecruitOrderListRequest
18 | } from '../../actions'
19 | import { HomeGroupCard, HomeRecruitCard } from '../../components'
20 |
21 | const data = [
22 | {
23 | path: 'HomeResumeOrders',
24 | icon: require('../../../assets/images/home_icon.png'),
25 | text: '寻找战队'
26 | },
27 | {
28 | path: 'HomeRecruitOrders',
29 | icon: require('../../../assets/images/home_icon.png'),
30 | text: '战队招募'
31 | },
32 | {
33 | path: 'HomeWarOrders',
34 | icon: require('../../../assets/images/home_icon.png'),
35 | text: '比赛约战'
36 | },
37 | {
38 | path: 'HomeGroupOrders',
39 | icon: require('../../../assets/images/home_icon.png'),
40 | text: '组队上分'
41 | },
42 | {
43 | path: 'HomeTeams',
44 | icon: require('../../../assets/images/home_icon.png'),
45 | text: '战队列表'
46 | },
47 | {
48 | path: 'HomeUserInfos',
49 | icon: require('../../../assets/images/home_icon.png'),
50 | text: '个人列表'
51 | }
52 | ]
53 |
54 | class Home extends Component {
55 | static propTypes = {
56 | navigateTo: PropTypes.func,
57 | groupOrder: PropTypes.object,
58 | recruitOrder: PropTypes.object,
59 | getHomeGroupOrderList: PropTypes.func,
60 | getHomeRecruitOrderList: PropTypes.func
61 | }
62 |
63 | _onPressButton = path => e => {
64 | this.props.navigateTo(path)
65 | }
66 |
67 | componentDidMount() {
68 | if (this.props.groupOrder.list.length === 0) {
69 | this.props.getHomeGroupOrderList({ page: 1 })
70 | }
71 | if (this.props.recruitOrder.list.length === 0) {
72 | this.props.getHomeRecruitOrderList({ page: 1 })
73 | }
74 | }
75 |
76 | render() {
77 | const { navigateTo, groupOrder, recruitOrder } = this.props
78 | return (
79 |
80 |
81 |
82 |
83 |
84 |
88 |
89 |
90 | 这个世界需要更多的英雄
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | (
103 |
106 |
107 |
111 | {dataItem.text}
112 |
113 |
114 | )}
115 | />
116 |
117 |
118 | '组队上分'}>
119 | {groupOrder.list.slice(0, 3).map((item, index) => {
120 | return (
121 |
126 | )
127 | })}
128 |
129 | '战队招募'}>
130 | {recruitOrder.list.slice(0, 3).map((item, index) => {
131 | return (
132 |
137 | )
138 | })}
139 |
140 |
141 | )
142 | }
143 | }
144 |
145 | const styles = StyleSheet.create({
146 | headerer: {
147 | height: 180,
148 | backgroundColor: '#fff'
149 | },
150 | grid: {
151 | backgroundColor: '#fff'
152 | }
153 | })
154 |
155 | const mapStateToProps = state => ({
156 | groupOrder: state.groupOrder.home.groupOrder,
157 | recruitOrder: state.recruitOrder.home.recruitOrder
158 | })
159 |
160 | const mapDispatchToProps = dispatch => ({
161 | getHomeGroupOrderList: payload => {
162 | dispatch(getHomeGroupOrderListRequest(payload))
163 | },
164 | getHomeRecruitOrderList: payload => {
165 | dispatch(getHomeRecruitOrderListRequest(payload))
166 | },
167 | navigateTo: (path, params) => {
168 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
169 | }
170 | })
171 |
172 | export default connect(mapStateToProps, mapDispatchToProps)(Home)
173 |
--------------------------------------------------------------------------------
/src/screens/SignUp/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Text, View, StyleSheet } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import { createForm } from 'rc-form'
6 | import {
7 | Button,
8 | InputItem,
9 | WhiteSpace,
10 | Flex,
11 | WingBlank,
12 | } from 'antd-mobile'
13 | import { postSignUpRequest } from '../../actions'
14 |
15 | class SignUp extends Component {
16 | constructor(props) {
17 | super(props)
18 | this.state = {
19 | username: '',
20 | password: '',
21 | email: ''
22 | }
23 | this.onUserNameChange = this.onUserNameChange.bind(this)
24 | this.onPasswordChange = this.onPasswordChange.bind(this)
25 | this.onEmailChange = this.onEmailChange.bind(this)
26 | this.onSubmit = this.onSubmit.bind(this)
27 | }
28 |
29 | onUserNameChange(value) {
30 | this.setState({
31 | username: value
32 | })
33 | }
34 |
35 | onPasswordChange(value) {
36 | this.setState({
37 | password: value
38 | })
39 | }
40 |
41 | onEmailChange(value) {
42 | this.setState({
43 | email: value
44 | })
45 | }
46 |
47 | onSubmit = () => {
48 | const { postSignUp, form } = this.props
49 | form.validateFields((error, value) => {
50 | if (!error) {
51 | postSignUp({
52 | username: this.state.username,
53 | password: this.state.password,
54 | email: this.state.email
55 | })
56 | } else {
57 | Toast.fail('格式错误,请检查后提交', 1.5)
58 | }
59 | })
60 | }
61 |
62 | componentDidMount() {}
63 |
64 | render() {
65 | const { getFieldProps, getFieldError } = this.props.form
66 | const { user_home } = this.props
67 | const usernameErrors = getFieldError('username')
68 | const passwordErrors = getFieldError('password')
69 | const emailErrors = getFieldError('email')
70 | return (
71 |
72 |
73 |
88 | 用户名
89 |
90 |
91 |
92 |
93 |
94 | {usernameErrors ? usernameErrors.join(',') : null}
95 |
96 |
97 |
98 |
114 | 密码
115 |
116 |
117 |
118 |
119 |
120 | {passwordErrors ? passwordErrors.join(',') : null}
121 |
122 |
123 |
124 |
135 | 邮箱
136 |
137 |
138 |
139 |
140 |
141 | {emailErrors ? emailErrors.join(',') : null}
142 |
143 |
144 |
145 |
146 |
147 |
150 |
151 |
152 |
153 |
154 |
155 | {user_home ? user_home.signupError : null}
156 |
157 |
158 |
159 |
160 | )
161 | }
162 | }
163 |
164 | const mapStateToProps = (state, ownProps) => {
165 | return {
166 | user_home: state.user.home
167 | }
168 | }
169 |
170 | const mapDispatchToProps = (dispatch, ownProps) => {
171 | return {
172 | postSignUp: payload => {
173 | dispatch(postSignUpRequest(payload))
174 | }
175 | }
176 | }
177 |
178 | SignUp.propTypes = {
179 | user_home: PropTypes.object,
180 | postSignUp: PropTypes.func,
181 | form: PropTypes.object
182 | }
183 |
184 | const styles = StyleSheet.create({
185 | error: {
186 | color: 'red',
187 | textAlign: 'center'
188 | }
189 | })
190 |
191 | export default connect(mapStateToProps, mapDispatchToProps)(
192 | createForm()(SignUp)
193 | )
194 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | import config from '../config'
2 |
3 | export function cutstr(str, len, flag) {
4 | var str_length = 0
5 | var str_cut = ''
6 | var str_len = str.length
7 | for (var i = 0; i < str_len; i++) {
8 | var a = str.charAt(i)
9 | str_length++
10 | if (escape(a).length > 4) {
11 | //中文字符的长度经编码之后大于4
12 | str_length++
13 | }
14 | str_cut = str_cut.concat(a)
15 | if (str_length >= len) {
16 | if (flag === 0) {
17 | str_cut = str_cut.concat('...')
18 | }
19 | return str_cut
20 | }
21 | }
22 | //如果给定字符串小于指定长度,则返回源字符串;
23 | if (str_length < len) {
24 | return str
25 | }
26 | }
27 |
28 | export function getNickName() {
29 | const nicks = [
30 | '末日铁拳',
31 | '源氏',
32 | '麦克雷',
33 | '法老之鹰',
34 | '死神',
35 | '士兵:76',
36 | '黑影',
37 | '堡垒',
38 | '半藏',
39 | '狂鼠',
40 | '美',
41 | '托比昂',
42 | '黑百合',
43 | 'D.Va',
44 | '奥丽莎',
45 | '莱因哈特',
46 | '路霸',
47 | '温斯顿',
48 | '查莉娅',
49 | '安娜',
50 | '卢西奥',
51 | '天使',
52 | '莫伊拉',
53 | '秩序之光',
54 | '禅雅塔'
55 | ]
56 | const i = nicks[Math.floor(Math.random() * nicks.length)]
57 | return i
58 | }
59 |
60 | export function getAvatar() {
61 | const avatars = [
62 | 'avatar/00007642.png',
63 | 'avatar/00007643.png',
64 | 'avatar/00007644.png',
65 | 'avatar/00007647.png',
66 | 'avatar/00007641.png',
67 | 'avatar/00008056.png',
68 | 'avatar/00007646.png',
69 | 'avatar/00008057.png',
70 | 'avatar/00007649.png',
71 | 'avatar/00008058.png',
72 | 'avatar/00007648.png',
73 | 'avatar/00008059.png',
74 | 'avatar/00008063.png',
75 | 'avatar/00008060.png',
76 | 'avatar/00008064.png',
77 | 'avatar/00008062.png',
78 | 'avatar/00007645.png',
79 | 'avatar/00008067.png',
80 | 'avatar/00008069.png',
81 | 'avatar/00008065.png',
82 | 'avatar/00008066.png',
83 | 'avatar/00007174.png',
84 | 'avatar/00008068.png',
85 | 'avatar/00007176.png',
86 | 'avatar/00007177.png',
87 | 'avatar/00007179.png',
88 | 'avatar/00007184.png',
89 | 'avatar/00007181.png',
90 | 'avatar/00007185.png',
91 | 'avatar/00007186.png',
92 | 'avatar/00007183.png',
93 | 'avatar/00007180.png',
94 | 'avatar/00007051.png',
95 | 'avatar/00007052.png',
96 | 'avatar/00007640.png',
97 | 'avatar/00007152.png',
98 | 'avatar/00007155.png',
99 | 'avatar/00007188.png',
100 | 'avatar/00007182.png',
101 | 'avatar/00007154.png',
102 | 'avatar/00007156.png',
103 | 'avatar/00007157.png',
104 | 'avatar/00007153.png',
105 | 'avatar/00007158.png',
106 | 'avatar/00007160.png',
107 | 'avatar/00007159.png',
108 | 'avatar/00007161.png',
109 | 'avatar/00007162.png',
110 | 'avatar/00007165.png',
111 | 'avatar/00007166.png',
112 | 'avatar/00007168.png',
113 | 'avatar/00007164.png',
114 | 'avatar/00007169.png',
115 | 'avatar/00007170.png',
116 | 'avatar/00007172.png',
117 | 'avatar/0000764B.png',
118 | 'avatar/0000764A.png',
119 | 'avatar/0000764C.png',
120 | 'avatar/0000805B.png',
121 | 'avatar/00007171.png',
122 | 'avatar/0000764D.png',
123 | 'avatar/0000764E.png',
124 | 'avatar/0000804C.png',
125 | 'avatar/0000805A.png',
126 | 'avatar/0000805C.png',
127 | 'avatar/0000805D.png',
128 | 'avatar/0000805E.png',
129 | 'avatar/0000806A.png',
130 | 'avatar/0000805F.png',
131 | 'avatar/0000806B.png',
132 | 'avatar/0000806C.png',
133 | 'avatar/0000806D.png',
134 | 'avatar/0000715A.png',
135 | 'avatar/0000715B.png',
136 | 'avatar/0000715D.png',
137 | 'avatar/0000715E.png',
138 | 'avatar/0000715C.png',
139 | 'avatar/0000716B.png',
140 | 'avatar/0000716A.png',
141 | 'avatar/0000715F.png',
142 | 'avatar/0000716F.png',
143 | 'avatar/0000716C.png',
144 | 'avatar/0000716D.png',
145 | 'avatar/0000716E.png',
146 | 'avatar/0000717B.png',
147 | 'avatar/0000717E.png',
148 | 'avatar/0000719A.png',
149 | 'avatar/0000717A.png',
150 | 'avatar/0000719B.png',
151 | 'avatar/0000719E.png',
152 | 'avatar/0000719F.png',
153 | 'avatar/000071A0.png',
154 | 'avatar/000051CC.png',
155 | 'avatar/000071A1.png',
156 | 'avatar/000071A4.png',
157 | 'avatar/000071A7.png',
158 | 'avatar/000071A5.png',
159 | 'avatar/000071A6.png',
160 | 'avatar/000071A9.png',
161 | 'avatar/000071AA.png',
162 | 'avatar/000071A8.png',
163 | 'avatar/0000717C.png',
164 | 'avatar/000071AC.png',
165 | 'avatar/000071AB.png',
166 | 'avatar/000071AE.png',
167 | 'avatar/000071B0.png',
168 | 'avatar/000071B1.png',
169 | 'avatar/000071AF.png',
170 | 'avatar/000071B2.png',
171 | 'avatar/000071B3.png'
172 | ]
173 | const i = avatars[Math.floor(Math.random() * avatars.length)]
174 | return config.BASE_PIC_URL + '/' + i
175 | }
176 |
177 | export function getPosition(type) {
178 | if (type) {
179 | const positions = {
180 | DPS: config.BASE_PIC_URL + '/dps.png',
181 | Flex: config.BASE_PIC_URL + '/flex.png',
182 | Tank: config.BASE_PIC_URL + '/tank.png',
183 | Support: config.BASE_PIC_URL + '/support.png'
184 | }
185 | return positions[type]
186 | } else {
187 | return config.BASE_PIC_URL + '/position.png'
188 | }
189 | }
190 |
191 | export function getDayStart() {
192 | return new Date(new Date(new Date().toDateString()).getTime())
193 | }
194 |
195 | export function getDayEnd() {
196 | return new Date(new Date(new Date().toDateString()).getTime()+24*60*60*1000-1)
197 | }
--------------------------------------------------------------------------------
/src/screens/SignIn/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View, StyleSheet, Text } from 'react-native'
3 | import PropTypes from 'prop-types'
4 | import { connect } from 'react-redux'
5 | import { createForm } from 'rc-form'
6 | import {
7 | Button,
8 | InputItem,
9 | WhiteSpace,
10 | Flex,
11 | WingBlank,
12 | Toast
13 | } from 'antd-mobile'
14 | import { NavigationActions } from 'react-navigation'
15 | import { postLoginRequest } from '../../actions'
16 |
17 | class SignIn extends Component {
18 | constructor(props) {
19 | super(props)
20 | this.state = {
21 | username: '',
22 | password: ''
23 | }
24 | this.onUserNameChange = this.onUserNameChange.bind(this)
25 | this.onPasswordChange = this.onPasswordChange.bind(this)
26 | this.onSignUp = this.onSignUp.bind(this)
27 | this.onSubmit = this.onSubmit.bind(this)
28 | }
29 |
30 | onUserNameChange(value) {
31 | this.setState({
32 | username: value
33 | })
34 | }
35 |
36 | onPasswordChange(value) {
37 | this.setState({
38 | password: value
39 | })
40 | }
41 |
42 | onSubmit = () => {
43 | const { postLogin, form } = this.props
44 | form.validateFields((error, value) => {
45 | if (!error) {
46 | postLogin({
47 | username: this.state.username,
48 | password: this.state.password
49 | })
50 | } else {
51 | Toast.fail('格式错误,请检查后提交', 1.5)
52 | }
53 | })
54 | }
55 |
56 | onSignUp() {
57 | this.props.navigateTo('SignUp')
58 | }
59 |
60 | componentDidMount() {}
61 |
62 | render() {
63 | const { getFieldProps, getFieldError } = this.props.form
64 | const { user_home, goBack } = this.props
65 | const usernameErrors = getFieldError('username')
66 | const passwordErrors = getFieldError('password')
67 | return (
68 |
69 |
70 |
71 |
87 | 用户名
88 |
89 |
90 |
91 |
92 |
93 | {usernameErrors ? usernameErrors.join(',') : null}
94 |
95 |
96 |
97 |
114 | 密码
115 |
116 |
117 |
118 |
119 |
120 | {passwordErrors ? passwordErrors.join(',') : null}
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
132 |
133 |
134 |
135 |
136 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | {user_home ? user_home.loginError : null}
148 |
149 |
150 |
151 |
152 |
153 | )
154 | }
155 | }
156 |
157 | const mapStateToProps = (state, ownProps) => {
158 | return {
159 | user_home: state.user.home
160 | }
161 | }
162 |
163 | const mapDispatchToProps = (dispatch, ownProps) => {
164 | return {
165 | postLogin: payload => {
166 | dispatch(postLoginRequest(payload))
167 | },
168 | navigateTo: (path, params) => {
169 | dispatch(NavigationActions.navigate({ routeName: path, params: params }))
170 | },
171 | }
172 | }
173 |
174 | SignIn.propTypes = {
175 | user_home: PropTypes.object,
176 | postLogin: PropTypes.func,
177 | navigateTo: PropTypes.func,
178 | form: PropTypes.object
179 | }
180 |
181 | const styles = StyleSheet.create({
182 | error: {
183 | color: 'red',
184 | textAlign: 'center'
185 | }
186 | })
187 |
188 | export default connect(mapStateToProps, mapDispatchToProps)(
189 | createForm()(SignIn)
190 | )
191 |
--------------------------------------------------------------------------------
/src/sagas/userSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { NavigationActions } from 'react-navigation'
4 | import { Toast } from 'antd-mobile'
5 | import {
6 | GET_CURRENTUSER_REQUEST,
7 | POST_SIGNUP_REQUEST,
8 | POST_LOGIN_REQUEST,
9 | POST_LOGOUT_REQUEST,
10 | PUT_USERINFO_REQUEST,
11 | GET_USERINFO_REQUEST,
12 | GET_HOME_USERINFO_LIST_REQUEST,
13 | GET_HOME_USERINFO_DETAIL_REQUEST
14 | } from '../constants/actionTypes'
15 | import * as action from '../actions'
16 | import { userService } from '../services/leanclound'
17 |
18 | function* getCurrentUserWorker() {
19 | try {
20 | const response = yield call(userService.getCurrentUserAsync)
21 | yield put(action.getCurrentUserSuccess(response))
22 | } catch (error) {
23 | yield put(action.getCurrentUserFailed())
24 | }
25 | }
26 |
27 | function* postSignUpWorker(payload) {
28 | try {
29 | Toast.loading('注册中')
30 | const response = yield call(userService.signUp, payload)
31 | yield put(action.postSignUpSuccess(response))
32 | Toast.success('注册成功', 1)
33 | yield put(
34 | NavigationActions.reset({
35 | index: 0,
36 | actions: [NavigationActions.navigate({ routeName: 'Account' })]
37 | })
38 | )
39 | } catch (error) {
40 | Toast.fail('注册失败', 1)
41 | yield put(action.postSignUpFailed(error))
42 | }
43 | }
44 |
45 | function* postLoginWorker(payload) {
46 | try {
47 | Toast.loading('登录中')
48 | const response = yield call(userService.logIn, payload)
49 | yield put(action.postLoginSuccess(response))
50 | Toast.success('登录成功', 1)
51 | yield put(
52 | NavigationActions.reset({
53 | index: 0,
54 | actions: [NavigationActions.navigate({ routeName: 'Account' })]
55 | })
56 | )
57 | } catch (error) {
58 | Toast.fail('登录失败', 1)
59 | yield put(action.postLoginFailed(error))
60 | }
61 | }
62 |
63 | function* postLogoutWorker() {
64 | try {
65 | yield call(userService.logOut)
66 | Toast.success('注销成功', 1)
67 | yield delay(1000)
68 | yield put(action.postLogoutSuccess())
69 | yield put(
70 | NavigationActions.reset({
71 | index: 0,
72 | actions: [NavigationActions.navigate({ routeName: 'Account' })]
73 | })
74 | )
75 | } catch (error) {
76 | Toast.success('注销失败', 1)
77 | yield put(action.postLogoutFailed(error))
78 | }
79 | }
80 |
81 | function* putUserInfoWorker(payload) {
82 | try {
83 | Toast.loading('提交中')
84 | const response = yield call(userService.putUserInfo, payload)
85 | yield put(action.putUserInfoSuccess(response))
86 | Toast.success('提交成功', 1.5)
87 | yield delay(1500)
88 | yield put(NavigationActions.back())
89 | } catch (error) {
90 | yield put(action.putUserInfoFailed(error))
91 | Toast.fail('提交失败', 1.5)
92 | }
93 | }
94 |
95 | function* getUserInfoWorker() {
96 | try {
97 | const currentUser = yield call(userService.getCurrentUserAsync)
98 | const response = yield call(userService.getUserInfoToJson, currentUser)
99 | yield put(action.getUserInfoSuccess(response))
100 | } catch (error) {
101 | yield put(action.getUserInfoFailed(error))
102 | }
103 | }
104 |
105 | function* getHomeUserInfoListWorker(payload) {
106 | try {
107 | const response = yield call(userService.getHomeUserInfoList, payload)
108 | yield put(action.getHomeUserInfoListSuccess(response))
109 | } catch (error) {
110 | yield put(action.getHomeUserInfoListFailed(error))
111 | }
112 | }
113 |
114 | function* getHomeUserInfoDetailWorker(payload) {
115 | try {
116 | Toast.loading('加载中')
117 | const response = yield call(userService.getHomeUserInfoDetail, payload)
118 | yield put(action.getHomeUserInfoDetailSuccess(response))
119 | Toast.hide()
120 | } catch (error) {
121 | yield put(action.getHomeUserInfoDetailFailed(error))
122 | Toast.hide()
123 | }
124 | }
125 |
126 | function* watchLogin() {
127 | while (true) {
128 | const { payload } = yield take(POST_LOGIN_REQUEST)
129 | yield fork(postLoginWorker, payload)
130 | }
131 | }
132 |
133 | function* watchSignUp() {
134 | while (true) {
135 | const { payload } = yield take(POST_SIGNUP_REQUEST)
136 | yield fork(postSignUpWorker, payload)
137 | }
138 | }
139 |
140 | function* watchLogout() {
141 | while (true) {
142 | yield take(POST_LOGOUT_REQUEST)
143 | yield fork(postLogoutWorker)
144 | }
145 | }
146 |
147 | function* watchPutUserInfo() {
148 | while (true) {
149 | const { payload } = yield take(PUT_USERINFO_REQUEST)
150 | yield fork(putUserInfoWorker, payload)
151 | }
152 | }
153 |
154 | function* watchGetUserInfo() {
155 | while (true) {
156 | yield take(GET_USERINFO_REQUEST)
157 | yield fork(getUserInfoWorker)
158 | }
159 | }
160 |
161 | function* watchGetHomeUserList() {
162 | while (true) {
163 | const { payload } = yield take(GET_HOME_USERINFO_LIST_REQUEST)
164 | yield fork(getHomeUserInfoListWorker, payload)
165 | }
166 | }
167 |
168 | function* watchGetHomeUserDetail() {
169 | while (true) {
170 | const { payload } = yield take(GET_HOME_USERINFO_DETAIL_REQUEST)
171 | yield fork(getHomeUserInfoDetailWorker, payload)
172 | }
173 | }
174 |
175 | function* watchGetCurrentUser() {
176 | while (true) {
177 | yield take(GET_CURRENTUSER_REQUEST)
178 | yield fork(getCurrentUserWorker)
179 | }
180 | }
181 |
182 | export {
183 | watchSignUp,
184 | watchLogin,
185 | watchLogout,
186 | watchGetUserInfo,
187 | watchPutUserInfo,
188 | watchGetHomeUserList,
189 | watchGetHomeUserDetail,
190 | watchGetCurrentUser
191 | }
192 |
--------------------------------------------------------------------------------
/src/sagas/teamsSaga.js:
--------------------------------------------------------------------------------
1 | import { put, fork, take, call } from 'redux-saga/effects'
2 | import { delay } from 'redux-saga'
3 | import { NavigationActions } from 'react-navigation'
4 | import { Toast } from 'antd-mobile'
5 | import {
6 | POST_TEAMS_REQUEST,
7 | PUT_TEAMS_REQUEST,
8 | DELETE_TEAM_MEMBER_REQUEST,
9 | DELETE_TEAM_REQUEST,
10 | GET_HOME_TEAM_LIST_REQUEST,
11 | GET_HOME_TEAM_DETAIL_REQUEST,
12 | GET_MY_TEAMS_REQUEST,
13 | GET_IN_TEAMS_REQUEST
14 | } from '../constants/actionTypes'
15 | import * as action from '../actions'
16 | import { teamsService, userService } from '../services/leanclound'
17 |
18 | function* postTeamsWorker(payload) {
19 | try {
20 | Toast.loading('提交中')
21 | const currentUser = yield call(userService.getCurrentUserAsync)
22 | const team = yield call(teamsService.getMyTeams, currentUser)
23 | if (team.length < currentUser.get('teamLimit')) {
24 | const response = yield call(teamsService.cerateTeam, payload, currentUser)
25 | yield put(action.postTeamsSuccess(response))
26 | Toast.success('提交成功', 1)
27 | yield delay(1000)
28 | yield put(NavigationActions.back())
29 | } else {
30 | yield put(action.postTeamsFailed())
31 | Toast.fail(
32 | '提交失败,每位用户最多可创建一支战队,若想创建多支战队,请联系管理员963577494@qq.com',
33 | 3
34 | )
35 | }
36 | } catch (error) {
37 | yield put(action.postTeamsFailed())
38 | Toast.fail('提交失败', 1)
39 | }
40 | }
41 |
42 | function* putTeamsWorker(payload) {
43 | try {
44 | Toast.loading('提交中')
45 | const response = yield call(teamsService.updateTeam, payload)
46 | yield put(action.putTeamsSuccess(response))
47 | Toast.success('提交成功', 1)
48 | yield delay(1000)
49 | yield put(NavigationActions.back())
50 | } catch (error) {
51 | yield put(action.putTeamsFailed())
52 | Toast.fail('提交失败', 1)
53 | }
54 | }
55 |
56 | function* getInTeamsWorker() {
57 | try {
58 | Toast.loading('加载中')
59 | const currentUser = yield call(userService.getCurrentUserAsync)
60 | const response = yield call(teamsService.getInTeams, currentUser)
61 | yield put(action.getInTeamsSuccess(response))
62 | Toast.hide()
63 | } catch (error) {
64 | yield put(action.getInTeamsFailed(error))
65 | Toast.hide()
66 | }
67 | }
68 |
69 | function* getMyTeamsWorker() {
70 | try {
71 | Toast.loading('加载中')
72 | const currentUser = yield call(userService.getCurrentUserAsync)
73 | const response = yield call(teamsService.getMyTeams, currentUser)
74 | yield put(action.getMyTeamsSuccess(response))
75 | Toast.hide()
76 | } catch (error) {
77 | yield put(action.getMyTeamsFailed(error))
78 | Toast.hide()
79 | }
80 | }
81 |
82 | function* deleteTeamMemberWorker(payload) {
83 | try {
84 | Toast.loading('提交中')
85 | const response = yield call(teamsService.removeMember, payload)
86 | yield put(action.deleteTeamMemberSuccess(response))
87 | Toast.success('移除队员成功', 1.5)
88 | yield delay(1500)
89 | yield put(NavigationActions.back())
90 | } catch (error) {
91 | yield put(action.deleteTeamMemberFailed(error))
92 | Toast.fail(error.message, 1.5)
93 | }
94 | }
95 |
96 | function* deleteTeamWorker(payload) {
97 | try {
98 | Toast.loading('提交中')
99 | const response = yield call(teamsService.removeTeam, payload)
100 | yield put(action.deleteTeamSuccess(response))
101 | Toast.success('解散队伍成功', 1.5)
102 | yield delay(1500)
103 | } catch (error) {
104 | yield put(action.deleteTeamFailed(error))
105 | Toast.fail(error.message, 1.5)
106 | }
107 | }
108 |
109 | function* getHomeTeamListWorker(payload) {
110 | try {
111 | Toast.loading('加载中')
112 | const response = yield call(teamsService.getHomeTeamsList, payload)
113 | yield put(action.getHomeTeamListSuccess(response))
114 | Toast.hide()
115 | } catch (error) {
116 | yield put(action.getHomeTeamListFailed(error))
117 | Toast.hide()
118 | }
119 | }
120 |
121 | function* getHomeTeamDetailWorker(payload) {
122 | try {
123 | Toast.loading('加载中')
124 | const response = yield call(teamsService.getHomeTeamDetail, payload)
125 | yield put(action.getHomeTeamDetailSuccess(response))
126 | Toast.hide()
127 | } catch (error) {
128 | yield put(action.getHomeTeamDetailFailed(error))
129 | Toast.hide()
130 | }
131 | }
132 |
133 | function* watchPostTeams() {
134 | while (true) {
135 | const { payload } = yield take(POST_TEAMS_REQUEST)
136 | yield fork(postTeamsWorker, payload)
137 | }
138 | }
139 |
140 | function* watchPutTeams() {
141 | while (true) {
142 | const { payload } = yield take(PUT_TEAMS_REQUEST)
143 | yield fork(putTeamsWorker, payload)
144 | }
145 | }
146 |
147 | function* watchDeleteTeamMember() {
148 | while (true) {
149 | const { payload } = yield take(DELETE_TEAM_MEMBER_REQUEST)
150 | yield fork(deleteTeamMemberWorker, payload)
151 | }
152 | }
153 |
154 | function* watchDeleteTeam() {
155 | while (true) {
156 | const { payload } = yield take(DELETE_TEAM_REQUEST)
157 | yield fork(deleteTeamWorker, payload)
158 | }
159 | }
160 |
161 | function* watchGetHomeTeamList() {
162 | while (true) {
163 | const { payload } = yield take(GET_HOME_TEAM_LIST_REQUEST)
164 | yield fork(getHomeTeamListWorker, payload)
165 | }
166 | }
167 |
168 | function* watchGetHomeTeamDetail() {
169 | while (true) {
170 | const { payload } = yield take(GET_HOME_TEAM_DETAIL_REQUEST)
171 | yield fork(getHomeTeamDetailWorker, payload)
172 | }
173 | }
174 |
175 | function* watchGetMyTeams() {
176 | while (true) {
177 | yield take(GET_MY_TEAMS_REQUEST)
178 | yield fork(getMyTeamsWorker)
179 | }
180 | }
181 |
182 | function* watchGetInTeams() {
183 | while (true) {
184 | yield take(GET_IN_TEAMS_REQUEST)
185 | yield fork(getInTeamsWorker)
186 | }
187 | }
188 |
189 | export {
190 | watchPostTeams,
191 | watchPutTeams,
192 | watchDeleteTeamMember,
193 | watchDeleteTeam,
194 | watchGetHomeTeamList,
195 | watchGetHomeTeamDetail,
196 | watchGetMyTeams,
197 | watchGetInTeams
198 | }
199 |
--------------------------------------------------------------------------------
/src/reducers/team.js:
--------------------------------------------------------------------------------
1 | import {
2 | POST_TEAMS_REQUEST,
3 | POST_TEAMS_SUCCESS,
4 | POST_TEAMS_FAILED,
5 | PUT_TEAMS_REQUEST,
6 | PUT_TEAMS_SUCCESS,
7 | PUT_TEAMS_FAILED,
8 | GET_MY_TEAMS_REQUEST,
9 | GET_MY_TEAMS_SUCCESS,
10 | GET_MY_TEAMS_FAILED,
11 | GET_IN_TEAMS_REQUEST,
12 | GET_IN_TEAMS_SUCCESS,
13 | GET_IN_TEAMS_FAILED,
14 | DELETE_TEAM_MEMBER_REQUEST,
15 | DELETE_TEAM_MEMBER_SUCCESS,
16 | DELETE_TEAM_MEMBER_FAILED,
17 | DELETE_TEAM_REQUEST,
18 | DELETE_TEAM_SUCCESS,
19 | DELETE_TEAM_FAILED,
20 | GET_HOME_TEAM_LIST_REQUEST,
21 | GET_HOME_TEAM_LIST_SUCCESS,
22 | GET_HOME_TEAM_LIST_FAILED,
23 | GET_HOME_TEAM_DETAIL_REQUEST,
24 | GET_HOME_TEAM_DETAIL_SUCCESS,
25 | GET_HOME_TEAM_DETAIL_FAILED
26 | } from '../constants/actionTypes'
27 |
28 | const initialTeamState = {
29 | home: {
30 | team: {
31 | list: [],
32 | current: {},
33 | isFetching: false,
34 | fetchingText: '加载中',
35 | isLoadMore: false,
36 | isRefreshing: false,
37 | page: 1,
38 | pagesize: 20
39 | }
40 | },
41 | account: {
42 | current: {},
43 | team: {
44 | myTeams: [],
45 | inTeams: []
46 | },
47 | pending: false
48 | }
49 | }
50 |
51 | function teamReducer(state = initialTeamState, action) {
52 | switch (action.type) {
53 | case POST_TEAMS_REQUEST:
54 | return {
55 | ...state,
56 | account: {
57 | ...state.account,
58 | team: {
59 | ...state.account.team,
60 | pending: true
61 | }
62 | }
63 | }
64 | case POST_TEAMS_SUCCESS:
65 | return {
66 | ...state,
67 | account: {
68 | ...state.account,
69 | team: {
70 | ...state.account.team,
71 | myTeams: [...state.account.team.myTeams, action.payload],
72 | pending: false
73 | }
74 | },
75 | home: {
76 | ...state.home,
77 | team: {
78 | ...state.home.team,
79 | myTeams: [...state.home.team.myTeams, action.payload]
80 | }
81 | }
82 | }
83 | case POST_TEAMS_FAILED:
84 | return {
85 | ...state,
86 | account: {
87 | ...state.account,
88 | team: {
89 | ...state.account.team,
90 | pending: false
91 | }
92 | }
93 | }
94 | case PUT_TEAMS_REQUEST:
95 | return {
96 | ...state,
97 | account: {
98 | ...state.account,
99 | team: {
100 | ...state.account.team,
101 | pending: true
102 | }
103 | }
104 | }
105 | case PUT_TEAMS_SUCCESS:
106 | const data = state.account.team.myTeams.map(item => {
107 | if (item.objectId === action.payload.objectId) {
108 | return Object.assign(item, action.payload)
109 | } else {
110 | return item
111 | }
112 | })
113 | return {
114 | ...state,
115 | account: {
116 | ...state.account,
117 | team: {
118 | ...state.account.team,
119 | myTeams: data
120 | }
121 | },
122 | home: {
123 | ...state.home,
124 | team: {
125 | ...state.home.team,
126 | myTeams: data
127 | }
128 | }
129 | }
130 | case PUT_TEAMS_FAILED:
131 | return {
132 | ...state,
133 | account: {
134 | ...state.account,
135 | team: {
136 | ...state.account.team,
137 | pending: false
138 | }
139 | }
140 | }
141 | case DELETE_TEAM_MEMBER_REQUEST:
142 | return state
143 | case DELETE_TEAM_MEMBER_SUCCESS:
144 | return state
145 | case DELETE_TEAM_MEMBER_FAILED:
146 | return state
147 | case DELETE_TEAM_REQUEST:
148 | return state
149 | case DELETE_TEAM_SUCCESS:
150 | const teams = state.account.team.myTeams.filter(
151 | x => x.objectId !== action.payload.objectId
152 | )
153 | return {
154 | ...state,
155 | account: {
156 | ...state.account,
157 | team: {
158 | ...state.account.team,
159 | myTeams: teams
160 | }
161 | },
162 | home: {
163 | ...state.home,
164 | team: {
165 | ...state.home.team,
166 | myTeams: teams
167 | }
168 | }
169 | }
170 | case DELETE_TEAM_FAILED:
171 | return state
172 | case GET_MY_TEAMS_REQUEST:
173 | return state
174 | case GET_MY_TEAMS_SUCCESS:
175 | return {
176 | ...state,
177 | account: {
178 | ...state.account,
179 | team: {
180 | ...state.account.team,
181 | myTeams: [].concat(action.payload)
182 | }
183 | }
184 | }
185 | case GET_MY_TEAMS_FAILED:
186 | return state
187 | case GET_IN_TEAMS_REQUEST:
188 | return state
189 | case GET_IN_TEAMS_SUCCESS:
190 | return {
191 | ...state,
192 | account: {
193 | ...state.account,
194 | team: {
195 | ...state.account.team,
196 | inTeams: [].concat(action.payload)
197 | }
198 | }
199 | }
200 | case GET_IN_TEAMS_FAILED:
201 | return state
202 | case GET_HOME_TEAM_LIST_REQUEST:
203 | return {
204 | ...state,
205 | home: {
206 | ...state.home,
207 | team: {
208 | ...state.home.team,
209 | isFetching: true,
210 | isRefreshing: action.payload.isRefreshing || false,
211 | list: action.payload.isRefreshing ? [] : state.home.team.list,
212 | page: action.payload.isRefreshing
213 | ? 1
214 | : action.payload.page ? action.payload.page : 1
215 | }
216 | }
217 | }
218 | case GET_HOME_TEAM_LIST_SUCCESS:
219 | return {
220 | ...state,
221 | home: {
222 | ...state.home,
223 | team: {
224 | ...state.home.team,
225 | list: state.home.team.list.concat(action.payload),
226 | isFetching: false,
227 | isRefreshing: false,
228 | isLoadMore: action.payload.length < 20 ? false : true
229 | }
230 | }
231 | }
232 | case GET_HOME_TEAM_LIST_FAILED:
233 | return {
234 | ...state,
235 | home: {
236 | ...state.home,
237 | team: {
238 | ...state.home.team,
239 | isFetching: false,
240 | isRefreshing: false
241 | }
242 | }
243 | }
244 | case GET_HOME_TEAM_DETAIL_REQUEST:
245 | return state
246 | case GET_HOME_TEAM_DETAIL_SUCCESS:
247 | return {
248 | ...state,
249 | home: {
250 | ...state.home,
251 | team: {
252 | ...state.home.team,
253 | current: action.payload
254 | }
255 | }
256 | }
257 | case GET_HOME_TEAM_DETAIL_FAILED:
258 | return state
259 | default:
260 | return state
261 | }
262 | }
263 |
264 | export { teamReducer }
265 |
--------------------------------------------------------------------------------
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | import { Constants } from 'expo'
2 |
3 | export const STATUS_BAR_HEIGHT = Constants.statusBarHeight
4 |
5 | export const TEAMPOSITIONS = [
6 | {
7 | value: 'DPS',
8 | label: '突击',
9 | desc: ''
10 | },
11 | {
12 | value: 'Flex',
13 | label: '自由人',
14 | desc: ''
15 | },
16 | {
17 | value: 'Tank',
18 | label: '坦克',
19 | desc: ''
20 | },
21 | {
22 | value: 'Support',
23 | label: '辅助',
24 | desc: ''
25 | }
26 | ]
27 | export const RANKS = [
28 | {
29 | value: 'bronze',
30 | label: '青铜',
31 | score: '1-1499'
32 | },
33 | {
34 | value: 'silver',
35 | label: '白银',
36 | score: '1500-1999'
37 | },
38 | {
39 | value: 'gold',
40 | label: '黄金',
41 | score: '2000-2499'
42 | },
43 | {
44 | value: 'platinum',
45 | label: '白金',
46 | score: '2500-2999'
47 | },
48 | {
49 | value: 'diamond',
50 | label: '钻石',
51 | score: '3000-3499'
52 | },
53 | {
54 | value: 'master',
55 | label: '大师',
56 | score: '3500-3999'
57 | },
58 | {
59 | value: 'grandmaster',
60 | label: '宗师',
61 | score: '4000-5000'
62 | },
63 | {
64 | value: 'top500',
65 | label: '500强',
66 | score: '4000-5000'
67 | }
68 | ]
69 |
70 | export const HEROS = [
71 | {
72 | value: 'doomfist',
73 | label: '末日铁拳',
74 | image:
75 | 'http://overwatch.nos.netease.com/1/assets/images/hero/doomfist/icon-portrait.png',
76 | position: 'offense',
77 | checked: false
78 | },
79 | {
80 | value: 'genji',
81 | label: '源氏',
82 | image:
83 | 'http://overwatch.nos.netease.com/1/assets/images/hero/genji/icon-portrait.png',
84 | position: 'offense',
85 | checked: false
86 | },
87 | {
88 | value: 'mccree',
89 | label: '麦克雷',
90 | image:
91 | 'http://overwatch.nos.netease.com/1/assets/images/hero/mccree/icon-portrait.png',
92 | position: 'offense',
93 | checked: false
94 | },
95 | {
96 | value: 'pharah',
97 | label: '法老之鹰',
98 | image:
99 | 'http://overwatch.nos.netease.com/1/assets/images/hero/pharah/icon-portrait.png',
100 | position: 'offense',
101 | checked: false
102 | },
103 | {
104 | value: 'reaper',
105 | label: '死神',
106 | image:
107 | 'http://overwatch.nos.netease.com/1/assets/images/hero/reaper/icon-portrait.png',
108 | position: 'offense',
109 | checked: false
110 | },
111 | {
112 | value: 'soldier-76',
113 | label: '士兵:76',
114 | image:
115 | 'http://overwatch.nos.netease.com/1/assets/images/hero/soldier-76/icon-portrait.png',
116 | position: 'offense',
117 | checked: false
118 | },
119 | {
120 | value: 'sombra',
121 | label: '黑影',
122 | image:
123 | 'http://overwatch.nos.netease.com/1/assets/images/hero/sombra/icon-portrait.png',
124 | position: 'offense',
125 | checked: false
126 | },
127 | {
128 | value: 'tracer',
129 | label: '猎空',
130 | image:
131 | 'http://overwatch.nos.netease.com/1/assets/images/hero/tracer/icon-portrait.png',
132 | position: 'offense',
133 | checked: false
134 | },
135 | {
136 | value: 'bastion',
137 | label: '堡垒',
138 | image:
139 | 'http://overwatch.nos.netease.com/1/assets/images/hero/bastion/icon-portrait.png',
140 | position: 'defense',
141 | checked: false
142 | },
143 | {
144 | value: 'hanzo',
145 | label: '半藏',
146 | image:
147 | 'http://overwatch.nos.netease.com/1/assets/images/hero/hanzo/icon-portrait.png',
148 | position: 'defense',
149 | checked: false
150 | },
151 | {
152 | value: 'junkrat',
153 | label: '狂鼠',
154 | image:
155 | 'http://overwatch.nos.netease.com/1/assets/images/hero/junkrat/icon-portrait.png',
156 | position: 'offense',
157 | checked: false
158 | },
159 | {
160 | value: 'mei',
161 | label: '美',
162 | image:
163 | 'http://overwatch.nos.netease.com/1/assets/images/hero/mei/icon-portrait.png',
164 | position: 'offense',
165 | checked: false
166 | },
167 | {
168 | value: 'torbjorn',
169 | label: '托比昂',
170 | image:
171 | 'http://overwatch.nos.netease.com/1/assets/images/hero/torbjorn/icon-portrait.png',
172 | position: 'offense',
173 | checked: false
174 | },
175 | {
176 | value: 'widowmaker',
177 | label: '黑百合',
178 | image:
179 | 'http://overwatch.nos.netease.com/1/assets/images/hero/widowmaker/icon-portrait.png',
180 | position: 'offense',
181 | checked: false
182 | },
183 | {
184 | value: 'dva',
185 | label: 'D.Va',
186 | image:
187 | 'http://overwatch.nos.netease.com/1/assets/images/hero/dva/icon-portrait.png',
188 | position: 'tank',
189 | checked: false
190 | },
191 | {
192 | value: 'orisa',
193 | label: '奥丽莎',
194 | image:
195 | 'http://overwatch.nos.netease.com/1/assets/images/hero/orisa/icon-portrait.png',
196 | position: 'tank',
197 | checked: false
198 | },
199 | {
200 | value: 'reinhardt',
201 | label: '莱因哈特',
202 | image:
203 | 'http://overwatch.nos.netease.com/1/assets/images/hero/reinhardt/icon-portrait.png',
204 | position: 'tank',
205 | checked: false
206 | },
207 | {
208 | value: 'roadhog',
209 | label: '路霸',
210 | image:
211 | 'http://overwatch.nos.netease.com/1/assets/images/hero/roadhog/icon-portrait.png',
212 | position: 'tank',
213 | checked: false
214 | },
215 | {
216 | value: 'winston',
217 | label: '温斯顿',
218 | image:
219 | 'http://overwatch.nos.netease.com/1/assets/images/hero/winston/icon-portrait.png',
220 | position: 'tank',
221 | checked: false
222 | },
223 | {
224 | value: 'zarya',
225 | label: '查莉娅',
226 | image:
227 | 'http://overwatch.nos.netease.com/1/assets/images/hero/zarya/icon-portrait.png',
228 | position: 'tank',
229 | checked: false
230 | },
231 | {
232 | value: 'ana',
233 | label: '安娜',
234 | image:
235 | 'http://overwatch.nos.netease.com/1/assets/images/hero/ana/icon-portrait.png',
236 | position: 'support',
237 | checked: false
238 | },
239 | {
240 | value: 'lucio',
241 | label: '卢西奥',
242 | image:
243 | 'http://overwatch.nos.netease.com/1/assets/images/hero/lucio/icon-portrait.png',
244 | position: 'support',
245 | checked: false
246 | },
247 | {
248 | value: 'mercy',
249 | label: '天使',
250 | image:
251 | 'http://overwatch.nos.netease.com/1/assets/images/hero/mercy/icon-portrait.png',
252 | position: 'support',
253 | checked: false
254 | },
255 | {
256 | value: 'moira',
257 | label: '莫伊拉',
258 | image:
259 | 'http://overwatch.nos.netease.com/1/assets/images/hero/moira/icon-portrait.png',
260 | position: 'support',
261 | checked: false
262 | },
263 | {
264 | value: 'symmetra',
265 | label: '秩序之光',
266 | image:
267 | 'http://overwatch.nos.netease.com/1/assets/images/hero/symmetra/icon-portrait.png',
268 | position: 'support',
269 | checked: false
270 | },
271 | {
272 | value: 'zenyatta',
273 | label: '禅雅塔',
274 | image:
275 | 'http://overwatch.nos.netease.com/1/assets/images/hero/zenyatta/icon-portrait.png',
276 | position: 'support',
277 | checked: false
278 | }
279 | ]
280 |
281 |
--------------------------------------------------------------------------------
/src/reducers/user.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_CURRENTUSER_REQUEST,
3 | GET_CURRENTUSER_SUCCESS,
4 | GET_CURRENTUSER_FAILED,
5 | POST_LOGIN_REQUEST,
6 | POST_LOGIN_SUCCESS,
7 | POST_LOGIN_FAILED,
8 | POST_LOGOUT_REQUEST,
9 | POST_LOGOUT_SUCCESS,
10 | POST_LOGOUT_FAILED,
11 | POST_SIGNUP_REQUEST,
12 | POST_SIGNUP_SUCCESS,
13 | POST_SIGNUP_FAILED,
14 | PUT_USERINFO_REQUEST,
15 | PUT_USERINFO_SUCCESS,
16 | PUT_USERINFO_FAILED,
17 | GET_USERINFO_REQUEST,
18 | GET_USERINFO_SUCCESS,
19 | GET_USERINFO_FAILED,
20 | GET_HOME_USERINFO_LIST_REQUEST,
21 | GET_HOME_USERINFO_LIST_SUCCESS,
22 | GET_HOME_USERINFO_LIST_FAILED,
23 | GET_HOME_USERINFO_DETAIL_REQUEST,
24 | GET_HOME_USERINFO_DETAIL_SUCCESS,
25 | GET_HOME_USERINFO_DETAIL_FAILED
26 | } from '../constants/actionTypes'
27 | import { HEROS } from '../constants'
28 |
29 | const initialUserState = {
30 | home: {
31 | loginError: '',
32 | signupError: '',
33 | userinfo: {
34 | list: [],
35 | current: {},
36 | isFetching: false,
37 | fetchingText: '加载中',
38 | isLoadMore: false,
39 | isRefreshing: false,
40 | page: 1,
41 | pagesize: 20
42 | }
43 | },
44 | account: {
45 | user: {},
46 | userinfo: {
47 | isLoaded: false,
48 | position: 'DPS',
49 | rank: 'top500',
50 | pending: false
51 | }
52 | }
53 | }
54 |
55 | function userReducer(state = initialUserState, action) {
56 | switch (action.type) {
57 | case GET_CURRENTUSER_REQUEST:
58 | return state
59 | case GET_CURRENTUSER_SUCCESS:
60 | return {
61 | ...state,
62 | account: { ...state.account, user: action.payload }
63 | }
64 | case GET_CURRENTUSER_FAILED:
65 | return state
66 | case POST_LOGIN_REQUEST:
67 | return { ...state, home: { ...state.home, loginError: '' } }
68 | case POST_LOGIN_SUCCESS:
69 | return {
70 | ...state,
71 | home: { ...state.home, loginError: '' },
72 | account: { ...state.account, user: action.payload }
73 | }
74 | case POST_LOGIN_FAILED:
75 | return {
76 | ...state,
77 | home: { ...state.home, loginError: action.payload.rawMessage }
78 | }
79 | case POST_LOGOUT_REQUEST:
80 | return state
81 | case POST_LOGOUT_SUCCESS:
82 | return {
83 | ...state,
84 | account: {
85 | ...state.account,
86 | user: {},
87 | userinfo: {
88 | isLoaded: false,
89 | position: 'DPS',
90 | rank: 'top500',
91 | pending: false
92 | }
93 | }
94 | }
95 | case POST_LOGOUT_FAILED:
96 | return state
97 | case POST_SIGNUP_REQUEST:
98 | return {
99 | ...state,
100 | home: { ...state.home, signupError: '' }
101 | }
102 | case POST_SIGNUP_SUCCESS:
103 | return {
104 | ...state,
105 | home: { ...state.home, loginError: '' },
106 | account: { ...state.account, user: {} }
107 | }
108 | case POST_SIGNUP_FAILED:
109 | return {
110 | ...state,
111 | home: { ...state.home, signupError: action.payload.rawMessage }
112 | }
113 |
114 | case PUT_USERINFO_REQUEST:
115 | return {
116 | ...state,
117 | account: {
118 | ...state.account,
119 | userinfo: {
120 | ...state.account.userinfo,
121 | pending: true
122 | }
123 | }
124 | }
125 | case PUT_USERINFO_SUCCESS:
126 | return {
127 | ...state,
128 | account: {
129 | ...state.account,
130 | userinfo: {
131 | ...state.account.userinfo,
132 | ...action.payload,
133 | heros: merge(HEROS, action.payload.heros),
134 | pending: false,
135 | isLoaded: true
136 | }
137 | }
138 | }
139 | case PUT_USERINFO_FAILED:
140 | return {
141 | ...state,
142 | account: {
143 | ...state.account,
144 | userinfo: {
145 | ...state.account.userinfo,
146 | pending: false
147 | }
148 | }
149 | }
150 | case GET_USERINFO_REQUEST:
151 | return state
152 | case GET_USERINFO_SUCCESS:
153 | return {
154 | ...state,
155 | account: {
156 | ...state.account,
157 | userinfo: {
158 | ...state.account.userinfo,
159 | ...action.payload,
160 | heros: merge(HEROS, action.payload.heros),
161 | isLoaded: true
162 | }
163 | }
164 | }
165 | case GET_USERINFO_FAILED:
166 | return state
167 | case GET_HOME_USERINFO_LIST_REQUEST:
168 | return {
169 | ...state,
170 | home: {
171 | ...state.home,
172 | userinfo: {
173 | ...state.home.userinfo,
174 | isFetching: true,
175 | isRefreshing: action.payload.isRefreshing || false,
176 | list: action.payload.isRefreshing ? [] : state.home.userinfo.list,
177 | page: action.payload.isRefreshing
178 | ? 1
179 | : action.payload.page ? action.payload.page : 1
180 | }
181 | }
182 | }
183 | case GET_HOME_USERINFO_LIST_SUCCESS:
184 | return {
185 | ...state,
186 | home: {
187 | ...state.home,
188 | userinfo: {
189 | ...state.home.userinfo,
190 | list: state.home.userinfo.list.concat(action.payload),
191 | isFetching: false,
192 | isRefreshing: false,
193 | isLoadMore: action.payload.length < 20 ? false : true
194 | }
195 | }
196 | }
197 | case GET_HOME_USERINFO_LIST_FAILED:
198 | return {
199 | ...state,
200 | home: {
201 | ...state.home,
202 | userinfo: {
203 | ...state.home.userinfo,
204 | isFetching: false,
205 | isRefreshing: false
206 | }
207 | }
208 | }
209 | case GET_HOME_USERINFO_DETAIL_REQUEST:
210 | return {
211 | ...state,
212 | home: {
213 | ...state.home,
214 | userinfo: {
215 | ...state.home.userinfo,
216 | isFetching: true
217 | }
218 | }
219 | }
220 | case GET_HOME_USERINFO_DETAIL_SUCCESS:
221 | return {
222 | ...state,
223 | home: {
224 | ...state.home,
225 | userinfo: {
226 | ...state.home.userinfo,
227 | isFetching: false,
228 | current: action.payload
229 | }
230 | }
231 | }
232 | case GET_HOME_USERINFO_DETAIL_FAILED:
233 | return {
234 | ...state,
235 | home: {
236 | ...state.home,
237 | userinfo: {
238 | ...state.home.userinfo,
239 | isFetching: false
240 | }
241 | }
242 | }
243 | default:
244 | return state
245 | }
246 | }
247 |
248 | function merge(o1, o2) {
249 | if (o2) {
250 | const result = o1.map(item1 => {
251 | return Object.assign(
252 | item1,
253 | o2.find(item2 => {
254 | return item2 && item1.value === item2.value
255 | })
256 | )
257 | })
258 | return result
259 | }
260 | }
261 |
262 | export { userReducer }
263 |
--------------------------------------------------------------------------------