panhui 5 سال پیش
والد
کامیت
4c680e413d

+ 34 - 29
App.js

@@ -9,6 +9,8 @@ import { UseAPIProvider } from '@umijs/use-request';
 
 import { Provider as PaperProvider } from 'react-native-paper';
 import { Provider } from '@ant-design/react-native';
+import { ThemeProvider } from 'react-native-magnus';
+
 import useModel from 'flooks';
 import { useUpdateEffect } from '@umijs/hooks';
 
@@ -21,6 +23,7 @@ import Toast from './flooks/Toast';
 import user from './flooks/User';
 import request from './Utils/RequestUtils';
 import theme from './constants/Theme';
+import ThemeMagnus from './constants/ThemeMagnus';
 
 const Stack = createStackNavigator();
 
@@ -51,7 +54,7 @@ export default function App() {
             {
               // name: initName,
               // name: 'MerchantDetail',
-              name: 'OrderDetail',
+              name: 'Evaluate',
             },
           ],
         })
@@ -64,34 +67,36 @@ export default function App() {
   } else {
     return (
       <View style={styles.container}>
-        <Provider>
-          <PaperProvider theme={theme}>
-            {Platform.OS !== 'ios' && (
-              <StatusBar translucent={false} backgroundColor="#FFC21C" />
-            )}
-            <UseAPIProvider
-              value={{
-                requestMethod: request,
-              }}
-            >
-              <NavigationContainer ref={navigationRef}>
-                <Stack.Navigator
-                  initRouteName="InitApp"
-                  screenOptions={{
-                    gestureEnabled: true,
-                    cardStyleInterpolator:
-                      CardStyleInterpolators.forHorizontalIOS,
-                  }}
-                  headerMode="none"
-                >
-                  {/* 基础功能页面 */}
-                  {BasicScreens(Stack.Screen)}
-                  <Stack.Screen name="Home" component={BottomTabNavigator} />
-                </Stack.Navigator>
-              </NavigationContainer>
-            </UseAPIProvider>
-          </PaperProvider>
-        </Provider>
+        <ThemeProvider theme={ThemeMagnus}>
+          <Provider>
+            <PaperProvider theme={theme}>
+              {Platform.OS !== 'ios' && (
+                <StatusBar translucent={false} backgroundColor="#FFC21C" />
+              )}
+              <UseAPIProvider
+                value={{
+                  requestMethod: request,
+                }}
+              >
+                <NavigationContainer ref={navigationRef}>
+                  <Stack.Navigator
+                    initRouteName="InitApp"
+                    screenOptions={{
+                      gestureEnabled: true,
+                      cardStyleInterpolator:
+                        CardStyleInterpolators.forHorizontalIOS,
+                    }}
+                    headerMode="none"
+                  >
+                    {/* 基础功能页面 */}
+                    {BasicScreens(Stack.Screen)}
+                    <Stack.Screen name="Home" component={BottomTabNavigator} />
+                  </Stack.Navigator>
+                </NavigationContainer>
+              </UseAPIProvider>
+            </PaperProvider>
+          </Provider>
+        </ThemeProvider>
       </View>
     );
   }

+ 153 - 0
Utils/SvgUtilsNew.js

@@ -257,4 +257,157 @@ export default new Map([
       ],
     },
   ],
+  [
+    'bestBad',
+    {
+      viewBox: '0 0 162.5 162.5',
+      pathList: [
+        {
+          fill: 'url(#orange_red)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.817 160.000,80.000 C160.000,124.183 124.183,160.000 80.000,160.000 C35.817,160.000 -0.000,124.183 -0.000,80.000 C-0.000,35.817 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: 'url(#orange_red)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.817 160.000,80.000 C160.000,124.183 124.183,160.000 80.000,160.000 C35.817,160.000 -0.000,124.183 -0.000,80.000 C-0.000,35.817 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M43.500,50.000 C51.508,50.000 58.000,56.492 58.000,64.500 C58.000,72.508 51.508,79.000 43.500,79.000 C35.492,79.000 29.000,72.508 29.000,64.500 C29.000,56.492 35.492,50.000 43.500,50.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M115.500,50.000 C123.508,50.000 130.000,56.492 130.000,64.500 C130.000,72.508 123.508,79.000 115.500,79.000 C107.492,79.000 101.000,72.508 101.000,64.500 C101.000,56.492 107.492,50.000 115.500,50.000 Z',
+        },
+        {
+          fill: 'none',
+          strokeWidth: 5,
+          fillRule: 'evenodd',
+          strokeLinecap: 'butt',
+          strokeLinejoin: 'miter',
+          stroke: '#fff',
+          d:
+            'M30.000,124.000 C42.861,112.966 59.056,106.568 76.000,106.000 C94.662,105.374 112.826,111.866 127.000,124.000',
+        },
+      ],
+    },
+  ],
+  [
+    'normal',
+    {
+      viewBox: '0 0 162.5 162.5',
+      pathList: [
+        {
+          fill: 'rgb(255, 216, 0)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.817 160.000,80.000 C160.000,124.183 124.183,160.000 80.000,160.000 C35.817,160.000 -0.000,124.183 -0.000,80.000 C-0.000,35.817 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M43.500,50.000 C51.508,50.000 58.000,56.492 58.000,64.500 C58.000,72.508 51.508,79.000 43.500,79.000 C35.492,79.000 29.000,72.508 29.000,64.500 C29.000,56.492 35.492,50.000 43.500,50.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M115.500,50.000 C123.508,50.000 130.000,56.492 130.000,64.500 C130.000,72.508 123.508,79.000 115.500,79.000 C107.492,79.000 101.000,72.508 101.000,64.500 C101.000,56.492 107.492,50.000 115.500,50.000 Z',
+        },
+        {
+          fill: 'none',
+          strokeWidth: 5,
+          fillRule: 'evenodd',
+          strokeLinecap: 'butt',
+          strokeLinejoin: 'miter',
+          stroke: '#fff',
+          d: 'M29.000,111.000 C63.333,111.000 97.667,111.000 132.000,111.000 ',
+        },
+      ],
+    },
+  ],
+  [
+    'veryGood',
+    {
+      viewBox: '0 0 162.5 162.5',
+      pathList: [
+        {
+          fill: 'rgb(255, 216, 0)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.817 160.000,80.000 C160.000,124.183 124.183,160.000 80.000,160.000 C35.817,160.000 -0.000,124.183 -0.000,80.000 C-0.000,35.817 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M43.500,50.000 C51.508,50.000 58.000,56.492 58.000,64.500 C58.000,72.508 51.508,79.000 43.500,79.000 C35.492,79.000 29.000,72.508 29.000,64.500 C29.000,56.492 35.492,50.000 43.500,50.000 Z',
+        },
+        {
+          fill: '#fff',
+          fillRule: 'evenodd',
+          d:
+            'M115.500,50.000 C123.508,50.000 130.000,56.492 130.000,64.500 C130.000,72.508 123.508,79.000 115.500,79.000 C107.492,79.000 101.000,72.508 101.000,64.500 C101.000,56.492 107.492,50.000 115.500,50.000 Z',
+        },
+        {
+          fill: '#fff',
+          strokeWidth: 5,
+          fillRule: 'evenodd',
+          strokeLinecap: 'butt',
+          strokeLinejoin: 'miter',
+          stroke: '#fff',
+          d:
+            'M81.000,102.000 C95.107,102.063 119.508,92.824 124.000,99.000 C127.702,104.089 121.612,115.293 113.000,127.000 C105.299,134.976 95.064,140.039 84.000,141.000 C70.159,142.202 56.666,136.856 47.000,127.000 C39.192,115.188 34.068,104.116 38.000,99.000 C42.895,92.631 67.764,101.941 81.000,102.000 Z',
+        },
+      ],
+    },
+  ],
+  [
+    'bad',
+    {
+      viewBox: '0 0 162.5 162.5',
+      pathList: [
+        {
+          fill: 'rgb(255, 216, 0)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.803 160.000,79.969 C160.000,124.134 124.183,159.937 80.000,159.937 C35.817,159.937 -0.000,124.134 -0.000,79.969 C-0.000,35.803 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: 'rgb(0, 0, 0)',
+          fillRule: 'evenodd',
+          d:
+            'M123.662,86.533 L103.088,91.118 C101.797,91.118 100.750,90.038 100.750,88.705 L100.750,28.371 C100.750,27.038 101.797,25.958 103.088,25.958 L123.662,30.543 C124.953,30.543 126.000,31.471 126.000,32.617 L126.000,84.459 C126.000,85.604 124.953,86.533 123.662,86.533 ZM100.516,87.843 C100.543,87.769 100.564,87.704 100.583,87.642 C100.548,87.923 100.516,88.342 100.516,88.946 L100.516,87.843 ZM98.649,90.970 C98.649,90.970 98.532,91.046 98.355,91.165 C97.315,92.285 95.825,93.527 93.702,94.739 C90.756,97.261 87.133,100.890 84.687,105.172 C78.572,115.881 75.809,129.600 75.458,131.436 C74.957,131.766 68.555,140.692 56.816,135.273 C43.582,129.162 51.948,108.493 53.439,105.141 C54.217,103.391 54.719,102.205 55.056,101.369 C36.466,101.826 12.658,97.795 27.807,60.710 C41.247,27.808 42.344,26.440 50.485,26.440 L98.179,26.440 C99.470,26.440 100.516,27.521 100.516,28.854 L100.516,87.843 C100.293,88.474 99.783,89.525 98.714,90.769 C98.691,90.836 98.678,90.904 98.649,90.970 ZM75.418,131.648 C75.418,131.648 75.432,131.571 75.458,131.436 C75.511,131.400 75.499,131.460 75.418,131.648 ZM100.583,87.642 C100.657,87.046 100.741,87.122 100.583,87.642 Z',
+        },
+      ],
+    },
+  ],
+  [
+    'good',
+    {
+      viewBox: '0 0 162.5 162.5',
+      pathList: [
+        {
+          fill: 'rgb(255, 216, 0)',
+          fillRule: 'evenodd',
+          d:
+            'M80.000,-0.000 C124.183,-0.000 160.000,35.803 160.000,79.969 C160.000,124.134 124.183,159.937 80.000,159.937 C35.817,159.937 -0.000,124.134 -0.000,79.969 C-0.000,35.803 35.817,-0.000 80.000,-0.000 Z',
+        },
+        {
+          fill: 'rgb(255, 177, 30)',
+          fillRule: 'evenodd',
+          d:
+            'M131.138,100.938 C117.569,134.150 116.462,135.530 108.242,135.530 L60.089,135.530 C58.786,135.530 57.729,134.439 57.729,133.094 L57.729,73.549 C57.955,72.912 58.469,71.851 59.549,70.595 C59.572,70.528 59.585,70.459 59.614,70.393 C59.614,70.393 59.732,70.316 59.910,70.197 C60.960,69.065 62.465,67.811 64.611,66.587 C67.584,64.041 71.242,60.378 73.710,56.056 C79.885,45.248 82.674,31.399 83.029,29.545 C83.534,29.212 89.998,20.201 101.850,25.672 C115.212,31.840 106.765,52.705 105.260,56.087 C104.474,57.854 103.967,59.051 103.626,59.895 C122.396,59.434 146.434,63.503 131.138,100.938 ZM57.662,73.752 C57.587,74.353 57.502,74.277 57.662,73.752 ZM57.729,72.436 L57.729,73.549 C57.702,73.624 57.681,73.689 57.662,73.752 C57.697,73.468 57.729,73.045 57.729,72.436 ZM83.069,29.331 C83.069,29.331 83.055,29.408 83.029,29.545 C82.975,29.581 82.987,29.520 83.069,29.331 ZM55.132,136.017 L34.360,131.388 C33.057,131.388 32.000,130.451 32.000,129.295 L32.000,76.965 C32.000,75.808 33.057,74.871 34.360,74.871 L55.132,70.243 C56.436,70.243 57.493,71.334 57.493,72.679 L57.493,133.581 C57.493,134.926 56.436,136.017 55.132,136.017 Z',
+        },
+      ],
+    },
+  ],
 ]);

+ 30 - 0
Utils/TotastUtils.js

@@ -1,7 +1,10 @@
 import React from 'react';
 import { Modal } from '@ant-design/react-native';
+import { Linking } from 'expo';
 import Text from '../components/Text';
 
+import { navigate } from '../navigation/RootNavigation';
+
 export function alert(title, content, submitEvent) {
   Modal.alert(
     <Text size="s1" bold center>
@@ -34,3 +37,30 @@ export function operation(list) {
     { text: '置顶聊天', onPress: () => console.log('置顶聊天被点击了') },
   ]);
 }
+
+export function connectKefu(orderId) {
+  console.log(orderId);
+  Modal.alert(
+    '',
+    <Text style={{ marginHorizontal: 15 }} more>
+      感谢您对我们的信任,我们将竭尽所能
+      的为您解决问题。您的每一个建议和反馈都对我们至关重要
+    </Text>,
+    [
+      {
+        text: '客服电话',
+        onPress: () => {
+          Linking.openURL('tel:+123456789');
+        },
+        style: { color: '#000', fontSize: 12, lineHeight: 30 },
+      },
+      {
+        text: '我要投诉',
+        onPress: () => {
+          navigate('Complaint', { orderId });
+        },
+        style: { color: '#FFC21C', fontSize: 12, lineHeight: 30 },
+      },
+    ]
+  );
+}

+ 144 - 0
components/Choose.js

@@ -0,0 +1,144 @@
+import * as React from 'react';
+import { Animated } from 'react-native';
+import { Div, Button, Image, Text, Tag, Icon } from 'react-native-magnus';
+import { useAnimation } from 'react-native-animation-hooks';
+
+import SvgIcon from './SvgIcon';
+
+const map = new Map([
+  [
+    'bad',
+    {
+      name: '非常差',
+    },
+  ],
+  [
+    'normal',
+    {
+      name: '一般',
+    },
+  ],
+  [
+    'good',
+    {
+      name: '超赞',
+    },
+  ],
+]);
+
+export default function Choose(props) {
+  const { chooseValue, changeValue } = props;
+
+  const badOpacity = useAnimation({
+    type: 'timing',
+    initialValue: 1,
+    duration: 100,
+    toValue: chooseValue && chooseValue !== 'bad' ? 0 : 1,
+  });
+  const normalOpacity = useAnimation({
+    type: 'timing',
+    initialValue: 1,
+    duration: 100,
+    toValue: chooseValue && chooseValue !== 'normal' ? 0 : 1,
+  });
+  const goodOpacity = useAnimation({
+    type: 'timing',
+    initialValue: 1,
+    duration: 100,
+    toValue: chooseValue && chooseValue !== 'good' ? 0 : 1,
+  });
+  const chooseStyle = {
+    position: 'absolute',
+    left: '50%',
+    transform: [{ translateX: 0 - 53 / 2 }],
+    top: 12,
+  };
+  return (
+    <Div>
+      <Div row justifyContent="space-around" py={12} px={15}>
+        <Animated.View
+          style={[
+            {
+              opacity: badOpacity,
+            },
+            chooseValue === 'bad' && chooseStyle,
+          ]}
+        >
+          <Button
+            bg="hide"
+            disabled={chooseValue}
+            onPress={() => changeValue('bad')}
+          >
+            <Div alignItems="center">
+              <SvgIcon name="bestBad" width={53} height={53} />
+              {!chooseValue && (
+                <Text color="gray500" mt={10} fontSize="xs">
+                  非常差
+                </Text>
+              )}
+            </Div>
+          </Button>
+        </Animated.View>
+        <Animated.View
+          style={[
+            {
+              opacity: normalOpacity,
+            },
+            chooseValue === 'normal' && chooseStyle,
+          ]}
+        >
+          <Button
+            bg="hide"
+            disabled={chooseValue}
+            onPress={() => changeValue('normal')}
+          >
+            <Div alignItems="center">
+              <SvgIcon name="normal" width={53} height={53} />
+              {!chooseValue && (
+                <Text color="gray500" mt={10} fontSize="xs">
+                  一般
+                </Text>
+              )}
+            </Div>
+          </Button>
+        </Animated.View>
+        <Animated.View
+          style={[
+            {
+              opacity: goodOpacity,
+            },
+            chooseValue === 'good' && chooseStyle,
+          ]}
+        >
+          <Button
+            bg="hide"
+            disabled={chooseValue}
+            onPress={() => changeValue('good')}
+          >
+            <Div alignItems="center">
+              <SvgIcon name="veryGood" width={53} height={53} />
+              {!chooseValue && (
+                <Text color="gray500" mt={10} fontSize="xs">
+                  超赞
+                </Text>
+              )}
+            </Div>
+          </Button>
+        </Animated.View>
+      </Div>
+      <Div alignItems="center">
+        <Div>
+          {!!chooseValue && (
+            <Tag
+              suffix={<Icon name="close" color="black700" fontSize="caption" />}
+              color="brand500"
+              onPress={() => changeValue('')}
+            >
+              当前已选择:{map.get(chooseValue).name}
+            </Tag>
+          )}
+        </Div>
+      </Div>
+    </Div>
+  );
+}

+ 99 - 0
components/ChooseMerchant.js

@@ -0,0 +1,99 @@
+import * as React from 'react';
+import { Animated } from 'react-native';
+import { Div, Button, Image, Text, Tag, Icon } from 'react-native-magnus';
+import { useAnimation } from 'react-native-animation-hooks';
+
+import SvgIcon from './SvgIcon';
+
+const map = new Map([
+  [
+    'bad',
+    {
+      name: '差评',
+    },
+  ],
+  [
+    'good',
+    {
+      name: '好评',
+    },
+  ],
+]);
+
+export default function Choose(props) {
+  const { chooseValue, changeValue } = props;
+
+  const badOpacity = useAnimation({
+    type: 'timing',
+    initialValue: 1,
+    duration: 100,
+    toValue: chooseValue && chooseValue !== 'bad' ? 0 : 1,
+  });
+  const goodOpacity = useAnimation({
+    type: 'timing',
+    initialValue: 1,
+    duration: 100,
+    toValue: chooseValue && chooseValue !== 'good' ? 0 : 1,
+  });
+  const chooseStyle = {
+    position: 'absolute',
+    left: '50%',
+    transform: [{ translateX: 0 - 53 / 2 }],
+    top: 12,
+  };
+  return (
+    <Div>
+      <Div row justifyContent="space-around" py={12} px={15}>
+        <Animated.View
+          style={[
+            {
+              opacity: badOpacity,
+            },
+            chooseValue === 'bad' && chooseStyle,
+          ]}
+        >
+          <Button
+            bg="hide"
+            disabled={chooseValue}
+            onPress={() => changeValue('bad')}
+          >
+            <Div alignItems="center">
+              <SvgIcon name="bad" width={53} height={53} />
+            </Div>
+          </Button>
+        </Animated.View>
+        <Animated.View
+          style={[
+            {
+              opacity: goodOpacity,
+            },
+            chooseValue === 'good' && chooseStyle,
+          ]}
+        >
+          <Button
+            bg="hide"
+            disabled={chooseValue}
+            onPress={() => changeValue('good')}
+          >
+            <Div alignItems="center">
+              <SvgIcon name="good" width={53} height={53} />
+            </Div>
+          </Button>
+        </Animated.View>
+      </Div>
+      <Div alignItems="center">
+        <Div>
+          {!!chooseValue && (
+            <Tag
+              suffix={<Icon name="close" color="black700" fontSize="caption" />}
+              color="brand500"
+              onPress={() => changeValue('')}
+            >
+              当前已选择:{map.get(chooseValue).name}
+            </Tag>
+          )}
+        </Div>
+      </Div>
+    </Div>
+  );
+}

+ 99 - 0
components/ImagePicker.js

@@ -0,0 +1,99 @@
+/* eslint-disable no-underscore-dangle */
+import * as React from 'react';
+import { Platform, View } from 'react-native';
+import { Button, Icon, Image, Div } from 'react-native-magnus';
+import * as ImagePicker from 'expo-image-picker';
+import useModel from 'flooks';
+
+import request from '../Utils/RequestUtils';
+import Toast from '../flooks/Toast';
+
+const _pickImage = (aspect, loading) => {
+  return ImagePicker.requestCameraRollPermissionsAsync()
+    .then((res) => {
+      if (!res.granted) {
+        return Promise.reject('notAllod');
+      } else {
+        return ImagePicker.launchImageLibraryAsync({
+          mediaTypes: ImagePicker.MediaTypeOptions.Images,
+          allowsEditing: true,
+          aspect: aspect || [1, 1],
+          quality: 0.6,
+          base64: true,
+        });
+      }
+    })
+    .then((res) => {
+      if (!res.cancelled) {
+        loading();
+        if (Platform.OS === 'web') {
+          return Promise.resolve(res.uri);
+        }
+        return Promise.resolve(res.base64);
+      } else {
+        return Promise.reject('cancel');
+      }
+    })
+    .then((img) => {
+      return request.post(`/upload/base64`, {
+        data: { base64: img },
+        requestType: 'form',
+      });
+    });
+};
+
+export default function ImagePickerCom(props) {
+  const { aspect, img, setImg, cancelEvent } = props;
+  const { loading, clearLoading } = useModel(Toast, []);
+
+  return (
+    <Div mr="sm" mb="sm">
+      <Button
+        bg="brand200"
+        h={67}
+        w={67}
+        rounded="xs"
+        onPress={() => {
+          _pickImage(aspect || [1, 1], loading)
+            .then((newImg) => {
+              if (setImg) {
+                setImg(newImg);
+              }
+            })
+            .catch((e) => {
+              console.log(e);
+            })
+            .finally(() => {
+              clearLoading();
+            });
+        }}
+      >
+        {!img && <Icon name="plus" color="white" fontSize="4xl" />}
+        {!!img && (
+          <Image
+            h={67}
+            w={67}
+            rounded="xs"
+            source={{
+              uri: img,
+            }}
+          />
+        )}
+      </Button>
+      {cancelEvent && (
+        <Button
+          bg="red500"
+          h={16}
+          w={16}
+          rounded="circle"
+          position="absolute"
+          right={-5}
+          top={-5}
+          onPress={cancelEvent}
+        >
+          <Icon name="minus" fontFamily="AntDesign" color="#fff" />
+        </Button>
+      )}
+    </Div>
+  );
+}

+ 10 - 1
components/SvgIcon.js

@@ -1,6 +1,6 @@
 /* eslint-disable no-underscore-dangle */
 import * as React from 'react';
-import Svg, { Path } from 'react-native-svg';
+import Svg, { Path, Defs, Stop, LinearGradient } from 'react-native-svg';
 import { View } from 'react-native';
 import { withTheme, Badge } from 'react-native-paper';
 import svgMap from '../Utils/SvgUtilsNew';
@@ -55,6 +55,15 @@ function Icon(props) {
   return (
     <View>
       <Svg width={width} height={height} viewBox={viewBox} style={style}>
+        <Defs>
+          <LinearGradient id="orange_red" x1="0%" y1="0%" x2="0%" y2="90%">
+            <Stop offset="0%" style={{ stopColor: '#000', stopOpacity: 1 }} />
+            <Stop
+              offset="50%"
+              style={{ stopColor: 'rgb(255, 216, 0)', stopOpacity: 1 }}
+            />
+          </LinearGradient>
+        </Defs>
         {pathComList()}
       </Svg>
 

+ 26 - 25
components/Text.js

@@ -9,7 +9,7 @@ import {
 } from 'react-native-paper';
 
 function MyText(props) {
-  const { type, size, children, bold, center } = props;
+  const { type, size, children, bold, center, more } = props;
   let { color, theme, style } = props;
   const { colors } = theme;
   if (type) {
@@ -24,6 +24,14 @@ function MyText(props) {
         text: color,
       },
     };
+  } else {
+    theme = {
+      ...theme,
+      colors: {
+        ...theme.colors,
+        text: '#000',
+      },
+    };
   }
 
   if (bold) {
@@ -39,7 +47,16 @@ function MyText(props) {
     };
   }
 
-  if (!!children) {
+  let lineProps = {
+    numberOfLines: 1,
+    ellipsizeMode: 'tail',
+  };
+
+  if (more) {
+    lineProps = {};
+  }
+
+  if (children) {
     switch (size) {
       case 'h1':
         // 24
@@ -47,9 +64,8 @@ function MyText(props) {
           <Headline
             theme={theme}
             style={{ ...style }}
-            numberOfLines={1}
-            ellipsizeMode="tail"
             align="center"
+            {...lineProps}
           >
             {children}
           </Headline>
@@ -57,48 +73,33 @@ function MyText(props) {
       case 's1':
         // 16
         return (
-          <Subheading
-            theme={theme}
-            style={{ ...style }}
-            numberOfLines={1}
-            ellipsizeMode="tail"
-          >
+          <Subheading theme={theme} style={{ ...style }} {...lineProps}>
             {children}
           </Subheading>
         );
       case 'c1':
         // 12
         return (
-          <Caption
+          <Text
             theme={theme}
-            style={{ ...style }}
+            style={[{ fontSize: 12 }, style]}
             numberOfLines={1}
             ellipsizeMode="tail"
           >
             {children}
-          </Caption>
+          </Text>
         );
       case 'c2':
         // 10
         return (
-          <Text
-            theme={theme}
-            style={[{ fontSize: 10 }, { ...style }]}
-            numberOfLines={1}
-            ellipsizeMode="tail"
-          >
+          <Text theme={theme} style={[{ fontSize: 10 }, style]} {...lineProps}>
             {children}
           </Text>
         );
       default:
         // 14
         return (
-          <Paragraph
-            theme={theme}
-            numberOfLines={1}
-            ellipsizeMode="tail"
-            style={{ ...style }}
-          >
+          <Paragraph theme={theme} {...lineProps} style={{ ...style }}>
             {children}
           </Paragraph>
         );

+ 27 - 0
constants/ThemeMagnus.js

@@ -0,0 +1,27 @@
+export default {
+  colors: {
+    hide: 'rgba(0,0,0,0)',
+    brand100: 'rgba(255,194,28,0.2)',
+    brand200: '#FFF5D8',
+    brand300: 'rgba(255,194,28,0.4)',
+    brand400: 'rgba(255,194,28,0.8)',
+    brand500: 'rgba(255,194,28,1)',
+    brand600: '#FFB11E',
+    red500: '#B00020',
+    gray200: '#eee',
+    gray300: '#B5B5B5',
+    gray400: '#BFBFBF',
+    gray500: '#787878',
+    gray600: '#000',
+  },
+  spacing: {
+    xs: 2,
+    sm: 6,
+  },
+  fontSize: {
+    xs: 10,
+    sm: 12,
+    xl: 16,
+    '4xl': 24,
+  },
+};

+ 37 - 4
navigation/BaseNavigator.jsx

@@ -1,9 +1,12 @@
 import * as React from 'react';
 import Login from '../screens/Login';
-import Detail from '../screens/Detail';
+import MerchantDetailScreen from '../screens/Detail/MerchantDetailScreen';
 import InitAppScreen from '../screens/InitAppScreen';
 
-import OrderDetail from '../screens/Order/OrderDetail';
+import OrderDetail from '../screens/Order/OrderDetailScreen';
+import Complaint from '../screens/Order/ComplaintScreen';
+import ComplaintNext from '../screens/Order/ComplaintNextScreen';
+import EvaluateScreen from '../screens/Order/EvaluateScreen';
 
 export default function BasicScreens(Screen) {
   return (
@@ -11,13 +14,43 @@ export default function BasicScreens(Screen) {
       <Screen name="InitApp" component={InitAppScreen} />
       {/* 登录路由 */}
       {Login(Screen)}
-      {Detail(Screen)}
+
+      {/* 详情页 */}
+      <Screen
+        name="MerchantDetail"
+        component={MerchantDetailScreen}
+        initialParams={{ merchantId: 189 }}
+      />
 
       <Screen
         name="OrderDetail"
-        initialParams={{ orderId: 189 }}
+        initialParams={{ orderId: 386 }}
         component={OrderDetail}
       />
+      {/* 投诉 */}
+      <Screen
+        name="Complaint"
+        initialParams={{ orderId: 1352 }}
+        component={Complaint}
+      />
+      {/* 投诉 2 */}
+      <Screen
+        name="ComplaintNext"
+        initialParams={{
+          orderId: 386,
+          target: 'MERCHANT',
+          type: '商家少送/漏送商品',
+        }}
+        component={ComplaintNext}
+      />
+      {/* 投诉 2 */}
+      <Screen
+        name="Evaluate"
+        initialParams={{
+          orderId: 386,
+        }}
+        component={EvaluateScreen}
+      />
     </>
   );
 }

+ 135 - 0
package-lock.json

@@ -9716,6 +9716,20 @@
       "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz",
       "integrity": "sha512-/VbpIEp8tSNNHIvstuA3Swx610whci1Zpc9mqNkqn14DkMbw+ORviln2u0XyHG1kPvvwTNGZY6QpeFwxYaSdbQ=="
     },
+    "react-native-magnus": {
+      "version": "1.0.39",
+      "resolved": "https://registry.npmjs.org/react-native-magnus/-/react-native-magnus-1.0.39.tgz",
+      "integrity": "sha512-Ku/ddeloTQrtpInmbn0hdlNiO0YhGRMhPL+d1S3V7IkybnbrDwSqoIg6kKfEtKhPzjgdRQfmS89YgBeViOSyjg=="
+    },
+    "react-native-modal": {
+      "version": "11.5.6",
+      "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-11.5.6.tgz",
+      "integrity": "sha512-APGNfbvgC4hXbJqcSADu79GLoMKIHUmgR3fDQ7rCGZNBypkStSP8imZ4PKK/OzIZZfjGU9aP49jhMgGbhY9KHA==",
+      "requires": {
+        "prop-types": "^15.6.2",
+        "react-native-animatable": "1.3.3"
+      }
+    },
     "react-native-modal-popover": {
       "version": "0.0.12",
       "resolved": "https://registry.npmjs.org/react-native-modal-popover/-/react-native-modal-popover-0.0.12.tgz",
@@ -9862,6 +9876,127 @@
         }
       }
     },
+    "react-native-vector-icons": {
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-6.6.0.tgz",
+      "integrity": "sha512-MImKVx8JEvVVBnaShMr7/yTX4Y062JZMupht1T+IEgbqBj4aQeQ1z2SH4VHWKNtWtppk4kz9gYyUiMWqx6tNSw==",
+      "requires": {
+        "lodash": "^4.0.0",
+        "prop-types": "^15.6.2",
+        "yargs": "^13.2.2"
+      },
+      "dependencies": {
+        "cliui": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+          "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+          "requires": {
+            "string-width": "^3.1.0",
+            "strip-ansi": "^5.2.0",
+            "wrap-ansi": "^5.1.0"
+          }
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "get-caller-file": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+        },
+        "require-main-filename": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+          "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "wrap-ansi": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+          "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+          "requires": {
+            "ansi-styles": "^3.2.0",
+            "string-width": "^3.0.0",
+            "strip-ansi": "^5.0.0"
+          }
+        },
+        "yargs": {
+          "version": "13.3.2",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+          "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+          "requires": {
+            "cliui": "^5.0.0",
+            "find-up": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^3.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^13.1.2"
+          }
+        },
+        "yargs-parser": {
+          "version": "13.1.2",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
     "react-native-view-shot": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz",

+ 5 - 0
package.json

@@ -28,6 +28,7 @@
     "@umijs/use-request": "^1.4.3",
     "acorn": "^7.2.0",
     "axios": "^0.19.2",
+    "color": "^3.1.2",
     "expo": "^37.0.0",
     "expo-asset": "~8.1.5",
     "expo-constants": "~9.0.0",
@@ -47,8 +48,11 @@
     "react": "16.9.0",
     "react-dom": "16.9.0",
     "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
+    "react-native-animatable": "^1.3.3",
     "react-native-animation-hooks": "^1.0.1",
     "react-native-gesture-handler": "^1.6.1",
+    "react-native-magnus": "^1.0.39",
+    "react-native-modal": "^11.5.6",
     "react-native-paper": "^3.10.1",
     "react-native-reanimated": "^1.7.1",
     "react-native-safe-area-context": "0.7.3",
@@ -57,6 +61,7 @@
     "react-native-svg": "11.0.1",
     "react-native-tab-view": "^2.14.2",
     "react-native-ui-lib": "^5.8.1",
+    "react-native-vector-icons": "^6.6.0",
     "react-native-web": "^0.11.7",
     "umi-request": "^1.3.3"
   },

+ 1 - 1
screens/Detail/MerchantDetailScreen.jsx

@@ -13,7 +13,7 @@ import Constants from 'expo-constants';
 import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
 
 import { useRoute, useNavigation } from '@react-navigation/native';
-import { useAnimation } from 'react-native-animation-hooks';
+// import { useAnimation } from 'react-native-animation-hooks';
 
 import useModel from 'flooks';
 import Detail from './model'; // detail模块通用方法

+ 0 - 15
screens/Detail/index.js

@@ -1,15 +0,0 @@
-import * as WebBrowser from 'expo-web-browser';
-import * as React from 'react';
-import MerchantDetailScreen from './MerchantDetailScreen';
-
-export default function Bank(Screen) {
-  return (
-    <>
-      <Screen
-        name="MerchantDetail"
-        component={MerchantDetailScreen}
-        initialParams={{ merchantId: 189 }}
-      />
-    </>
-  );
-}

+ 154 - 0
screens/Order/ComplaintNextScreen.jsx

@@ -0,0 +1,154 @@
+import * as WebBrowser from 'expo-web-browser';
+import * as React from 'react';
+import { StyleSheet, View } from 'react-native';
+import { Flex, List, TextareaItem } from '@ant-design/react-native';
+import { TouchableRipple, Divider } from 'react-native-paper';
+import { Div, Button } from 'react-native-magnus';
+import { ScrollView } from 'react-native-gesture-handler';
+import Icon from 'react-native-vector-icons/FontAwesome';
+
+import { useRoute } from '@react-navigation/native';
+import { useCreation } from '@umijs/hooks';
+
+import useModel from 'flooks';
+import Order from './model'; // Order模块通用方法
+
+import Header from './Header'; // 头部
+
+import Text from '../../components/Text';
+import ImagePicker from '../../components/ImagePicker';
+const Name = new Map([
+  ['MERCHANT', '商家'],
+  ['RIDER', '骑手'],
+]);
+
+export default function ComplaintScreen({ navigation }) {
+  const { complaintSave } = useModel(Order, []);
+
+  const route = useRoute();
+  const { params } = route;
+  const { orderId, type, target } = params || {};
+
+  const [imgList, setimgList] = React.useState(['']);
+  const [content, setcontent] = React.useState('');
+
+  function changeImg(img, index) {
+    const list = [...imgList];
+    if (!img) {
+      list.splice(index, 1);
+    } else {
+      list.splice(index, 1, img);
+    }
+    if (index === list.length - 1 && list.length < 4) {
+      list.push('');
+    }
+    setimgList(list);
+  }
+
+  function deleteImg(index) {
+    const list = [...imgList];
+    if (!list[index]) {
+      return null;
+    } else {
+      return () => changeImg('', index);
+    }
+  }
+
+  function submit() {
+    const img = [...imgList].filter((item) => {
+      return item;
+    });
+    complaintSave(orderId, target, type, content, img.join(',')).then(() => {
+      navigation.goBack();
+    });
+  }
+
+  const canSubmit = useCreation(() => {
+    if (orderId && target && type && content) {
+      return true;
+    } else {
+      return false;
+    }
+  }, [orderId, target, type, content, imgList]);
+
+  return (
+    <>
+      <Header title="订单投诉" />
+      <ScrollView contentContainerStyle={styles.scroll}>
+        <View style={styles.card}>
+          <Flex>
+            <Flex.Item>
+              <Text size="c2" type="info">
+                投诉{Name.get(target)}
+              </Text>
+            </Flex.Item>
+            <Text size="c2">{type || ''}</Text>
+          </Flex>
+          <View style={styles.textarea}>
+            <TextareaItem
+              rows={4}
+              placeholder="可补充具体投诉内容,您的投诉真实性对我们的处理至关重要"
+              style={styles.text}
+              count={100}
+              onChange={setcontent}
+            />
+          </View>
+          <Div row>
+            {imgList.map((item, index) => {
+              return (
+                <ImagePicker
+                  key={index}
+                  img={item}
+                  setImg={(img) => changeImg(img, index)}
+                  cancelEvent={deleteImg(index)}
+                />
+              );
+            })}
+          </Div>
+        </View>
+
+        <Button
+          block
+          bg="brand500"
+          color="white"
+          my={10}
+          mx={20}
+          onPress={submit}
+          disabled={!canSubmit}
+        >
+          提交
+        </Button>
+      </ScrollView>
+    </>
+  );
+}
+
+const styles = StyleSheet.create({
+  scroll: {
+    paddingVertical: 10,
+  },
+  card: {
+    paddingHorizontal: 15,
+    paddingVertical: 20,
+    backgroundColor: '#fff',
+    marginBottom: 10,
+  },
+  item: {
+    paddingVertical: 5,
+  },
+  main: {
+    paddingVertical: 5,
+  },
+  bottom: {
+    paddingVertical: 10,
+  },
+  textarea: {
+    marginVertical: 10,
+  },
+  text: {
+    backgroundColor: '#eeeeee',
+    paddingVertical: 10,
+    fontSize: 10,
+    borderBottomWidth: 0,
+  },
+});

+ 154 - 0
screens/Order/ComplaintScreen.jsx

@@ -0,0 +1,154 @@
+import * as WebBrowser from 'expo-web-browser';
+import * as React from 'react';
+import { StyleSheet, View } from 'react-native';
+import { Flex } from '@ant-design/react-native';
+import { TouchableRipple, Divider } from 'react-native-paper';
+import { ScrollView } from 'react-native-gesture-handler';
+import Icon from 'react-native-vector-icons/FontAwesome';
+
+import { useRoute } from '@react-navigation/native';
+import { useRequest } from '@umijs/hooks';
+
+import Header from './Header'; // 头部
+
+import Text from '../../components/Text';
+import Button from '../../components/Button';
+
+const Types = new Map([
+  ['MERCHANT', ['商家少送/漏送商品', '食品品质/安全问题']],
+  [
+    'RIDER',
+    [
+      '等待时间过长,超市配送',
+      '提前点击确认送达',
+      '错送商品',
+      '商品破损',
+      '错送商品',
+      '威胁/辱骂/殴打',
+      '骚扰顾客',
+    ],
+  ],
+]);
+
+export default function ComplaintScreen({ navigation }) {
+  const route = useRoute();
+  const { params } = route;
+  const { orderId } = params || 0;
+
+  const [orderInfo, setorderInfo] = React.useState({ merchant: {} });
+
+  const { merchant } = orderInfo;
+
+  useRequest(() => `/orderInfo/get/${orderId}`, {
+    refreshDeps: [orderId],
+    onSuccess: (result) => {
+      setorderInfo(result);
+    },
+  });
+
+  return (
+    <>
+      <Header title="订单投诉" />
+      <ScrollView contentContainerStyle={styles.scroll}>
+        <View style={styles.card}>
+          <Text size="c2" type="info">
+            投诉商家
+          </Text>
+          <Text size="s1">{merchant.showName || ' '}</Text>
+          <Divider />
+          <View style={styles.main}>
+            {Types.get('MERCHANT').map((item, index) => {
+              return (
+                <TouchableRipple
+                  key={index}
+                  onPress={() => {
+                    navigation.navigate('ComplaintNext', {
+                      orderId,
+                      type: item,
+                      target: 'MERCHANT',
+                    });
+                  }}
+                >
+                  <Flex style={styles.item}>
+                    <Flex.Item>
+                      <Text size="c2" type="info">
+                        {item}
+                      </Text>
+                    </Flex.Item>
+                    <Icon name="angle-right" color="#B4B4B4" />
+                  </Flex>
+                </TouchableRipple>
+              );
+            })}
+          </View>
+        </View>
+
+        <View style={styles.card}>
+          <Text size="c2" type="info">
+            投诉骑手
+          </Text>
+          <Text size="s1">胖待还</Text>
+          <Divider />
+
+          <View style={styles.main}>
+            {Types.get('RIDER').map((item, index) => {
+              return (
+                <TouchableRipple
+                  key={index}
+                  onPress={() => {
+                    navigation.navigate('ComplaintNext', {
+                      orderId,
+                      type: item,
+                      target: 'RIDER',
+                    });
+                  }}
+                >
+                  <Flex style={styles.item}>
+                    <Flex.Item>
+                      <Text size="c2" type="info">
+                        {item}
+                      </Text>
+                    </Flex.Item>
+                    <Icon name="angle-right" color="#B4B4B4" />
+                  </Flex>
+                </TouchableRipple>
+              );
+            })}
+          </View>
+          <Divider />
+          <Flex style={styles.bottom}>
+            <Flex.Item>
+              <Text size="c2" type="info">
+                未找到投诉项
+              </Text>
+            </Flex.Item>
+            <Button size="small" outline>
+              联系客服
+            </Button>
+          </Flex>
+        </View>
+      </ScrollView>
+    </>
+  );
+}
+
+const styles = StyleSheet.create({
+  scroll: {
+    paddingVertical: 10,
+  },
+  card: {
+    paddingHorizontal: 15,
+    paddingVertical: 20,
+    backgroundColor: '#fff',
+    marginBottom: 10,
+  },
+  item: {
+    paddingVertical: 5,
+  },
+  main: {
+    paddingVertical: 5,
+  },
+  bottom: {
+    paddingVertical: 10,
+  },
+});

+ 263 - 0
screens/Order/EvaluateScreen.jsx

@@ -0,0 +1,263 @@
+import * as WebBrowser from 'expo-web-browser';
+import * as React from 'react';
+import { StyleSheet, View } from 'react-native';
+import { Div, Button, Image, Text, Tag } from 'react-native-magnus';
+import { TextareaItem } from '@ant-design/react-native';
+import { ScrollView } from 'react-native-gesture-handler';
+
+import { useRoute } from '@react-navigation/native';
+import { useCreation, useRequest, useSet } from '@umijs/hooks';
+
+import useModel from 'flooks';
+import Order from './model'; // Order模块通用方法
+
+import Header from './Header'; // 头部
+
+import ImagePicker from '../../components/ImagePicker';
+import Choose from '../../components/Choose';
+import ChooseMerchant from '../../components/ChooseMerchant';
+
+const map = new Map([
+  [
+    'bad',
+    [
+      '不送上楼',
+      '着装脏乱',
+      '服务态度差',
+      '未带保温箱',
+      '食品凉了',
+      '额外索取费用',
+      '配送慢',
+      '提前点送达',
+    ],
+  ],
+  ['normal', ['味道一般', '速度很慢']],
+  ['good', ['超好吃', '超级慢']],
+]);
+
+export default function EvaluateScreen({ navigation }) {
+  const { userAppraisal } = useModel(Order, []);
+
+  const route = useRoute();
+  const { params } = route;
+  const { orderId } = params || {};
+
+  const [orderInfo, setorderInfo] = React.useState({ merchant: {} });
+  const [choose, setchoose] = React.useState('');
+  const [chooseMer, setchooseMer] = React.useState('');
+  const [content, setcontent] = React.useState('');
+
+  const [imgList, setimgList] = React.useState(['']);
+  const [content2, setcontent2] = React.useState('');
+
+  function changeImg(img, index) {
+    const list = [...imgList];
+    if (!img) {
+      list.splice(index, 1);
+    } else {
+      list.splice(index, 1, img);
+    }
+    if (index === list.length - 1 && list.length < 4) {
+      list.push('');
+    }
+    setimgList(list);
+  }
+
+  function deleteImg(index) {
+    const list = [...imgList];
+    if (!list[index]) {
+      return null;
+    } else {
+      return () => changeImg('', index);
+    }
+  }
+
+  const [tags, tagEvent] = useSet([]);
+  React.useEffect(() => {
+    tagEvent.reset();
+  }, [choose]);
+
+  const { merchant, merchantId, riderId } = orderInfo;
+
+  useRequest(() => `/orderInfo/get/${orderId}`, {
+    refreshDeps: [orderId],
+    onSuccess: (result) => {
+      setorderInfo(result);
+    },
+  });
+
+  function submit() {
+    const img = [...imgList].filter((item) => {
+      return item;
+    });
+    const riderScore = [...map.keys()].indexOf(choose);
+    userAppraisal(
+      orderId,
+      merchantId,
+      riderId,
+      img,
+      chooseMer === 'good',
+      content2,
+      riderScore,
+      content
+    ).then(() => {
+      navigation.goBack();
+    });
+  }
+
+  const canSubmit = useCreation(() => {
+    if (orderId && choose && chooseMer) {
+      return true;
+    } else {
+      return false;
+    }
+  }, [orderId, choose, chooseMer, content, imgList, content2]);
+
+  return (
+    <>
+      <Header title="评价完就有红包拿哦!" />
+      <ScrollView contentContainerStyle={styles.scroll}>
+        <View style={styles.card}>
+          <Div row alignItems="center">
+            <Image
+              h={40}
+              w={40}
+              rounded="sm"
+              source={{
+                uri: 'https://picsum.photos/700',
+              }}
+            />
+            <Text fontSize="xl" fontWeight="bold" flex={1} mx={8}>
+              猪猪费
+            </Text>
+            <Text fontSize="xs" color="gray300">
+              已对骑手匿名
+            </Text>
+          </Div>
+          <Choose chooseValue={choose} changeValue={setchoose} />
+
+          {!!choose && (
+            <Div row flexWrap="wrap" py={14}>
+              {map.get(choose).map((item, index) => {
+                const isChoose = tagEvent.has(item);
+                return (
+                  <Tag
+                    key={index}
+                    ml="sm"
+                    mb="sm"
+                    fontSize="xs"
+                    borderColor={isChoose ? 'white' : 'gray300'}
+                    borderWidth={1}
+                    color={isChoose ? 'white' : 'gray400'}
+                    bg={isChoose ? 'brand500' : 'hide'}
+                    onPress={() => {
+                      if (isChoose) {
+                        tagEvent.remove(item);
+                      } else {
+                        tagEvent.add(item);
+                      }
+                    }}
+                  >
+                    {item}
+                  </Tag>
+                );
+              })}
+            </Div>
+          )}
+
+          <TextareaItem
+            rows={4}
+            placeholder="说哪里好,哪里不够好。其他顾客想知道,商家也想知道"
+            style={styles.text}
+            count={100}
+            onChange={setcontent}
+          />
+        </View>
+        <View style={styles.card}>
+          <Div row alignItems="center">
+            <Image
+              h={40}
+              w={40}
+              rounded="sm"
+              source={{
+                uri: merchant.logo,
+              }}
+            />
+            <Text fontSize="xl" fontWeight="bold" flex={1} mx={8}>
+              {merchant.showName || ' '}
+            </Text>
+            <Text fontSize="xs" color="gray300">
+              已对商家匿名
+            </Text>
+          </Div>
+          <ChooseMerchant chooseValue={chooseMer} changeValue={setchooseMer} />
+
+          <Div pt={10} />
+
+          <TextareaItem
+            rows={4}
+            placeholder="说哪里好,哪里不够好。其他顾客想知道,商家也想知道"
+            style={styles.text}
+            count={100}
+            onChange={setcontent2}
+          />
+
+          <Div row mt={10}>
+            {imgList.map((item, index) => {
+              return (
+                <ImagePicker
+                  key={index}
+                  img={item}
+                  setImg={(img) => changeImg(img, index)}
+                  cancelEvent={deleteImg(index)}
+                />
+              );
+            })}
+          </Div>
+        </View>
+
+        <Button
+          block
+          bg="brand500"
+          color="white"
+          my={5}
+          mx={10}
+          onPress={submit}
+          disabled={!canSubmit}
+        >
+          提交
+        </Button>
+      </ScrollView>
+    </>
+  );
+}
+
+const styles = StyleSheet.create({
+  scroll: {
+    paddingVertical: 10,
+  },
+  card: {
+    paddingHorizontal: 15,
+    paddingVertical: 20,
+    backgroundColor: '#fff',
+    marginBottom: 10,
+  },
+  item: {
+    paddingVertical: 5,
+  },
+  main: {
+    paddingVertical: 5,
+  },
+  bottom: {
+    paddingVertical: 10,
+  },
+  textarea: {
+    marginVertical: 10,
+  },
+  text: {
+    backgroundColor: '#eeeeee',
+    paddingVertical: 10,
+    fontSize: 10,
+    borderBottomWidth: 0,
+  },
+});

+ 9 - 1
screens/Order/Header.js

@@ -1,18 +1,26 @@
 /* eslint-disable react/style-prop-object */
 import * as React from 'react';
+import { View } from 'react-native';
 import { StatusBar } from 'expo-status-bar';
 import { Appbar } from 'react-native-paper';
+import { goBack } from '../../navigation/RootNavigation';
 
-export default function Header({ title }) {
+export default function Header({ title, noBack }) {
   return (
     <>
       <StatusBar backgroundColor="#FFC21C" style="light" translucent />
 
       <Appbar.Header dark>
+        {!noBack && <Appbar.BackAction onPress={goBack} />}
         <Appbar.Content
           title={title || '我的订单'}
           titleStyle={{ textAlign: 'center', fontSize: 16 }}
         />
+        {!noBack && (
+          <Appbar.Action
+            icon={() => <View style={{ width: 24, height: 24 }} />}
+          />
+        )}
       </Appbar.Header>
     </>
   );

+ 0 - 52
screens/Order/OrderDetail.jsx

@@ -1,52 +0,0 @@
-import * as WebBrowser from 'expo-web-browser';
-import * as React from 'react';
-import { StyleSheet, View, FlatList, Image } from 'react-native';
-import { Flex } from '@ant-design/react-native';
-import { ScrollView } from 'react-native-gesture-handler';
-
-import Header from './Header'; // 头部
-
-import Text from '../../components/Text';
-import Button from '../../components/Button';
-
-export default function OrderScreen() {
-  const [orderList, setorderList] = React.useState([{ id: 1, name: '订单1' }]);
-
-  return (
-    <>
-      <Header title="订单已送达" />
-      <ScrollView contentContainerStyle={styles.scroll}>
-        <View style={[styles.card]}>
-          <Flex>
-            <Text size="s1">感谢您使用叮咚,期待您再次使用!</Text>
-          </Flex>
-          <Flex justify="between">
-            <Button outline size="small" fontColor="#000">
-              联系商家
-            </Button>
-            <Button outline size="small" fontColor="#000">
-              联系商家
-            </Button>
-            <Button outline size="small" fontColor="#000">
-              联系商家
-            </Button>
-          </Flex>
-        </View>
-      </ScrollView>
-    </>
-  );
-}
-
-const styles = StyleSheet.create({
-  scroll: {
-    flexGrow: 1,
-    paddingHorizontal: 15,
-    paddingVertical: 10,
-  },
-  card: {
-    borderRadius: 3,
-    backgroundColor: '#fff',
-    marginBottom: 5,
-    padding: 15,
-  },
-});

+ 264 - 0
screens/Order/OrderDetailScreen.jsx

@@ -0,0 +1,264 @@
+import * as WebBrowser from 'expo-web-browser';
+import * as React from 'react';
+import { StyleSheet, View, FlatList, Image } from 'react-native';
+import { Flex } from '@ant-design/react-native';
+import { ScrollView } from 'react-native-gesture-handler';
+
+import { useRoute } from '@react-navigation/native';
+
+import Header from './Header'; // 头部
+
+import Text from '../../components/Text';
+import Button from '../../components/Button';
+
+import { connectKefu } from '../../Utils/TotastUtils';
+
+export default function OrderScreen() {
+  const [orderList, setorderList] = React.useState([{ id: 1, name: '订单1' }]);
+
+  const route = useRoute();
+  const { params } = route;
+  const { orderId } = params || 0;
+
+  return (
+    <>
+      <Header title="订单已送达" />
+      <ScrollView contentContainerStyle={styles.scroll}>
+        <View style={[styles.card]}>
+          <Flex>
+            <Text size="s1">感谢您使用叮咚,期待您再次使用!</Text>
+          </Flex>
+          <Flex justify="between">
+            <Button
+              outline
+              size="small"
+              fontColor="#000"
+              onPress={() => connectKefu(orderId)}
+            >
+              联系客服
+            </Button>
+            <Button outline size="small" fontColor="#000">
+              联系商家
+            </Button>
+            <Button outline size="small" fontColor="#000">
+              联系商家
+            </Button>
+          </Flex>
+        </View>
+
+        <View style={[styles.card]}>
+          <Text size="s1">麦当劳(奥体大街店)</Text>
+          <View style={styles.main}>
+            <Flex style={styles.item}>
+              <Image
+                style={styles.icon}
+                resizeMode="cover"
+                source={{ uri: 'https://picsum.photos/700' }}
+              />
+              <Flex.Item style={styles.goodsMain}>
+                <Flex>
+                  <Flex.Item>
+                    <Text size="c1">原味板烧鸡腿麦满分套餐</Text>
+                  </Flex.Item>
+                  <Text size="c1" style={{ marginLeft: 5 }}>
+                    x1
+                  </Text>
+                  <Text size="c1" style={{ marginLeft: 5 }}>
+                    ¥9999.99
+                  </Text>
+                </Flex>
+                <Text size="c2" type="info">
+                  规格1/规格1
+                </Text>
+              </Flex.Item>
+            </Flex>
+
+            <Flex style={styles.item}>
+              <Image
+                style={styles.icon}
+                resizeMode="cover"
+                source={{ uri: 'https://picsum.photos/700' }}
+              />
+              <Flex.Item style={styles.goodsMain}>
+                <Flex>
+                  <Flex.Item>
+                    <Text size="c1">原味板烧鸡腿麦满分套餐</Text>
+                  </Flex.Item>
+                  <Text size="c1" style={{ marginLeft: 5 }}>
+                    x1
+                  </Text>
+                  <Text size="c1" style={{ marginLeft: 5 }}>
+                    ¥9999.99
+                  </Text>
+                </Flex>
+                <Text size="c2" type="info">
+                  规格1/规格1
+                </Text>
+              </Flex.Item>
+            </Flex>
+          </View>
+
+          <Flex style={styles.info}>
+            <Flex.Item>
+              <Flex>
+                <Text size="c2" type="info">
+                  包装费
+                </Text>
+                <Text size="c2" type="info">
+                  餐盒
+                </Text>
+              </Flex>
+            </Flex.Item>
+            <Text size="c2" type="info">
+              ¥99.99
+            </Text>
+          </Flex>
+          <Flex style={styles.info}>
+            <Flex.Item>
+              <Text size="c2" type="info">
+                叮咚快递
+              </Text>
+            </Flex.Item>
+            <Text size="c2" type="info">
+              ¥99.99
+            </Text>
+          </Flex>
+          <Flex style={styles.info}>
+            <Flex.Item>
+              <Text size="c2" type="info">
+                满减
+              </Text>
+            </Flex.Item>
+            <Text size="c2" type="error">
+              ¥99.99
+            </Text>
+          </Flex>
+          <Flex style={styles.info}>
+            <Flex.Item>
+              <Text size="c2" type="info">
+                红包
+              </Text>
+            </Flex.Item>
+            <Text size="c2" type="error">
+              ¥99.99
+            </Text>
+          </Flex>
+
+          <Flex style={styles.total} justify="end">
+            <Text size="s1">小计 ¥9999.99</Text>
+          </Flex>
+        </View>
+
+        <View style={[styles.card]}>
+          <Text size="c1">订单信息</Text>
+          <View style={styles.main}>
+            <Flex style={styles.info2}>
+              <Flex.Item>
+                <Text size="c2">下单时间</Text>
+              </Flex.Item>
+              <Text size="c2">2019/12/31 11:49</Text>
+            </Flex>
+            <Flex style={styles.info2}>
+              <Flex.Item>
+                <Text size="c2">送达时间</Text>
+              </Flex.Item>
+              <Text size="c2">2019/12/31 11:49</Text>
+            </Flex>
+            <Flex style={styles.info2}>
+              <Flex.Item style={styles.address}>
+                <Text size="c2">收货地址</Text>
+              </Flex.Item>
+              <Text size="c2">金童大厦奥体大街2999号1单元10楼10搂等会灯虎</Text>
+            </Flex>
+            <Flex style={styles.info2}>
+              <Flex.Item>
+                <Text size="c2">配送起手</Text>
+              </Flex.Item>
+              <Flex>
+                <Button outline size="mini" width={100} fontColor="#000">
+                  联系骑手
+                </Button>
+                <Text size="c2" style={{ marginLeft: 20 }}>
+                  周猩猩
+                </Text>
+              </Flex>
+            </Flex>
+            <Flex style={styles.info2}>
+              <Flex.Item>
+                <Text size="c2">订 单 号</Text>
+              </Flex.Item>
+              <Flex>
+                <Text size="c2">1234 5678 9101 1112 121</Text>
+                <Button
+                  style={{ marginLeft: 5 }}
+                  outline
+                  size="mini"
+                  width={100}
+                  fontColor="#000"
+                >
+                  复制
+                </Button>
+              </Flex>
+            </Flex>
+            <Flex style={styles.info2}>
+              <Flex.Item>
+                <Text size="c2">支付方式</Text>
+              </Flex.Item>
+              <Text size="c2">在线支付</Text>
+            </Flex>
+          </View>
+        </View>
+      </ScrollView>
+    </>
+  );
+}
+
+const styles = StyleSheet.create({
+  scroll: {
+    flexGrow: 1,
+    paddingHorizontal: 15,
+    paddingVertical: 10,
+  },
+  card: {
+    borderRadius: 3,
+    backgroundColor: '#fff',
+    marginBottom: 5,
+    padding: 15,
+  },
+  item: {
+    paddingVertical: 5,
+  },
+  icon: {
+    width: 33,
+    height: 33,
+    borderRadius: 3,
+  },
+  goodsMain: {
+    marginLeft: 10,
+  },
+  type: {
+    // marginTop:3,
+  },
+  main: {
+    marginTop: 10,
+    paddingVertical: 5,
+    borderColor: '#E5E5E5',
+    borderTopWidth: 1,
+  },
+  info: {
+    marginTop: 10,
+  },
+  total: {
+    paddingTop: 10,
+    marginTop: 10,
+    borderColor: '#E5E5E5',
+    borderTopWidth: 1,
+  },
+  address: {
+    minWidth: 50,
+    marginRight: 30,
+  },
+  info2: {
+    marginTop: 5,
+  },
+});

+ 1 - 1
screens/Order/OrderScreen.jsx

@@ -13,7 +13,7 @@ export default function OrderScreen() {
 
   return (
     <>
-      <Header />
+      <Header noBack />
 
       <FlatList
         contentContainerStyle={styles.list}

+ 65 - 0
screens/Order/model.js

@@ -0,0 +1,65 @@
+import request from '../../Utils/RequestUtils';
+import Toast from '../../flooks/Toast';
+
+const OrderModel = (now) => ({
+  // 投诉
+  complaintSave(orderId, target, type, content, img) {
+    const { loading, warnning, success } = now(Toast);
+    loading();
+    return request
+      .post(`/complaint/save`, {
+        data: {
+          orderId,
+          target,
+          type,
+          content,
+          img,
+        },
+      })
+      .then(() => {
+        success('提交成功');
+        return Promise.resolve();
+      })
+      .catch((e) => {
+        warnning(e.error);
+      });
+  },
+
+  // 用户评价
+  userAppraisal(
+    orderId,
+    merchantId,
+    jobNumber,
+    img,
+    goodsLike,
+    goodsAppraise,
+    riderLike,
+    riderAppraise
+  ) {
+    const { loading, warnning, success } = now(Toast);
+    loading();
+    return request
+      .post(`/appraisal/userAppraisal`, {
+        data: {
+          orderId,
+          merchantId,
+          jobNumber,
+          img,
+          goodsLike,
+          goodsAppraise,
+          riderLike,
+          riderAppraise,
+        },
+        requestType: 'form',
+      })
+      .then(() => {
+        success('提交成功');
+        return Promise.resolve();
+      })
+      .catch((e) => {
+        warnning(e.error);
+      });
+  },
+});
+
+export default OrderModel;