OrderDetailScreen.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import { StackScreenProps } from '@react-navigation/stack';
  2. import * as React from 'react';
  3. import { Div, Button, Image, Text, Avatar, Icon } from 'react-native-magnus';
  4. import { Menu } from 'teaset';
  5. import { ScrollView } from 'react-native-gesture-handler';
  6. import { useRequest, useCreation, useMount } from 'ahooks';
  7. import { RefreshControl, Dimensions } from 'react-native';
  8. import { toastInfo, toastSuccess, alert } from '../utils/SystemUtils';
  9. import { RiderStatusMap } from '../utils/RiderInfoUtils';
  10. import ImagePicker from '../components/ImagePicker';
  11. import { useTranslation } from 'react-i18next';
  12. import useModel from 'flooks';
  13. import OrderModel from './model';
  14. import { useSafeArea } from 'react-native-safe-area-context';
  15. import { changeCord, goMap } from '../utils/MapUtils';
  16. import { connect } from '../utils/SystemUtils';
  17. import Request from '../utils/RequestUtils';
  18. function RightMenuItem({ text = '' }) {
  19. return (
  20. <Text fontSize="sm" color="black">
  21. {text}
  22. </Text>
  23. );
  24. }
  25. export default function OrderDetailScreen({
  26. navigation,
  27. route,
  28. }: StackScreenProps) {
  29. const { t } = useTranslation();
  30. const { params } = route;
  31. const { orderId } = params;
  32. const [pickImg, setpickImg] = React.useState<string>('');
  33. const { receiverOrder, changeStatus, changeStatusAll } = useModel(
  34. OrderModel,
  35. []
  36. );
  37. navigation.setOptions({
  38. headerRight: (props) => (
  39. <Button
  40. bg="none"
  41. px={15}
  42. py={0}
  43. w={50}
  44. h={50}
  45. onPress={() => {
  46. const x = Dimensions.get('window').width - 80,
  47. y = 0,
  48. width = 50,
  49. height = 50;
  50. let items = [
  51. {
  52. title: '联系客服',
  53. onPress: () => connect(navigation),
  54. },
  55. ];
  56. if (!isRefund && statusInfo.type && statusInfo.type !== 'order') {
  57. items.splice(0, 0, {
  58. title: '取消订单申请',
  59. onPress: () => {
  60. navigation.navigate('Modal', {
  61. screen: 'Applay',
  62. params: {
  63. orderId,
  64. },
  65. });
  66. },
  67. });
  68. }
  69. Menu.show({ x, y, width, height }, items, {
  70. popoverStyle: {
  71. backgroundColor: '#fff',
  72. },
  73. });
  74. }}
  75. >
  76. <Icon
  77. name="more-horizontal"
  78. color="white"
  79. fontFamily="Feather"
  80. fontSize="lg"
  81. ></Icon>
  82. </Button>
  83. ),
  84. });
  85. const { data, loading, reload, run } = useRequest(
  86. `/orderInfo/get/${orderId}`,
  87. {
  88. manual: true,
  89. defaultLoading: false,
  90. debounceInterval: 1000,
  91. initialData: {},
  92. }
  93. );
  94. useMount(() => {
  95. run();
  96. });
  97. const orderGoodsSpecs = useCreation(() => {
  98. return data.orderGoodsSpecs || [];
  99. }, [data]);
  100. const statusInfo = useCreation(() => {
  101. if (data.riderStatus) {
  102. return RiderStatusMap.get(data.riderStatus);
  103. } else {
  104. return {};
  105. }
  106. }, [data]);
  107. const totalNum = useCreation(() => {
  108. return orderGoodsSpecs.reduce((total, currentValue) => {
  109. return total + currentValue.num;
  110. }, 0);
  111. }, [orderGoodsSpecs]);
  112. const isRefund = useCreation(() => {
  113. if (data.status === 'REFUNDED_PENDING') {
  114. return true;
  115. } else {
  116. return false;
  117. }
  118. }, [data]);
  119. function orderChange() {
  120. alert(navigation, {
  121. msg: statusInfo.infoText,
  122. hasCancel: true,
  123. dangers: true,
  124. submitEvent: () => {
  125. changeStatus(
  126. data.id,
  127. statusInfo.nextStatus,
  128. (res) => {
  129. toastSuccess(statusInfo.successText);
  130. },
  131. () => {
  132. alert(navigation, {
  133. msg: statusInfo.errorText,
  134. hasCancel: true,
  135. dangers: true,
  136. submitEvent: () => {
  137. changeStatusAll(data.id, statusInfo.nextStatus, (res) => {
  138. toastSuccess(statusInfo.successText);
  139. run();
  140. });
  141. },
  142. });
  143. },
  144. pickImg
  145. );
  146. },
  147. });
  148. }
  149. return (
  150. <Div bg="gray100" flex={1}>
  151. {/* <ModalDropdown ref={menu} options={['option 1', 'option 2']} /> */}
  152. {statusInfo.type && statusInfo.type !== 'order' && (
  153. <Div row bg="white" py={12}>
  154. {__DEV__ &&
  155. !isRefund &&
  156. statusInfo.type &&
  157. statusInfo.type !== 'order' && (
  158. <Button
  159. onPress={() => {
  160. navigation.navigate('Modal', {
  161. screen: 'Applay',
  162. params: {
  163. orderId,
  164. },
  165. });
  166. }}
  167. >
  168. 取消订单
  169. </Button>
  170. )}
  171. <Div flex={1} alignItems="center">
  172. <Text fontSize="xl" color="red500">
  173. {data.deliveryAmount}
  174. </Text>
  175. <Text fontSize="sm" color="gray600">
  176. {t('ben-dan-pai-song-shou-ru')}
  177. </Text>
  178. </Div>
  179. <Div w={1} bg="gray100" />
  180. {statusInfo.type === 'finish' ? (
  181. <Div flex={1} alignItems="center">
  182. <Text fontSize="xl" color="red500">
  183. {data.userReceivedTime}
  184. </Text>
  185. <Text fontSize="sm" color="gray600">
  186. {t('yong-hu-shou-dao-shi-jian')}
  187. </Text>
  188. </Div>
  189. ) : (
  190. <Div flex={1} alignItems="center">
  191. <Text fontSize="xl" color="red500">
  192. {data.timeOfArrival}
  193. </Text>
  194. <Text fontSize="sm" color="gray600">
  195. {t('yu-ji-song-da-shi-jian')}
  196. </Text>
  197. </Div>
  198. )}
  199. </Div>
  200. )}
  201. <ScrollView
  202. contentContainerStyle={{
  203. flexGrow: 1,
  204. backgroundColor: '#f2f2f2',
  205. paddingHorizontal: 15,
  206. paddingVertical: 10,
  207. }}
  208. refreshControl={
  209. <RefreshControl refreshing={loading} onRefresh={reload} />
  210. }
  211. >
  212. {statusInfo.type && (
  213. <>
  214. <Div bg="white" rounded="xs" px={15} py={10}>
  215. {statusInfo.type == 'user' && (
  216. <Div row py={10}>
  217. <Div flex={1}>
  218. <Text fontSize="xl" fontWeight="bold" color="yellow500">
  219. {statusInfo.name}
  220. </Text>
  221. <Text fontSize="sm" color="gray600" mt={2}>
  222. {t('liu-shui-hao')}:{data.id}
  223. </Text>
  224. </Div>
  225. <Button bg="yellow500" w={112} onPress={orderChange}>
  226. {t('que-ren-song-da')}
  227. </Button>
  228. </Div>
  229. )}
  230. {statusInfo.type === 'merchant' && (
  231. <>
  232. <Div row py={10}>
  233. <Div flex={1}>
  234. <Text>{t('wo-yi-dao-dian')}</Text>
  235. <Text fontSize="sm" color="gray600" mt={2}>
  236. {t('liu-shui-hao')}:{data.id}
  237. </Text>
  238. </Div>
  239. <Button
  240. bg="yellow500"
  241. w={112}
  242. disabled={statusInfo.status != 0}
  243. onPress={orderChange}
  244. >
  245. {t('wo-yi-dao-dian')}
  246. </Button>
  247. </Div>
  248. {statusInfo.status === 1 && (
  249. <>
  250. <Div row py={10}>
  251. <Div flex={1}>
  252. <Text>{t('wo-yi-qu-huo')}</Text>
  253. <Text fontSize="sm" color="gray600" mt={2} pr={48}>
  254. {t('ordertips')}
  255. </Text>
  256. </Div>
  257. <Button bg="yellow500" w={112} onPress={orderChange}>
  258. {t('pai-zhao-qu-huo')}
  259. </Button>
  260. </Div>
  261. <ImagePicker
  262. img={pickImg}
  263. setImg={(img) => setpickImg(img)}
  264. />
  265. </>
  266. )}
  267. </>
  268. )}
  269. {(statusInfo.type === 'user' || statusInfo.type === 'finish') && (
  270. <Div py={10}>
  271. <Text fontSize="sm">{t('yong-hu-xin-xi')}</Text>
  272. <Div px={10} mt={10}>
  273. <Text fontSize="xl" fontWeight="bold">
  274. {data.nickname}
  275. </Text>
  276. <Text fontSize="sm" mt={5}>
  277. {data.userAddress}
  278. </Text>
  279. </Div>
  280. <Button
  281. block
  282. bg="yellow500"
  283. mt={15}
  284. onPress={() => {
  285. if (!data.userId) {
  286. toastInfo('该订单缺少用户id,请重新下单');
  287. return;
  288. }
  289. navigation.navigate('NoticeStack', {
  290. screen: 'Chat',
  291. params: {
  292. toUserId: data.userId,
  293. toUserName: data.nickname,
  294. },
  295. });
  296. }}
  297. >
  298. {t('lian-xi-ke-hu')}
  299. </Button>
  300. </Div>
  301. )}
  302. {(statusInfo.type === 'merchant' ||
  303. statusInfo.type === 'finish') && (
  304. <Div borderTopColor="gray100" borderTopWidth={1} py={10}>
  305. <Text fontSize="sm">{t('shang-jia-xin-xi')}</Text>
  306. <Div px={10} mt={10}>
  307. <Text fontSize="xl" fontWeight="bold">
  308. {data.merShowName}
  309. </Text>
  310. <Text fontSize="sm" mt={5}>
  311. {data.merAddress}
  312. </Text>
  313. </Div>
  314. <Button
  315. block
  316. bg="yellow500"
  317. mt={15}
  318. onPress={() => {
  319. if (!data.muserId) {
  320. toastInfo('该订单缺少商家id,请重新下单');
  321. return;
  322. }
  323. navigation.navigate('NoticeStack', {
  324. screen: 'Chat',
  325. params: {
  326. toUserId: data.muserId,
  327. toUserName: data.merShowName,
  328. },
  329. });
  330. }}
  331. >
  332. {t('lian-xi-shang-jia')}
  333. </Button>
  334. </Div>
  335. )}
  336. {statusInfo.type === 'order' && (
  337. <Div py={15}>
  338. <Div row mt={10}>
  339. <Text fontSize="sm" color="gray600">
  340. 1.5Km
  341. </Text>
  342. <Div flex={1} ml={12}>
  343. <Text fontSize="md" pb={3}>
  344. {data.merShowName}
  345. </Text>
  346. <Text fontSize="sm">{data.merAddress}</Text>
  347. </Div>
  348. </Div>
  349. <Div row mt={10}>
  350. <Text fontSize="sm" color="gray600">
  351. 1.5Km
  352. </Text>
  353. <Div flex={1} ml={12}>
  354. {data.nickName && (
  355. <Text fontSize="md" pb={5}>
  356. {data.nickName}
  357. </Text>
  358. )}
  359. <Text fontSize="sm">{data.userAddress}</Text>
  360. </Div>
  361. </Div>
  362. </Div>
  363. )}
  364. <Div borderTopColor="gray100" borderTopWidth={1} py={10}>
  365. <Div row>
  366. <Text fontSize="sm" flex={2}>
  367. {t('shang-pin-xin-xi')}
  368. </Text>
  369. <Text fontSize="sm" flex={1} textAlign="right">
  370. {totalNum}
  371. {t('fen')}
  372. </Text>
  373. <Text fontSize="sm" flex={1} color="red500" textAlign="right">
  374. ¥{data.realAmount || 0}
  375. </Text>
  376. </Div>
  377. <Div p={15}>
  378. {orderGoodsSpecs.map((item) => {
  379. const goods = item.goods;
  380. return (
  381. <Div row key={item.id} mt={5}>
  382. <Text fontSize="sm"> {goods.name}</Text>
  383. <Text fontSize="sm" color="gray500" ml={5}>
  384. {item.specification}
  385. </Text>
  386. <Text fontSize="sm" flex={1} textAlign="right">
  387. X {item.num}
  388. </Text>
  389. </Div>
  390. );
  391. })}
  392. </Div>
  393. </Div>
  394. {statusInfo.type === 'order' && (
  395. <Button
  396. bg="yellow500"
  397. w={112}
  398. alignSelf="flex-end"
  399. my={10}
  400. onPress={() => {
  401. receiverOrder(data.id, (res) => {
  402. toastSuccess(t('jie-dan-cheng-gong'));
  403. });
  404. }}
  405. >
  406. {t('jie-dan')}
  407. </Button>
  408. )}
  409. </Div>
  410. <Div h={35} w="100%" />
  411. {(statusInfo.type === 'merchant' || statusInfo.type === 'user') &&
  412. !isRefund && (
  413. <Div position="absolute" bottom={0} left={0} right={0}>
  414. <Button
  415. block
  416. mx={15}
  417. my={10}
  418. bg="yellow500"
  419. onPress={() => {
  420. goMap(
  421. statusInfo.type === 'merchant'
  422. ? data.merShowName
  423. : data.userAddress,
  424. statusInfo.type === 'merchant'
  425. ? changeCord(data.merLocation)
  426. : changeCord(data.location),
  427. navigation
  428. );
  429. }}
  430. >
  431. {t('cha-kan-dao-hang')}
  432. </Button>
  433. </Div>
  434. )}
  435. {isRefund && (
  436. <Div
  437. row
  438. position="absolute"
  439. bottom={0}
  440. left={0}
  441. right={0}
  442. px={15}
  443. py={10}
  444. >
  445. <Button flex={1} bg="gray300" disabled>
  446. 订单申请取消中
  447. </Button>
  448. <Button
  449. w={105}
  450. ml={5}
  451. bg="yellow500"
  452. onPress={() => {
  453. Request.get('/orderRefundApply/cancel', {
  454. params: {
  455. orderId,
  456. },
  457. })
  458. .then((res) => {
  459. toastSuccess('取消成功');
  460. })
  461. .catch((e) => {
  462. toastInfo(e.error);
  463. })
  464. .finally(() => {
  465. run();
  466. });
  467. }}
  468. >
  469. 撤销申请
  470. </Button>
  471. </Div>
  472. )}
  473. </>
  474. )}
  475. </ScrollView>
  476. </Div>
  477. );
  478. }