├── chat-buy-react
├── server
│ ├── key.js
│ ├── config.js
│ ├── model.js
│ ├── router.js
│ ├── user-model.js
│ ├── allorders-model.js
│ ├── server.js
│ ├── jwtMiddleware.js
│ ├── chat-model.js
│ ├── chat.js
│ ├── db.js
│ ├── foods.json
│ ├── goods.js
│ ├── user.js
│ ├── order.js
│ └── socket.js
├── public
│ ├── favicon.ico
│ ├── manifest.json
│ └── index.html
├── src
│ ├── images
│ │ ├── goods.png
│ │ ├── order.png
│ │ ├── user.png
│ │ ├── message.png
│ │ ├── user-sel.png
│ │ ├── goods-sel.png
│ │ ├── order-sel.png
│ │ └── message-sel.png
│ ├── common
│ │ ├── history.js
│ │ ├── unit.js
│ │ └── axiosMiddleware.js
│ ├── container
│ │ ├── my.jsx
│ │ ├── allOrders.jsx
│ │ ├── goods.jsx
│ │ ├── login.jsx
│ │ ├── register.jsx
│ │ ├── myOrder.jsx
│ │ ├── message.jsx
│ │ ├── chat.jsx
│ │ └── dashboard.jsx
│ ├── reducers
│ │ ├── orders.js
│ │ ├── index.js
│ │ ├── goods.js
│ │ ├── chat.js
│ │ └── user.js
│ ├── components
│ │ ├── message
│ │ │ ├── chatListItem.jsx
│ │ │ ├── chatList.jsx
│ │ │ └── messageList.jsx
│ │ ├── navBar
│ │ │ └── backNavBar.jsx
│ │ ├── goods
│ │ │ ├── buy.jsx
│ │ │ └── goodsList.jsx
│ │ ├── common
│ │ │ └── 404.jsx
│ │ ├── allOrders
│ │ │ └── list.jsx
│ │ ├── login
│ │ │ └── loginForm.jsx
│ │ ├── register
│ │ │ └── registerForm.jsx
│ │ └── myOrder
│ │ │ └── myOrderItem.jsx
│ ├── store
│ │ └── configureStore.js
│ ├── asyncComponent.jsx
│ ├── router
│ │ └── router.jsx
│ ├── actions
│ │ ├── type.js
│ │ ├── goods.js
│ │ ├── order.js
│ │ ├── user.js
│ │ └── chat.js
│ ├── index.js
│ ├── styles
│ │ └── index.scss
│ └── registerServiceWorker.js
├── tsconfig.json
├── .gitignore
├── config
│ ├── jest
│ │ ├── fileTransform.js
│ │ └── cssTransform.js
│ ├── polyfills.js
│ ├── paths.js
│ ├── env.js
│ ├── webpackDevServer.config.js
│ ├── webpack.config.dev.js
│ └── webpack.config.prod.js
├── scripts
│ ├── test.js
│ ├── start.js
│ └── build.js
└── package.json
├── README-EN.md
├── README.md
└── LICENSE
/chat-buy-react/server/key.js:
--------------------------------------------------------------------------------
1 | import config from "./config";
2 |
3 | module.exports = config.privateKey;
4 |
--------------------------------------------------------------------------------
/chat-buy-react/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/public/favicon.ico
--------------------------------------------------------------------------------
/chat-buy-react/src/images/goods.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/goods.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/order.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/order.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/user.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/message.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/user-sel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/user-sel.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/goods-sel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/goods-sel.png
--------------------------------------------------------------------------------
/chat-buy-react/src/images/order-sel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/order-sel.png
--------------------------------------------------------------------------------
/chat-buy-react/src/common/history.js:
--------------------------------------------------------------------------------
1 | import createHistory from "history/createBrowserHistory";
2 |
3 | export default createHistory();
4 |
--------------------------------------------------------------------------------
/chat-buy-react/src/images/message-sel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KieSun/Chat-Buy-React/HEAD/chat-buy-react/src/images/message-sel.png
--------------------------------------------------------------------------------
/chat-buy-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "experimentalDecorators": true,
4 | "allowJs": true
5 | }
6 | }
--------------------------------------------------------------------------------
/chat-buy-react/server/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | DEV_DB_URL: "mongodb://localhost/chat-buy-react",
3 | PRODUCTION_DB_URL: "",
4 | PORT: process.env.PORT || 1717,
5 | privateKey: "chat-buy-react"
6 | }
7 |
--------------------------------------------------------------------------------
/chat-buy-react/server/model.js:
--------------------------------------------------------------------------------
1 |
2 | import "./db";
3 | import user from "./user-model";
4 | import allOrders from "./allorders-model";
5 | import chat from "./chat-model";
6 |
7 |
8 | module.exports = {
9 | user,
10 | allOrders,
11 | chat
12 | };
13 |
--------------------------------------------------------------------------------
/chat-buy-react/server/router.js:
--------------------------------------------------------------------------------
1 | const user = require("./user");
2 | const goods = require("./goods");
3 | const order = require("./order");
4 | const chat = require("./chat");
5 |
6 |
7 | module.exports = function(app) {
8 | [
9 | user,
10 | goods,
11 | order,
12 | chat
13 | ].forEach((p) => {
14 | p(app);
15 | })
16 | }
--------------------------------------------------------------------------------
/chat-buy-react/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/chat-buy-react/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/chat-buy-react/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/chat-buy-react/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/my.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { List } from "antd-mobile";
3 |
4 | const Item = List.Item;
5 |
6 | const My = ({ history }) => (
7 |
8 |
9 | - history.push("/me/orders")}>
10 | 我的订单
11 |
12 |
13 |
14 | );
15 |
16 | export default My;
17 |
--------------------------------------------------------------------------------
/chat-buy-react/src/reducers/orders.js:
--------------------------------------------------------------------------------
1 | import { GET_ALL_ORDERS } from "../actions/type";
2 | import { Map, List } from "immutable";
3 |
4 | const initialState = Map({
5 | allOrders: List([])
6 | });
7 |
8 | export default function(state = initialState, action) {
9 | switch (action.type) {
10 | case GET_ALL_ORDERS:
11 | return state.set("allOrders", action.payload);
12 | default:
13 | break;
14 | }
15 | return state;
16 | }
17 |
--------------------------------------------------------------------------------
/chat-buy-react/server/user-model.js:
--------------------------------------------------------------------------------
1 | import mongoose , { Schema } from "mongoose";
2 |
3 | const userSchema = Schema({
4 | // 用户名
5 | user: { type: String, require: true },
6 | // 密码
7 | pwd: { type: String, require: true },
8 | // 类型
9 | type: { type: String, require: true },
10 | // 所有订单
11 | orders: [{ type: Schema.Types.ObjectId, ref: "allOrders" }]
12 | });
13 |
14 | const user = mongoose.model("user", userSchema);
15 | export default user;
--------------------------------------------------------------------------------
/chat-buy-react/src/components/message/chatListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ChatListItem = ({ messageObj, userId }) => {
4 | const isMine = messageObj.get("from") === userId;
5 | return (
6 |
7 |
{isMine ? "我" : "对方"}
8 |
{messageObj.get("message")}
9 |
10 | );
11 | };
12 |
13 | export default ChatListItem;
14 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/navBar/backNavBar.jsx:
--------------------------------------------------------------------------------
1 | import { NavBar, Icon } from "antd-mobile";
2 | import React from "react";
3 | import PropTypes from "prop-types";
4 |
5 | const BackNavBar = ({ title, backClick, hasIcon = true }) => (
6 | }
9 | onLeftClick={() => backClick()}
10 | >
11 | {title}
12 |
13 | );
14 |
15 | BackNavBar.propTypes = {
16 | title: PropTypes.string.isRequired,
17 | backClick: PropTypes.func,
18 | hasIcon: PropTypes.bool
19 | };
20 |
21 | export default BackNavBar;
22 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/goods/buy.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const Buy = ({ price, handleBuy }) => {
5 | return (
6 |
7 |
¥{price}
8 |
0 ? "buy-button" : "no-buy-button"}
10 | onClick={() => price > 0 && handleBuy()}
11 | >
12 | 购买
13 |
14 |
15 | );
16 | };
17 |
18 | Buy.propTypes = {
19 | price: PropTypes.number.isRequired,
20 | handleBuy: PropTypes.func.isRequired
21 | };
22 |
23 | export default Buy;
24 |
--------------------------------------------------------------------------------
/chat-buy-react/src/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from "redux";
2 | import thunkMiddleware from "redux-thunk";
3 | import rootReducer from "../reducers/index";
4 |
5 | export default function configureStore() {
6 | const store = createStore(
7 | rootReducer,
8 | compose(
9 | applyMiddleware(thunkMiddleware),
10 | window.devToolsExtension ? window.devToolsExtension() : f => f
11 | )
12 | );
13 |
14 | if (module.hot) {
15 | module.hot.accept("../reducers", () => {
16 | const nextRootReducer = require("../reducers/index");
17 | store.replaceReducer(nextRootReducer);
18 | });
19 | }
20 |
21 | return store;
22 | }
23 |
--------------------------------------------------------------------------------
/chat-buy-react/src/asyncComponent.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | export default function asyncComponent(importComponent) {
4 | class AsyncComponent extends Component {
5 | constructor(props) {
6 | super(props);
7 |
8 | this.state = {
9 | component: null
10 | };
11 | }
12 |
13 | async componentDidMount() {
14 | const { default: component } = await importComponent();
15 |
16 | this.setState({
17 | component: component
18 | });
19 | }
20 |
21 | render() {
22 | const C = this.state.component;
23 |
24 | return C ? : null;
25 | }
26 | }
27 |
28 | return AsyncComponent;
29 | }
30 |
--------------------------------------------------------------------------------
/chat-buy-react/server/allorders-model.js:
--------------------------------------------------------------------------------
1 | import mongoose, { Schema } from "mongoose";
2 |
3 | const allOrdersSchema = Schema({
4 | // 价格
5 | price: { type: Number, require: true },
6 | // 购买商品描述
7 | desc: { type: String, require: true },
8 | // 购买多少商品
9 | count: { type: Number, require: true },
10 | // 订单状态 0:未接单 1:接单 2:已送达
11 | state: { type: Number, require: true },
12 | // 下单时间
13 | date: { type: Date, require: true, default: Date.now },
14 | // 购买者
15 | customer: { type: Schema.Types.ObjectId, ref: "user" },
16 | // 接单者
17 | deliver: { type: Schema.Types.ObjectId, ref: "user" }
18 | });
19 |
20 | const allOrders = mongoose.model("allOrders", allOrdersSchema);
21 | export default allOrders;
--------------------------------------------------------------------------------
/chat-buy-react/src/components/common/404.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Nav from "../navBar/backNavBar";
3 | import { Button } from "antd-mobile";
4 | import { withRouter } from "react-router-dom";
5 |
6 | @withRouter
7 | class NotFound extends React.Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 | 抱歉,找不到该页面!
14 |
21 |
22 |
23 | );
24 | }
25 | }
26 |
27 | export default NotFound;
28 |
--------------------------------------------------------------------------------
/chat-buy-react/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 |
3 | const bodyParser = require("body-parser");
4 | const cookieParser = require("cookie-parser");
5 | const jwt = require("jsonwebtoken");
6 | const key = require("./key");
7 |
8 | const app = global.app = express();
9 |
10 | const server = require("http").createServer(app);
11 | const io = global.io = require("socket.io")(server);
12 |
13 |
14 | app.use(bodyParser.json());
15 | app.use(bodyParser.urlencoded({ extended: false }));
16 | app.use(cookieParser());
17 | app.use(require("./jwtMiddleware")());
18 |
19 | require("./socket")(io);
20 | require('./router')(app)
21 |
22 | server.listen(1717, function() {
23 | console.log("Node app start at port 1717");
24 | });
25 |
--------------------------------------------------------------------------------
/chat-buy-react/src/router/router.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Router, Switch } from "react-router-dom";
3 | import asyncComponent from "../asyncComponent";
4 | import history from "../common/history";
5 |
6 | const Login = asyncComponent(() => import("../container/login"));
7 | const Register = asyncComponent(() => import("../container/register"));
8 | const DashBoard = asyncComponent(() => import("../container/dashboard.jsx"));
9 |
10 | const Root = () => (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 |
20 | export default Root;
21 |
--------------------------------------------------------------------------------
/chat-buy-react/src/actions/type.js:
--------------------------------------------------------------------------------
1 | export const REGISTER = "REGISTER";
2 | export const LOGIN = "LOGIN";
3 | export const GET_INFO = "GET_INFO";
4 | export const LOG_OUT = "LOG_OUT";
5 | export const GOODS_LIST = "GOODS_LIST";
6 | export const ADD_TO_CART = "ADD_TO_CART";
7 | export const BUY_SUCCESS = "BUY_SUCCESS";
8 | export const GET_MY_ORDERS = "GET_MY_ORDERS";
9 | export const GET_ALL_ORDERS = "GET_ALL_ORDERS";
10 | export const GET_ORDER = "GET_ORDER";
11 | export const GET_ORDER_SUCCESS = "GET_ORDER_SUCCESS";
12 | export const AFFIRM_ORDER = "AFFIRM_ORDER";
13 | export const GET_USERNAME = "GET_USERNAME";
14 | export const GET_MESSAGE = "GET_MESSAGE";
15 | export const GET_MESSAGE_LIST = "GET_MESSAGE_LIST";
16 | export const SET_CURRENT_LIST = "SET_CURRENT_LIST";
17 | export const CLEAN_NO_READ = "CLEAN_NO_READ";
18 |
--------------------------------------------------------------------------------
/chat-buy-react/src/common/unit.js:
--------------------------------------------------------------------------------
1 | export function filterNoReadCount(userId, messageList) {
2 | let noReadCountArray = [];
3 | messageList.map(v => {
4 | // 找到当前用户的 lastId
5 | const sendObj = v.get("bothSide").find(v => v.get("user") === userId);
6 | let readId;
7 | if (sendObj) {
8 | readId = sendObj.get("lastId");
9 | }
10 | // 把发给我的消息过滤出来
11 | const messages = v.get("messages").filter(v => {
12 | return v.get("to") === userId;
13 | });
14 | if (readId) {
15 | // 把上次未读消息的 id 索引找到
16 | let index = messages.findIndex(v => readId === v.get("_id"));
17 | noReadCountArray.push(messages.size - index - 1);
18 | } else {
19 | noReadCountArray.push(messages.size);
20 | }
21 | return noReadCountArray
22 | });
23 | return noReadCountArray;
24 | }
25 |
--------------------------------------------------------------------------------
/chat-buy-react/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | // import { combineReducers } from "redux";
2 | import { combineReducers } from "redux-immutable";
3 | import UserReducer from "./user";
4 | import GoodsReducer from "./goods";
5 | import OrdersReducer from "./orders";
6 | import ChatReducer from "./chat";
7 | import { LOG_OUT } from "../actions/type";
8 | import history from "../common/history";
9 |
10 | const appReducer = combineReducers({
11 | user: UserReducer,
12 | goods: GoodsReducer,
13 | order: OrdersReducer,
14 | chat: ChatReducer
15 | });
16 |
17 | const rootReducer = (state, action) => {
18 | if (action.type === LOG_OUT) {
19 | history.push("/login");
20 | window.localStorage.clear();
21 | state = undefined;
22 | }
23 | return appReducer(state, action);
24 | };
25 |
26 | export default rootReducer;
27 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/allOrders/list.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MyOrderItem from "../myOrder/myOrderItem";
3 | import PropTypes from "prop-types";
4 | import ImmutablePropTypes from "react-immutable-proptypes";
5 |
6 | const List = ({allOrders, getOrder}) => (
7 | !allOrders.isEmpty() ? (
8 |
9 | {allOrders.map(v => (
10 |
16 | ))}
17 |
18 | ) : null
19 | )
20 |
21 | List.propTypes = {
22 | allOrders: ImmutablePropTypes.list.isRequired,
23 | getOrder: PropTypes.func.isRequired,
24 | };
25 |
26 | export default List;
27 |
--------------------------------------------------------------------------------
/chat-buy-react/server/jwtMiddleware.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const key = require("./key");
3 |
4 | // token 中间件 在请求过来时统一判断 token 是否失效
5 | module.exports = function(options) {
6 | // 备用参数
7 | options = options || {}
8 |
9 | return function(req, res, next) {
10 | if (req.url === "/user/login" || req.url === "/user/register") {
11 | next();
12 | return;
13 | }
14 |
15 | const token = req.body.token || req.query.token || req.headers["x-access-token"];
16 | if (token) {
17 | jwt.verify(token, key, function(err, decoded) {
18 | if (err) {
19 | return res.status(401).json({ errorMsg: "token失效" });
20 | }
21 | req.decoded = decoded;
22 | next();
23 | });
24 | } else {
25 | return res.status(401).json({ errorMsg: "token失效" });
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/chat-buy-react/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 | const jest = require('jest');
19 | const argv = process.argv.slice(2);
20 |
21 | // Watch unless on CI or in coverage mode
22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) {
23 | argv.push('--watch');
24 | }
25 |
26 |
27 | jest.run(argv);
28 |
--------------------------------------------------------------------------------
/chat-buy-react/server/chat-model.js:
--------------------------------------------------------------------------------
1 | import mongoose, { Schema } from "mongoose";
2 |
3 | const chatSchema = new Schema({
4 | messageId: String,
5 | // 聊天双方
6 | bothSide: [
7 | {
8 | user: {
9 | type: Schema.Types.ObjectId
10 | },
11 | name: {
12 | type: String
13 | },
14 | lastId: {
15 | type: String
16 | }
17 | }
18 | ],
19 | messages: [
20 | {
21 | // 发送方
22 | from: {
23 | type: Schema.Types.ObjectId,
24 | ref: "user"
25 | },
26 | // 接收方
27 | to: {
28 | type: Schema.Types.ObjectId,
29 | ref: "user"
30 | },
31 | // 发送的消息
32 | message: String,
33 | // 发送日期
34 | date: { type: Date, default: Date.now }
35 | }
36 | ]
37 | });
38 |
39 | const chat = mongoose.model("chat", chatSchema);
40 | export default chat;
--------------------------------------------------------------------------------
/chat-buy-react/src/container/allOrders.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import List from "../components/allOrders/list";
3 | import { connect } from "react-redux";
4 | import { getAllOrders, getOrder } from "../actions/order";
5 |
6 | @connect(state => ({ order: state.get("order") }), {
7 | getAllOrders,
8 | getOrder
9 | })
10 | class AllOrders extends React.Component {
11 | componentDidMount() {
12 | this.props.getAllOrders();
13 | }
14 | render() {
15 | const { order, getOrder } = this.props;
16 | const allOrders = order.get("allOrders");
17 | return (
18 |
19 |
23 |
24 | )
25 | }
26 | }
27 |
28 | export default AllOrders;
29 |
--------------------------------------------------------------------------------
/chat-buy-react/config/polyfills.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof Promise === 'undefined') {
4 | // Rejection tracking prevents a common issue where React gets into an
5 | // inconsistent state due to an error, but it gets swallowed by a Promise,
6 | // and the user has no idea what causes React's erratic future behavior.
7 | require('promise/lib/rejection-tracking').enable();
8 | window.Promise = require('promise/lib/es6-extensions.js');
9 | }
10 |
11 | // fetch() polyfill for making API calls.
12 | require('whatwg-fetch');
13 |
14 | // Object.assign() is commonly used with React.
15 | // It will use the native implementation if it's present and isn't buggy.
16 | Object.assign = require('object-assign');
17 |
18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
19 | // We don't polyfill it in the browser--this is user's responsibility.
20 | if (process.env.NODE_ENV === 'test') {
21 | require('raf').polyfill(global);
22 | }
23 |
--------------------------------------------------------------------------------
/chat-buy-react/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { Provider } from "react-redux";
4 | import { AppContainer } from "react-hot-loader";
5 | import App from "./router/router";
6 | import registerServiceWorker from "./registerServiceWorker";
7 | import configureStore from "./store/configureStore";
8 | import "./common/axiosMiddleware";
9 | import "fastclick";
10 | import "./styles/index.scss";
11 |
12 | const store = configureStore();
13 |
14 | function render(Component) {
15 | ReactDOM.render(
16 |
17 |
18 |
19 |
20 | ,
21 | document.getElementById("root")
22 | );
23 | }
24 |
25 | render(App);
26 |
27 | registerServiceWorker();
28 |
29 | if (module.hot) {
30 | module.hot.accept("./router/router", () => {
31 | const NextApp = require("./router/router").default;
32 | render(NextApp);
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/chat-buy-react/server/chat.js:
--------------------------------------------------------------------------------
1 | const model = require("./model");
2 | const Chat = model.chat;
3 |
4 | module.exports = function(app) {
5 | // 获取所有聊天信息
6 | app.post("/chat/getMessageList", function(req, res) {
7 | const { id } = req.decoded;
8 | Chat.find({
9 | messageId: { $regex: id }
10 | }).exec(function(err, doc) {
11 | if (err) {
12 | return res.status(500).json({ msg: "后端出错" });
13 | }
14 | return res.status(200).json({ code: 0, data: doc });
15 | });
16 | });
17 | // 清除未读消息
18 | app.post("/chat/cleanNoRead", function(req, res) {
19 | const { id } = req.decoded;
20 | const { messageId, readId } = req.body;
21 | Chat.findOneAndUpdate(
22 | { "bothSide.user": id, messageId },
23 | { $set: { "bothSide.$.lastId": readId } },
24 | function(error, result) {
25 | if (error || !result) {
26 | return res.status(500).json({ msg: "后端出错" });
27 | }
28 | return res.json({ code: 0 });
29 | }
30 | );
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/message/chatList.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ChatListItem from "./chatListItem";
3 | import ImmutablePropTypes from "react-immutable-proptypes";
4 | import PropTypes from "prop-types";
5 |
6 | class ChatList extends React.PureComponent {
7 | componentDidUpdate() {
8 | window.scrollTo(0, document.body.scrollHeight);
9 | }
10 | render() {
11 | const { currentChatList, userId } = this.props;
12 | console.log('render')
13 | return (
14 |
15 |
16 | {!currentChatList.isEmpty() &&
17 | currentChatList.map(v => (
18 |
23 | ))}
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | ChatList.propTypes = {
31 | currentChatList: ImmutablePropTypes.list.isRequired,
32 | userId: PropTypes.string.isRequired
33 | };
34 |
35 | export default ChatList;
36 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/goods.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import { getGoodsInfo, addToCart, buy } from "../actions/goods";
4 | import GoodsList from "../components/goods/goodsList";
5 | import Buy from "../components/goods/buy";
6 |
7 | @connect(state => ({ goods: state.get("goods") }), {
8 | getGoodsInfo,
9 | addToCart,
10 | buy
11 | })
12 | class Goods extends React.Component {
13 | componentDidMount() {
14 | if (this.props.goods.get("goodsList").isEmpty()) {
15 | this.props.getGoodsInfo();
16 | }
17 | }
18 | render() {
19 | const { goods, addToCart, buy } = this.props;
20 | const goodsList = goods.get("goodsList");
21 | return goodsList.isEmpty() ? null : (
22 |
23 |
28 |
29 |
30 | );
31 | }
32 | }
33 |
34 | export default Goods;
35 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/login.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LoginForm from "../components/login/loginForm";
3 | import { connect } from "react-redux";
4 | import { login } from "../actions/user";
5 |
6 | @connect(null, { login })
7 | class Login extends React.Component {
8 | constructor() {
9 | super();
10 | this.state = {
11 | user: "",
12 | pwd: ""
13 | };
14 | this.handleTextChange = this.handleTextChange.bind(this);
15 | this.handleLogin = this.handleLogin.bind(this);
16 | this.handlePush = this.handlePush.bind(this);
17 | }
18 | handleTextChange(key, value) {
19 | this.setState({
20 | [key]: value
21 | });
22 | }
23 | handleLogin() {
24 | this.props.login(this.state);
25 | }
26 | handlePush() {
27 | this.props.history.push("register");
28 | }
29 | render() {
30 | return (
31 |
32 |
37 |
38 | );
39 | }
40 | }
41 |
42 | export default Login;
43 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/register.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import RegisterForm from "../components/register/registerForm";
3 | import { connect } from "react-redux";
4 | import { register } from "../actions/user";
5 |
6 | @connect(null, { register })
7 | class Register extends React.Component {
8 | constructor() {
9 | super();
10 | this.state = {
11 | user: "",
12 | pwd: "",
13 | type: "deliver"
14 | };
15 | this.handleTextChange = this.handleTextChange.bind(this);
16 | this.handleRegister = this.handleRegister.bind(this);
17 | this.radioData = [
18 | { type: "deliver", text: "送货员" },
19 | { type: "customer", text: "顾客" }
20 | ];
21 | }
22 | handleTextChange(key, value) {
23 | this.setState({
24 | [key]: value
25 | });
26 | }
27 | handleRegister() {
28 | this.props.register(this.state);
29 | }
30 | render() {
31 | return (
32 |
33 |
39 |
40 | );
41 | }
42 | }
43 |
44 | export default Register;
45 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/login/loginForm.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { List, InputItem, WingBlank, WhiteSpace, Button } from "antd-mobile";
3 | import PropTypes from "prop-types";
4 |
5 | const LoginForm = ({ login, push, handleTextChange }) => (
6 |
7 |
8 |
9 | handleTextChange("user", v)}>
10 | 用户名
11 |
12 |
13 | handleTextChange("pwd", v)}>密码
14 |
15 |
16 |
19 |
20 |
21 |
24 |
27 |
28 |
29 |
30 | );
31 |
32 | LoginForm.propTypes = {
33 | login: PropTypes.func.isRequired,
34 | push: PropTypes.func.isRequired,
35 | handleTextChange: PropTypes.func.isRequired
36 | };
37 |
38 | export default LoginForm;
39 |
--------------------------------------------------------------------------------
/chat-buy-react/server/db.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | import config from "./config";
3 |
4 | // Control the number of connections to the mongonDB
5 | let retryCount = 1;
6 |
7 | // mongoose.Promise = global.Promise;
8 | mongoose.Promise = require("bluebird");
9 |
10 | const connections = mongoose.connection;
11 | const logger = console;
12 |
13 | connections.on('connecting', () => {
14 | logger.info('connecting to mongonDB...', config.DEV_DB_URL)
15 | })
16 | connections.on('error', (err) => {
17 | logger.error('Error in connecting mongonDB', config.DEV_DB_URL, err)
18 | })
19 | connections.on('connected', () => {
20 | logger.info("mongonDB connected", config.DEV_DB_URL)
21 | })
22 | connections.once('open', function() {
23 | logger.info('MongoDB connection opened!', config.DEV_DB_URL);
24 | });
25 | connections.on('reconnected', function () {
26 | logger.info('MongoDB reconnected!', config.DEV_DB_URL);
27 | });
28 | connections.on('disconnected', function() {
29 | logger.info('MongoDB disconnected!', config.DEV_DB_URL);
30 | // auto reconnect
31 | if (retryCount < 100) {
32 | mongoose.connect(config.DEV_DB_URL, { useMongoClient: true });
33 | retryCount ++;
34 | }
35 | });
36 | mongoose.connect(config.DEV_DB_URL, { useMongoClient: true });
37 |
--------------------------------------------------------------------------------
/chat-buy-react/server/foods.json:
--------------------------------------------------------------------------------
1 | {
2 | "list": [
3 | {
4 | "name": "招牌双拼套餐",
5 | "price": 59,
6 | "id": 1266111989
7 | },
8 | {
9 | "name": "秘制手枪腿饭",
10 | "price": 37,
11 | "id": 1249852143
12 | },
13 | {
14 | "name": "南洋咖喱鸡肉饭套餐",
15 | "price": 36,
16 | "id": 1250432023
17 | },
18 | {
19 | "name": "招牌卤肉拼蜜汁鸡腿排",
20 | "price": 40,
21 | "id": 1250491784
22 | },
23 | {
24 | "name": "照烧鸡腿排拼卤肉",
25 | "price": 60,
26 | "id": 1290660345
27 | },
28 | {
29 | "name": "新奥尔良烤翅中饭",
30 | "price": 45,
31 | "id": 1250405263
32 | },
33 | {
34 | "name": "香菇滑鸡饭",
35 | "price": 49,
36 | "id": 1265013352
37 | },
38 | {
39 | "name": "秘制手枪腿饭",
40 | "price": 42,
41 | "id": 1249852129
42 | },
43 | {
44 | "name": "矿泉水",
45 | "price": 4,
46 | "id": 1249953392
47 | },
48 | {
49 | "name": "鲜果橙",
50 | "price": 8,
51 | "id": 1249953999
52 | },
53 | {
54 | "name": "招牌卤肉饭",
55 | "price": 65,
56 | "id": 1249846564
57 | },
58 | {
59 | "name": "招牌卤肉拼鱼香肉丝",
60 | "price": 52,
61 | "id": 1250481334
62 | }
63 | ]
64 | }
--------------------------------------------------------------------------------
/chat-buy-react/src/components/register/registerForm.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | List,
4 | InputItem,
5 | WingBlank,
6 | WhiteSpace,
7 | Button,
8 | Radio
9 | } from "antd-mobile";
10 | import PropTypes from "prop-types";
11 |
12 | const RegisterForm = ({ register, radioData, handleTextChange, type }) => (
13 |
14 |
15 |
16 | handleTextChange("user", v)}>
17 | 用户名
18 |
19 |
20 | handleTextChange("pwd", v)}>密码
21 |
22 | {radioData.map(i => (
23 | handleTextChange("type", i.type)}
27 | >
28 | {i.text}
29 |
30 | ))}
31 |
32 |
33 |
36 |
37 |
38 | );
39 |
40 | RegisterForm.propTypes = {
41 | register: PropTypes.func.isRequired,
42 | radioData: PropTypes.array.isRequired,
43 | handleTextChange: PropTypes.func.isRequired,
44 | type: PropTypes.string.isRequired
45 | };
46 |
47 | export default RegisterForm;
48 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/myOrder.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import { getMyOrders } from "../actions/user";
4 | import { affirmOrder } from "../actions/order";
5 | import MyOrderItem from "../components/myOrder/myOrderItem";
6 | import NavBar from "../components/navBar/backNavBar";
7 |
8 | @connect(state => ({ user: state.get("user") }), { getMyOrders, affirmOrder })
9 | class MyOrder extends React.Component {
10 | constructor() {
11 | super();
12 | this.handleChat = this.handleChat.bind(this);
13 | }
14 | componentDidMount() {
15 | this.props.getMyOrders();
16 | }
17 | handleChat(id) {
18 | this.props.history.push(`/chat/${id}`);
19 | }
20 | render() {
21 | const { history, affirmOrder, user } = this.props;
22 | return (
23 |
24 |
25 |
26 | {user
27 | .get("orders")
28 | .map(v => (
29 |
37 | ))}
38 |
39 |
40 | );
41 | }
42 | }
43 |
44 | export default MyOrder;
45 |
--------------------------------------------------------------------------------
/chat-buy-react/src/actions/goods.js:
--------------------------------------------------------------------------------
1 | import { GOODS_LIST, ADD_TO_CART, BUY_SUCCESS } from "./type";
2 | import { Toast } from "antd-mobile";
3 | import axios from "axios";
4 | import { fromJS } from "immutable";
5 |
6 | // 获取商品列表
7 | export function getGoodsInfo() {
8 | return async dispatch => {
9 | try {
10 | const res = await axios.get("/goods/list");
11 | if (res.status === 200 && res.data.code === 0) {
12 | dispatch({ type: GOODS_LIST, payload: fromJS(res.data.data) });
13 | }
14 | } catch (error) {
15 | console.log(error)
16 | }
17 | };
18 | }
19 |
20 | // 添加商品到购物车
21 | export function addToCart({ id, price, count }) {
22 | if (!Number.isInteger(count)) {
23 | count = 0;
24 | }
25 | if (count > 99) {
26 | count = 99;
27 | }
28 | if (count < 0) {
29 | count = 0;
30 | }
31 |
32 | return { type: ADD_TO_CART, payload: { id, price, count } };
33 | }
34 |
35 | // 购买
36 | export function buy() {
37 | return async (dispatch, state) => {
38 | try {
39 | const res = await axios.post("/goods/buy", {
40 | buyList: state()
41 | .get("goods")
42 | .get("shopCart")
43 | .toJS()
44 | });
45 | if (res.status === 200 && res.data.code === 0) {
46 | Toast.success("购买成功", 1);
47 | dispatch({ type: BUY_SUCCESS });
48 | }
49 | } catch (error) {
50 | console.log(error)
51 | }
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/chat-buy-react/src/container/message.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MessageList from "../components/message/messageList";
3 | import { setCurrentChatList } from "../actions/chat";
4 | import { connect } from "react-redux";
5 |
6 | @connect(state => ({ chat: state.get("chat") }), { setCurrentChatList })
7 | class Message extends React.Component {
8 | constructor() {
9 | super();
10 | this.handlePush = this.handlePush.bind(this);
11 | this.getSideObj = this.getSideObj.bind(this);
12 | }
13 | // 找出聊天对象昵称
14 | getSideObj(userId, bothSide) {
15 | return bothSide.find(v => v.get("user") !== userId);
16 | }
17 | handlePush(value) {
18 | const { chat, setCurrentChatList, history } = this.props;
19 | const userId = chat.get("userId");
20 | setCurrentChatList(value);
21 | history.push(
22 | `/chat/${this.getSideObj(userId, value.get("bothSide")).get("user")}`
23 | );
24 | }
25 | render() {
26 | const { chat } = this.props;
27 | const messageList = chat.get("messageList");
28 | const userId = chat.get("userId");
29 | const noReadCounts = chat.get("noReadCounts");
30 | return (
31 |
32 |
39 |
40 | );
41 | }
42 | }
43 |
44 | export default Message;
45 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/goods/goodsList.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { List, Stepper } from "antd-mobile";
3 | import ImmutablePropTypes from "react-immutable-proptypes";
4 | import PropTypes from "prop-types";
5 |
6 | const Item = List.Item;
7 | const Brief = Item.Brief;
8 |
9 | function getCount(shopCart, id) {
10 | let obj = shopCart.find(value => {
11 | return id === value.get("id");
12 | });
13 | return obj ? obj.get("count") : 0;
14 | }
15 |
16 | const GoodsList = ({ goodsList, addToCart, shopCart }) => {
17 | return (
18 |
19 | {goodsList.map(v => (
20 | -
31 | addToCart({ id: v.get("id"), price: v.get("price"), count })
32 | }
33 | />
34 | }
35 | >
36 | {v.get("name")} ¥{v.get("price")}
37 |
38 | ))}
39 |
40 | );
41 | };
42 |
43 | GoodsList.propTypes = {
44 | goodsList: ImmutablePropTypes.list.isRequired,
45 | addToCart: PropTypes.func.isRequired,
46 | shopCart: ImmutablePropTypes.list.isRequired
47 | };
48 |
49 | export default GoodsList;
50 |
--------------------------------------------------------------------------------
/chat-buy-react/server/goods.js:
--------------------------------------------------------------------------------
1 |
2 | const model = require("./model");
3 | const foods = require("./foods.json");
4 | const User = model.user;
5 | const AllOrders = model.allOrders;
6 |
7 |
8 | module.exports = function(app) {
9 | // 获取商品列表
10 | app.get("/goods/list", function(req, res) {
11 | return res.json({ code: 0, data: foods.list });
12 | });
13 | // 购买商品
14 | app.post("/goods/buy", function(req, res) {
15 | const { id } = req.decoded;
16 | const { buyList } = req.body;
17 | let price = 0;
18 | let count = 0;
19 | let desc = "";
20 | buyList.forEach(v => {
21 | price += v.price * v.count;
22 | count += v.count;
23 | if (!desc) {
24 | desc = foods.list.find(food => {
25 | return food.id === v.id;
26 | }).name;
27 | }
28 | });
29 | const order = {
30 | price,
31 | count,
32 | state: 0,
33 | desc,
34 | customer: id
35 | };
36 | const model = new AllOrders(order);
37 | model.save(function(error, data) {
38 | if (error || !data) {
39 | return res.status(500).json({ msg: "后端出错" });
40 | }
41 |
42 | User.update(
43 | { _id: id },
44 | {
45 | $push: {
46 | orders: data._id
47 | }
48 | },
49 | function(e, user) {
50 | if (e || !user) {
51 | return res.status(500).json({ msg: "后端出错" });
52 | }
53 |
54 | return res.status(200).json({ code: 0, msg: "购买成功" });
55 | }
56 | );
57 | });
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/chat-buy-react/src/components/message/messageList.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { List } from "antd-mobile";
3 | import PropTypes from "prop-types";
4 | import ImmutablePropTypes from "react-immutable-proptypes";
5 |
6 | const Item = List.Item;
7 | const Brief = Item.Brief;
8 |
9 | const MessageList = ({
10 | messageList,
11 | noReadCounts,
12 | push,
13 | userId,
14 | getSideObj
15 | }) => (
16 |
17 | {!!messageList.size && (
18 |
19 | {messageList.map((v, index) => (
20 | -
26 | {noReadCounts.get(index)}
27 |
28 | )
29 | }
30 | onClick={() => {
31 | push(v);
32 | }}
33 | >
34 | {getSideObj(userId, v.get("bothSide")).get("name")}
35 |
36 | {v
37 | .get("messages")
38 | .get(-1)
39 | .get("message")}
40 |
41 |
42 | ))}
43 |
44 | )}
45 |
46 | );
47 |
48 | MessageList.propTypes = {
49 | messageList: ImmutablePropTypes.list.isRequired,
50 | noReadCounts: ImmutablePropTypes.list.isRequired,
51 | getSideObj: PropTypes.func.isRequired,
52 | push: PropTypes.func.isRequired,
53 | userId: PropTypes.string.isRequired
54 | };
55 |
56 | export default MessageList;
57 |
--------------------------------------------------------------------------------
/chat-buy-react/src/actions/order.js:
--------------------------------------------------------------------------------
1 | import { GET_ALL_ORDERS, AFFIRM_ORDER, GET_ORDER_SUCCESS } from "./type";
2 |
3 | import { Toast } from "antd-mobile";
4 | import axios from "axios";
5 | import history from "../common/history";
6 | import { fromJS } from "immutable";
7 |
8 | export function getAllOrders() {
9 | return async dispatch => {
10 | try {
11 | const res = await axios.get("/order/allOrders");
12 | if (res.status === 200 && res.data.code === 0) {
13 | dispatch({ type: GET_ALL_ORDERS, payload: fromJS(res.data.data) });
14 | }
15 | } catch (error) {
16 | console.log(error)
17 | }
18 | };
19 | }
20 |
21 | export function getOrderSuccess(data) {
22 | return { type: GET_ORDER_SUCCESS, payload: data };
23 | }
24 |
25 | export function affirmOrderSuccess(id) {
26 | return { type: AFFIRM_ORDER, payload: id };
27 | }
28 |
29 | export function getOrder(orderId) {
30 | return async dispatch => {
31 | try {
32 | const res = await axios.post("/order/getOrder", { orderId });
33 | if (res.status === 200 && res.data.code === 0) {
34 | history.push("/me/orders");
35 | Toast.success("接单成功", 1);
36 | }
37 | } catch (error) {
38 | console.log(error)
39 | }
40 | };
41 | }
42 |
43 | export function affirmOrder(orderId) {
44 | return async dispatch => {
45 | try {
46 | const res = await axios.post("/order/affirm", { orderId });
47 | if (res.status === 200 && res.data.code === 0) {
48 | Toast.success("确认订单成功", 1);
49 | dispatch(affirmOrderSuccess(orderId));
50 | }
51 | } catch (error) {
52 | console.log(error)
53 | }
54 | };
55 | }
56 |
--------------------------------------------------------------------------------
/chat-buy-react/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/chat-buy-react/src/common/axiosMiddleware.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { Toast } from "antd-mobile";
3 | import configureStore from "../store/configureStore";
4 | import { logout } from "../actions/user";
5 |
6 | const store = configureStore();
7 |
8 | axios.defaults.timeout = 5000;
9 | // 超时重试次数和间隔
10 | axios.defaults.maxRetryCount = 2;
11 | axios.defaults.delay = 1000;
12 |
13 | axios.interceptors.request.use(config => {
14 | Toast.loading(config.loadText || "正在加载", 0);
15 | if (window.localStorage.getItem("token")) {
16 | config.headers["x-access-token"] = window.localStorage.getItem("token");
17 | }
18 | return config;
19 | });
20 |
21 | axios.interceptors.response.use(
22 | response => {
23 | Toast.hide();
24 | const data = response.data;
25 | if (data.code === 1) {
26 | Toast.fail(data.msg, 1);
27 | }
28 | return response;
29 | },
30 | error => {
31 | Toast.hide();
32 | if (error.response) {
33 | switch (error.response.status) {
34 | case 401:
35 | store.dispatch(logout());
36 | break;
37 | case 500:
38 | Toast.fail(error.response.data.msg || "服务器出错啦", 1);
39 | break;
40 | default:
41 | break;
42 | }
43 | return Promise.reject(error);
44 | } else {
45 | // 请求超时重试机制
46 | Toast.hide();
47 | const config = error.config;
48 | config.loadText = "请求超时,正在重试";
49 | console.log(error);
50 | if (!config || !config.maxRetryCount) return Promise.reject(error);
51 | config.retryCount = config.retryCount || 0;
52 | if (config.retryCount >= config.maxRetryCount) {
53 | Toast.fail("还是超时,不请求了", 2);
54 | return Promise.reject(error);
55 | }
56 | config.retryCount += 1;
57 | const request = new Promise(resolve => {
58 | setTimeout(() => resolve(), config.delay || 1);
59 | });
60 | return request.then(() => {
61 | axios(config);
62 | });
63 | }
64 | }
65 | );
66 |
--------------------------------------------------------------------------------
/chat-buy-react/src/reducers/goods.js:
--------------------------------------------------------------------------------
1 | import { GOODS_LIST, ADD_TO_CART, BUY_SUCCESS } from "../actions/type";
2 | import { List, Map } from "immutable";
3 |
4 | const initialState = Map({
5 | // 当前商品列表
6 | goodsList: List([]),
7 | // 购物车
8 | shopCart: List([]),
9 | // 总价
10 | totalPrice: 0
11 | });
12 |
13 | /**
14 | * @param {} shopCart 当前购物车
15 | * @param {} {id 商品 ID
16 | * @param {} price 商品价格
17 | * @param {} count} 商品件数
18 | * @param {} totalPrice 当前总价
19 | */
20 | function changeShopCart(shopCart, { id, price, count }, totalPrice) {
21 | // 在当前购物车寻找是否已经添加过该商品
22 | const index = shopCart.findIndex(item => item.get("id") === id);
23 | if (index === -1) {
24 | // 判断当前购物车是否找到该商品,没有就 push 商品,并修改总价
25 | if (count === 0) {
26 | return [shopCart, totalPrice];
27 | }
28 | shopCart = shopCart.push(Map({ id, price, count }));
29 | totalPrice += price * count;
30 | return [shopCart, totalPrice];
31 | }
32 | shopCart = shopCart.update(index, product => {
33 | // 如果在购物车中找到该商品,修改总价
34 | const currentCount = product.get("count");
35 | if (currentCount > count) {
36 | totalPrice = totalPrice - (currentCount - count) * price;
37 | } else {
38 | totalPrice = totalPrice + (count - currentCount) * price;
39 | }
40 | return product.set("count", count);
41 | });
42 | if (count === 0) {
43 | // 判断如果该商品件数等于0,就删除购物中的当前商品
44 | shopCart = shopCart.remove(index);
45 | return [shopCart, totalPrice];
46 | } else {
47 | return [shopCart, totalPrice];
48 | }
49 | }
50 |
51 | export default function(state = initialState, action) {
52 | switch (action.type) {
53 | case GOODS_LIST:
54 | return state.set("goodsList", action.payload);
55 | case ADD_TO_CART:
56 | let data = changeShopCart(
57 | state.get("shopCart"),
58 | action.payload,
59 | state.get("totalPrice")
60 | );
61 | return state.merge({ shopCart: data[0], totalPrice: data[1] });
62 | case BUY_SUCCESS:
63 | return state.merge({ shopCart: List([]), totalPrice: 0 });
64 | default:
65 | break;
66 | }
67 | return state;
68 | }
69 |
--------------------------------------------------------------------------------
/chat-buy-react/src/reducers/chat.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_USERNAME,
3 | GET_MESSAGE,
4 | GET_MESSAGE_LIST,
5 | SET_CURRENT_LIST,
6 | CLEAN_NO_READ
7 | } from "../actions/type";
8 | import { Map, List } from "immutable";
9 |
10 | const initialState = Map({
11 | // 当前聊天对象昵称
12 | chatUserName: "",
13 | // 当前聊天列表
14 | currentChatList: List([]),
15 | // 当前聊天 ID
16 | currentMessageId: "",
17 | // 所有消息列表
18 | messageList: List([]),
19 | // 当前用户 ID
20 | userId: "",
21 | // 未读消息总数
22 | noReadCount: 0,
23 | // 未读消息数组,对应每个消息
24 | noReadCounts: List([])
25 | });
26 |
27 | // 给消息列表按照时间顺序排序
28 | function sortMessageList(list) {
29 | return list.sort((a, b) => {
30 | return (
31 | a
32 | .get("messages")
33 | .get(-1)
34 | .get("date") <
35 | b
36 | .get("messages")
37 | .get(-1)
38 | .get("date")
39 | );
40 | });
41 | }
42 |
43 | export default function(state = initialState, action) {
44 | switch (action.type) {
45 | case GET_USERNAME:
46 | return state.merge({ userName: action.payload });
47 | case GET_MESSAGE:
48 | return state.merge({
49 | messageList: action.messageList,
50 | noReadCount: state.get("noReadCount") + action.isNoRead,
51 | currentChatList: action.currentChatList,
52 | noReadCounts: action.noReadCounts
53 | });
54 | case GET_MESSAGE_LIST:
55 | return state.merge({
56 | messageList: sortMessageList(action.payload),
57 | userId: action.userId,
58 | noReadCounts: action.noReadCounts,
59 | noReadCount: action.noReadCounts.reduce((sum, value) => sum + value, 0)
60 | });
61 | case SET_CURRENT_LIST:
62 | return state.merge({
63 | currentChatList: action.payload
64 | ? action.payload.get("messages")
65 | : List([]),
66 | currentMessageId: action.messageId
67 | });
68 | case CLEAN_NO_READ:
69 | return state.merge({
70 | noReadCounts: action.noReadCounts,
71 | noReadCount: state.get("noReadCount") - action.readCount
72 | });
73 | default:
74 | return state;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/chat-buy-react/src/reducers/user.js:
--------------------------------------------------------------------------------
1 | import {
2 | REGISTER,
3 | LOGIN,
4 | GET_INFO,
5 | GET_MY_ORDERS,
6 | AFFIRM_ORDER,
7 | GET_ORDER_SUCCESS
8 | } from "../actions/type";
9 | import { List, Map } from "immutable";
10 |
11 | function changeOrderState(orders, id, state, userId) {
12 | return orders.update(orders.findIndex(v => v.get("_id") === id), order => {
13 | // 如果该订单被接单,需要手动加上外卖员的 ID 用以聊天
14 | if (userId) {
15 | return order.set("state", state).set("deliver", userId);
16 | } else {
17 | return order.set("state", state);
18 | }
19 | });
20 | }
21 |
22 | const initialState = Map({
23 | // 用户名
24 | user: "",
25 | // 用户类型
26 | type: "",
27 | // 用户 ID
28 | id: "",
29 | // 用户可以跳转的路由
30 | path: "",
31 | // 我的所有订单
32 | orders: List([])
33 | });
34 |
35 | export default function(state = initialState, action) {
36 | switch (action.type) {
37 | case REGISTER:
38 | return state.merge({
39 | ...action.payload,
40 | path: action.payload.type === "customer" ? "/goods" : "/allOrders"
41 | });
42 | case LOGIN:
43 | console.log(
44 | state.merge({
45 | ...action.payload,
46 | path: action.payload.type === "customer" ? "/goods" : "/allOrders"
47 | })
48 | );
49 | return state.merge({
50 | ...action.payload,
51 | path: action.payload.type === "customer" ? "/goods" : "/allOrders"
52 | });
53 | case GET_INFO:
54 | return state.merge({
55 | ...action.payload,
56 | path: action.payload.type === "customer" ? "/goods" : "/allOrders"
57 | });
58 | case GET_MY_ORDERS:
59 | return state.set("orders", action.payload);
60 | case AFFIRM_ORDER:
61 | return state.set(
62 | "orders",
63 | changeOrderState(state.get("orders"), action.payload, 2)
64 | );
65 | case GET_ORDER_SUCCESS:
66 | return state.set(
67 | "orders",
68 | changeOrderState(
69 | state.get("orders"),
70 | action.payload.orderId,
71 | 1,
72 | action.payload.id
73 | )
74 | );
75 | default:
76 | break;
77 | }
78 | return state;
79 | }
80 |
--------------------------------------------------------------------------------
/chat-buy-react/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right