OrderDetailScreen.jsx 23 KB

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