panhui 5 лет назад
Родитель
Сommit
0b929fdee4

+ 15 - 6
Utils/TimeUtil.js

@@ -1,19 +1,28 @@
 import moment from "moment";
 
 export function getNowYear() {
-    return new Date().getFullYear();
+  return new Date().getFullYear();
 }
 
 export function getNowDate() {
-    return new Date().getDate();
+  return new Date().getDate();
 }
 
 export function getDateStr(date) {
-    return moment(date).format("YYYY-MM-DD");
+  return moment(date).format("YYYY-MM-DD");
 }
 
 export function getAddDateStr(num) {
-    return moment()
-        .add(num || 2, "days")
-        .format("YYYY-MM-DD");
+  return moment()
+    .add(num || 2, "days")
+    .format("YYYY-MM-DD");
+}
+
+export function checkSmallNow(time) {
+  if (
+    moment(time, "YYYY-MM-DD").format("X") < moment().add(1, "days").format("X")
+  ) {
+    return true;
+  }
+  return false;
 }

+ 253 - 235
components/OrderCard.js

@@ -3,272 +3,290 @@
 import React from "react";
 import { StyleSheet } from "react-native";
 import {
-    Card,
-    Text,
-    Button,
-    Layout,
-    MenuGroup,
-    MenuItem,
+  Card,
+  Text,
+  Button,
+  Layout,
+  MenuGroup,
+  MenuItem,
 } from "@ui-kitten/components";
 import { Linking } from "expo";
 import { useModel } from "flooks";
 
 const styles = StyleSheet.create({
-    orderItem1: {
-        justifyContent: "space-between",
-        flexDirection: "row",
-        marginBottom: 10,
-    },
-    orderItem2: {
-        alignItems: "flex-start",
-        flexDirection: "row",
-        marginBottom: 15,
-    },
-    leftText: {
-        flexShrink: 0,
-        marginRight: 8,
-        minWidth: 60,
-    },
-    menuGroup: {
-        padding: 0,
-        margin: 0,
-    },
-    lay: {
-        flex: 1,
-    },
-    menuTitle: {
-        flexDirection: "row",
-        flex: 1,
-    },
-    footerContainer: {
-        flexDirection: "row",
-        justifyContent: "flex-end",
-    },
-    footerControl: {
-        width: 112,
-        marginLeft: 5,
-    },
-    sub: {
-        flex: 1,
-        flexShrink: 0,
-    },
-    menuItem: {
-        flex: 1,
-        // paddingHorizontal: 0,
-        // paddingVertical: 0,
-        // paddingLeft: 0,
-        // marginTop: 5,
-        // marginHorizontal: 5,
-    },
-    right: {
-        textAlign: "right",
-    },
-    center: {
-        textAlign: "center",
-    },
+  orderItem1: {
+    justifyContent: "space-between",
+    flexDirection: "row",
+    marginBottom: 10,
+  },
+  orderItem2: {
+    alignItems: "flex-start",
+    flexDirection: "row",
+    marginBottom: 15,
+  },
+  leftText: {
+    flexShrink: 0,
+    marginRight: 8,
+    minWidth: 60,
+  },
+  menuGroup: {
+    padding: 0,
+    margin: 0,
+  },
+  lay: {
+    flex: 1,
+  },
+  menuTitle: {
+    flexDirection: "row",
+    flex: 1,
+  },
+  footerContainer: {
+    flexDirection: "row",
+    justifyContent: "flex-end",
+  },
+  footerControl: {
+    width: 112,
+    marginLeft: 5,
+  },
+  sub: {
+    flex: 1,
+    flexShrink: 0,
+  },
+  menuItem: {
+    flex: 1,
+    // paddingHorizontal: 0,
+    // paddingVertical: 0,
+    // paddingLeft: 0,
+    // marginTop: 5,
+    // marginHorizontal: 5,
+  },
+  right: {
+    textAlign: "right",
+  },
+  center: {
+    textAlign: "center",
+  },
 });
 
-
 const MenuTitle = (title, sub, num, index) => (
   <Layout style={styles.menuTitle} key={index || 0}>
-    <Text category='h1' style={styles.sub}>
+    <Text category="h1" style={styles.sub}>
       {title}
     </Text>
     {num && (
-    <Text category='h1' style={[styles.sub, styles.center]}>
-      {num}
-    </Text>
-        )}
-    <Text category='h1' style={[styles.sub, styles.right]}>
+      <Text category="h1" style={[styles.sub, styles.center]}>
+        {num}
+      </Text>
+    )}
+    <Text category="h1" style={[styles.sub, styles.right]}>
       {sub}
     </Text>
   </Layout>
 );
 
-const lsitMenu = () => {
-    return (
-      <Layout style={styles.menuItem}>
-        {[1, 2].map((item, index) => {
-                return MenuTitle(`圣诞节副科${  item}`, "¥9999.99", 2, index + 1);
-            })}
-      </Layout>
-    );
+const lsitMenu = list => {
+  return (
+    <Layout style={styles.menuItem}>
+      {[...list].map((item, index) => {
+        const { goods } = item;
+        return MenuTitle(goods.name, item.goodsRealPrice, item.num, index + 1);
+      })}
+    </Layout>
+  );
 };
 
+function getGoodsInfo(orderGoodsSpecs) {
+  const info = {
+    list: orderGoodsSpecs,
+    name: "",
+    num: 0,
+  };
+
+  if (orderGoodsSpecs.length > 0) {
+    info.num = orderGoodsSpecs.reduce((pre, cur) => {
+      return pre + cur.num;
+    }, 0);
+
+    info.name = orderGoodsSpecs[0].goods.name;
+  }
+  return info;
+}
 
 export default function OrderCard(props) {
-    const {
-        orderInfo2,
-        orderInfo3,
-        orderInfo4,
-        orderInfo5,
-        orderInfo6,
-        getWordsStr,
-        orderButton1,
-        orderButton2,
-        overTips,
-    } = useModel("wordsModel");
-    const { showDialog } = useModel("dialogModel");
-    const { receivedOrder } = useModel("orderInfoModel");
-    const { info, updateInfo,style } = props;
-    const {
-        id,
-        payMethod,
-        orderTime,
-        userAddress,
-        merchantStatus,
-        user,
-        riderStatus,
-    } = info || {};
+  const {
+    orderInfo2,
+    orderInfo3,
+    orderInfo4,
+    orderInfo5,
+    orderInfo6,
+    getWordsStr,
+    orderButton1,
+    orderButton2,
+    overTips,
+  } = useModel("wordsModel");
+  const { showDialog } = useModel("dialogModel");
+  const { receivedOrder } = useModel("orderInfoModel");
+  const { info, updateInfo, style } = props;
+  const {
+    id,
+    payMethod,
+    orderTime,
+    userAddress,
+    merchantStatus,
+    user,
+    riderStatus,
+    remark,
+    orderGoodsSpecs,
+    realAmount,
+    riderName,
+    timeOfArrival,
+    userReceivedTime,
+  } = info || {};
 
-    const Footer = (props) => {
-        if (riderStatus) {
-            return (
-              <Layout
-                {...props}
-                style={[props.style]}
-              >
-                <Layout style={styles.orderItem1}>
-                  <Text category='c2'>骑手:胖齐齐</Text>
-                  <Button
-                    appearance='outline'
-                    onPress={() => {
-                                Linking.openURL(`tel:+${  user.phone}`);
-                            }}
-                  >
-                    联系骑手
-                  </Button>
-                </Layout>
-                <Layout style={styles.orderItem1}>
-                  <Text category='c2'>
-                    预计送达时间:2019-12-31 13:17
-                  </Text>
-                </Layout>
-                {riderStatus === "CARRY_OUT" && (
-                <Layout style={styles.orderItem1}>
-                  <Text category='c2'>
-                    实际送达时间:2019-12-31 13:17
-                  </Text>
-                </Layout>
-                    )}
-              </Layout>
-            );
-        } if (merchantStatus === "NOT_RECEIVED") {
-            return (
-              <Layout
-                {...props}
-                style={[props.style, styles.footerContainer]}
-              >
-                <Button
-                  style={styles.footerControl}
-                  appearance='outline'
-                  onPress={() => {
-                            showDialog({
-                                bodyText: overTips,
-                                status: "danger",
-                                cancelable: true,
-                                confirmCallback: () => {
-                                    receivedOrder(id, false).then((res) => {
-                                        updateInfo(res);
-                                    });
-                                },
-                            });
-                        }}
-                >
-                  {orderButton1}
-                </Button>
-                <Button
-                  style={styles.footerControl}
-                  onPress={() => {
-                            receivedOrder(id, true).then((res) => {
-                                updateInfo(res);
-                            });
-                        }}
-                  appearance='outline'
-                >
-                  {orderButton2}
-                </Button>
-              </Layout>
-            );
-        } 
-            return null;
-        
-    };
-    return (
-      <Card
-        appearance='orderCard'
-        disabled
-        style={[style]}
-        footer={Footer}
-      >
-        {riderStatus ? (
-          <Text>
-            骑手
-            {getWordsStr(riderStatus)}
-          </Text>
-            ) : (
-              <Text>
-                商家
-                {getWordsStr(merchantStatus)}
+  const Footer = props => {
+    if (riderStatus !== "NOT_RECEIVED") {
+      return (
+        <Layout {...props} style={[props.style]}>
+          <Layout style={styles.orderItem1}>
+            <Text category="c2">
+              骑手:
+              {riderName}
+            </Text>
+            <Button
+              appearance="outline"
+              onPress={() => {
+                Linking.openURL(`tel:+${user.phone}`);
+              }}
+            >
+              联系骑手
+            </Button>
+          </Layout>
+          <Layout style={styles.orderItem1}>
+            <Text category="c2">
+              预计送达时间:
+              {timeOfArrival}
+            </Text>
+          </Layout>
+          {riderStatus === "CARRY_OUT" && (
+            <Layout style={styles.orderItem1}>
+              <Text category="c2">
+                实际送达时间:
+                {userReceivedTime}
               </Text>
-            )}
-        <Layout style={styles.orderItem1}>
-          <Text category='c2'>{id}</Text>
-          <Text category='c2'>
-            {orderInfo2}
-            :
-            {orderTime}
-          </Text>
-        </Layout>
-        <Layout style={styles.orderItem1}>
-          <Text category='h6'>{user.nickname}</Text>
-          <Text category='h6'>{getWordsStr(payMethod)}</Text>
+            </Layout>
+          )}
         </Layout>
-        <Layout style={styles.orderItem1}>
-          <Text category='c2'>{user.phone}</Text>
+      );
+    }
+    if (merchantStatus === "NOT_RECEIVED") {
+      return (
+        <Layout {...props} style={[props.style, styles.footerContainer]}>
+          <Button
+            style={styles.footerControl}
+            appearance="outline"
+            onPress={() => {
+              showDialog({
+                bodyText: overTips,
+                status: "danger",
+                cancelable: true,
+                confirmCallback: () => {
+                  receivedOrder(id, false).then(res => {
+                    updateInfo(res);
+                  });
+                },
+              });
+            }}
+          >
+            {orderButton1}
+          </Button>
           <Button
-            appearance='outline'
+            style={styles.footerControl}
             onPress={() => {
-                        Linking.openURL(`tel:+${  user.phone}`);
-                    }}
+              receivedOrder(id, true).then(res => {
+                updateInfo(res);
+              });
+            }}
+            appearance="outline"
           >
-            {orderInfo3}
+            {orderButton2}
           </Button>
         </Layout>
+      );
+    }
+    return null;
+  };
 
-        <Layout style={styles.orderItem2}>
-          <Text category='c2' style={styles.leftText}>
-            {orderInfo4}
-          </Text>
-          <Text category='h1'>{userAddress}</Text>
-        </Layout>
+  const orderGoodsSpecsShowInfo = getGoodsInfo(orderGoodsSpecs || []);
+  return (
+    <Card appearance="orderCard" disabled style={[style]} footer={Footer}>
+      {riderStatus ? (
+        <Text>
+          骑手
+          {getWordsStr(riderStatus)}
+        </Text>
+      ) : (
+        <Text>
+          商家
+          {getWordsStr(merchantStatus)}
+        </Text>
+      )}
+      <Layout style={styles.orderItem1}>
+        <Text category="c2">{id}</Text>
+        <Text category="c2">
+          {orderInfo2}
+          :
+          {orderTime}
+        </Text>
+      </Layout>
+      <Layout style={styles.orderItem1}>
+        <Text category="h6">{user.nickname}</Text>
+        <Text category="h6">{getWordsStr(payMethod)}</Text>
+      </Layout>
+      <Layout style={styles.orderItem1}>
+        <Text category="c2">{user.phone}</Text>
+        <Button
+          appearance="outline"
+          onPress={() => {
+            Linking.openURL(`tel:+${user.phone}`);
+          }}
+        >
+          {orderInfo3}
+        </Button>
+      </Layout>
 
-        <Layout style={styles.orderItem2}>
-          <Text category='c2' style={styles.leftText}>
-            {orderInfo5}
-          </Text>
-          <Layout style={styles.lay}>
-            <MenuGroup
-              appearance='orderProduct'
-              title={() => MenuTitle("圣诞节副科", "¥9999.99")}
-              style={styles.menuGroup}
-            >
-              <MenuItem
-                disabled
-                style={styles.menuItem}
-                title={lsitMenu}
-              />
-            </MenuGroup>
-          </Layout>
-        </Layout>
+      <Layout style={styles.orderItem2}>
+        <Text category="c2" style={styles.leftText}>
+          {orderInfo4}
+        </Text>
+        <Text category="h1">{userAddress}</Text>
+      </Layout>
 
-        <Layout style={styles.orderItem2}>
-          <Text category='c2' style={styles.leftText}>
-            {orderInfo6}
-            :
-          </Text>
-          <Text category='h1'>多放辣椒</Text>
+      <Layout style={styles.orderItem2}>
+        <Text category="c2" style={styles.leftText}>
+          {orderInfo5}
+        </Text>
+        <Layout style={styles.lay}>
+          <MenuGroup
+            appearance="orderProduct"
+            title={() => MenuTitle(orderGoodsSpecsShowInfo.name, realAmount)}
+            style={styles.menuGroup}
+          >
+            <MenuItem
+              disabled
+              style={styles.menuItem}
+              title={lsitMenu(orderGoodsSpecsShowInfo.list)}
+            />
+          </MenuGroup>
         </Layout>
-      </Card>
-    );
+      </Layout>
+
+      <Layout style={styles.orderItem2}>
+        <Text category="c2" style={styles.leftText}>
+          {orderInfo6}
+          :
+        </Text>
+        <Text category="h1">{remark || "无"}</Text>
+      </Layout>
+    </Card>
+  );
 }

+ 9 - 1
models/couponModel.js

@@ -20,7 +20,15 @@ export default {
           isAll: false,
         },
         true
-      );
+      ).then(res => {
+        const list = res.filter(item => {
+          return (
+            TimeUtil.checkSmallNow(item.startDate) &&
+            !TimeUtil.checkSmallNow(item.endDate)
+          );
+        });
+        return Promise.resolve(list);
+      });
     },
     // 获取商家优惠券
     getMyList(page, size) {

+ 93 - 0
notice/ChatScreen.tsx

@@ -0,0 +1,93 @@
+import { StackScreenProps } from '@react-navigation/stack';
+import React, { useState, useCallback, useEffect } from 'react';
+import { Platform, View, KeyboardAvoidingView } from 'react-native';
+import { Div, Button, Image, Text, Avatar } from 'react-native-magnus';
+import { GiftedChat } from 'react-native-gifted-chat';
+import { ScrollView } from 'react-native-gesture-handler';
+import * as Localization from 'expo-localization';
+import { useModel } from "flooks";
+import User from '../stores/User';
+import IM from './model';
+import { parse } from '../utils/TimeUtils';
+import { useTranslation } from 'react-i18next';
+
+import { useRequest } from 'ahooks';
+import { userInfo } from 'os';
+
+
+
+export default function ChatScreen({ navigation, route }: StackScreenProps) {
+  const { params } = route;
+  const { toUserId, toUserName } = params;
+  const { t } = useTranslation();
+
+  if (toUserName) {
+    navigation.setOptions({
+      title: toUserName,
+    });
+  }
+
+  const { userInfo }
+	} = useModel("userModel");
+	
+	const {id}=userInfo;
+
+	const [messages, setMessages] = useState([]);
+  const { sendMessage,chatInfo } = useModel('IMModel');
+
+  const { httpGet,httpPost } = useModel("httpModel");
+  const { loading, run } = useRequest(
+    ()=>{
+			return httpGet(`/chat/showChat?userOne=${id}&userTwo=${toUserId}`)
+		},
+    {
+      defaultLoading: false,
+      initialData: [],
+      onSuccess: (data) => {
+        const list = data.map((item, index) => {
+          return {
+            _id: index,
+            text: item.content,
+            createdAt: parse(item.sendTime),
+            user: {
+              _id: item.sendUserId,
+              name: item.sendNickName,
+              avatar: item.sendAvatar,
+            },
+          };
+        });
+        setMessages(list);
+      },
+    }
+  );
+
+  React.useEffect(() => {
+    if (chatInfo && chatInfo.from === toUserId.toString()) {
+      run();
+    }
+  }, [chatInfo]);
+
+  const onSend = useCallback((messages = []) => {
+    console.log(messages);
+    sendMessage(messages[0].text, toUserId);
+    setMessages((previousMessages) =>
+      GiftedChat.append(previousMessages, messages)
+    );
+  }, []);
+
+  return (
+    <Div flex={1}>
+      <GiftedChat
+        placeholder={t('qing-shu-ru-liao-tian-nei-rong')}
+        messages={messages}
+        onSend={(messages) => onSend(messages)}
+        showUserAvatar={true}
+        user={{
+          _id: userInfo.id,
+          avatar: userInfo.avatar,
+        }}
+      />
+      {/* {Platform.OS === 'android' && <KeyboardAvoidingView behavior="padding" />} */}
+    </Div>
+  );
+}

+ 68 - 0
notice/EmailDetailScreen.tsx

@@ -0,0 +1,68 @@
+import { StackScreenProps } from "@react-navigation/stack";
+import * as React from "react";
+import { RefreshControl } from "react-native";
+import { Div, Button, Image, Text, Avatar } from "react-native-magnus";
+import { ScrollView } from "react-native-gesture-handler";
+
+import { useModel } from "flooks";
+import { RootStackParamList } from "../types";
+import { getChatTime } from "../Utils/TimeUtils";
+import request from "../utils/RequestUtils";
+
+export default function RegisterScreen({ navigation, route }) {
+  const { params } = route;
+  const { emailId } = params;
+
+  const { httpGet,httpPost } = useModel("httpModel");
+  const { data, loading, reload, run } = useRequest(
+    () => {
+      return httpGet(`/email/get/${emailId}`);
+    },
+    {
+      refreshDeps: [emailId],
+      initialData: {},
+      onSuccess: res => {
+        if (!res.isRead) {
+         httpPost(
+           "/email/save",
+           {
+             ...data,
+             isRead: true,
+           },
+           { body: "json" }
+         );
+        }
+      },
+    }
+  );
+
+  return (
+    <Div bg="gray100">
+      <ScrollView
+        contentContainerStyle={{ flexGrow: 1, backgroundColor: "#f2f2f2" }}
+        refreshControl={
+          <RefreshControl refreshing={loading} onRefresh={reload} />
+        }
+      >
+        <Div my={10} px={15} py={5} bg="white">
+          <Div row justifyContent="space-between" py={10}>
+            <Text fontSize="xl" fontWeight="bold">
+              {data.title}
+            </Text>
+            <Text>{getChatTime(data.sendTime)}</Text>
+          </Div>
+
+          <Text
+            py={10}
+            color="gray600"
+            borderTopColor="gray100"
+            borderTopWidth={1}
+            fontSize="sm"
+          >
+            {data.content}
+          </Text>
+        </Div>
+      </ScrollView>
+    </Div>
+  );
+}

+ 72 - 0
notice/NoticeCom.tsx

@@ -0,0 +1,72 @@
+import * as React from 'react';
+import { Div, Button, Image, Text, Avatar } from 'react-native-magnus';
+
+const logo = require('../assets/images/logo.png');
+import { getChatTime } from '../utils/TimeUtils';
+
+export default function NoticeCom({
+  info = {
+    receiveUserId: 0,
+  },
+  type,
+  userId,
+  onPress,
+  toUserName = '',
+}) {
+  const {
+    receiveUserId,
+    isRead,
+    content,
+    receiveAvatar,
+    sendAvatar,
+    sendTime,
+  } = info;
+  const isReceive = type === 'email' || receiveUserId === userId;
+  const active = !isRead && isReceive;
+  const avatar = isReceive ? sendAvatar : receiveAvatar;
+  return (
+    <Button block bg="white" py={0} px={13} rounded="none" onPress={onPress}>
+      <Div
+        flex={1}
+        row
+        py={10}
+        alignItems="center"
+        borderBottomWidth={1}
+        borderBottomColor="gray100"
+      >
+        <Div
+          w={10}
+          h={10}
+          bg={active ? 'yellow500' : 'gray100'}
+          rounded="circle"
+        />
+
+        {type !== 'user' ? (
+          <Div w={33} h={33} bg="green300" rounded="sm" ml={10} />
+        ) : (
+          <Image
+            w={33}
+            h={33}
+            source={avatar ? { uri: avatar } : logo}
+            ml={10}
+            rounded="circle"
+          />
+        )}
+
+        <Div flex={1} justifyContent="space-between" ml={5}>
+          <Div row>
+            <Text fontSize="sm" flex={1}>
+              {type === 'user' ? toUserName : info.title}
+            </Text>
+            <Text fontSize="xs" color="gray400">
+              {getChatTime(sendTime)}
+            </Text>
+          </Div>
+          <Text fontSize="xs" color="gray400" numberOfLines={1}>
+            {content || ''}
+          </Text>
+        </Div>
+      </Div>
+    </Button>
+  );
+}

+ 165 - 0
notice/NoticeScreen.tsx

@@ -0,0 +1,165 @@
+import { StackScreenProps } from '@react-navigation/stack';
+import * as React from 'react';
+import { useFocusEffect } from '@react-navigation/native';
+import { Div, Button, Image, Text, Avatar } from 'react-native-magnus';
+import { ScrollView, FlatList } from 'react-native-gesture-handler';
+import { useNavigation } from '@react-navigation/native';
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
+import Constants from 'expo-constants';
+
+import {useModel} from 'flooks';
+import { useMount } from 'ahooks';
+
+import IM from './model';
+import User from '../stores/User';
+
+
+import NoticeCom from './NoticeCom';
+import { SoundPlay } from '../utils/SoundUtils';
+
+const NoticeTab = createMaterialTopTabNavigator();
+
+export default function NoticeScreen({
+  navigation,
+}: StackScreenProps) {
+  const webRef = React.useRef();
+  const { initChat } = useModel("IMModel");
+
+  useFocusEffect(
+    React.useCallback(() => {
+      initChat();
+    }, [])
+  );
+  return (
+    <Div bg="gray100" flex={1}>
+      <Div bg="yellow500" pt={Constants.statusBarHeight + 11} pb={11}>
+        <Text color="white" textAlign="center" fontSize="xl" fontWeight="bold">
+      系统通知
+        </Text>
+      </Div>
+      <NoticeTab.Navigator
+        initialRouteName="Sys"
+        style={{ flexGrow: 1 }}
+        tabBarOptions={{
+          inactiveTintColor: '#000',
+          activeTintColor: '#FFC21C',
+          indicatorStyle: { height: 0 },
+          labelStyle: { fontSize: 15 },
+          style: {
+            backgroundColor: '#f2f2f2',
+            elevation: 0,
+            shadowOffset: {
+              width: 0,
+              height: 0,
+            },
+            shadowOpacity: 0,
+            shadowRadius: 0,
+          },
+        }}
+      >
+        <NoticeTab.Screen
+          name="Sys"
+          component={SysNoticeScreen}
+          options={{
+            title: t('xi-tong-xiao-xi'),
+          }}
+        />
+        <NoticeTab.Screen
+          name="Chat"
+          component={UserChatScreen}
+          options={{
+            title: t('yong-hu-xiao-xi'),
+          }}
+        />
+      </NoticeTab.Navigator>
+    </Div>
+  );
+}
+
+function SysNoticeScreen({ navigation }) {
+  const { sysNoticeList, sendPushNotification } = useModel(IM, [
+    'sysNoticeList',
+  ]);
+  const { t } = useTranslation();
+  return (
+    <>
+      {/* <Button
+        onPress={() => {
+          sendPushNotification();
+          SoundPlay('voice_664');
+        }}
+      >
+        发送系统消息
+      </Button> */}
+      <FlatList
+        renderItem={({ item }) => {
+          return (
+            <NoticeCom
+              info={item}
+              type="email"
+              onPress={() => {
+                navigation.navigate('NoticeStack', {
+                  screen: 'EmailDetail',
+                  params: {
+                    emailId: item.id,
+                  },
+                });
+              }}
+            />
+          );
+        }}
+        data={sysNoticeList}
+        keyExtractor={(item, index) => `email${index}`}
+        ListEmptyComponent={() => {
+          return (
+            <Text color="gary200" fontSize="sm" textAlign="center" py={20}>
+             暂无数据
+            </Text>
+          );
+        }}
+        contentContainerStyle={{ flexGrow: 1, backgroundColor: '#fff' }}
+      />
+    </>
+  );
+}
+
+function UserChatScreen({ navigation }) {
+  const { chatList } = useModel(IM, ['chatList']);
+  const { id } = useModel(User, ['id']);
+  return (
+    <FlatList
+      renderItem={({ item }) => {
+        const isReceive = item.receiveUserId === id;
+        const toUserId = isReceive ? item.sendUserId : item.receiveUserId;
+        const toUserName = isReceive ? item.sendNickname : item.receiveNickname;
+        return (
+          <NoticeCom
+            info={item}
+            userId={id}
+            type="user"
+            toUserName={toUserName}
+            onPress={() => {
+              navigation.navigate('NoticeStack', {
+                screen: 'Chat',
+                params: {
+                  toUserId,
+                  toUserName,
+                },
+              });
+            }}
+          />
+        );
+      }}
+      data={chatList}
+      keyExtractor={(item, index) => `chat${index}`}
+      contentContainerStyle={{ flexGrow: 1, backgroundColor: '#fff' }}
+      ListEmptyComponent={() => {
+        return (
+          <Text color="gary200" fontSize="sm" textAlign="center" py={20}>
+            暂无数据
+          </Text>
+        );
+      }}
+    />
+  );
+}

+ 153 - 0
notice/TimScreen.tsx

@@ -0,0 +1,153 @@
+import Constants from 'expo-constants';
+import * as Notifications from 'expo-notifications';
+import * as Permissions from 'expo-permissions';
+import { StackScreenProps } from '@react-navigation/stack';
+import * as React from 'react';
+import { useNavigation } from '@react-navigation/native';
+import { WebView } from 'react-native-webview';
+import {useModel,setModel} from 'flooks';
+import { useMount } from 'ahooks';
+
+import IM from './model';
+
+setModel("IMModel", IM);
+
+import { SoundPlay } from '../utils/SoundUtils';
+if (!__DEV__) {
+  Notifications.setNotificationHandler({
+    handleNotification: async () => ({
+      shouldShowAlert: true,
+      shouldPlaySound: false,
+      shouldSetBadge: false,
+    }),
+  });
+}
+
+export default function NoticeScreen({
+  navigation,
+}: StackScreenProps<RootStackParamList, 'Login'>) {
+  const webRef = React.useRef();
+  const {
+    getChat,
+    getSysNotice,
+    userID,
+    userSig,
+    tim,
+    setTim,
+    initChat,
+    updateChatInfo,
+    sendPushNotification,
+  } = useModel("IMModel");
+  useMount(() => {
+    initChat();
+  });
+
+  const [expoPushToken, setExpoPushToken] = React.useState('');
+  const [notification, setNotification] = React.useState(false);
+  const notificationListener = React.useRef();
+  const responseListener = React.useRef();
+
+  React.useEffect(() => {
+    if (!__DEV__) {
+      registerForPushNotificationsAsync().then((token) =>
+        setExpoPushToken(token)
+      );
+
+      notificationListener.current = Notifications.addNotificationReceivedListener(
+        (notification) => {
+          setNotification(notification);
+        }
+      );
+
+      responseListener.current = Notifications.addNotificationResponseReceivedListener(
+        (response) => {
+          console.log(response);
+        }
+      );
+
+      return () => {
+        Notifications.removeNotificationSubscription(notificationListener);
+        Notifications.removeNotificationSubscription(responseListener);
+      };
+    }
+  }, []);
+
+  React.useEffect(() => {
+    if (userSig && tim)
+      tim.injectJavaScript(
+        `window.chatLogin(${JSON.stringify(userID)},${JSON.stringify(userSig)})`
+      );
+  }, [userSig, tim]);
+
+  return (
+    <>
+      <WebView
+        ref={webRef}
+        source={{
+          uri: `http://dingdong.izouma.com/map/tim`,
+        }}
+        onMessage={({ nativeEvent }) => {
+          console.log(nativeEvent);
+          const info =
+            nativeEvent.data.indexOf('{') !== -1
+              ? JSON.parse(nativeEvent.data)
+              : {};
+          console.log(info);
+          if (Array.isArray(info)) {
+            getChat();
+            const message = info[0];
+            if (/^\d{n}$/.test(message.from)) {
+              updateChatInfo(message);
+            } else {
+              getSysNotice();
+              const { payload } = message;
+              let body = payload ? payload.text : '';
+              if (body.indexOf('voice_') !== -1) {
+                const textInfo = body.split(';');
+                body = textInfo[1];
+                SoundPlay(textInfo[0]);
+              }
+              sendPushNotification('系统通知', body);
+            }
+          }
+        }}
+        onLoadEnd={() => {
+          setTim(webRef.current);
+        }}
+      />
+    </>
+  );
+}
+
+async function registerForPushNotificationsAsync() {
+  let token;
+  if (Constants.isDevice) {
+    const { status: existingStatus } = await Permissions.getAsync(
+      Permissions.NOTIFICATIONS
+    );
+    let finalStatus = existingStatus;
+    if (existingStatus !== 'granted') {
+      const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
+      finalStatus = status;
+    }
+    if (finalStatus !== 'granted') {
+      alert('Failed to get push token for push notification!');
+      return;
+    }
+    token = (await Notifications.getExpoPushTokenAsync()).data;
+    console.log(token);
+  } else {
+    alert('Must use physical device for Push Notifications');
+  }
+
+  if (Platform.OS === 'android') {
+    Notifications.setNotificationChannelAsync('default', {
+      name: 'default',
+      importance: Notifications.AndroidImportance.MAX,
+      vibrationPattern: [0, 250, 250, 250],
+      lightColor: '#FF231F7C',
+    });
+  }
+
+  return token;
+}

+ 85 - 0
notice/model.ts

@@ -0,0 +1,85 @@
+import TIM from 'tim-js-sdk';
+import COS from 'cos-js-sdk-v5';
+import request from '../utils/RequestUtils';
+import User from '../stores/User';
+import * as Notifications from 'expo-notifications';
+
+const IM = (now) => ({
+  SDKAppID: 1400401634,
+  userID: 0,
+  userSig: null,
+  messages: [],
+  chatList: [],
+  sysNoticeList: [],
+  chatInfo: {},
+  tim: null,
+  getSysNotice() {
+    request.get('/email/my').then((res) => {
+      now({ sysNoticeList: res });
+    });
+  },
+  getChat() {
+    request.get('/chat/my').then((res) => {
+      if (res) {
+        now({ chatList: res });
+      }
+    });
+  },
+  initChat() {
+    const { getSysNotice, getChat, getUserSig } = now();
+    getUserSig();
+    getSysNotice();
+    getChat();
+  },
+  getUserSig() {
+    const { id } = now(User);
+    request
+      .get('/chat/getUserSig', {
+        params: {
+          userId: id,
+        },
+      })
+      .then((res) => {
+        now({
+          userSig: res,
+          userID: id,
+        });
+      });
+  },
+  updateChatInfo(info) {
+    now({ chatInfo: info });
+  },
+  sendMessage(content, receiveUserId) {
+    const { id } = now(User);
+    request
+      .post('/chat/save', {
+        data: {
+          sendUserId: id,
+          receiveUserId,
+          content,
+        },
+      })
+      .then((res) => {
+        const { timSend } = now();
+        timSend(content, receiveUserId);
+      });
+  },
+  setTim(tim) {
+    now({ tim });
+  },
+  timSend(text, toUserId) {
+    const { tim } = now();
+    if (tim) tim.injectJavaScript(`window.sendMessage(${text},${toUserId})`);
+  },
+  async sendPushNotification(title, body) {
+    await Notifications.scheduleNotificationAsync({
+      content: {
+        title: title || '您有一条新的信息 📬',
+        body: body || '请打开app查看',
+      },
+      trigger: { seconds: 2 },
+    });
+  },
+});
+
+export default IM;

+ 1 - 1
screens/HomeScreen.js

@@ -265,7 +265,7 @@ export default function HomeScreen({ navigation, route }) {
             userLogout();
           }}
         />
-        <MenuItem title="0814版本(评论)" />
+        <MenuItem title="0824版本" />
         <MenuItem title={`切换语言(${local})`} onPress={changeLocal} />
       </OverflowMenu>
     </>

+ 11 - 3
screens/HomeScreenPage3.js

@@ -111,7 +111,7 @@ export default function HomePage3() {
     img,
     updateMerchant,
     introduction,
-    name,
+    showName,
     phone,
     address,
     category,
@@ -247,11 +247,19 @@ export default function HomePage3() {
       </Layout>
       <Menu style={styles.menu}>
         <MenuItem
-          title={props => Label(props, getWordsStr("register_form_1"), name)}
+          title={props =>
+            Label(props, getWordsStr("register_form_1"), showName)
+          }
           accessoryRight={ForwardIcon}
           style={styles.menuItem}
           onPress={() =>
-            showChange("name", name, getWordsStr("register_form_1"), 25, "text")
+            showChange(
+              "showName",
+              showName,
+              getWordsStr("register_form_1"),
+              25,
+              "text"
+            )
           }
         />
         <MenuItem