OrderDetailScreen.jsx 19 KB

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