OrderDetailScreen.jsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /* eslint-disable react/jsx-props-no-spreading */
  2. /* eslint-disable no-underscore-dangle */
  3. import * as WebBrowser from 'expo-web-browser';
  4. import * as React from 'react';
  5. import {
  6. StyleSheet,
  7. View,
  8. Clipboard,
  9. Dimensions,
  10. Animated,
  11. PanResponder,
  12. findNodeHandle,
  13. UIManager,
  14. TouchableOpacity,
  15. } from 'react-native';
  16. import { Div, Button, Image, Text } from 'react-native-magnus';
  17. import { Appbar, Menu } from 'react-native-paper';
  18. import Constants from 'expo-constants';
  19. import { Flex } from '@ant-design/react-native';
  20. import { ScrollView } from 'react-native-gesture-handler';
  21. import { useRequest, useCreation, useMount } from '@umijs/hooks';
  22. import { useTranslation } from 'react-i18next';
  23. import { useRoute } from '@react-navigation/native';
  24. import { useAnimation } from 'react-native-animation-hooks';
  25. import useModel from 'flooks';
  26. import Toast from '../../flooks/Toast';
  27. import Header from './Header'; // 头部
  28. import { connectKefu } from '../../Utils/TotastUtils';
  29. import {
  30. merchantStatusMap,
  31. orderStatusMap,
  32. RiderStatusMap,
  33. payMap,
  34. reasonMap,
  35. } from '../../Utils/OrderUtils';
  36. import Time from '../../Utils/TimeUtils';
  37. import MapScreen from '../Map/MapScreen';
  38. export default function OrderScreen({ navigation }) {
  39. const { t } = useTranslation();
  40. const [orderInfo, setorderInfo] = React.useState({
  41. merchant: {},
  42. orderGoodsSpecs: [],
  43. });
  44. const { loading, success, warnning, clearLoading } = useModel(Toast, []);
  45. const route = useRoute();
  46. const { params } = route;
  47. const { orderId } = params || 0;
  48. const orderRequest = useRequest(`/orderInfo/get/${orderId}`, {
  49. refreshDeps: [orderId],
  50. onSuccess: (result) => {
  51. setorderInfo(result);
  52. },
  53. });
  54. const { merchant, orderGoodsSpecs } = orderInfo;
  55. const statusInfo = useCreation(() => {
  56. const statusList = [];
  57. if (orderInfo.status) {
  58. statusList.push(orderStatusMap.get(orderInfo.status));
  59. }
  60. if (orderInfo.merchantStatus) {
  61. statusList.push(merchantStatusMap.get(orderInfo.merchantStatus));
  62. }
  63. if (orderInfo.riderStatus) {
  64. statusList.push(RiderStatusMap.get(orderInfo.riderStatus));
  65. }
  66. if (statusList.length > 0) {
  67. return statusList.sort((a, b) => {
  68. return b.sort - a.sort;
  69. })[0];
  70. } else {
  71. return {
  72. name: t('ding-dan-xiang-qing'),
  73. };
  74. }
  75. }, [orderInfo]);
  76. const finish = useCreation(() => {
  77. if (
  78. orderInfo.riderStatus === 'CARRY_OUT' ||
  79. orderInfo.status === 'CANCELLED'
  80. ) {
  81. return true;
  82. } else {
  83. return false;
  84. }
  85. }, [orderInfo]);
  86. const cancelOrder = useCreation(() => {
  87. if (orderInfo.status === 'CANCELLED') {
  88. return true;
  89. } else {
  90. return false;
  91. }
  92. }, [orderInfo]);
  93. const Allfinish = useCreation(() => {
  94. if (orderInfo.status === 'COMPLETED') {
  95. return true;
  96. } else {
  97. return false;
  98. }
  99. }, [orderInfo]);
  100. const isPay = useCreation(() => {
  101. if (orderInfo.status === 'UNPAID') {
  102. return false;
  103. } else {
  104. return true;
  105. }
  106. }, [orderInfo]);
  107. const hasRider = useCreation(() => {
  108. if (orderInfo.riderStatus !== 'NOT_RECEIVED' && orderInfo.riderStatus) {
  109. return true;
  110. } else {
  111. return false;
  112. }
  113. });
  114. const detailRef = React.useRef();
  115. const pan = React.useRef(new Animated.ValueXY()).current;
  116. const minMove = React.useRef(new Animated.Value(0)).current;
  117. const maxMove = React.useRef(
  118. new Animated.Value(Dimensions.get('window').height * 0.6)
  119. ).current;
  120. const panResponder = React.useRef(
  121. PanResponder.create({
  122. onMoveShouldSetPanResponder: () => {
  123. console.log('a2');
  124. return false;
  125. },
  126. onMoveShouldSetPanResponderCapture: () => false,
  127. onStartShouldSetPanResponder: () => {
  128. console.log('a1');
  129. return true;
  130. },
  131. onStartShouldSetPanResponderCapture: () => false,
  132. onResponderTerminationRequest: () => true,
  133. onPanResponderGrant: () => {
  134. pan.setOffset({
  135. y: pan.y._value,
  136. });
  137. },
  138. onPanResponderMove: (e, ges) => {
  139. const y = ges.dy + pan.y._offset;
  140. if (y >= minMove._value && y <= maxMove._value) {
  141. pan.setValue({
  142. y: ges.dy,
  143. });
  144. } else if (y < minMove._value) {
  145. pan.setValue({
  146. y: minMove._value - pan.y._offset,
  147. });
  148. } else {
  149. pan.setValue({
  150. y: maxMove._value - pan.y._offset,
  151. });
  152. if (ges.moveY > 500) {
  153. orderRequest.run();
  154. }
  155. }
  156. if (maxMove._value !== 0) {
  157. if (maxMove._value - y < 200) {
  158. setbackgorundColor(
  159. `rgba(255, 194, 28,${Math.ceil(
  160. (1 / 200) * (maxMove._value - y)
  161. )})`
  162. );
  163. setshowDetail(false);
  164. } else {
  165. setbackgorundColor('#FFC21C');
  166. setshowDetail(true);
  167. }
  168. }
  169. },
  170. onPanResponderRelease: () => {
  171. pan.flattenOffset();
  172. },
  173. })
  174. ).current;
  175. React.useEffect(() => {
  176. if (finish) {
  177. maxMove.setValue(0);
  178. pan.setValue({
  179. y: 0,
  180. });
  181. setshowDetail(true);
  182. setbackgorundColor('#FFC21C');
  183. } else {
  184. maxMove.setValue(Dimensions.get('window').height * 0.6);
  185. pan.setValue({
  186. y: Dimensions.get('window').height * 0.6,
  187. });
  188. setshowDetail(false);
  189. }
  190. }, [finish]);
  191. const [backgorundColor, setbackgorundColor] = React.useState(
  192. 'rgba(255, 194, 28,0)'
  193. );
  194. const [showDetail, setshowDetail] = React.useState(false);
  195. React.useEffect(() => {
  196. if (orderRequest.loading) {
  197. loading();
  198. } else {
  199. clearLoading();
  200. }
  201. }, [orderRequest.loading]);
  202. return (
  203. <Div bg="gray200" w="100%" flex={1}>
  204. <Header
  205. title={showDetail ? statusInfo.name : ' '}
  206. bg={backgorundColor}
  207. orderId={orderId}
  208. hasRight={!finish && !Allfinish && !cancelOrder}
  209. />
  210. {/* <RefreshControl
  211. refreshing={orderRequest.loading}
  212. onRefresh={orderRequest.run}
  213. > */}
  214. {!finish && (
  215. <Div
  216. w={Dimensions.get('window').width}
  217. h={Dimensions.get('window').height + Constants.statusBarHeight}
  218. position="absolute"
  219. top={0}
  220. left={0}
  221. zIndex={1}
  222. >
  223. <MapScreen orderInfo={orderInfo} />
  224. </Div>
  225. )}
  226. <Animated.View
  227. ref={detailRef}
  228. style={{
  229. zIndex: 2,
  230. backgroundColor: 'rgba(0,0,0,0)',
  231. transform: [{ translateY: pan.y }],
  232. }}
  233. onLayout={({ nativeEvent }) => {
  234. minMove.setValue(
  235. Dimensions.get('window').height -
  236. nativeEvent.layout.height -
  237. nativeEvent.layout.y -
  238. Constants.statusBarHeight
  239. );
  240. }}
  241. >
  242. <View {...panResponder.panHandlers}>
  243. <Div pt={10} pb={10} px={15}>
  244. <View style={[styles.card]}>
  245. {!cancelOrder &&
  246. (finish ? (
  247. <Text fontSize="xl">
  248. <Text
  249. fontSize="xl"
  250. color="brand500"
  251. mr={10}
  252. fontWeight="bold"
  253. >
  254. {statusInfo.name}
  255. </Text>
  256. {new Time(
  257. orderInfo.userReceivedTime,
  258. 'yyyy-MM-DD HH:mm:ss'
  259. ).getFormat('HH:mm')}
  260. {t('song-da')}
  261. </Text>
  262. ) : (
  263. <Text fontSize="xl">
  264. <Text
  265. fontSize="xl"
  266. color="brand500"
  267. mr={10}
  268. fontWeight="bold"
  269. >
  270. {statusInfo.name}
  271. </Text>
  272. {t('yu-ji')}
  273. {new Time(
  274. orderInfo.timeOfArrival,
  275. 'yyyy-MM-DD HH:mm:ss'
  276. ).getFormat('HH:mm')}
  277. {t('song-da')}
  278. </Text>
  279. ))}
  280. {cancelOrder && (
  281. <Div row>
  282. <Text
  283. fontSize="xl"
  284. color="brand500"
  285. mr={10}
  286. fontWeight="bold"
  287. >
  288. {statusInfo.name}
  289. </Text>
  290. {!!orderInfo.reason && (
  291. <Text fontSize="xl" color="red500">
  292. ({reasonMap.get(orderInfo.reason).name})
  293. </Text>
  294. )}
  295. </Div>
  296. )}
  297. <Div row mt={10}>
  298. <Button
  299. fontSize="xs"
  300. flex={1}
  301. mx={5}
  302. bg="white"
  303. color="gray600"
  304. borderColor="brand500"
  305. borderWidth={1}
  306. rounded={3}
  307. onPress={() => connectKefu(orderId)}
  308. >
  309. {t('lian-xi-ke-fu')}
  310. </Button>
  311. <Button
  312. fontSize="xs"
  313. flex={1}
  314. mx={5}
  315. bg="white"
  316. color="gray600"
  317. borderColor="brand500"
  318. borderWidth={1}
  319. rounded={3}
  320. onPress={() => connectKefu(orderId)}
  321. >
  322. 立即支付
  323. </Button>
  324. {!finish && hasRider && (
  325. <Button
  326. fontSize="xs"
  327. flex={1}
  328. mx={5}
  329. bg="white"
  330. color="gray600"
  331. borderColor="brand500"
  332. borderWidth={1}
  333. rounded={3}
  334. onPress={(e) => {
  335. if (!orderInfo.ruserId) {
  336. warnning('骑手信息有误请联系客服');
  337. } else {
  338. navigation.navigate('Chat', {
  339. toUserId: orderInfo.ruserId,
  340. toUserName: orderInfo.riderName,
  341. });
  342. }
  343. console.log(e);
  344. }}
  345. >
  346. {t('lian-xi-qi-shou')}
  347. </Button>
  348. )}
  349. {!finish && isPay && (
  350. <Button
  351. fontSize="xs"
  352. flex={1}
  353. mx={5}
  354. bg="white"
  355. color="gray600"
  356. borderColor="brand500"
  357. borderWidth={1}
  358. rounded={3}
  359. onPress={() => {
  360. if (!orderInfo.muserId) {
  361. warnning('商家信息有误请联系客服帮你催单');
  362. } else {
  363. navigation.navigate('Chat', {
  364. toUserId: orderInfo.muserId,
  365. toUserName: orderInfo.merShowName,
  366. type: 'Reminder',
  367. });
  368. }
  369. }}
  370. >
  371. {t('cui-dan')}
  372. </Button>
  373. )}
  374. {finish && !cancelOrder && (
  375. <Button
  376. fontSize="xs"
  377. flex={1}
  378. mx={5}
  379. bg="white"
  380. color="gray600"
  381. borderColor="brand500"
  382. borderWidth={1}
  383. rounded={3}
  384. onPress={() => {
  385. navigation.navigate('RewardRider', {
  386. orderId,
  387. });
  388. }}
  389. >
  390. {t('da-shang-qi-shou')}
  391. </Button>
  392. )}
  393. {finish && !Allfinish && !cancelOrder && (
  394. <Button
  395. fontSize="xs"
  396. flex={1}
  397. mx={5}
  398. bg="white"
  399. color="gray600"
  400. borderColor="brand500"
  401. borderWidth={1}
  402. rounded={3}
  403. onPress={() => {
  404. navigation.navigate('Evaluate', {
  405. orderId,
  406. });
  407. }}
  408. >
  409. {t('li-ji-ping-jia')}
  410. </Button>
  411. )}
  412. </Div>
  413. </View>
  414. <View style={[styles.card]}>
  415. <Div row pb={10}>
  416. <Text fontSize="xl" fontWeight="bold">
  417. {orderInfo.merShowName}
  418. </Text>
  419. <Button
  420. fontSize="xs"
  421. w={100}
  422. bg="white"
  423. color="gray600"
  424. borderColor="brand500"
  425. borderWidth={1}
  426. rounded={3}
  427. ml={14}
  428. onPress={() => {
  429. if (!orderInfo.muserId) {
  430. warnning('商家信息有误请联系客服帮你催单');
  431. } else {
  432. navigation.navigate('Chat', {
  433. toUserId: orderInfo.muserId,
  434. toUserName: orderInfo.merShowName,
  435. });
  436. }
  437. }}
  438. >
  439. {t('lian-xi-shang-jia')}
  440. </Button>
  441. </Div>
  442. <Div>
  443. {orderGoodsSpecs.map((item) => {
  444. return (
  445. <GoodsItem info={item} goods={item.goods} key={item.id} />
  446. );
  447. })}
  448. </Div>
  449. <Flex style={styles.info}>
  450. <Flex.Item>
  451. <Flex>
  452. <Text fontSize="xs" color="gray300" textAlign="left">
  453. {t('bao-zhuang-fei')}
  454. </Text>
  455. <Text fontSize="xs" color="gray300" textAlign="left">
  456. {t('can-he')}
  457. </Text>
  458. </Flex>
  459. </Flex.Item>
  460. <Text fontSize="xs" color="gray300" textAlign="left">
  461. ¥{orderInfo.packingPrice}
  462. </Text>
  463. </Flex>
  464. <Flex style={styles.info}>
  465. <Flex.Item>
  466. <Text fontSize="xs" color="gray300" textAlign="left">
  467. {t('pei-song-fei-ding-dong-zhuan-song')}
  468. </Text>
  469. </Flex.Item>
  470. <Text fontSize="xs" color="gray300" textAlign="left">
  471. ¥{orderInfo.deliveryAmount}
  472. </Text>
  473. </Flex>
  474. <Flex style={styles.info}>
  475. <Flex.Item>
  476. <Text fontSize="xs" color="gray300" textAlign="left">
  477. {t('man-jian')}
  478. </Text>
  479. </Flex.Item>
  480. <Text fontSize="xs" color="red500" textAlign="left">
  481. -¥{orderInfo.fullReduction}
  482. </Text>
  483. </Flex>
  484. {!!orderInfo.firstBuy && (
  485. <Flex style={styles.info}>
  486. <Flex.Item>
  487. <Text fontSize="xs" color="gray300" textAlign="left">
  488. {t('shou-dan')}
  489. </Text>
  490. </Flex.Item>
  491. <Text fontSize="xs" color="red500" textAlign="left">
  492. ¥{orderInfo.firstBuy}
  493. </Text>
  494. </Flex>
  495. )}
  496. <Flex style={styles.info}>
  497. <Flex.Item>
  498. <Text fontSize="xs" color="gray300" textAlign="left">
  499. {t('hong-bao')}
  500. </Text>
  501. </Flex.Item>
  502. <Text fontSize="xs" color="red500" textAlign="left">
  503. -¥{orderInfo.redBag || 0}
  504. </Text>
  505. </Flex>
  506. <Flex style={styles.total} justify="end">
  507. <Text size="s1">
  508. {t('xiao-ji')} ¥{orderInfo.realAmount}
  509. </Text>
  510. </Flex>
  511. </View>
  512. <View style={[styles.card]}>
  513. <Text size="c1">{t('ding-dan-xin-xi')}</Text>
  514. <View style={styles.main}>
  515. <Flex style={styles.info2}>
  516. <Flex.Item>
  517. <Text fontSize="xs" textAlign="left">
  518. {t('xia-dan-shi-jian')}
  519. </Text>
  520. </Flex.Item>
  521. <Text fontSize="xs" textAlign="left">
  522. {orderInfo.orderTime}
  523. </Text>
  524. </Flex>
  525. {orderInfo.userReceivedTime ? (
  526. <Flex style={styles.info2}>
  527. <Flex.Item>
  528. <Text fontSize="xs" textAlign="left">
  529. {t('song-da-shi-jian')}
  530. </Text>
  531. </Flex.Item>
  532. <Text fontSize="xs" textAlign="left">
  533. {orderInfo.userReceivedTime}
  534. </Text>
  535. </Flex>
  536. ) : (
  537. <Flex style={styles.info2}>
  538. <Flex.Item>
  539. <Text fontSize="xs" textAlign="left">
  540. {t('yu-ji-song-da-shi-jian')}
  541. </Text>
  542. </Flex.Item>
  543. <Text fontSize="xs" textAlign="left">
  544. {orderInfo.timeOfArrival}
  545. </Text>
  546. </Flex>
  547. )}
  548. <Flex style={styles.info2}>
  549. <Flex.Item style={styles.address}>
  550. <Text fontSize="xs" textAlign="left">
  551. {t('shou-huo-di-zhi')}
  552. </Text>
  553. </Flex.Item>
  554. <Text fontSize="xs" textAlign="left">
  555. {orderInfo.userAddress}
  556. </Text>
  557. </Flex>
  558. {hasRider && (
  559. <Flex style={styles.info2}>
  560. <Flex.Item>
  561. <Text fontSize="xs" textAlign="left">
  562. {t('pei-song-qi-shou')}
  563. </Text>
  564. </Flex.Item>
  565. <Flex>
  566. <Button
  567. fontSize="xs"
  568. w={100}
  569. bg="white"
  570. color="gray600"
  571. borderColor="brand500"
  572. borderWidth={1}
  573. rounded={3}
  574. onPress={() => {
  575. if (!orderInfo.muserId) {
  576. warnning('骑手信息有误请联系客服');
  577. } else {
  578. navigation.navigate('Chat', {
  579. toUserId: orderInfo.muserId,
  580. toUserName: orderInfo.merShowName,
  581. });
  582. }
  583. }}
  584. >
  585. {t('lian-xi-qi-shou')}
  586. </Button>
  587. <Text fontSize="xs" ml={20} textAlign="left">
  588. {orderInfo.riderName}
  589. </Text>
  590. </Flex>
  591. </Flex>
  592. )}
  593. <Flex style={styles.info2}>
  594. <Text fontSize="xs" textAlign="left">
  595. {t('ding-dan-hao')}
  596. </Text>
  597. <Text fontSize="xs" textAlign="right" ml={10} flex={1}>
  598. {orderInfo.id}
  599. </Text>
  600. <Button
  601. fontSize="xs"
  602. w={62}
  603. bg="white"
  604. color="gray600"
  605. borderColor="brand500"
  606. borderWidth={1}
  607. rounded={3}
  608. onPress={() => {
  609. Clipboard.setString(orderInfo.id.toString());
  610. success(t('fu-zhi-cheng-gong'));
  611. }}
  612. ml={5}
  613. >
  614. {t('fu-zhi')}
  615. </Button>
  616. </Flex>
  617. {payMap.has(orderInfo.payMethod) && (
  618. <Flex style={styles.info2}>
  619. <Text fontSize="xs" flex={1}>
  620. {t('zhi-fu-fang-shi')}
  621. </Text>
  622. <Text fontSize="xs" textAlign="left">
  623. {payMap.get(orderInfo.payMethod).name}
  624. </Text>
  625. </Flex>
  626. )}
  627. </View>
  628. </View>
  629. </Div>
  630. </View>
  631. </Animated.View>
  632. {/* </RefreshControl> */}
  633. </Div>
  634. );
  635. }
  636. const GoodsItem = ({ info, goods }) => {
  637. return (
  638. <Div row>
  639. <Image w={33} h={33} rounded={3} source={{ uri: goods.img }} />
  640. <Div flex={1} ml={10}>
  641. <Div row>
  642. <Text fontSize="sm" flex={1}>
  643. {goods.name}
  644. </Text>
  645. <Text fontSize="sm">X {info.num}</Text>
  646. <Text ml={10}>¥{info.goodsRealPrice}</Text>
  647. </Div>
  648. <Text fontSize="xs" color="gray400">
  649. {info.specification}
  650. </Text>
  651. </Div>
  652. </Div>
  653. );
  654. };
  655. const styles = StyleSheet.create({
  656. scroll: {
  657. flexGrow: 1,
  658. },
  659. card: {
  660. borderRadius: 3,
  661. backgroundColor: '#fff',
  662. marginBottom: 5,
  663. padding: 15,
  664. },
  665. item: {
  666. paddingVertical: 5,
  667. },
  668. icon: {
  669. width: 33,
  670. height: 33,
  671. borderRadius: 3,
  672. },
  673. goodsMain: {
  674. marginLeft: 10,
  675. },
  676. type: {
  677. // marginTop:3,
  678. },
  679. main: {
  680. marginTop: 10,
  681. paddingVertical: 5,
  682. borderColor: '#E5E5E5',
  683. borderTopWidth: 1,
  684. },
  685. info: {
  686. marginTop: 10,
  687. },
  688. total: {
  689. paddingTop: 10,
  690. marginTop: 10,
  691. borderColor: '#E5E5E5',
  692. borderTopWidth: 1,
  693. },
  694. address: {
  695. minWidth: 50,
  696. marginRight: 30,
  697. },
  698. info2: {
  699. marginTop: 10,
  700. },
  701. });