├── .editorconfig ├── .eslintignore ├── .eslintrc-backup ├── .gitignore ├── .webpackrc ├── README.md ├── dist ├── index.css ├── index.html ├── index.js ├── router.css ├── router.js └── static │ └── yay.44dd3333.jpg ├── docs └── images │ ├── bill.png │ ├── index.png │ ├── order.png │ ├── project.png │ ├── resource.png │ └── supplierBill.png ├── mock └── example.js ├── package-lock.json ├── package.json ├── proxy.config.js ├── service ├── app.js ├── bin │ └── www ├── constants │ └── constants.js ├── models │ ├── customers.js │ ├── orders.js │ ├── productStocks.js │ ├── products.js │ ├── settlement.js │ ├── storage.js │ ├── suppliers.js │ └── user.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── stylesheets │ │ └── style.css ├── routes │ ├── auth.js │ ├── customerBills.js │ ├── customers.js │ ├── index.js │ ├── orders.js │ ├── productStocks.js │ ├── products.js │ ├── resource.js │ ├── settlement.js │ ├── storage.js │ ├── supplierBills.js │ ├── suppliers.js │ ├── system.js │ ├── uploadProductImg.js │ └── users.js ├── tmp │ └── .ignore ├── utils │ └── utils.js └── views │ ├── error.jade │ ├── index.jade │ └── layout.jade ├── src ├── assets │ └── yay.jpg ├── components │ ├── BreadcrumbList │ │ ├── BreadcrumbList.jsx │ │ └── index.css │ ├── CustomerBills │ │ ├── ClearCustomerBillsModal │ │ │ ├── ClearCustomerBillsModal.jsx │ │ │ └── index.css │ │ ├── ClearDebtOrdersModal │ │ │ ├── ClearDebtOrdersModal.jsx │ │ │ └── index.css │ │ ├── CustomerBillsList │ │ │ ├── CustomerBillsList.jsx │ │ │ └── index.css │ │ ├── CustomerBillsSearchForm │ │ │ ├── CustomerBillsSearchForm.jsx │ │ │ └── index.css │ │ └── DebtOrdersList │ │ │ ├── DebtOrdersList.jsx │ │ │ └── index.css │ ├── Customers │ │ ├── AddCustomer │ │ │ ├── AddCustomer.jsx │ │ │ └── index.css │ │ ├── CustomerCommon │ │ │ ├── CustomerForm │ │ │ │ ├── CustomerForm.jsx │ │ │ │ └── index.css │ │ │ └── CustomerTitle │ │ │ │ ├── CustomerTitle.jsx │ │ │ │ └── index.css │ │ └── CustomerList │ │ │ ├── CustomerList.jsx │ │ │ └── index.css │ ├── EditableCell │ │ ├── EditableCell.jsx │ │ └── index.css │ ├── Example.js │ ├── Funds │ │ ├── FundsList │ │ │ ├── FundsList.jsx │ │ │ └── index.css │ │ └── FundsSearchForm │ │ │ ├── FundsSearchForm.js │ │ │ └── index.css │ ├── Header │ │ ├── Header.jsx │ │ └── index.css │ ├── ListEditableCell │ │ ├── ListEditableCell.jsx │ │ └── index.css │ ├── LoginModal │ │ ├── LoginModal.jsx │ │ └── index.css │ ├── LogupModal │ │ ├── LogupModal.jsx │ │ └── index.css │ ├── NavLink │ │ ├── NavLink.jsx │ │ └── index.css │ ├── Orders │ │ ├── AddOrder │ │ │ ├── AddOrder.jsx │ │ │ └── index.css │ │ ├── ModifyOrder │ │ │ ├── ModifyOrder.jsx │ │ │ └── index.css │ │ ├── OrderCommon │ │ │ ├── AddOrderGrid │ │ │ │ ├── AddOrderGrid.jsx │ │ │ │ └── index.css │ │ │ ├── OrderForm │ │ │ │ ├── OrderForm.jsx │ │ │ │ └── index.css │ │ │ ├── OrderRemarkForm │ │ │ │ ├── OrderRemarkForm.jsx │ │ │ │ └── index.css │ │ │ └── OrderTitle │ │ │ │ ├── OrderTitle.jsx │ │ │ │ └── index.css │ │ ├── OrderList │ │ │ ├── OrderList.jsx │ │ │ └── index.css │ │ └── OrderSearchForm │ │ │ ├── OrderSearchForm.jsx │ │ │ └── index.css │ ├── Products │ │ ├── AddProduct │ │ │ ├── AddProduct.jsx │ │ │ └── index.css │ │ ├── ProductCommon │ │ │ ├── ProductForm │ │ │ │ ├── ProductForm.jsx │ │ │ │ └── index.css │ │ │ └── ProductTitle │ │ │ │ ├── ProductTitle.jsx │ │ │ │ └── index.css │ │ └── ProductList │ │ │ ├── ProductList.jsx │ │ │ └── index.css │ ├── Resource │ │ └── ResourceSearchForm │ │ │ ├── ResourceSearchForm.js │ │ │ └── index.css │ ├── SearchBar │ │ ├── SearchBar.jsx │ │ └── index.css │ ├── SearchForm │ │ ├── SearchForm.jsx │ │ └── index.css │ ├── Settlement │ │ ├── SettlementList │ │ │ ├── SettlementList.jsx │ │ │ └── index.css │ │ └── SettlementSearchForm │ │ │ ├── SettlementSearchForm.jsx │ │ │ └── index.css │ ├── Spliter │ │ ├── Spliter.jsx │ │ └── index.css │ ├── Stocks │ │ ├── StockList │ │ │ ├── StockList.jsx │ │ │ └── index.css │ │ └── StockSearchForm │ │ │ ├── StockSearchForm.js │ │ │ └── index.css │ ├── Storage │ │ ├── AddStorage │ │ │ ├── AddStorage.jsx │ │ │ └── index.css │ │ ├── ModifyStorage │ │ │ ├── ModifyStorage.jsx │ │ │ └── index.css │ │ ├── StorageCommon │ │ │ ├── AddStorageGrid │ │ │ │ ├── AddStorageGrid.jsx │ │ │ │ └── index.css │ │ │ ├── StorageForm │ │ │ │ ├── StorageForm.jsx │ │ │ │ └── index.css │ │ │ ├── StorageRemarkForm │ │ │ │ ├── StorageRemarkForm.jsx │ │ │ │ └── index.css │ │ │ └── StorageTitle │ │ │ │ ├── StorageTitle.jsx │ │ │ │ └── index.css │ │ ├── StorageList │ │ │ ├── StorageList.jsx │ │ │ └── index.css │ │ └── StorageSearchForm │ │ │ ├── StorageSearchForm.jsx │ │ │ └── index.css │ ├── SupplierBills │ │ ├── ClearDebtStorageModal │ │ │ ├── ClearDebtStorageModal.jsx │ │ │ └── index.css │ │ ├── ClearSupplierBillsModal │ │ │ ├── ClearSupplierBillsModal.jsx │ │ │ └── index.css │ │ ├── DebtStorageList │ │ │ ├── DebtStorageList.jsx │ │ │ └── index.css │ │ ├── SupplierBillsList │ │ │ ├── SupplierBillsList.jsx │ │ │ └── index.css │ │ └── SupplierBillsSearchForm │ │ │ ├── SupplierBillsSearchForm.jsx │ │ │ └── index.css │ ├── Suppliers │ │ ├── AddSupplier │ │ │ ├── AddSupplier.jsx │ │ │ └── index.css │ │ ├── SupplierCommon │ │ │ ├── SupplierForm │ │ │ │ ├── SupplierForm.jsx │ │ │ │ └── index.css │ │ │ └── SupplierTitle │ │ │ │ ├── SupplierTitle.jsx │ │ │ │ └── index.css │ │ └── SupplierList │ │ │ ├── SupplierList.jsx │ │ │ └── index.css │ └── SystemInfo │ │ ├── SystemInfo.jsx │ │ └── index.css ├── constants │ └── constants.js ├── index.html ├── index.js ├── index.less ├── models │ ├── customerBills.js │ ├── customers.js │ ├── example.js │ ├── funds.js │ ├── home.js │ ├── manage.js │ ├── orders.js │ ├── products.js │ ├── resource.js │ ├── settlement.js │ ├── stocks.js │ ├── storage.js │ ├── supplierBills.js │ ├── suppliers.js │ └── systemUser.js ├── router.js ├── routes │ ├── Bills │ │ ├── Bills.jsx │ │ └── index.css │ ├── CustomerBills │ │ ├── CustomerBills.jsx │ │ └── index.css │ ├── Customers │ │ ├── Customers.jsx │ │ └── index.css │ ├── Funds │ │ ├── Funds.jsx │ │ └── index.css │ ├── HomePage │ │ ├── HomePage.jsx │ │ └── index.css │ ├── IndexPage │ │ ├── IndexPage.js │ │ └── index.css │ ├── Manage │ │ ├── Manage.jsx │ │ └── index.css │ ├── Orders │ │ ├── Orders.jsx │ │ └── index.css │ ├── Products │ │ ├── Products.jsx │ │ └── index.css │ ├── Resource │ │ ├── Resource.jsx │ │ └── index.css │ ├── Settlement │ │ ├── Settlement.jsx │ │ └── index.css │ ├── Stock │ │ ├── Stock.jsx │ │ └── index.css │ ├── Storage │ │ ├── Storage.jsx │ │ └── index.css │ ├── SupplierBills │ │ ├── SupplierBills.jsx │ │ └── index.css │ └── Suppliers │ │ ├── Suppliers.jsx │ │ └── index.css ├── services │ ├── customerBills.js │ ├── customers.js │ ├── example.js │ ├── funds.js │ ├── orders.js │ ├── productStocks.js │ ├── products.js │ ├── resource.js │ ├── serviceConfig.js │ ├── settlement.js │ ├── stocks.js │ ├── storage.js │ ├── supplierBills.js │ ├── suppliers.js │ └── systemUser.js ├── tests │ └── models │ │ └── example-test.js └── utils │ ├── dateFormat.js │ ├── numberFormat.js │ ├── request.js │ └── webSessionUtils.js ├── system.config.js └── upload └── uploadfiles └── .ignore /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/**/*-test.js 2 | -------------------------------------------------------------------------------- /.eslintrc-backup: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "eslint-config-airbnb", 4 | "rules": { 5 | "spaced-comment": [0], 6 | "no-unused-vars": [0], 7 | "no-empty": [0], 8 | "react/wrap-multilines": [0], 9 | "react/no-multi-comp": [0], 10 | "no-constant-condition": [0], 11 | "react/jsx-no-bind": [0], 12 | "react/prop-types": [0], 13 | "arrow-body-style": [0], 14 | "react/prefer-stateless-function": [0], 15 | "semi": [0], 16 | "global-require": [0], 17 | "no-shadow": [0], 18 | "no-useless-computed-key": [0], 19 | "no-underscore-dangle": [0] 20 | }, 21 | "ecmaFeatures": { 22 | "experimentalObjectRestSpread": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | server.config.js 4 | -------------------------------------------------------------------------------- /.webpackrc: -------------------------------------------------------------------------------- 1 | { 2 | "entry": "src/*.js", 3 | "publicPath": "/", 4 | "extraBabelPlugins": [ 5 | ["import", { "libraryName": "antd", "style": "css" }] 6 | ], 7 | "theme": { 8 | "@primary-color": "#1DA57A", 9 | "@link-color": "#1DA57A", 10 | "@border-radius-base": "2px", 11 | "@font-size-base": "16px", 12 | "@line-height-base": "1.2" 13 | }, 14 | "env": { 15 | "development": { 16 | "extraBabelPlugins": [ 17 | "dva-hmr" 18 | ] 19 | }, 20 | "production": { 21 | "extraBabelPlugins": [ 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AccountSystem 一个小型库存管理系统 2 | 3 | ![](https://camo.githubusercontent.com/4c82c2bade9204481be86bfdbc0b773be2c823dd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c616e67756167652d4a6176617363726970742d79656c6c6f772e7376673f7374796c653d666c6174) 4 | ![](https://camo.githubusercontent.com/3a5d997143423893d291af21f6a10bddf6716fd1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c616e67756167652d4e6f64652d677265656e2e7376673f7374796c653d666c6174) 5 | ![](https://camo.githubusercontent.com/c55a47ce085cee081ab8038d88db04e3638fee48/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f44617461626173652d4d6f6e676f44422d677265656e2e7376673f7374796c653d666c6174)
6 | 7 | ![](./docs/images/index.png)
8 | 9 | ## [在线预览](http://mingdi.yvanwang.com/) 10 | 用户名:guest
11 | 密码:123456 12 | 13 | ## 如何安装 14 | 1`.` 请确保安装MongoDB并正确启动( mongodb相关资料请移步[这里](https://docs.mongodb.com/manual/installation/) )
15 | 2`.` 请确保全局安装pm2 `npm install -g pm2`
16 | 3`.` Clone 该项目到本地 `git clone https://github.com/yvanwangl/AccountSystem.git`
17 | 4`.` 安装前端依赖包 `npm install`
18 | 5`.` 安装后端依赖包 `cd service` & `npm install`
19 | 6`.` 该项目采用前后端分离模式开发,如果要在开发模式运行则执行以下命令:
20 |    * 启动node server开发服务 `npm run start:dev`
21 |    * 启动前端dev server `npm start`
22 | 7`.` 如果以发布模式运行则执行以下命令:
23 |    * 前端资源打包 `npm run build`
24 |    * 启动node server部署服务 `npm run start:prod`
25 | 26 | ## 项目技术栈 27 | 前端技术:React + React-Router + Redux + React-Redux + Redux-Saga + Webpack
28 | 前端脚手架:[dva](https://github.com/dvajs/dva)
29 | UI组件库:[ant-design](https://github.com/ant-design/ant-design)
30 | 后端技术:Express + Mongoose
31 | 32 | 该项目采用前后端分离技术,前端使用React全家桶,项目整体框架使用dva,dva是一个将redux、redux-saga 和 react-router 等进行封装的前端框架,方便项目配置及代码管理;后端使用express + mongoose 进行后端业务处理及数据库操作。
33 | 34 | ## 实现功能 35 | 1`.` 登录注册功能
36 | 2`.` 基础数据管理功能,包括:客户管理、商品管理、供应商管理
37 | 3`.` 订单及进货单管理功能,包括:订单管理、入库管理
38 | 4`.` 物资管理功能
39 | 5`.` 结算管理功能
40 | 6`.` 账单管理功能,包括:客户对账管理、供应商对账管理
41 | 42 | ## 项目结构 43 | ![](./docs/images/project.png)
44 | 45 | ## 订单管理 46 | 订单管理包括增加、修改、删除及查看订单详情,可以根据订单生成日期、客户名称及订单编号进行查询,订单编号支持模糊查询
47 | 48 | ![](./docs/images/order.png)
49 | 50 | ## 入库管理 51 | 入库管理包括增加、修改、删除及查看入库单详情的功能,可根据入库单生成日期、供应商名称及入库单编号进行查询,入库单编号支持模糊查询
52 | 53 | ![](./docs/images/bill.png)
54 | 55 | ## 物资管理 56 | 物资管理将仓库库存的物资和资金进行分类统计,可以查看仓库中当前剩余的商品的种类和数量;资金管理从商品分类的角度对系统流出资金和流入资金进行统计汇总,方便查看不同商品对应的资金情况。
57 | 58 | ![](./docs/images/resource.png)
59 | 60 | ## 对账管理——供应商对账 61 | 供应商对账管理从负债入库单和负债供应商两个角度对负债账务进行分类,同时可以根据供应商的名称对负债入库账单和负债供应商进行过滤。从两个角度进行分类统计是为了方便能够按照入库单进行分批清账,或直接向供应商清账,方便账务分类管理
62 | 63 | ![](./docs/images/supplierBill.png)
64 | 65 | 其他一些界面的功能就不再一一介绍,感兴趣的话自己clone一份代码,运行一下便知:)
66 | 67 | #### 欢迎Star!
68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 铭帝系统门窗管理系统 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /dist/static/yay.44dd3333.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/dist/static/yay.44dd3333.jpg -------------------------------------------------------------------------------- /docs/images/bill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/bill.png -------------------------------------------------------------------------------- /docs/images/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/index.png -------------------------------------------------------------------------------- /docs/images/order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/order.png -------------------------------------------------------------------------------- /docs/images/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/project.png -------------------------------------------------------------------------------- /docs/images/resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/resource.png -------------------------------------------------------------------------------- /docs/images/supplierBill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/docs/images/supplierBill.png -------------------------------------------------------------------------------- /mock/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | 'GET /api/login': function (req, res) { 6 | setTimeout(function () { 7 | res.json({ 8 | success: true, 9 | data: ['foo', 'bar'], 10 | }); 11 | }, 500); 12 | }, 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "entry": { 4 | "index": "./src/index.js" 5 | }, 6 | "dependencies": { 7 | "antd": "^2.6.1", 8 | "babel-plugin-import": "^1.1.0", 9 | "dva": "^1.2.1", 10 | "eslint-config-airbnb": "^16.1.0", 11 | "moment": "^2.17.1", 12 | "numeral": "^2.0.6", 13 | "qs": "^6.3.0", 14 | "react": "^15.3.2", 15 | "react-dom": "^15.3.2", 16 | "roadhog": "^2.3.0" 17 | }, 18 | "devDependencies": { 19 | "atool-test-mocha": "^0.1.5", 20 | "babel-plugin-dev-expression": "^0.2.1", 21 | "babel-plugin-dva-hmr": "^0.4.0", 22 | "babel-runtime": "^6.9.2", 23 | "cross-env": "^5.0.5", 24 | "expect": "^1.20.2", 25 | "redbox-react": "^1.3.2" 26 | }, 27 | "scripts": { 28 | "start": "roadhog dev", 29 | "start:prod": "cross-env NODE_ENV=production pm2 start ./service/bin/www --name accountSystem", 30 | "start:dev": "cross-env NODE_ENV=development pm2 start ./service/bin/www", 31 | "build": "cross-env NODE_ENV=production roadhog build", 32 | "test": "atool-test-mocha ./src/**/*-test.js" 33 | }, 34 | "theme": { 35 | "primary-color": "#1DA57A" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /proxy.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mock = {}; 4 | 5 | require('fs').readdirSync(require('path').join(__dirname + '/mock')) 6 | .forEach(function (file) { 7 | Object.assign(mock, require('./mock/' + file)); 8 | }); 9 | 10 | module.exports = mock; 11 | -------------------------------------------------------------------------------- /service/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('generator:server'); 9 | var http = require('http'); 10 | let systemConfig = require('../../system.config'); 11 | 12 | /** 13 | * Get port from environment and store in Express. 14 | */ 15 | 16 | var port = normalizePort(process.env.PORT || systemConfig.serverPort); 17 | app.set('port', port); 18 | 19 | /** 20 | * Create HTTP server. 21 | */ 22 | 23 | var server = http.createServer(app); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | server.listen(port); 30 | server.on('error', onError); 31 | server.on('listening', onListening); 32 | 33 | /** 34 | * Normalize a port into a number, string, or false. 35 | */ 36 | 37 | function normalizePort(val) { 38 | var port = parseInt(val, 10); 39 | 40 | if (isNaN(port)) { 41 | // named pipe 42 | return val; 43 | } 44 | 45 | if (port >= 0) { 46 | // port number 47 | return port; 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /** 54 | * Event listener for HTTP server "error" event. 55 | */ 56 | 57 | function onError(error) { 58 | if (error.syscall !== 'listen') { 59 | throw error; 60 | } 61 | 62 | var bind = typeof port === 'string' 63 | ? 'Pipe ' + port 64 | : 'Port ' + port; 65 | 66 | // handle specific listen errors with friendly messages 67 | switch (error.code) { 68 | case 'EACCES': 69 | console.error(bind + ' requires elevated privileges'); 70 | process.exit(1); 71 | break; 72 | case 'EADDRINUSE': 73 | console.error(bind + ' is already in use'); 74 | process.exit(1); 75 | break; 76 | default: 77 | throw error; 78 | } 79 | } 80 | 81 | /** 82 | * Event listener for HTTP server "listening" event. 83 | */ 84 | 85 | function onListening() { 86 | var addr = server.address(); 87 | var bind = typeof addr === 'string' 88 | ? 'pipe ' + addr 89 | : 'port ' + addr.port; 90 | debug('Listening on ' + bind); 91 | } 92 | -------------------------------------------------------------------------------- /service/constants/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/18. 3 | */ 4 | 5 | const PAGE_SIZE = 10; 6 | 7 | module.exports = { 8 | PAGE_SIZE: PAGE_SIZE 9 | }; -------------------------------------------------------------------------------- /service/models/customers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/2/28. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let customerSchema = new Schema({ 8 | customerName: String, 9 | contactPeople: String, 10 | contactPhone: String, 11 | address: String, 12 | mem: String, 13 | accountName: String, 14 | accountBank: String, 15 | accountNo: String, 16 | userId: String 17 | }); 18 | 19 | /** 20 | *here can add same methods or statics 21 | */ 22 | customerSchema.statics.findById = function (customerId, cb) { 23 | return this.find({_id:customerId}, cb); 24 | }; 25 | 26 | module.exports = mongoose.model('Customer', customerSchema); -------------------------------------------------------------------------------- /service/models/orders.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let orderSchema = new Schema({ 8 | createInstance: Date, 9 | sequence: Number, 10 | orderNumber: String, 11 | customerId: String, 12 | customerName: String, 13 | totalAmount: Number, 14 | paymentAmount: Number, 15 | debtAmount: Number, 16 | mem: String, 17 | products: Array, 18 | userId: String 19 | }); 20 | 21 | /** 22 | *here can add same methods or statics 23 | */ 24 | orderSchema.statics.findByOrderId=function(orderId, cb){ 25 | return this.find({_id:orderId}, cb); 26 | }; 27 | 28 | module.exports = mongoose.model('Order', orderSchema); -------------------------------------------------------------------------------- /service/models/productStocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/2/28. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let productStocksSchema = new Schema({ 8 | productId: String, 9 | productName: String, 10 | quantity: String, 11 | price: Number, 12 | amount: Number, 13 | productUnit: String, 14 | type: String, 15 | userId: String 16 | }); 17 | 18 | /** 19 | *here can add same methods or statics 20 | */ 21 | productStocksSchema.statics.findById = function (productId, cb) { 22 | return this.find({productId:productId}, cb); 23 | }; 24 | 25 | module.exports = mongoose.model('ProductStocks', productStocksSchema); -------------------------------------------------------------------------------- /service/models/products.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/2/28. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let productSchema = new Schema({ 8 | productCode: String, 9 | productName: String, 10 | productType: String, 11 | productUnit: String, 12 | productImg: String, 13 | userId: String 14 | }); 15 | 16 | /** 17 | *here can add same methods or statics 18 | */ 19 | productSchema.statics.findById = function (supplierId, cb) { 20 | return this.find({_id:supplierId}, cb); 21 | }; 22 | 23 | module.exports = mongoose.model('Product', productSchema); -------------------------------------------------------------------------------- /service/models/settlement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let settlementSchema = new Schema({ 8 | createInstance: Date, 9 | userId: String, 10 | userName: String, 11 | settlementAmount: Number, 12 | products: Array, 13 | userId: String 14 | }); 15 | 16 | /** 17 | *here can add same methods or statics 18 | */ 19 | /*settlementSchema.statics.findByOrderId=function(orderId, cb){ 20 | return this.find({_id:orderId}, cb); 21 | };*/ 22 | 23 | module.exports = mongoose.model('Settlement', settlementSchema); -------------------------------------------------------------------------------- /service/models/storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let storageSchema = new Schema({ 8 | createInstance: Date, 9 | sequence: Number, 10 | noteNumber: String, 11 | supplierId: String, 12 | supplierName: String, 13 | totalAmount: Number, 14 | paymentAmount: Number, 15 | debtAmount: Number, 16 | mem: String, 17 | products: Array, 18 | userId: String 19 | }); 20 | 21 | /** 22 | *here can add same methods or statics 23 | */ 24 | storageSchema.statics.findByStorageId=function(storageId, cb){ 25 | return this.find({_id:storageId}, cb); 26 | }; 27 | 28 | module.exports = mongoose.model('Storage', storageSchema); -------------------------------------------------------------------------------- /service/models/suppliers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/2/28. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let supplierSchema = new Schema({ 8 | supplierName: String, 9 | contactPeople: String, 10 | contactPhone: String, 11 | address: String, 12 | payment: Number, 13 | mem: String, 14 | accountName: String, 15 | accountBank: String, 16 | accountNo: String, 17 | userId: String 18 | }); 19 | 20 | /** 21 | *here can add same methods or statics 22 | */ 23 | supplierSchema.statics.findById = function (supplierId, cb) { 24 | return this.find({_id:supplierId}, cb); 25 | }; 26 | 27 | module.exports = mongoose.model('Supplier', supplierSchema); -------------------------------------------------------------------------------- /service/models/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | let mongoose = require('mongoose'); 5 | let Schema = mongoose.Schema; 6 | 7 | let userSchema = new Schema({ 8 | username: String, 9 | password: String, 10 | salt: String, 11 | admin: Boolean 12 | }); 13 | 14 | /** 15 | *here can add same methods or statics 16 | */ 17 | userSchema.statics.findByUserName=function(username, cb){ 18 | return this.find({username:new RegExp(username, 'i')}, cb); 19 | }; 20 | 21 | module.exports = mongoose.model('User', userSchema); -------------------------------------------------------------------------------- /service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node-dev ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.15.1", 10 | "compression": "^1.6.2", 11 | "connect-mongo": "^2.0.1", 12 | "cookie-parser": "~1.4.3", 13 | "debug": "~2.2.0", 14 | "express": "~4.13.4", 15 | "express-session": "^1.15.6", 16 | "formidable": "^1.1.1", 17 | "jade": "~1.11.0", 18 | "moment": "^2.17.1", 19 | "mongoose": "^4.7.6", 20 | "morgan": "~1.7.0", 21 | "node-dev": "^3.1.3", 22 | "serve-favicon": "~2.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /service/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/service/public/favicon.ico -------------------------------------------------------------------------------- /service/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /service/routes/auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/16. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | 7 | router.route('/') 8 | .get(function (req, res, next) { 9 | let currentUser = req.session.userInfo; 10 | if (currentUser && currentUser._id && currentUser.username) { 11 | res.send({ 12 | isAuth: true 13 | }); 14 | } else { 15 | res.send({ 16 | isAuth: false 17 | }); 18 | } 19 | }); 20 | 21 | module.exports = router; -------------------------------------------------------------------------------- /service/routes/index.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | const routerAuth = function (req, res, next) { 5 | //对业务数据路由进行拦截 6 | console.log(req.url); 7 | if(/\/api\//.test(req.url)){ 8 | let currentUser = req.session.userInfo; 9 | if (currentUser && currentUser._id && currentUser.username) { 10 | next(); 11 | } else { 12 | res.send({ 13 | isAuth: false 14 | }); 15 | }; 16 | } else { 17 | next(); 18 | } 19 | } 20 | 21 | module.exports = routerAuth; 22 | -------------------------------------------------------------------------------- /service/routes/productStocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hanlu on 2017/2/28. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let ProductStocks = require('../models/productStocks'); 7 | 8 | router.route('/') 9 | .get((req, res, next)=>{ 10 | let currentUser = req.session.userInfo; 11 | let queryCondition = { 12 | userId: currentUser['_id'], 13 | type:'in' 14 | }; 15 | ProductStocks.find(queryCondition,(err, productStocks)=>{ 16 | if(err){ 17 | res.send({ 18 | success: false, 19 | error: err 20 | }); 21 | }else { 22 | let productDuplicates = []; 23 | let products = []; 24 | productStocks.map(product=> { 25 | if(productDuplicates.indexOf(product['productId'])==-1){ 26 | let newProduct = { 27 | _id: product['productId'], 28 | productName: product['productName'], 29 | productUnit: product['productUnit'], 30 | }; 31 | productDuplicates.push(product['productId']); 32 | products.push(newProduct); 33 | } 34 | }); 35 | res.send({ 36 | success: true, 37 | products: products 38 | }); 39 | } 40 | }); 41 | }); 42 | 43 | module.exports = router; 44 | -------------------------------------------------------------------------------- /service/routes/products.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hanlu on 2017/2/28. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let Product = require('../models/products'); 7 | let constants = require('../constants/constants'); 8 | 9 | router.route('/') 10 | .get((req, res, next)=>{ 11 | let {page, productName}=req.query; 12 | let limit = constants.PAGE_SIZE; 13 | let skip = (page - 1) * limit; 14 | let currentUser = req.session.userInfo; 15 | let queryCondition = { 16 | userId: currentUser['_id'] 17 | }; 18 | if(productName){ 19 | queryCondition['productName'] = new RegExp(productName); 20 | } 21 | Product.count(queryCondition, (err, count)=>{ 22 | Product.find(queryCondition) 23 | .limit(limit) 24 | .skip(skip) 25 | .exec((err, products)=>{ 26 | if(err){ 27 | res.send({ 28 | success: false, 29 | error: err 30 | }); 31 | }else { 32 | res.send({ 33 | success: true, 34 | products: products, 35 | page: { 36 | total: count, 37 | current: page 38 | } 39 | }); 40 | } 41 | }); 42 | }); 43 | }) 44 | .post((req, res, next)=>{ 45 | let product = req.body; 46 | let currentUser = req.session.userInfo; 47 | let newProduct = new Product(Object.assign({}, product, {userId: currentUser['_id']})); 48 | newProduct.save((err, product)=>{ 49 | if(err){ 50 | res.send({ 51 | success: false, 52 | error: err 53 | }); 54 | }else { 55 | res.send({ 56 | success: true, 57 | product: product 58 | }); 59 | } 60 | }); 61 | }); 62 | 63 | router.route('/:productId') 64 | .put((req, res, next)=>{ 65 | let productId = req.params.productId; 66 | let product = req.body; 67 | let newProduct = Object.assign({}, product); 68 | Product.findOneAndUpdate({_id:productId}, newProduct, {new: true}, (err, product)=>{ 69 | if(err){ 70 | res.send({ 71 | success: false, 72 | error: err 73 | }); 74 | }else { 75 | res.send({ 76 | success: true, 77 | product: product 78 | }); 79 | } 80 | }); 81 | }) 82 | .delete((req, res, next)=>{ 83 | let productId = req.params.productId; 84 | Product.remove({_id: productId}, (err)=>{ 85 | if (err) { 86 | res.send({ 87 | success: false, 88 | error: err 89 | }); 90 | } else { 91 | res.send({ 92 | success: true 93 | }); 94 | } 95 | }); 96 | }); 97 | 98 | module.exports = router; 99 | -------------------------------------------------------------------------------- /service/routes/settlement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hanlu on 2017/2/28. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let Settlement = require('../models/settlement'); 7 | let constants = require('../constants/constants'); 8 | 9 | router.route('/') 10 | .get((req, res, next)=>{ 11 | let {page, productName}=req.query; 12 | let limit = constants.PAGE_SIZE; 13 | let skip = (page - 1) * limit; 14 | let currentUser = req.session.userInfo; 15 | let queryCondition = { 16 | userId: currentUser['_id'] 17 | }; 18 | if(productName){ 19 | queryCondition['productName'] = new RegExp(productName); 20 | } 21 | Settlement.count(queryCondition, (err, count)=>{ 22 | Settlement.find(queryCondition) 23 | .limit(limit) 24 | .skip(skip) 25 | .exec((err, settlements)=>{ 26 | if(err){ 27 | res.send({ 28 | success: false, 29 | error: err 30 | }); 31 | }else { 32 | res.send({ 33 | success: true, 34 | settlements: settlements, 35 | page: { 36 | total: count, 37 | current: page 38 | } 39 | }); 40 | } 41 | }); 42 | }); 43 | }); 44 | 45 | router.route('/:settlementId') 46 | .get((req, res, next)=>{ 47 | let settlementId = req.params.settlementId; 48 | Settlement.find({_id:settlementId},(err, settlement)=>{ 49 | if(err) { 50 | res.send({ 51 | success: false, 52 | error: err 53 | }); 54 | }else { 55 | console 56 | res.send({ 57 | success: true, 58 | settlementItems: settlement[0].products 59 | }); 60 | } 61 | }); 62 | }); 63 | 64 | module.exports = router; 65 | -------------------------------------------------------------------------------- /service/routes/uploadProductImg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2016/11/23. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let formidable = require('formidable'); 7 | let fs = require('fs'); 8 | let path = require('path'); 9 | let systemConfig = require('../../system.config'); 10 | 11 | //139.224.195.74 12 | let server = systemConfig.uploadImgServer; 13 | let port = systemConfig.serverPort; 14 | // 文件将要上传到哪个文件夹下面 15 | let uploadfoldername = 'uploadfiles'; 16 | let uploadfolderpath = path.join(__dirname, '../../upload', uploadfoldername); 17 | 18 | router.route('/') 19 | .post(function (req, res, next) { 20 | // 使用第三方的 formidable 插件初始化一个 form 对象 21 | let form = new formidable.IncomingForm(); 22 | form.uploadDir=path.join(__dirname, '../','tmp'); 23 | 24 | form.parse(req, function (err, fields, files) { 25 | if(err){ 26 | return console.log('formidable, form.parse err'); 27 | } 28 | 29 | console.log('formidable, form.parse ok'); 30 | // 显示参数,例如 token 31 | console.log('显示上传时的参数 begin'); 32 | console.log(fields); 33 | console.log('显示上传时的参数 end'); 34 | 35 | let item; 36 | // 计算 files 长度 37 | let length = 0; 38 | for (item in files) { 39 | length++; 40 | } 41 | if (length === 0) { 42 | return console.log('files no data'); 43 | } 44 | 45 | for (item in files) { 46 | let file = files[item]; 47 | // formidable 会将上传的文件存储为一个临时文件,现在获取这个文件的目录 48 | let tempfilepath = file.path; 49 | // 获取文件类型 50 | let type = file.type; 51 | 52 | // 获取文件名,并根据文件名获取扩展名 53 | let filename = file.name; 54 | let extname = filename.lastIndexOf('.') >= 0 55 | ? filename.slice(filename.lastIndexOf('.') - filename.length) 56 | : ''; 57 | // 文件名没有扩展名时候,则从文件类型中取扩展名 58 | if (extname === '' && type.indexOf('/') >= 0) { 59 | extname = '.' + type.split('/')[1]; 60 | } 61 | // 将文件名重新赋值为一个随机数(避免文件重名) 62 | filename = Math.random().toString().slice(2) + extname; 63 | 64 | // 构建将要存储的文件的路径 65 | let filenewpath = path.join(uploadfolderpath, filename); 66 | 67 | // 将临时文件保存为正式的文件 68 | fs.rename(tempfilepath, filenewpath, function (err) { 69 | // 存储结果 70 | let result = ''; 71 | console.log(tempfilepath); 72 | console.log(filenewpath); 73 | 74 | if (err) { 75 | // 发生错误 76 | console.log(err); 77 | console.log('fs.rename err'); 78 | result = 'error|save error'; 79 | } else { 80 | // 保存成功 81 | console.log('fs.rename done'); 82 | // 拼接图片url地址 83 | result = 'http://' + server +':'+ port + '/' + uploadfoldername + '/' + filename; 84 | } 85 | 86 | // 返回结果 87 | res.writeHead(200, { 88 | 'Content-type': 'text/html' 89 | }); 90 | res.end(result); 91 | }); // fs.rename 92 | } // for in 93 | }); 94 | }); 95 | 96 | module.exports = router; 97 | -------------------------------------------------------------------------------- /service/routes/users.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function (req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /service/tmp/.ignore: -------------------------------------------------------------------------------- 1 | #ignore file -------------------------------------------------------------------------------- /service/utils/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | 5 | function getAuthToken(len) { 6 | let tokenStr = '0123456789abcdefghijklmnopqrstuvwxy'; 7 | let token = ''; 8 | for (let i = 0; i < len; i++) { 9 | token += tokenStr[Math.floor(Math.random() * tokenStr.length)]; 10 | } 11 | return token; 12 | } 13 | 14 | function getOrderNumber(number) { 15 | return getNumber('MDC', number); 16 | } 17 | 18 | function getNoteNumber(number) { 19 | return getNumber('MDS', number); 20 | } 21 | 22 | function getNumber(prefix, number) { 23 | let date = new Date(); 24 | let year = date.getFullYear(); 25 | let month = prefixO(date.getMonth() + 1); 26 | let day = prefixO(date.getDate()); 27 | return prefix + year + month + day + (prefixOOO(number)); 28 | } 29 | 30 | function prefixO(number) { 31 | return ('0' + number).substr(-2); 32 | } 33 | 34 | function prefixOOO(number) { 35 | return ('000' + number).substr(-4); 36 | } 37 | 38 | 39 | module.exports = { 40 | getAuthToken: getAuthToken, 41 | getOrderNumber: getOrderNumber, 42 | getNoteNumber: getNoteNumber 43 | }; -------------------------------------------------------------------------------- /service/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /service/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /service/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /src/assets/yay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/src/assets/yay.jpg -------------------------------------------------------------------------------- /src/components/BreadcrumbList/BreadcrumbList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Breadcrumb} from 'antd'; 3 | import NavLink from '../NavLink/NavLink'; 4 | import {breadcrumb} from './index.css'; 5 | 6 | const BreadcrumbItem = Breadcrumb.Item; 7 | 8 | export default class BreadcrumbList extends Component { 9 | constructor(props) { 10 | super(props); 11 | } 12 | 13 | render() { 14 | let {breadcrumbItems} = this.props; 15 | return ( 16 |
17 | 18 | { 19 | breadcrumbItems.map(([target, linkText], index)=> { 20 | return ( 21 | 22 | 23 | 24 | ); 25 | }) 26 | } 27 | 28 |
29 | ); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/components/BreadcrumbList/index.css: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | margin-bottom: 14px; 3 | text-align: left; 4 | } 5 | .breadcrumb span { 6 | font-size: 14px; 7 | } -------------------------------------------------------------------------------- /src/components/CustomerBills/ClearCustomerBillsModal/ClearCustomerBillsModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd' 3 | import numberFormat from '../../../utils/numberFormat'; 4 | import {modal} from './index.css'; 5 | 6 | const FormItem = Form.Item; 7 | const formItemLayout = { 8 | labelCol: { 9 | span: 6 10 | }, 11 | wrapperCol: { 12 | span: 14 13 | } 14 | }; 15 | const ClearCustomerBillsModal = ({ 16 | visible, 17 | onConfirm, 18 | onCancel, 19 | currentItem, 20 | form: { 21 | getFieldDecorator, 22 | validateFields 23 | } 24 | }) => { 25 | function handleConfirm() { 26 | validateFields((errors, values) => { 27 | if (!!errors) { 28 | return; 29 | } 30 | onConfirm({customerId: values.customerId}); 31 | }) 32 | } 33 | 34 | const modalOpts = { 35 | title: '清账操作', 36 | visible, 37 | onOk: handleConfirm, 38 | onCancel 39 | }; 40 | 41 | const { 42 | customerId, 43 | customerName, 44 | totalAmount, 45 | paymentAmount, 46 | } = currentItem; 47 | 48 | const clearBillAmount = (totalAmount - paymentAmount).toFixed(2); 49 | 50 | return ( 51 | 52 |
53 | 54 | { 55 | getFieldDecorator('customerId', { 56 | initialValue: customerId 57 | })( 58 | 59 | ) 60 | } 61 | 62 | 63 | { 64 | getFieldDecorator('customerName', { 65 | initialValue: customerName 66 | })( 67 | 68 | ) 69 | } 70 | 71 | 72 | { 73 | getFieldDecorator('totalAmount', { 74 | initialValue: numberFormat(totalAmount) 75 | })( 76 | 77 | ) 78 | } 79 | 80 | 81 | { 82 | getFieldDecorator('paymentAmount', { 83 | initialValue: numberFormat(paymentAmount) 84 | })( 85 | 86 | ) 87 | } 88 | 89 | 90 | { 91 | getFieldDecorator('clearBillAmount', { 92 | initialValue: numberFormat(clearBillAmount) 93 | })( 94 | 95 | ) 96 | } 97 | 98 |
99 |
100 | ); 101 | }; 102 | 103 | ClearCustomerBillsModal.propTypes = { 104 | visible: PropTypes.any, 105 | onConfirm: PropTypes.func, 106 | onCancel: PropTypes.func, 107 | currentItem: PropTypes.object, 108 | form: PropTypes.object.isRequired 109 | }; 110 | export default Form.create()(ClearCustomerBillsModal); -------------------------------------------------------------------------------- /src/components/CustomerBills/ClearCustomerBillsModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/CustomerBills/ClearDebtOrdersModal/ClearDebtOrdersModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd'; 3 | import numberFormat from '../../../utils/numberFormat'; 4 | import {modal} from './index.css'; 5 | 6 | const FormItem = Form.Item; 7 | const formItemLayout = { 8 | labelCol: { 9 | span: 6 10 | }, 11 | wrapperCol: { 12 | span: 14 13 | } 14 | }; 15 | const ClearDebtOrdersModal = ({ 16 | visible, 17 | onConfirm, 18 | onCancel, 19 | currentItem, 20 | form: { 21 | getFieldDecorator, 22 | validateFields 23 | } 24 | }) => { 25 | function handleConfirm() { 26 | validateFields((errors, values) => { 27 | if (!!errors) { 28 | return; 29 | } 30 | onConfirm({ 31 | orderId: values.orderId, 32 | paymentAmount: (values.paymentAmount * 1 + values.clearOrderAmount * 1).toFixed(2) * 1, 33 | }); 34 | }) 35 | } 36 | 37 | const modalOpts = { 38 | title: '清单操作', 39 | visible, 40 | onOk: handleConfirm, 41 | onCancel 42 | }; 43 | 44 | const { 45 | _id, 46 | orderNumber, 47 | customerId, 48 | customerName, 49 | totalAmount, 50 | paymentAmount 51 | } = currentItem; 52 | 53 | return ( 54 | 55 |
56 | 57 | { 58 | getFieldDecorator('orderId', { 59 | initialValue: _id 60 | })( 61 | 62 | ) 63 | } 64 | 65 | 66 | { 67 | getFieldDecorator('orderNumber', { 68 | initialValue: orderNumber 69 | })( 70 | 71 | ) 72 | } 73 | 74 | 75 | { 76 | getFieldDecorator('customerId', { 77 | initialValue: customerId 78 | })( 79 | 80 | ) 81 | } 82 | 83 | 84 | { 85 | getFieldDecorator('customerName', { 86 | initialValue: customerName 87 | })( 88 | 89 | ) 90 | } 91 | 92 | 93 | { 94 | getFieldDecorator('totalAmount', { 95 | initialValue: numberFormat(totalAmount) 96 | })( 97 | 98 | ) 99 | } 100 | 101 | 102 | { 103 | getFieldDecorator('paymentAmount', { 104 | initialValue: numberFormat(paymentAmount) 105 | })( 106 | 107 | ) 108 | } 109 | 110 | 111 | { 112 | getFieldDecorator('clearOrderAmount', { 113 | initialValue: 0 114 | })( 115 | 116 | ) 117 | } 118 | 119 |
120 |
121 | ); 122 | }; 123 | 124 | ClearDebtOrdersModal.propTypes = { 125 | visible: PropTypes.any, 126 | onConfirm: PropTypes.func, 127 | onCancel: PropTypes.func, 128 | currentItem: PropTypes.object, 129 | form: PropTypes.object.isRequired 130 | }; 131 | 132 | export default Form.create()(ClearDebtOrdersModal); -------------------------------------------------------------------------------- /src/components/CustomerBills/ClearDebtOrdersModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/CustomerBills/CustomerBillsList/CustomerBillsList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import * as moment from 'moment'; 8 | import {customerBillsList, customerBillsListTitle} from './index.css'; 9 | 10 | class CustomerBillsList extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | selectId: '' 15 | }; 16 | const {onClearBill} = this.props; 17 | this.columns = [ 18 | { 19 | title: '序号', 20 | dataIndex: 'serialNumber', 21 | key: 'serialNumber', 22 | render: (text, record, index) => {index + 1} 23 | }, 24 | { 25 | title: '客户名称', 26 | dataIndex: 'customerName', 27 | key: 'customerName' 28 | }, 29 | { 30 | title: '应付金额', 31 | dataIndex: 'totalAmount', 32 | key: 'totalAmount', 33 | render: (text, record, index) => numberFormat(text) 34 | }, 35 | { 36 | title: '已付金额', 37 | dataIndex: 'paymentAmount', 38 | key: 'paymentAmount', 39 | render: (text, record, index) => numberFormat(text) 40 | }, 41 | { 42 | title: '所欠金额', 43 | dataIndex: 'debtAmount', 44 | key: 'debtAmount', 45 | render: (text, record, index) => {numberFormat(text)} 46 | }, 47 | { 48 | title: '操作', 49 | key: 'operation', 50 | render: (text, record) => ( 51 |

52 | onClearBill(record)}>清账 53 |

54 | ) 55 | } 56 | ]; 57 | } 58 | 59 | static propTypes = { 60 | onClearBill: PropTypes.func, 61 | onPageChange: PropTypes.func, 62 | dataSource: PropTypes.array, 63 | loading: PropTypes.any, 64 | total: PropTypes.any, 65 | current: PropTypes.any 66 | }; 67 | 68 | onPageChange = (page) => { 69 | const {onPageChange} = this.props; 70 | onPageChange(page); 71 | }; 72 | 73 | render() { 74 | const { 75 | loading, 76 | dataSource 77 | } = this.props; 78 | return ( 79 |
80 |

欠账客户列表

81 | record._id} 86 | pagination={false} 87 | /> 88 | 89 | ) 90 | } 91 | } 92 | 93 | export default CustomerBillsList; -------------------------------------------------------------------------------- /src/components/CustomerBills/CustomerBillsList/index.css: -------------------------------------------------------------------------------- 1 | .customerBillsList { 2 | font-size: 16px; 3 | } 4 | 5 | .customerBillsListTitle { 6 | padding: 20px 0; 7 | border-bottom: solid 2px #979797; 8 | text-align: center; 9 | font-size: 20px; 10 | position: relative; 11 | margin-bottom: 30px; 12 | font-weight: normal; 13 | } -------------------------------------------------------------------------------- /src/components/CustomerBills/CustomerBillsSearchForm/CustomerBillsSearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {customerBillsSearchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const Option = Select.Option; 7 | const RangePicker = DatePicker.RangePicker; 8 | 9 | const CustomerBillsSearchForm = ({ 10 | onSearch, 11 | customers, 12 | form: { 13 | getFieldDecorator, 14 | validateFields 15 | } 16 | }) => { 17 | const onSubmit = (e) => { 18 | e.preventDefault(); 19 | validateFields((errors, values) => { 20 | if (!!errors) { 21 | return false; 22 | } 23 | onSearch(values); 24 | }) 25 | }; 26 | 27 | return ( 28 |
29 |
30 | 31 | { 32 | getFieldDecorator('customerId')( 33 | 40 | ) 41 | } 42 | 43 | 44 | 45 |
46 | ); 47 | }; 48 | 49 | CustomerBillsSearchForm.propTypes = { 50 | form: PropTypes.object, 51 | onSearch: PropTypes.func, 52 | customers: PropTypes.array 53 | }; 54 | 55 | export default Form.create()(CustomerBillsSearchForm); -------------------------------------------------------------------------------- /src/components/CustomerBills/CustomerBillsSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .customerBillsSearchForm { 2 | display: block; 3 | text-align: left; 4 | } 5 | 6 | .customerBillsSearchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/CustomerBills/DebtOrdersList/DebtOrdersList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import * as moment from 'moment'; 8 | import {debtOrdersList, debtOrdersListTitle} from './index.css'; 9 | 10 | class DebtOrdersList extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | selectId: '' 15 | }; 16 | const {onClearOrder} = this.props; 17 | this.columns = [ 18 | { 19 | title: '序号', 20 | dataIndex: 'serialNumber', 21 | key: 'serialNumber', 22 | render: (text, record, index) => {index + 1} 23 | }, 24 | { 25 | title: '单据编号', 26 | dataIndex: 'orderNumber', 27 | key: 'orderNumber' 28 | }, 29 | { 30 | title: '下单日期', 31 | dataIndex: 'createInstance', 32 | key: 'createInstance', 33 | render: (text) => {moment.parseZone(text).local().format('YYYY-MM-DD HH:mm')} 34 | }, 35 | { 36 | title: '客户名称', 37 | dataIndex: 'customerName', 38 | key: 'customerName' 39 | }, 40 | { 41 | title: '应付金额', 42 | dataIndex: 'totalAmount', 43 | key: 'totalAmount', 44 | render: (text, record, index) => numberFormat(text) 45 | }, 46 | { 47 | title: '已付金额', 48 | dataIndex: 'paymentAmount', 49 | key: 'paymentAmount', 50 | render: (text, record, index) => numberFormat(text) 51 | }, 52 | { 53 | title: '所欠金额', 54 | dataIndex: 'debtAmount', 55 | key: 'debtAmount', 56 | render: (text, record, index) => {numberFormat(text)} 57 | }, 58 | { 59 | title: '操作', 60 | key: 'operation', 61 | render: (text, record) => ( 62 |

63 | onClearOrder(record)}>清单 64 |

65 | ) 66 | } 67 | ]; 68 | } 69 | 70 | static propTypes = { 71 | onClearOrder: PropTypes.func, 72 | onPageChange: PropTypes.func, 73 | dataSource: PropTypes.array, 74 | loading: PropTypes.any, 75 | total: PropTypes.any, 76 | current: PropTypes.any 77 | }; 78 | 79 | onPageChange = (page) => { 80 | const {onPageChange} = this.props; 81 | onPageChange(page); 82 | }; 83 | 84 | render() { 85 | const { 86 | total, 87 | current, 88 | loading, 89 | dataSource 90 | } = this.props; 91 | return ( 92 |
93 |

欠账订单列表

94 |
record._id} 99 | pagination={false} 100 | /> 101 | 108 | 109 | ) 110 | } 111 | } 112 | 113 | export default DebtOrdersList; -------------------------------------------------------------------------------- /src/components/CustomerBills/DebtOrdersList/index.css: -------------------------------------------------------------------------------- 1 | .debtOrdersList { 2 | font-size: 16px; 3 | } 4 | 5 | .debtOrdersListTitle { 6 | padding: 20px 0; 7 | border-bottom: solid 2px #979797; 8 | text-align: center; 9 | font-size: 20px; 10 | position: relative; 11 | margin-bottom: 30px; 12 | font-weight: normal; 13 | } -------------------------------------------------------------------------------- /src/components/Customers/AddCustomer/AddCustomer.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import AddCustomerTitle from '../CustomerCommon/CustomerTitle/CustomerTitle'; 5 | import AddCustomerForm from '../CustomerCommon/CustomerForm/CustomerForm'; 6 | import {connect} from 'dva'; 7 | import {addCustomer, customerWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 8 | 9 | 10 | class AddCustomer extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.handleConfirm = this.handleConfirm.bind(this); 14 | this.handleCancel = this.handleCancel.bind(this); 15 | } 16 | 17 | handleConfirm() { 18 | /** 19 | * 数据保存前,做数据校验, 20 | * 所有数据均为必填项,包括:客户名称,联系人,联系方式,地址 21 | */ 22 | let {onConfirm} = this.props; 23 | this.refs.addCustomerForm.validateFields((err, values) => { 24 | if (!!err) { 25 | return; 26 | } 27 | onConfirm(values); 28 | }); 29 | } 30 | 31 | handleCancel() { 32 | let {onCancel} = this.props; 33 | onCancel(); 34 | } 35 | 36 | render() { 37 | let {customer, disabled} = this.props; 38 | return ( 39 |
40 |
41 | 42 | 43 |
44 |
45 | { 46 | disabled ? 47 | null: 48 | 51 | } 52 | 53 |
54 | 55 |
56 | ); 57 | } 58 | } 59 | 60 | function mapStateToProps({customers}) { 61 | return {customers}; 62 | } 63 | 64 | export default connect(mapStateToProps)(AddCustomer); 65 | -------------------------------------------------------------------------------- /src/components/Customers/AddCustomer/index.css: -------------------------------------------------------------------------------- 1 | .addCustomer { 2 | font-size: 16px; 3 | } 4 | 5 | .customerWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | border: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Customers/CustomerCommon/CustomerForm/index.css: -------------------------------------------------------------------------------- 1 | .customerForm { 2 | width: 100%; 3 | margin: 28px 0 22px 0; 4 | } 5 | 6 | .formColumn { 7 | display: inline-block; 8 | width: 50%; 9 | } 10 | 11 | .formTitle { 12 | font-size: 18px; 13 | margin: 30px 0 20px; 14 | } -------------------------------------------------------------------------------- /src/components/Customers/CustomerCommon/CustomerTitle/CustomerTitle.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {customerTitle} from './index.css'; 3 | 4 | const CustomerTitle = ({titleText}) => { 5 | 6 | return ( 7 |
8 | {titleText} 9 |
10 | ); 11 | }; 12 | 13 | export default CustomerTitle; -------------------------------------------------------------------------------- /src/components/Customers/CustomerCommon/CustomerTitle/index.css: -------------------------------------------------------------------------------- 1 | .customerTitle { 2 | padding: 20px 0; 3 | border-bottom: solid 2px #979797; 4 | text-align: center; 5 | font-size: 20px; 6 | position: relative; 7 | } -------------------------------------------------------------------------------- /src/components/Customers/CustomerList/CustomerList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import {customerList} from './index.css'; 7 | 8 | const CustomerList = ({ 9 | total, 10 | current, 11 | loading, 12 | dataSource, 13 | onPageChange, 14 | onModify, 15 | onDel, 16 | onDetail 17 | }) => { 18 | const columns = [ 19 | { 20 | title: '序号', 21 | dataIndex: 'serialNumber', 22 | key: 'serialNumber', 23 | render: (text, record, index)=>{index + 1} 24 | }, 25 | { 26 | title: '客户名称', 27 | dataIndex: 'customerName', 28 | key: 'customerName' 29 | }, 30 | { 31 | title: '联系人', 32 | dataIndex: 'contactPeople', 33 | key: 'contactPeople' 34 | }, 35 | { 36 | title: '联系方式', 37 | dataIndex: 'contactPhone', 38 | key: 'contactPhone' 39 | }, 40 | { 41 | title: '地址', 42 | dataIndex: 'address', 43 | key: 'address', 44 | }, 45 | { 46 | title: '备注', 47 | dataIndex: 'mem', 48 | key: 'mem', 49 | }, 50 | { 51 | title: '操作', 52 | key: 'operation', 53 | render: (text, record)=>( 54 |

55 | onModify(record)}>编辑 56 | 57 | onDetail(record)}>详情 58 |

59 | ) 60 | } 61 | ]; 62 | 63 | const rowSelection = { 64 | onChange: (selectedRowKeys, selectedRows) => { 65 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); 66 | }, 67 | onSelect: (record, selected, selectedRows) => { 68 | console.log(record, selected, selectedRows); 69 | }, 70 | onSelectAll: (selected, selectedRows, changeRows) => { 71 | console.log(selected, selectedRows, changeRows); 72 | }, 73 | getCheckboxProps: record => ({ 74 | disabled: record.name === 'Disabled User', // Column configuration not to be checked 75 | }), 76 | }; 77 | 78 | return ( 79 |
80 |
record._id} 85 | pagination={false} 86 | rowSelection={rowSelection} 87 | /> 88 | 95 | 96 | ); 97 | }; 98 | 99 | CustomerList.propTypes = { 100 | onPageChange: PropTypes.func, 101 | onModify: PropTypes.func, 102 | onDel: PropTypes.func, 103 | dataSource: PropTypes.array, 104 | loading: PropTypes.any, 105 | total: PropTypes.any, 106 | current: PropTypes.any 107 | }; 108 | 109 | export default CustomerList; 110 | -------------------------------------------------------------------------------- /src/components/Customers/CustomerList/index.css: -------------------------------------------------------------------------------- 1 | .customerList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/EditableCell/EditableCell.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Input, Icon} from 'antd'; 3 | import {editCell, editLine, inputWrapper, textWrapper, checkIcon, editIcon, hiddenIcon} from './index.css'; 4 | 5 | class EditableCell extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | value: this.props.value, 10 | editable: false 11 | }; 12 | this.check = this.check.bind(this); 13 | this.edit = this.edit.bind(this); 14 | this.handleChange = this.handleChange.bind(this); 15 | } 16 | 17 | componentWillReceiveProps(nextProps){ 18 | this.setState({ 19 | value: nextProps.value 20 | }); 21 | } 22 | 23 | handleChange(e) { 24 | let value = e.target.value; 25 | this.setState({value}); 26 | } 27 | 28 | check() { 29 | this.setState({ 30 | editable: false 31 | }); 32 | if (this.props.onChange) { 33 | this.props.onChange(this.state.value); 34 | } 35 | } 36 | 37 | edit() { 38 | let {disabled} = this.props; 39 | if (disabled) { 40 | return false; 41 | } 42 | this.setState({ 43 | editable: true 44 | }); 45 | } 46 | 47 | render() { 48 | let {value, editable} = this.state; 49 | let {editType, disabled, fieldType} = this.props; 50 | return ( 51 |
52 | { 53 | editable ? 54 | ( 55 |
56 | 64 | 69 |
70 | ) : 71 | ( 72 |
73 | {value} 74 | 79 |
80 | ) 81 | 82 | } 83 |
84 | ); 85 | } 86 | } 87 | 88 | EditableCell.propTypes = { 89 | value: PropTypes.any, 90 | onChange: PropTypes.func, 91 | }; 92 | 93 | export default EditableCell; -------------------------------------------------------------------------------- /src/components/EditableCell/index.css: -------------------------------------------------------------------------------- 1 | .editCell, 2 | .editLine { 3 | position: relative; 4 | } 5 | .editLine { 6 | display: inline-block; 7 | } 8 | 9 | .inputWrapper, 10 | .textWrapper { 11 | padding-right: 24px; 12 | } 13 | 14 | .textWrapper { 15 | padding: 5px 24px 5px 5px; 16 | } 17 | 18 | .editLine .inputWrapper { 19 | border-top: none; 20 | border-right: none; 21 | border-left: none; 22 | } 23 | .editLine .textWrapper { 24 | padding: 5px 20px 5px 5px; 25 | min-width: 20px; 26 | border-bottom: 1px solid #ccc; 27 | } 28 | 29 | .editIcon, 30 | .checkIcon { 31 | position: absolute; 32 | right: 0; 33 | width: 20px; 34 | cursor: pointer; 35 | } 36 | 37 | .editIcon { 38 | line-height: 18px; 39 | /*display: none;*/ 40 | } 41 | 42 | .checkIcon { 43 | line-height: 28px; 44 | } 45 | 46 | .editableCell:hover .editIcon { 47 | display: inline-block; 48 | } 49 | 50 | .editIcon:hover, 51 | .checkIcon:hover { 52 | color:#2db7f5; 53 | } 54 | 55 | .hiddenIcon{ 56 | display: none; 57 | } 58 | -------------------------------------------------------------------------------- /src/components/Example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Example = (props) => { 4 | return ( 5 |
6 | Example 7 |
8 | ); 9 | }; 10 | 11 | Example.propTypes = { 12 | }; 13 | 14 | export default Example; 15 | -------------------------------------------------------------------------------- /src/components/Funds/FundsList/FundsList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import {fundsList} from './index.css'; 8 | 9 | const FundsList = ({ 10 | loading, 11 | dataSource 12 | }) => { 13 | const columns = [ 14 | { 15 | title: '序号', 16 | dataIndex: 'serialNumber', 17 | key: 'serialNumber', 18 | render: (text, record, index)=>text=='总计'? text:{index + 1} 19 | }, 20 | { 21 | title: '商品编码', 22 | dataIndex: 'productCode', 23 | key: 'productCode' 24 | }, 25 | { 26 | title: '商品名称', 27 | dataIndex: 'productName', 28 | key: 'productName' 29 | }, 30 | { 31 | title: '商品类别', 32 | dataIndex: 'productType', 33 | key: 'productType' 34 | }, 35 | { 36 | title: '购买金额', 37 | dataIndex: 'purchasePrice', 38 | key: 'purchasePrice', 39 | render: (text, record, index)=> numberFormat(text) 40 | }, 41 | { 42 | title: '销售金额', 43 | dataIndex: 'salePrice', 44 | key: 'salePrice', 45 | render: (text, record, index)=> numberFormat(text) 46 | }, 47 | { 48 | title: '利润额', 49 | dataIndex: 'profitPrice', 50 | key: 'profitPrice', 51 | render: (text)=> {numberFormat(text)} 52 | } 53 | ]; 54 | 55 | const rowSelection = { 56 | onChange: (selectedRowKeys, selectedRows) => { 57 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); 58 | }, 59 | onSelect: (record, selected, selectedRows) => { 60 | console.log(record, selected, selectedRows); 61 | }, 62 | onSelectAll: (selected, selectedRows, changeRows) => { 63 | console.log(selected, selectedRows, changeRows); 64 | }, 65 | getCheckboxProps: record => ({ 66 | disabled: record.name === 'Disabled User', // Column configuration not to be checked 67 | }), 68 | }; 69 | 70 | const computeTotal = (dataSource, key)=>{ 71 | return dataSource.map(data=> data[key]).reduce((total, amount)=> total += amount, 0); 72 | }; 73 | 74 | const getTotalData = (dataSource)=>{ 75 | if(!dataSource.computed){ 76 | let totalData = { 77 | _id:'total', 78 | serialNumber: '总计', 79 | productCode: '', 80 | productName: '', 81 | productType: '', 82 | purchasePrice: computeTotal(dataSource, 'purchasePrice'), 83 | salePrice: computeTotal(dataSource, 'salePrice'), 84 | profitPrice: computeTotal(dataSource, 'profitPrice') 85 | }; 86 | dataSource.push(totalData); 87 | dataSource.computed = true; 88 | } 89 | return dataSource; 90 | }; 91 | 92 | return ( 93 |
94 |
record._id} 99 | pagination={false} 100 | rowSelection={rowSelection} 101 | /> 102 | 103 | ); 104 | }; 105 | 106 | FundsList.propTypes = { 107 | dataSource: PropTypes.array, 108 | loading: PropTypes.any 109 | }; 110 | 111 | export default FundsList; -------------------------------------------------------------------------------- /src/components/Funds/FundsList/index.css: -------------------------------------------------------------------------------- 1 | .fundsList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Funds/FundsSearchForm/FundsSearchForm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/3/5. 3 | */ 4 | import React, {PropTypes} from 'react'; 5 | import {Form, Button, Select} from 'antd'; 6 | import styles from './index.css'; 7 | 8 | const FormItem = Form.Item; 9 | const Option = Select.Option; 10 | 11 | function FundsSearchForm({ 12 | onSearch, 13 | form: { 14 | getFieldDecorator, 15 | getFieldsValue, 16 | validateFields 17 | } 18 | }){ 19 | const onSubmit = (e)=>{ 20 | e.preventDefault(); 21 | validateFields((err, values)=>{ 22 | if(!!err){ 23 | return; 24 | } 25 | onSearch(values); 26 | }); 27 | }; 28 | 29 | return ( 30 |
31 |
32 | 33 | { 34 | getFieldDecorator('productType', { 35 | initialValue: '1' 36 | })( 37 | 41 | ) 42 | } 43 | 44 | 45 | { 46 | getFieldDecorator('productName', { 47 | initialValue: '1' 48 | })( 49 | 53 | ) 54 | } 55 | 56 | 57 | 58 |
59 | ); 60 | } 61 | 62 | FundsSearchForm.propTypes = {}; 63 | 64 | export default Form.create()(FundsSearchForm); -------------------------------------------------------------------------------- /src/components/Funds/FundsSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .fundsSearchForm { 2 | display: block; 3 | text-align: left; 4 | margin: 30px 0 20px 0; 5 | } 6 | 7 | .fundsSearchForm button { 8 | margin-top: 2px; 9 | } -------------------------------------------------------------------------------- /src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Menu, Icon} from 'antd'; 3 | import NavLink from '../NavLink/NavLink'; 4 | import {header, menuList, menuItem, activeItem} from './index.css'; 5 | 6 | const MenuItem = Menu.Item; 7 | const SubMenu = Menu.SubMenu; 8 | 9 | const menus = [ 10 | ['index', '/', '首页', 'home'], 11 | ['orders', '/orders', '订单', 'solution'], 12 | ['storage', '/storage', '入库', 'upload'], 13 | /*['stock', '/stock', '仓库', 'folder'], 14 | ['funds', '/funds', '资金', 'pay-circle-o'],*/ 15 | ['resource', '/resource', '物资', 'pay-circle-o'], 16 | ['settlement', '/settlement', '结算', 'pushpin-o'], 17 | ['bills', '/bills', '对账', 'copy'], 18 | ['manage', '/manage', '管理', 'setting'], 19 | ]; 20 | 21 | const manageChildMenus = [ 22 | ['customer', '/customer', '客户', 'user'], 23 | ['product', '/product', '商品', 'inbox'], 24 | ['supplier', '/supplier', '供应商', 'team'], 25 | ]; 26 | 27 | const billsChildMenus = [ 28 | ['customerBills', '/customerBills', '客户对账', 'user-add'], 29 | ['supplierBills', '/supplierBills', '供应商对账', 'usergroup-add'] 30 | ]; 31 | 32 | export default class Header extends Component { 33 | constructor(props) { 34 | super(props); 35 | this.state = { 36 | activeIndex: 0 37 | }; 38 | } 39 | 40 | componentWillReceiveProps(nextProps) { 41 | this.setState({ 42 | activeIndex: nextProps.activeIndex 43 | }); 44 | } 45 | 46 | render() { 47 | return ( 48 |
49 | 55 | { 56 | menus.map(([key, path, text, icon], index) => { 57 | if (key == 'manage') { 58 | return ( 59 | {text}}> 60 | { 61 | manageChildMenus.map(([key, path, text, icon], index) => ( 62 | 63 | {text}}/> 65 | 66 | )) 67 | } 68 | 69 | ) 70 | } else if (key == 'bills') { 71 | return ( 72 | {text}}> 73 | { 74 | billsChildMenus.map(([key, path, text, icon], index) => ( 75 | 76 | {text}}/> 78 | 79 | )) 80 | } 81 | 82 | ) 83 | } else { 84 | return ( 85 | 86 | {text}}/> 88 | 89 | ) 90 | } 91 | } 92 | ) 93 | } 94 | 95 |
96 | ); 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /src/components/Header/index.css: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | top: 0; 4 | left:0 ; 5 | bottom: 0; 6 | width:300px; 7 | background: #404040; 8 | overflow: scroll; 9 | } 10 | .menuList { 11 | margin-top: 60px; 12 | border: none; 13 | } 14 | 15 | .header ::-webkit-scrollbar { 16 | display: none; 17 | } -------------------------------------------------------------------------------- /src/components/ListEditableCell/ListEditableCell.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Input, Icon, Select} from 'antd'; 3 | import {editCell, editLine, inputWrapper, textWrapper, checkIcon, editIcon, hiddenIcon} from './index.css'; 4 | 5 | const Option = Select.Option; 6 | 7 | class ListEditableCell extends Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | value: this.props.value, 12 | name: '', 13 | editable: false 14 | }; 15 | } 16 | 17 | handleSelect = (value) => { 18 | this.setState({value}); 19 | }; 20 | 21 | check = () => { 22 | this.setState({ 23 | editable: false 24 | }); 25 | if (this.props.onChange) { 26 | this.props.onChange(this.state.value); 27 | } 28 | }; 29 | 30 | edit = () => { 31 | let {disabled} = this.props; 32 | if (disabled) { 33 | return false; 34 | } 35 | this.setState({ 36 | editable: true 37 | }); 38 | }; 39 | 40 | render() { 41 | let {value, editable} = this.state; 42 | let { disabled, productList} = this.props; 43 | return ( 44 |
45 | { 46 | editable ? 47 | ( 48 |
49 | 63 | 68 |
69 | ) : 70 | ( 71 |
72 | {value.label} 73 | 78 |
79 | ) 80 | 81 | } 82 |
83 | ); 84 | } 85 | } 86 | 87 | ListEditableCell.propTypes = { 88 | value: PropTypes.any, 89 | onChange: PropTypes.func, 90 | }; 91 | 92 | export default ListEditableCell; -------------------------------------------------------------------------------- /src/components/ListEditableCell/index.css: -------------------------------------------------------------------------------- 1 | .editCell, 2 | .editLine { 3 | position: relative; 4 | } 5 | .editLine { 6 | display: inline-block; 7 | } 8 | 9 | .inputWrapper, 10 | .textWrapper { 11 | padding-right: 24px; 12 | } 13 | 14 | .textWrapper { 15 | padding: 5px 24px 5px 5px; 16 | } 17 | 18 | .editLine .inputWrapper { 19 | border-top: none; 20 | border-right: none; 21 | border-left: none; 22 | } 23 | .editLine .textWrapper { 24 | padding: 5px 20px 5px 5px; 25 | min-width: 20px; 26 | border-bottom: 1px solid #ccc; 27 | } 28 | 29 | .editIcon, 30 | .checkIcon { 31 | position: absolute; 32 | right: 0; 33 | width: 20px; 34 | cursor: pointer; 35 | } 36 | 37 | .editIcon { 38 | line-height: 18px; 39 | /*display: none;*/ 40 | } 41 | 42 | .checkIcon { 43 | line-height: 28px; 44 | } 45 | 46 | .editableCell:hover .editIcon { 47 | display: inline-block; 48 | } 49 | 50 | .editIcon:hover, 51 | .checkIcon:hover { 52 | color:#2db7f5; 53 | } 54 | .hiddenIcon { 55 | display: none; 56 | } 57 | -------------------------------------------------------------------------------- /src/components/LoginModal/LoginModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd' 3 | import {modal} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const formItemLayout = { 7 | labelCol: { 8 | span: 6 9 | }, 10 | wrapperCol: { 11 | span: 14 12 | } 13 | }; 14 | const LoginModal =({ 15 | visible, 16 | onConfirm, 17 | onCancel, 18 | form: { 19 | getFieldDecorator, 20 | validateFields, 21 | getFieldsValue 22 | } 23 | })=>{ 24 | function handleConfirm() { 25 | validateFields((errors)=>{ 26 | if(!!errors){ 27 | return; 28 | } 29 | let userData = {...getFieldsValue()}; 30 | onConfirm(userData); 31 | }) 32 | } 33 | 34 | function handleKeyDown(e){ 35 | if(e.keyCode == 13) { 36 | handleConfirm(); 37 | } 38 | } 39 | 40 | const modalOpts = { 41 | title: '系统用户登录', 42 | visible, 43 | onOk:handleConfirm, 44 | onCancel 45 | }; 46 | return ( 47 | 48 |
49 | 50 | { 51 | getFieldDecorator('username',{ 52 | rules:[ 53 | { 54 | required:true, 55 | message:'请输入用户名!' 56 | } 57 | ] 58 | })( 59 | 60 | ) 61 | } 62 | 63 | 64 | { 65 | getFieldDecorator('password',{ 66 | rules:[ 67 | { 68 | required:true, 69 | message:'请输入密码!' 70 | } 71 | ] 72 | })( 73 | 74 | ) 75 | } 76 | 77 | 78 |
79 | ); 80 | }; 81 | 82 | LoginModal.propTypes = { 83 | visible:PropTypes.any, 84 | onConfirm:PropTypes.func, 85 | onCancel:PropTypes.func, 86 | form:PropTypes.object.isRequired 87 | }; 88 | export default Form.create()(LoginModal); -------------------------------------------------------------------------------- /src/components/LoginModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/LogupModal/LogupModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd' 3 | import {modal} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const formItemLayout = { 7 | labelCol: { 8 | span: 6 9 | }, 10 | wrapperCol: { 11 | span: 14 12 | } 13 | }; 14 | const LogupModal =({ 15 | visible, 16 | onConfirm, 17 | onCancel, 18 | form 19 | })=>{ 20 | 21 | const { 22 | getFieldDecorator, 23 | validateFields, 24 | getFieldsValue 25 | } = form; 26 | 27 | function handleConfirm() { 28 | validateFields((errors)=>{ 29 | if(!!errors){ 30 | return; 31 | } 32 | let userData = {...getFieldsValue()}; 33 | onConfirm(userData); 34 | }) 35 | } 36 | 37 | function handleKeyDown(e){ 38 | if(e.keyCode == 13) { 39 | handleConfirm(); 40 | } 41 | } 42 | 43 | const modalOpts = { 44 | title: '系统用户注册', 45 | visible, 46 | onOk:handleConfirm, 47 | onCancel 48 | }; 49 | 50 | const checkPass = (rule, value, callback)=>{ 51 | if(value){ 52 | if(value.length<6){ 53 | callback('密码长度不能小于6位!'); 54 | } 55 | if (!/^([\d]+[a-zA-Z]+)|([a-zA-Z]+[\d]+)$/.test(value)) { 56 | return callback('密码必须由数字和字母组成!'); 57 | } 58 | callback(); 59 | }else { 60 | callback(); 61 | } 62 | }; 63 | 64 | const checkConfirmPass = (rule, value, callback)=>{ 65 | if(value && value!==form.getFieldValue('password')){ 66 | callback('确认密码与密码不一致!'); 67 | } 68 | callback(); 69 | }; 70 | 71 | return ( 72 | 73 |
74 | 75 | { 76 | getFieldDecorator('username',{ 77 | rules:[ 78 | { 79 | required:true, 80 | message:'请输入用户名!' 81 | } 82 | ] 83 | })( 84 | 85 | ) 86 | } 87 | 88 | 89 | { 90 | getFieldDecorator('password',{ 91 | rules:[ 92 | { 93 | required:true, 94 | message:'请输入密码!' 95 | }, 96 | { 97 | validator: checkPass 98 | } 99 | ] 100 | })( 101 | 102 | ) 103 | } 104 | 105 | 106 | { 107 | getFieldDecorator('confirmPassword',{ 108 | rules:[ 109 | { 110 | required:true, 111 | message:'请重新输入密码!' 112 | }, 113 | { 114 | validator: checkConfirmPass 115 | } 116 | ] 117 | })( 118 | 119 | ) 120 | } 121 | 122 | 123 |
124 | ); 125 | }; 126 | 127 | LogupModal.propTypes = { 128 | visible:PropTypes.any, 129 | onConfirm:PropTypes.func, 130 | onCancel:PropTypes.func, 131 | form:PropTypes.object.isRequired 132 | }; 133 | export default Form.create()(LogupModal); -------------------------------------------------------------------------------- /src/components/LogupModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/NavLink/NavLink.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Link} from 'dva/router'; 3 | import {activeLink, link} from './index.css'; 4 | 5 | const NavLink = ({target, linkText})=>( 6 | {linkText} 7 | ); 8 | 9 | export default NavLink; -------------------------------------------------------------------------------- /src/components/NavLink/index.css: -------------------------------------------------------------------------------- 1 | .activeLink { 2 | background: rgba(215, 215, 215, 0.24); 3 | } 4 | .link { 5 | color: #000; 6 | } -------------------------------------------------------------------------------- /src/components/Orders/AddOrder/AddOrder.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import AddOrderTitle from '../OrderCommon/OrderTitle/OrderTitle'; 5 | import AddOrderForm from '../OrderCommon/OrderForm/OrderForm'; 6 | import AddOrderGrid from '../OrderCommon/AddOrderGrid/AddOrderGrid'; 7 | import AddRemarkForm from '../OrderCommon/OrderRemarkForm/OrderRemarkForm'; 8 | import {connect} from 'dva'; 9 | import {addOrder, orderWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 10 | 11 | const AddOrder = ({ 12 | dispatch, 13 | orders 14 | }) => { 15 | const {order, customers, productList} = orders; 16 | const addOrderFormProps = { 17 | customers, 18 | disabled: false, 19 | onSelect(customerId){ 20 | dispatch({ 21 | type: 'orders/setCustomer', 22 | payload: { 23 | customerId 24 | } 25 | }) 26 | } 27 | }; 28 | 29 | const onSetMem = (mem) => { 30 | dispatch({ 31 | type: 'orders/setMem', 32 | payload: { 33 | mem: mem 34 | } 35 | }); 36 | }; 37 | 38 | const handleConfirm = () => { 39 | /** 40 | * 数据保存前,做数据校验, 41 | * 用户不允许为空,并且至少需要保存一条商品数据 42 | */ 43 | const {customerId, products, totalAmount} = order; 44 | if (customerId == null) { 45 | message.error('请选择一个客户!'); 46 | return null; 47 | } 48 | if (products.length == 0) { 49 | message.error('请至少添加一个商品条目!'); 50 | return null; 51 | } 52 | if (totalAmount == 0) { 53 | message.error('合计金额应大于0元!'); 54 | return null; 55 | } 56 | dispatch({ 57 | type: 'orders/create', 58 | payload: { 59 | order 60 | } 61 | }); 62 | dispatch({ 63 | type: 'orders/query' 64 | }); 65 | }; 66 | 67 | const handleCancel = () => { 68 | dispatch({ 69 | type: 'orders/resetOrder' 70 | }); 71 | }; 72 | 73 | const addOrderGridProps = { 74 | products: order.products, 75 | productList, 76 | totalAmount: order.totalAmount, 77 | paymentAmount: order.paymentAmount, 78 | disabled: false, 79 | editProducts(products, totalAmount, paymentAmount){ 80 | dispatch({ 81 | type: 'orders/setProducts', 82 | payload: { 83 | products, 84 | totalAmount, 85 | paymentAmount 86 | } 87 | }); 88 | } 89 | }; 90 | 91 | return ( 92 |
93 |
94 | 95 | 96 | 97 | 98 |
99 |
100 | 101 | 102 |
103 |
104 | ); 105 | }; 106 | 107 | AddOrder.propTypes = { 108 | onPageChange: PropTypes.func, 109 | onModify: PropTypes.func, 110 | onDel: PropTypes.func, 111 | dataSource: PropTypes.array, 112 | loading: PropTypes.any, 113 | total: PropTypes.any, 114 | current: PropTypes.any 115 | }; 116 | 117 | function mapStateToProps({orders}) { 118 | return {orders}; 119 | } 120 | 121 | export default connect(mapStateToProps)(AddOrder); -------------------------------------------------------------------------------- /src/components/Orders/AddOrder/index.css: -------------------------------------------------------------------------------- 1 | .addOrder { 2 | font-size: 16px; 3 | } 4 | 5 | .orderWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | border: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Orders/ModifyOrder/ModifyOrder.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import ModifyOrderTitle from '../OrderCommon/OrderTitle/OrderTitle'; 5 | import ModifyOrderForm from '../OrderCommon/OrderForm/OrderForm'; 6 | import ModifyOrderGrid from '../OrderCommon/AddOrderGrid/AddOrderGrid'; 7 | import OrderRemarkForm from '../OrderCommon/OrderRemarkForm/OrderRemarkForm'; 8 | import {connect} from 'dva'; 9 | import {modifyOrder, orderWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 10 | 11 | const ModifyOrder = ({ 12 | dispatch, 13 | editorType, 14 | orders 15 | }) => { 16 | const {order, currentItem, customers, productList} = orders; 17 | const disabled = editorType != 'modify'; 18 | const modifyOrderFormProps = { 19 | customers, 20 | customerId: currentItem.customerId, 21 | disabled: disabled, 22 | onSelect(customerId){ 23 | dispatch({ 24 | type: 'orders/setCustomer', 25 | payload: { 26 | customerId 27 | } 28 | }) 29 | } 30 | }; 31 | 32 | const onSetMem = (mem) => { 33 | dispatch({ 34 | type: 'orders/setMem', 35 | payload: { 36 | mem: mem 37 | } 38 | }); 39 | }; 40 | 41 | const handleConfirm = () => { 42 | /** 43 | * 数据保存前,做数据校验, 44 | * 用户不允许为空,并且至少需要保存一条商品数据 45 | */ 46 | const {customerId, products, totalAmount} = order; 47 | order['orderNumber'] = currentItem['orderNumber']; 48 | if (customerId == null) { 49 | order['customerId'] = currentItem['customerId']; 50 | } 51 | if (products.length == 0) { 52 | message.error('请至少添加一个商品条目!'); 53 | return null; 54 | } 55 | if (totalAmount == 0) { 56 | message.error('合计金额应大于0元!'); 57 | return null; 58 | } 59 | dispatch({ 60 | type: 'orders/modify', 61 | payload: { 62 | order 63 | } 64 | }); 65 | dispatch({ 66 | type: 'orders/query' 67 | }); 68 | }; 69 | 70 | const handleCancel = () => { 71 | dispatch({ 72 | type: 'orders/resetOrder' 73 | }); 74 | }; 75 | 76 | const modifyOrderGridProps = { 77 | products: currentItem.products, 78 | productList, 79 | totalAmount: currentItem.totalAmount, 80 | paymentAmount: currentItem.paymentAmount, 81 | disabled: disabled, 82 | editProducts(products, totalAmount, paymentAmount){ 83 | console.log(totalAmount + '--' + paymentAmount); 84 | dispatch({ 85 | type: 'orders/setProducts', 86 | payload: { 87 | products, 88 | totalAmount, 89 | paymentAmount 90 | } 91 | }); 92 | } 93 | }; 94 | 95 | return ( 96 |
97 |
98 | 99 | 100 | 101 | 102 |
103 |
104 | { 105 | editorType == 'modify' && 106 | 107 | } 108 | 109 |
110 |
111 | ); 112 | }; 113 | 114 | ModifyOrder.propTypes = { 115 | onPageChange: PropTypes.func, 116 | onModify: PropTypes.func, 117 | onDel: PropTypes.func, 118 | dataSource: PropTypes.array, 119 | loading: PropTypes.any, 120 | total: PropTypes.any, 121 | current: PropTypes.any 122 | }; 123 | 124 | function mapStateToProps({orders}) { 125 | return {orders}; 126 | } 127 | 128 | export default connect(mapStateToProps)(ModifyOrder); -------------------------------------------------------------------------------- /src/components/Orders/ModifyOrder/index.css: -------------------------------------------------------------------------------- 1 | .addOrder { 2 | font-size: 16px; 3 | } 4 | 5 | .orderWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | border: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/AddOrderGrid/index.css: -------------------------------------------------------------------------------- 1 | .addOrderGrid .ant-table { 2 | font-size: 16px; 3 | } 4 | 5 | .rowClassName { 6 | font-size: 14px; 7 | } 8 | 9 | .totalAmountClass > div { 10 | font-size: 14px; 11 | margin: 14px 10px; 12 | font-weight: bold; 13 | } 14 | 15 | .paymentAmountClass { 16 | display: inline-block; 17 | min-width: 100px; 18 | } 19 | 20 | .remarkClass { 21 | margin-top: 20px; 22 | } -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderForm/OrderForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Select} from 'antd'; 3 | import {orderForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const Option = Select.Option; 7 | 8 | const OrderForm = ({ 9 | customers, 10 | customerId, 11 | onSelect, 12 | disabled, 13 | form: { 14 | getFieldDecorator 15 | } 16 | }) => { 17 | const handleChange = (value)=> { 18 | onSelect(value); 19 | }; 20 | 21 | return ( 22 |
23 |
24 | 25 | { 26 | getFieldDecorator('customerId', { 27 | initialValue: customerId, 28 | rules:[ 29 | { 30 | required: true, 31 | message: '客户名称不能为空' 32 | } 33 | ], 34 | })( 35 | 50 | ) 51 | } 52 | 53 | 54 |
55 | ); 56 | }; 57 | 58 | OrderForm.propTypes = { 59 | form: PropTypes.object.isRequired, 60 | onSelect: PropTypes.func, 61 | customers: PropTypes.any 62 | }; 63 | 64 | export default Form.create()(OrderForm); -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderForm/index.css: -------------------------------------------------------------------------------- 1 | .orderForm { 2 | margin: 28px 0 22px 0; 3 | } -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderRemarkForm/OrderRemarkForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input} from 'antd'; 3 | import {orderRemarkForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | 7 | const OrderRemarkForm = ({ 8 | onSetMem, 9 | mem, 10 | disabled, 11 | form: { 12 | getFieldDecorator 13 | } 14 | }) => { 15 | const handleChange = (e)=> { 16 | onSetMem(e.target.value); 17 | }; 18 | 19 | return ( 20 |
21 |
22 | 23 | { 24 | getFieldDecorator('mem', { 25 | initialValue: mem 26 | })( 27 | 35 | ) 36 | } 37 | 38 | 39 |
40 | ); 41 | }; 42 | 43 | OrderRemarkForm.propTypes = { 44 | form: PropTypes.object.isRequired, 45 | onSetMem: PropTypes.func 46 | }; 47 | 48 | export default Form.create()(OrderRemarkForm); -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderRemarkForm/index.css: -------------------------------------------------------------------------------- 1 | .orderRemarkForm { 2 | margin: 28px 0 22px 0; 3 | } -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderTitle/OrderTitle.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {orderTitle, orderNumberClass} from './index.css'; 3 | 4 | const OrderTitle = ({orderNumber}) => { 5 | 6 | return ( 7 |
8 | 铭帝系统门窗出货单 9 | 单据编号:{orderNumber} 10 |
11 | ); 12 | }; 13 | 14 | export default OrderTitle; -------------------------------------------------------------------------------- /src/components/Orders/OrderCommon/OrderTitle/index.css: -------------------------------------------------------------------------------- 1 | .orderTitle { 2 | padding: 20px 0; 3 | border-bottom: solid 2px #979797; 4 | text-align: center; 5 | font-size: 18px; 6 | position: relative; 7 | } 8 | 9 | .orderNumberClass { 10 | position: absolute; 11 | right: 0; 12 | top: 50%; 13 | transform: translate(0, -50%); 14 | font-size: 14px; 15 | color: #9b9b9b; 16 | } -------------------------------------------------------------------------------- /src/components/Orders/OrderList/OrderList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import numberFormat from '../../../utils/numberFormat'; 6 | import * as moment from 'moment'; 7 | import Spliter from '../../Spliter/Spliter'; 8 | import {orderList} from './index.css'; 9 | 10 | const OrderList = ({ 11 | total, 12 | current, 13 | loading, 14 | dataSource, 15 | onPageChange, 16 | onModify, 17 | onReadOnly, 18 | onDel 19 | }) => { 20 | const columns = [ 21 | { 22 | title: '序号', 23 | dataIndex: 'serialNumber', 24 | key: 'serialNumber', 25 | render: (text, record, index) => {index + 1} 26 | }, 27 | { 28 | title: '下单日期', 29 | dataIndex: 'createInstance', 30 | key: 'createInstance', 31 | render: (text) => {moment.parseZone(text).local().format('YYYY-MM-DD HH:mm')} 32 | }, 33 | { 34 | title: '单据编号', 35 | dataIndex: 'orderNumber', 36 | key: 'orderNumber' 37 | }, 38 | { 39 | title: '客户名称', 40 | dataIndex: 'customerName', 41 | key: 'customerName' 42 | }, 43 | { 44 | title: '应付金额', 45 | dataIndex: 'totalAmount', 46 | key: 'totalAmount', 47 | render: (text, record, index) => numberFormat(text) 48 | }, 49 | { 50 | title: '已付金额', 51 | dataIndex: 'paymentAmount', 52 | key: 'paymentAmount', 53 | render: (text, record, index) => numberFormat(text) 54 | }, 55 | { 56 | title: '备注', 57 | dataIndex: 'mem', 58 | key: 'mem', 59 | }, 60 | { 61 | title: '操作', 62 | key: 'operation', 63 | render: (text, record) => ( 64 |

65 | onModify(record['_id'])}>编辑 66 | 67 | onDel(record['_id'])}> 68 | 删除 69 | 70 | 71 | onReadOnly(record['_id'])}>详情 72 |

73 | ) 74 | } 75 | ]; 76 | 77 | const rowSelection = { 78 | onChange: (selectedRowKeys, selectedRows) => { 79 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); 80 | }, 81 | onSelect: (record, selected, selectedRows) => { 82 | console.log(record, selected, selectedRows); 83 | }, 84 | onSelectAll: (selected, selectedRows, changeRows) => { 85 | console.log(selected, selectedRows, changeRows); 86 | }, 87 | getCheckboxProps: record => ({ 88 | disabled: record.name === 'Disabled User', // Column configuration not to be checked 89 | }), 90 | }; 91 | 92 | return ( 93 |
94 |
record._id} 99 | pagination={false} 100 | rowSelection={rowSelection} 101 | /> 102 | 109 | 110 | ); 111 | }; 112 | 113 | OrderList.propTypes = { 114 | onPageChange: PropTypes.func, 115 | onModify: PropTypes.func, 116 | onDel: PropTypes.func, 117 | dataSource: PropTypes.array, 118 | loading: PropTypes.any, 119 | total: PropTypes.any, 120 | current: PropTypes.any 121 | }; 122 | 123 | export default OrderList; -------------------------------------------------------------------------------- /src/components/Orders/OrderList/index.css: -------------------------------------------------------------------------------- 1 | .orderList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Orders/OrderSearchForm/OrderSearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {orderSearchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const RangePicker = DatePicker.RangePicker; 7 | const Option = Select.Option; 8 | 9 | const OrderSearchForm = ({ 10 | onSearch, 11 | customers, 12 | form: { 13 | getFieldDecorator, 14 | getFieldsValue, 15 | validateFields 16 | } 17 | }) => { 18 | const onSubmit = (e) => { 19 | e.preventDefault(); 20 | validateFields((errors, values) => { 21 | if (!!errors) { 22 | return false; 23 | } 24 | if (values['timeRange']) { 25 | values['timeRange'] = values['timeRange'].map((time) => time.toLocaleString()); 26 | } 27 | onSearch(values); 28 | }) 29 | }; 30 | 31 | return ( 32 |
33 |
34 | 35 | { 36 | getFieldDecorator('timeRange')( 37 | 38 | ) 39 | } 40 | 41 | 42 | { 43 | getFieldDecorator('customerId')( 44 | 51 | ) 52 | } 53 | 54 | 55 | { 56 | getFieldDecorator('orderNumber')( 57 | 58 | ) 59 | } 60 | 61 | 62 | 63 |
64 | ); 65 | }; 66 | 67 | OrderSearchForm.propTypes = { 68 | form: PropTypes.object, 69 | onSearch: PropTypes.func, 70 | customers: PropTypes.array 71 | }; 72 | 73 | export default Form.create()(OrderSearchForm); -------------------------------------------------------------------------------- /src/components/Orders/OrderSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .orderSearchForm { 2 | display: inline-block; 3 | text-align: left; 4 | } 5 | 6 | .orderSearchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Products/AddProduct/AddProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import AddProductTitle from '../ProductCommon/ProductTitle/ProductTitle'; 5 | import AddProductForm from '../ProductCommon/ProductForm/ProductForm'; 6 | import {connect} from 'dva'; 7 | import {addProduct, productWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 8 | 9 | 10 | class AddProduct extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.handleConfirm = this.handleConfirm.bind(this); 14 | this.handleCancel = this.handleCancel.bind(this); 15 | } 16 | 17 | handleConfirm() { 18 | /** 19 | * 数据保存前,做数据校验, 20 | * 所有数据均为必填项,包括:客户名称,联系人,联系方式,地址 21 | */ 22 | let {onConfirm} = this.props; 23 | this.refs.addProductForm.validateFields((err, values) => { 24 | if (!!err) { 25 | return; 26 | } 27 | onConfirm(values); 28 | }); 29 | } 30 | 31 | handleCancel() { 32 | let {onCancel} = this.props; 33 | onCancel(); 34 | } 35 | 36 | render() { 37 | let {product, disabled} = this.props; 38 | return ( 39 |
40 |
41 | 42 | 43 |
44 |
45 | { 46 | disabled? 47 | null: 48 | 51 | } 52 | 53 | 54 |
55 | 56 |
57 | ); 58 | } 59 | } 60 | 61 | function mapStateToProps({products}) { 62 | return {products}; 63 | } 64 | 65 | export default connect(mapStateToProps)(AddProduct); 66 | -------------------------------------------------------------------------------- /src/components/Products/AddProduct/index.css: -------------------------------------------------------------------------------- 1 | .addProduct { 2 | font-size: 16px; 3 | } 4 | 5 | .productWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | border: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Products/ProductCommon/ProductForm/index.css: -------------------------------------------------------------------------------- 1 | .productForm { 2 | width: 100%; 3 | margin: 28px 0 22px 0; 4 | } 5 | 6 | .formColumn { 7 | display: inline-block; 8 | width: 50%; 9 | } 10 | 11 | .formTitle { 12 | font-size: 18px; 13 | margin: 30px 0 20px; 14 | } -------------------------------------------------------------------------------- /src/components/Products/ProductCommon/ProductTitle/ProductTitle.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {productTitle} from './index.css'; 3 | 4 | const ProductTitle = ({titleText}) => { 5 | 6 | return ( 7 |
8 | {titleText} 9 |
10 | ); 11 | }; 12 | 13 | export default ProductTitle; -------------------------------------------------------------------------------- /src/components/Products/ProductCommon/ProductTitle/index.css: -------------------------------------------------------------------------------- 1 | .productTitle { 2 | padding: 20px 0; 3 | border-bottom: solid 2px #979797; 4 | text-align: center; 5 | font-size: 20px; 6 | position: relative; 7 | } -------------------------------------------------------------------------------- /src/components/Products/ProductList/index.css: -------------------------------------------------------------------------------- 1 | .supplierList { 2 | font-size: 16px; 3 | } 4 | .productImg { 5 | cursor: -webkit-zoom-in; 6 | cursor: -ms-zoom-in; 7 | cursor: -moz-zoom-in; 8 | cursor: zoom-in; 9 | } 10 | .productImgPreview { 11 | max-width: 450px; 12 | max-height: 450px; 13 | } -------------------------------------------------------------------------------- /src/components/Resource/ResourceSearchForm/ResourceSearchForm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/3/5. 3 | */ 4 | import React, {PropTypes} from 'react'; 5 | import {Form, Button, Select} from 'antd'; 6 | import styles from './index.css'; 7 | 8 | const FormItem = Form.Item; 9 | const Option = Select.Option; 10 | 11 | function ResourceSearchForm({ 12 | onSearch, 13 | products, 14 | form: { 15 | getFieldDecorator, 16 | getFieldsValue, 17 | validateFields 18 | } 19 | }){ 20 | const onSubmit = (e)=>{ 21 | e.preventDefault(); 22 | validateFields((err, values)=>{ 23 | if(!!err){ 24 | return; 25 | } 26 | onSearch(values); 27 | }); 28 | }; 29 | 30 | return ( 31 |
32 |
33 | 34 | { 35 | getFieldDecorator('productId')( 36 | 43 | ) 44 | } 45 | 46 | 47 | 48 |
49 | ); 50 | } 51 | 52 | ResourceSearchForm.propTypes = {}; 53 | 54 | export default Form.create()(ResourceSearchForm); -------------------------------------------------------------------------------- /src/components/Resource/ResourceSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .resourceSearchForm { 2 | display: block; 3 | text-align: left; 4 | margin: 30px 0 20px 0; 5 | } 6 | 7 | .resourceSearchForm button { 8 | margin-top: 2px; 9 | } -------------------------------------------------------------------------------- /src/components/SearchBar/SearchBar.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Button} from 'antd'; 3 | import {search, addButton} from './index.css'; 4 | 5 | const SearchBar = ({onAdd, children}) => { 6 | 7 | return ( 8 |
9 | {children} 10 |
11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default SearchBar; -------------------------------------------------------------------------------- /src/components/SearchBar/index.css: -------------------------------------------------------------------------------- 1 | .search { 2 | margin-bottom: 20px; 3 | display: flex; 4 | flex-direction: row; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .addButton { 10 | display: inline-block; 11 | } -------------------------------------------------------------------------------- /src/components/SearchForm/SearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {searchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const RangePicker = DatePicker.RangePicker; 7 | const Option = Select.Option; 8 | 9 | const SearchForm = ({ 10 | onSearch, 11 | fieldName, 12 | labelName, 13 | form: { 14 | getFieldDecorator, 15 | getFieldsValue 16 | } 17 | }) => { 18 | const onSubmit = (e)=> { 19 | e.preventDefault(); 20 | onSearch(getFieldsValue()); 21 | }; 22 | 23 | return ( 24 |
25 |
26 | 27 | { 28 | getFieldDecorator(fieldName)( 29 | 30 | ) 31 | } 32 | 33 | 34 | 35 |
36 | ); 37 | }; 38 | 39 | SearchForm.propTypes = { 40 | form: PropTypes.object, 41 | onSearch: PropTypes.func, 42 | customers: PropTypes.array 43 | }; 44 | 45 | export default Form.create()(SearchForm); -------------------------------------------------------------------------------- /src/components/SearchForm/index.css: -------------------------------------------------------------------------------- 1 | .searchForm { 2 | display: inline-block; 3 | text-align: left; 4 | } 5 | 6 | .searchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Settlement/SettlementList/SettlementList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import * as moment from 'moment'; 8 | import {settlementList} from './index.css'; 9 | 10 | class SettlementList extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | selectId: '' 15 | }; 16 | this.columns = [ 17 | { 18 | title: '序号', 19 | dataIndex: 'serialNumber', 20 | key: 'serialNumber', 21 | render: (text, record, index) => {index + 1} 22 | }, 23 | { 24 | title: '结算日期', 25 | dataIndex: 'createInstance', 26 | key: 'createInstance', 27 | width: '30%', 28 | render: (text) => {moment.parseZone(text).local().format('YYYY-MM-DD HH:mm')} 29 | }, 30 | { 31 | title: '结算金额', 32 | dataIndex: 'settlementAmount', 33 | key: 'settlementAmount', 34 | width: '30%', 35 | render: (text, record, index)=> numberFormat(text) 36 | }, 37 | { 38 | title: '结算操作员', 39 | dataIndex: 'userName', 40 | key: 'userName' 41 | }, 42 | 43 | ]; 44 | } 45 | 46 | static propTypes = { 47 | onSettlementSelect: PropTypes.func, 48 | onPageChange: PropTypes.func, 49 | dataSource: PropTypes.array, 50 | loading: PropTypes.any, 51 | total: PropTypes.any, 52 | current: PropTypes.any 53 | }; 54 | 55 | onRowSelect = (record, index) => { 56 | const {onSettlementSelect} = this.props; 57 | const recordId = record._id; 58 | if (recordId != this.state.selectId) { 59 | this.setState({ 60 | selectId: recordId 61 | }); 62 | onSettlementSelect(recordId); 63 | } 64 | }; 65 | 66 | onPageChange = (page) => { 67 | const {onPageChange} = this.props; 68 | onPageChange(page); 69 | }; 70 | 71 | componentWillReceiveProps(nextProps) { 72 | const {onSettlementSelect, settlementId} = nextProps; 73 | if (settlementId != this.state.selectId) { 74 | this.setState({ 75 | selectId: settlementId 76 | }, () => onSettlementSelect(this.state.selectId)); 77 | } 78 | } 79 | 80 | render() { 81 | const { 82 | total, 83 | current, 84 | loading, 85 | dataSource 86 | } = this.props; 87 | return ( 88 |
89 |
record._id} 94 | pagination={false} 95 | onRowClick={this.onRowSelect} 96 | rowClassName={(record, index) => record._id == this.state.selectId ? 'ant-table-row-select' : ''} 97 | /> 98 | 105 | 106 | ) 107 | } 108 | } 109 | 110 | export default SettlementList; -------------------------------------------------------------------------------- /src/components/Settlement/SettlementList/index.css: -------------------------------------------------------------------------------- 1 | .storageList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Settlement/SettlementSearchForm/SettlementSearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {settlementSearchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const RangePicker = DatePicker.RangePicker; 7 | 8 | const SettlementSearchForm = ({ 9 | onSearch, 10 | form: { 11 | getFieldDecorator, 12 | validateFields 13 | } 14 | }) => { 15 | const onSubmit = (e)=> { 16 | e.preventDefault(); 17 | validateFields((errors, values)=> { 18 | if (!!errors) { 19 | return false; 20 | } 21 | if(values['timeRange']){ 22 | values['timeRange'] = values['timeRange'].map((time)=> time.toLocaleString()); 23 | } 24 | onSearch(values); 25 | }) 26 | }; 27 | 28 | return ( 29 |
30 |
31 | 32 | { 33 | getFieldDecorator('timeRange')( 34 | 35 | ) 36 | } 37 | 38 | 39 | 40 |
41 | ); 42 | }; 43 | 44 | SettlementSearchForm.propTypes = { 45 | form: PropTypes.object, 46 | onSearch: PropTypes.func, 47 | customers: PropTypes.array 48 | }; 49 | 50 | export default Form.create()(SettlementSearchForm); -------------------------------------------------------------------------------- /src/components/Settlement/SettlementSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .settlementSearchForm { 2 | display: block; 3 | text-align: left; 4 | } 5 | 6 | .settlementSearchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Spliter/Spliter.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {spliter} from './index.css'; 3 | 4 | const Spliter = ({spliterText})=>( 5 | {spliterText} 6 | ); 7 | 8 | export default Spliter; -------------------------------------------------------------------------------- /src/components/Spliter/index.css: -------------------------------------------------------------------------------- 1 | .spliter { 2 | color: #e9e9e9; 3 | padding:6px; 4 | } -------------------------------------------------------------------------------- /src/components/Stocks/StockList/index.css: -------------------------------------------------------------------------------- 1 | .orderList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Stocks/StockSearchForm/StockSearchForm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/3/5. 3 | */ 4 | import React, {PropTypes} from 'react'; 5 | import {Form, Button, Select} from 'antd'; 6 | import styles from './index.css'; 7 | 8 | const FormItem = Form.Item; 9 | const Option = Select.Option; 10 | 11 | function StockSearchForm({ 12 | onSearch, 13 | form: { 14 | getFieldDecorator, 15 | getFieldsValue, 16 | validateFields 17 | } 18 | }){ 19 | const onSubmit = (e)=>{ 20 | e.preventDefault(); 21 | validateFields((err, values)=>{ 22 | if(!!err){ 23 | return; 24 | } 25 | onSearch(values); 26 | }); 27 | }; 28 | 29 | return ( 30 |
31 |
32 | 33 | { 34 | getFieldDecorator('productType', { 35 | initialValue: '1' 36 | })( 37 | 41 | ) 42 | } 43 | 44 | 45 | { 46 | getFieldDecorator('productName', { 47 | initialValue: '1' 48 | })( 49 | 53 | ) 54 | } 55 | 56 | 57 | 58 |
59 | ); 60 | } 61 | 62 | StockSearchForm.propTypes = {}; 63 | 64 | export default Form.create()(StockSearchForm); -------------------------------------------------------------------------------- /src/components/Stocks/StockSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .stockSearchForm { 2 | display: block; 3 | text-align: left; 4 | margin: 30px 0 20px 0; 5 | } 6 | 7 | .stockSearchForm button { 8 | margin-top: 2px; 9 | } -------------------------------------------------------------------------------- /src/components/Storage/AddStorage/AddStorage.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import AddStorageTitle from '../StorageCommon/StorageTitle/StorageTitle'; 5 | import AddStorageForm from '../StorageCommon/StorageForm/StorageForm'; 6 | import AddStorageGrid from '../StorageCommon/AddStorageGrid/AddStorageGrid'; 7 | import AddRemarkForm from '../StorageCommon/StorageRemarkForm/StorageRemarkForm'; 8 | import {connect} from 'dva'; 9 | import {addStorage, storageWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 10 | 11 | const AddStorage = ({ 12 | dispatch, 13 | storage 14 | }) => { 15 | const {storageData, suppliers, productList} = storage; 16 | const addStorageFormProps = { 17 | suppliers, 18 | disabled: false, 19 | onSelect(supplierId){ 20 | dispatch({ 21 | type: 'storage/setSupplier', 22 | payload: { 23 | supplierId 24 | } 25 | }) 26 | } 27 | }; 28 | 29 | const onSetMem = (mem)=> { 30 | dispatch({ 31 | type: 'storage/setMem', 32 | payload: { 33 | mem: mem 34 | } 35 | }); 36 | }; 37 | 38 | const handleConfirm = ()=> { 39 | /** 40 | * 数据保存前,做数据校验, 41 | * 用户不允许为空,并且至少需要保存一条商品数据 42 | */ 43 | const {supplierId, products, totalAmount} = storageData; 44 | if (supplierId == null) { 45 | message.error('请选择一个供应商!'); 46 | return null; 47 | } 48 | if (products.length == 0) { 49 | message.error('请至少添加一个商品条目!'); 50 | return null; 51 | } 52 | if (totalAmount == 0) { 53 | message.error('合计金额应大于0元!'); 54 | return null; 55 | } 56 | dispatch({ 57 | type: 'storage/create', 58 | payload: { 59 | storageData 60 | } 61 | }); 62 | dispatch({ 63 | type: 'storage/query' 64 | }); 65 | }; 66 | 67 | const handleCancel = ()=> { 68 | dispatch({ 69 | type: 'storage/resetStorage' 70 | }); 71 | }; 72 | 73 | const addStorageGridProps = { 74 | products: storageData.products, 75 | productList, 76 | totalAmount: storageData.totalAmount, 77 | paymentAmount: storageData.paymentAmount, 78 | disabled: false, 79 | editProducts(products, totalAmount, paymentAmount){ 80 | dispatch({ 81 | type: 'storage/setProducts', 82 | payload: { 83 | products, 84 | totalAmount, 85 | paymentAmount 86 | } 87 | }); 88 | } 89 | }; 90 | 91 | return ( 92 |
93 |
94 | 95 | 96 | 97 | 98 |
99 |
100 | 101 | 102 |
103 |
104 | ); 105 | }; 106 | 107 | AddStorage.propTypes = { 108 | onPageChange: PropTypes.func, 109 | onModify: PropTypes.func, 110 | onDel: PropTypes.func, 111 | dataSource: PropTypes.array, 112 | loading: PropTypes.any, 113 | total: PropTypes.any, 114 | current: PropTypes.any 115 | }; 116 | 117 | function mapStateToProps({storage}) { 118 | return {storage}; 119 | } 120 | 121 | export default connect(mapStateToProps)(AddStorage); -------------------------------------------------------------------------------- /src/components/Storage/AddStorage/index.css: -------------------------------------------------------------------------------- 1 | .addStorage { 2 | font-size: 16px; 3 | } 4 | 5 | .storageWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | bstorage: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Storage/ModifyStorage/index.css: -------------------------------------------------------------------------------- 1 | .addStorage { 2 | font-size: 16px; 3 | } 4 | 5 | .storageWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | bstorage: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/AddStorageGrid/index.css: -------------------------------------------------------------------------------- 1 | .addStorageGrid .ant-table { 2 | font-size: 16px; 3 | } 4 | 5 | .rowClassName { 6 | font-size: 14px; 7 | } 8 | 9 | .totalAmountClass > div { 10 | font-size: 14px; 11 | margin: 14px 10px; 12 | font-weight: bold; 13 | } 14 | 15 | .paymentAmountClass { 16 | display: inline-block; 17 | min-width: 100px; 18 | } 19 | 20 | .remarkClass { 21 | margin-top: 20px; 22 | } -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageForm/StorageForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Select} from 'antd'; 3 | import {storageForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const Option = Select.Option; 7 | 8 | const StorageForm = ({ 9 | suppliers, 10 | supplierId, 11 | onSelect, 12 | disabled, 13 | form: { 14 | getFieldDecorator 15 | } 16 | }) => { 17 | const handleChange = (value)=> { 18 | onSelect(value); 19 | }; 20 | 21 | return ( 22 |
23 |
24 | 25 | { 26 | getFieldDecorator('supplierId', { 27 | initialValue: supplierId, 28 | rules:[ 29 | { 30 | required: true, 31 | message: '供应商名称不能为空' 32 | } 33 | ] 34 | })( 35 | 50 | ) 51 | } 52 | 53 | 54 |
55 | ); 56 | }; 57 | 58 | StorageForm.propTypes = { 59 | form: PropTypes.object.isRequired, 60 | onSelect: PropTypes.func, 61 | customers: PropTypes.any 62 | }; 63 | 64 | export default Form.create()(StorageForm); -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageForm/index.css: -------------------------------------------------------------------------------- 1 | .storageForm { 2 | margin: 28px 0 22px 0; 3 | } -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageRemarkForm/StorageRemarkForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input} from 'antd'; 3 | import {storageRemarkForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | 7 | const StorageRemarkForm = ({ 8 | onSetMem, 9 | mem, 10 | disabled, 11 | form: { 12 | getFieldDecorator 13 | } 14 | }) => { 15 | const handleChange = (e)=> { 16 | onSetMem(e.target.value); 17 | }; 18 | 19 | return ( 20 |
21 |
22 | 23 | { 24 | getFieldDecorator('mem', { 25 | initialValue: mem 26 | })( 27 | 35 | ) 36 | } 37 | 38 | 39 |
40 | ); 41 | }; 42 | 43 | StorageRemarkForm.propTypes = { 44 | form: PropTypes.object.isRequired, 45 | onSetMem: PropTypes.func 46 | }; 47 | 48 | export default Form.create()(StorageRemarkForm); -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageRemarkForm/index.css: -------------------------------------------------------------------------------- 1 | .storageRemarkForm { 2 | margin: 28px 0 22px 0; 3 | } -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageTitle/StorageTitle.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {storageTitle, storageNumberClass} from './index.css'; 3 | 4 | const StorageTitle = ({storageNumber}) => { 5 | 6 | return ( 7 |
8 | 铭帝系统门窗出货单 9 | 单据编号:{storageNumber} 10 |
11 | ); 12 | }; 13 | 14 | export default StorageTitle; -------------------------------------------------------------------------------- /src/components/Storage/StorageCommon/StorageTitle/index.css: -------------------------------------------------------------------------------- 1 | .storageTitle { 2 | padding: 20px 0; 3 | border-bottom: solid 2px #979797; 4 | text-align: center; 5 | font-size: 18px; 6 | position: relative; 7 | } 8 | 9 | .storageNumberClass { 10 | position: absolute; 11 | right: 0; 12 | top: 50%; 13 | transform: translate(0, -50%); 14 | font-size: 14px; 15 | color: #9b9b9b; 16 | } -------------------------------------------------------------------------------- /src/components/Storage/StorageList/index.css: -------------------------------------------------------------------------------- 1 | .storageList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Storage/StorageSearchForm/StorageSearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {storageSearchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const RangePicker = DatePicker.RangePicker; 7 | const Option = Select.Option; 8 | 9 | const StorageSearchForm = ({ 10 | onSearch, 11 | suppliers, 12 | form: { 13 | getFieldDecorator, 14 | getFieldsValue, 15 | validateFields 16 | } 17 | }) => { 18 | const onSubmit = (e)=> { 19 | e.preventDefault(); 20 | validateFields((errors, values)=> { 21 | if (!!errors) { 22 | return false; 23 | } 24 | if(values['timeRange']){ 25 | values['timeRange'] = values['timeRange'].map((time)=> time.toLocaleString()); 26 | } 27 | onSearch(values); 28 | }) 29 | }; 30 | 31 | return ( 32 |
33 |
34 | 35 | { 36 | getFieldDecorator('timeRange')( 37 | 38 | ) 39 | } 40 | 41 | 42 | { 43 | getFieldDecorator('supplierId')( 44 | 51 | ) 52 | } 53 | 54 | 55 | { 56 | getFieldDecorator('noteNumber')( 57 | 58 | ) 59 | } 60 | 61 | 62 | 63 |
64 | ); 65 | }; 66 | 67 | StorageSearchForm.propTypes = { 68 | form: PropTypes.object, 69 | onSearch: PropTypes.func, 70 | customers: PropTypes.array 71 | }; 72 | 73 | export default Form.create()(StorageSearchForm); -------------------------------------------------------------------------------- /src/components/Storage/StorageSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .storageSearchForm { 2 | display: inline-block; 3 | text-align: left; 4 | } 5 | 6 | .storageSearchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/SupplierBills/ClearDebtStorageModal/ClearDebtStorageModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd' 3 | import numberFormat from '../../../utils/numberFormat'; 4 | import {modal} from './index.css'; 5 | 6 | const FormItem = Form.Item; 7 | const formItemLayout = { 8 | labelCol: { 9 | span: 6 10 | }, 11 | wrapperCol: { 12 | span: 14 13 | } 14 | }; 15 | const ClearDebtStorageModal =({ 16 | visible, 17 | onConfirm, 18 | onCancel, 19 | currentItem, 20 | form: { 21 | getFieldDecorator, 22 | validateFields 23 | } 24 | })=>{ 25 | function handleConfirm() { 26 | validateFields((errors, values)=>{ 27 | if(!!errors){ 28 | return; 29 | } 30 | onConfirm({ 31 | storageId: values.storageId, 32 | paymentAmount: (values.paymentAmount*1 + values.clearStorageAmount*1).toFixed(2)*1, 33 | }); 34 | }) 35 | } 36 | 37 | const modalOpts = { 38 | title: '清单操作', 39 | visible, 40 | onOk:handleConfirm, 41 | onCancel 42 | }; 43 | 44 | const { 45 | _id, 46 | noteNumber, 47 | supplierId, 48 | supplierName, 49 | totalAmount, 50 | paymentAmount 51 | } = currentItem; 52 | 53 | return ( 54 | 55 |
56 | 57 | { 58 | getFieldDecorator('storageId', { 59 | initialValue: _id 60 | })( 61 | 62 | ) 63 | } 64 | 65 | 66 | { 67 | getFieldDecorator('noteNumber', { 68 | initialValue: noteNumber 69 | })( 70 | 71 | ) 72 | } 73 | 74 | 75 | { 76 | getFieldDecorator('supplierId', { 77 | initialValue: supplierId 78 | })( 79 | 80 | ) 81 | } 82 | 83 | 84 | { 85 | getFieldDecorator('supplierName', { 86 | initialValue: supplierName 87 | })( 88 | 89 | ) 90 | } 91 | 92 | 93 | { 94 | getFieldDecorator('totalAmount', { 95 | initialValue: numberFormat(totalAmount) 96 | })( 97 | 98 | ) 99 | } 100 | 101 | 102 | { 103 | getFieldDecorator('paymentAmount', { 104 | initialValue: numberFormat(paymentAmount) 105 | })( 106 | 107 | ) 108 | } 109 | 110 | 111 | { 112 | getFieldDecorator('clearStorageAmount', { 113 | initialValue: 0 114 | })( 115 | 116 | ) 117 | } 118 | 119 | 120 |
121 | ); 122 | }; 123 | 124 | ClearDebtStorageModal.propTypes = { 125 | visible:PropTypes.any, 126 | onConfirm:PropTypes.func, 127 | onCancel:PropTypes.func, 128 | currentItem:PropTypes.object, 129 | form:PropTypes.object.isRequired 130 | }; 131 | 132 | export default Form.create()(ClearDebtStorageModal); -------------------------------------------------------------------------------- /src/components/SupplierBills/ClearDebtStorageModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/SupplierBills/ClearSupplierBillsModal/ClearSupplierBillsModal.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Modal} from 'antd' 3 | import numberFormat from '../../../utils/numberFormat'; 4 | import {modal} from './index.css'; 5 | 6 | const FormItem = Form.Item; 7 | const formItemLayout = { 8 | labelCol: { 9 | span: 6 10 | }, 11 | wrapperCol: { 12 | span: 14 13 | } 14 | }; 15 | const ClearSupplierBillsModal =({ 16 | visible, 17 | onConfirm, 18 | onCancel, 19 | currentItem, 20 | form: { 21 | getFieldDecorator, 22 | validateFields 23 | } 24 | })=>{ 25 | function handleConfirm() { 26 | validateFields((errors, values)=>{ 27 | if(!!errors){ 28 | return; 29 | } 30 | onConfirm({supplierId: values.supplierId}); 31 | }) 32 | } 33 | 34 | const modalOpts = { 35 | title: '清账操作', 36 | visible, 37 | onOk:handleConfirm, 38 | onCancel 39 | }; 40 | 41 | const { 42 | supplierId, 43 | supplierName, 44 | totalAmount, 45 | paymentAmount, 46 | } = currentItem; 47 | 48 | const clearBillAmount = (totalAmount-paymentAmount).toFixed(2); 49 | 50 | return ( 51 | 52 |
53 | 54 | { 55 | getFieldDecorator('supplierId', { 56 | initialValue: supplierId 57 | })( 58 | 59 | ) 60 | } 61 | 62 | 63 | { 64 | getFieldDecorator('supplierName', { 65 | initialValue: supplierName 66 | })( 67 | 68 | ) 69 | } 70 | 71 | 72 | { 73 | getFieldDecorator('totalAmount', { 74 | initialValue: numberFormat(totalAmount) 75 | })( 76 | 77 | ) 78 | } 79 | 80 | 81 | { 82 | getFieldDecorator('paymentAmount', { 83 | initialValue: numberFormat(paymentAmount) 84 | })( 85 | 86 | ) 87 | } 88 | 89 | 90 | { 91 | getFieldDecorator('clearBillAmount', { 92 | initialValue: numberFormat(clearBillAmount) 93 | })( 94 | 95 | ) 96 | } 97 | 98 | 99 |
100 | ); 101 | }; 102 | 103 | ClearSupplierBillsModal.propTypes = { 104 | visible:PropTypes.any, 105 | onConfirm:PropTypes.func, 106 | onCancel:PropTypes.func, 107 | currentItem:PropTypes.object, 108 | form:PropTypes.object.isRequired 109 | }; 110 | export default Form.create()(ClearSupplierBillsModal); -------------------------------------------------------------------------------- /src/components/SupplierBills/ClearSupplierBillsModal/index.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | 3 | } -------------------------------------------------------------------------------- /src/components/SupplierBills/DebtStorageList/DebtStorageList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import * as moment from 'moment'; 8 | import {debtStorageList, debtStorageListTitle} from './index.css'; 9 | 10 | class DebtStorageList extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | selectId: '' 15 | }; 16 | const {onClearStorage} = this.props; 17 | this.columns = [ 18 | { 19 | title: '序号', 20 | dataIndex: 'serialNumber', 21 | key: 'serialNumber', 22 | render: (text, record, index) => {index + 1} 23 | }, 24 | { 25 | title: '单据编号', 26 | dataIndex: 'noteNumber', 27 | key: 'noteNumber' 28 | }, 29 | { 30 | title: '下单日期', 31 | dataIndex: 'createInstance', 32 | key: 'createInstance', 33 | render: (text)=>{moment.parseZone(text).local().format('YYYY-MM-DD HH:mm')} 34 | }, 35 | { 36 | title: '供应商名称', 37 | dataIndex: 'supplierName', 38 | key: 'supplierName' 39 | }, 40 | { 41 | title: '应付金额', 42 | dataIndex: 'totalAmount', 43 | key: 'totalAmount', 44 | render: (text, record, index)=> numberFormat(text) 45 | }, 46 | { 47 | title: '已付金额', 48 | dataIndex: 'paymentAmount', 49 | key: 'paymentAmount', 50 | render: (text, record, index)=> numberFormat(text) 51 | }, 52 | { 53 | title: '负债金额', 54 | dataIndex: 'debtAmount', 55 | key: 'debtAmount', 56 | render: (text, record, index)=> {numberFormat(text)} 57 | }, 58 | { 59 | title: '操作', 60 | key: 'operation', 61 | render: (text, record)=>( 62 |

63 | onClearStorage(record)}>清单 64 |

65 | ) 66 | } 67 | ]; 68 | } 69 | 70 | static propTypes = { 71 | onClearStorage: PropTypes.func, 72 | onPageChange: PropTypes.func, 73 | dataSource: PropTypes.array, 74 | loading: PropTypes.any, 75 | total: PropTypes.any, 76 | current: PropTypes.any 77 | }; 78 | 79 | onPageChange = (page) => { 80 | const {onPageChange} = this.props; 81 | onPageChange(page); 82 | }; 83 | 84 | render() { 85 | const { 86 | total, 87 | current, 88 | loading, 89 | dataSource 90 | } = this.props; 91 | return ( 92 |
93 |

负债入库单列表

94 |
record._id} 99 | pagination={false} 100 | /> 101 | 108 | 109 | ) 110 | } 111 | } 112 | 113 | export default DebtStorageList; -------------------------------------------------------------------------------- /src/components/SupplierBills/DebtStorageList/index.css: -------------------------------------------------------------------------------- 1 | .debtStorageList { 2 | font-size: 16px; 3 | } 4 | 5 | .debtStorageListTitle { 6 | padding: 20px 0; 7 | border-bottom: solid 2px #979797; 8 | text-align: center; 9 | font-size: 20px; 10 | position: relative; 11 | margin-bottom: 30px; 12 | font-weight: normal; 13 | } -------------------------------------------------------------------------------- /src/components/SupplierBills/SupplierBillsList/SupplierBillsList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import numberFormat from '../../../utils/numberFormat'; 7 | import * as moment from 'moment'; 8 | import {supplierBillsList, supplierBillsListTitle} from './index.css'; 9 | 10 | class SupplierBillsList extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | selectId: '' 15 | }; 16 | const {onClearBill} = this.props; 17 | this.columns = [ 18 | { 19 | title: '序号', 20 | dataIndex: 'serialNumber', 21 | key: 'serialNumber', 22 | render: (text, record, index) => {index + 1} 23 | }, 24 | { 25 | title: '供应商名称', 26 | dataIndex: 'supplierName', 27 | key: 'supplierName' 28 | }, 29 | { 30 | title: '应付金额', 31 | dataIndex: 'totalAmount', 32 | key: 'totalAmount', 33 | render: (text, record, index)=> numberFormat(text) 34 | }, 35 | { 36 | title: '已付金额', 37 | dataIndex: 'paymentAmount', 38 | key: 'paymentAmount', 39 | render: (text, record, index)=> numberFormat(text) 40 | }, 41 | { 42 | title: '负债金额', 43 | dataIndex: 'debtAmount', 44 | key: 'debtAmount', 45 | render: (text, record, index)=> {numberFormat(text)} 46 | }, 47 | { 48 | title: '操作', 49 | key: 'operation', 50 | render: (text, record)=>( 51 |

52 | onClearBill(record)}>清账 53 |

54 | ) 55 | } 56 | ]; 57 | } 58 | 59 | static propTypes = { 60 | onClearBill: PropTypes.func, 61 | onPageChange: PropTypes.func, 62 | dataSource: PropTypes.array, 63 | loading: PropTypes.any, 64 | total: PropTypes.any, 65 | current: PropTypes.any 66 | }; 67 | 68 | onPageChange = (page) => { 69 | const {onPageChange} = this.props; 70 | onPageChange(page); 71 | }; 72 | 73 | render() { 74 | const { 75 | loading, 76 | dataSource 77 | } = this.props; 78 | return ( 79 |
80 |

负债供应商列表

81 |
record._id} 86 | pagination={false} 87 | /> 88 | 89 | ) 90 | } 91 | } 92 | 93 | export default SupplierBillsList; -------------------------------------------------------------------------------- /src/components/SupplierBills/SupplierBillsList/index.css: -------------------------------------------------------------------------------- 1 | .supplierBillsList { 2 | font-size: 16px; 3 | } 4 | 5 | .supplierBillsListTitle { 6 | padding: 20px 0; 7 | border-bottom: solid 2px #979797; 8 | text-align: center; 9 | font-size: 20px; 10 | position: relative; 11 | margin-bottom: 30px; 12 | font-weight: normal; 13 | } -------------------------------------------------------------------------------- /src/components/SupplierBills/SupplierBillsSearchForm/SupplierBillsSearchForm.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Form, Input, Select, Button, DatePicker} from 'antd'; 3 | import {supplierBillsSearchForm} from './index.css'; 4 | 5 | const FormItem = Form.Item; 6 | const Option = Select.Option; 7 | const RangePicker = DatePicker.RangePicker; 8 | 9 | const SupplierBillsSearchForm = ({ 10 | onSearch, 11 | suppliers, 12 | form: { 13 | getFieldDecorator, 14 | validateFields 15 | } 16 | }) => { 17 | const onSubmit = (e)=> { 18 | e.preventDefault(); 19 | validateFields((errors, values)=> { 20 | if (!!errors) { 21 | return false; 22 | } 23 | onSearch(values); 24 | }) 25 | }; 26 | 27 | return ( 28 |
29 |
30 | 31 | { 32 | getFieldDecorator('supplierId')( 33 | 40 | ) 41 | } 42 | 43 | 44 | 45 |
46 | ); 47 | }; 48 | 49 | SupplierBillsSearchForm.propTypes = { 50 | form: PropTypes.object, 51 | onSearch: PropTypes.func, 52 | suppliers: PropTypes.array 53 | }; 54 | 55 | export default Form.create()(SupplierBillsSearchForm); -------------------------------------------------------------------------------- /src/components/SupplierBills/SupplierBillsSearchForm/index.css: -------------------------------------------------------------------------------- 1 | .supplierBillsSearchForm { 2 | display: block; 3 | text-align: left; 4 | } 5 | 6 | .supplierBillsSearchForm button { 7 | margin-top: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Suppliers/AddSupplier/AddSupplier.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Button} from 'antd'; 3 | import {message} from 'antd'; 4 | import AddSupplierTitle from '../SupplierCommon/SupplierTitle/SupplierTitle'; 5 | import AddSupplierForm from '../SupplierCommon/SupplierForm/SupplierForm'; 6 | import {connect} from 'dva'; 7 | import {addSupplier, supplierWrapper, buttonGroup, confirmButton, cancelButton} from './index.css'; 8 | 9 | 10 | class AddSupplier extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.handleConfirm = this.handleConfirm.bind(this); 14 | this.handleCancel = this.handleCancel.bind(this); 15 | } 16 | 17 | handleConfirm() { 18 | /** 19 | * 数据保存前,做数据校验, 20 | * 所有数据均为必填项,包括:客户名称,联系人,联系方式,地址 21 | */ 22 | let {onConfirm} = this.props; 23 | this.refs.addSupplierForm.validateFields((err, values) => { 24 | if (!!err) { 25 | return; 26 | } 27 | onConfirm(values); 28 | }); 29 | } 30 | 31 | handleCancel() { 32 | let {onCancel} = this.props; 33 | onCancel(); 34 | } 35 | 36 | render() { 37 | let {supplier, disabled} = this.props; 38 | return ( 39 |
40 |
41 | 42 | 43 |
44 |
45 | { 46 | disabled? 47 | null: 48 | 51 | } 52 | 53 | 54 |
55 | 56 |
57 | ); 58 | } 59 | } 60 | 61 | function mapStateToProps({suppliers}) { 62 | return {suppliers}; 63 | } 64 | 65 | export default connect(mapStateToProps)(AddSupplier); 66 | -------------------------------------------------------------------------------- /src/components/Suppliers/AddSupplier/index.css: -------------------------------------------------------------------------------- 1 | .addSupplier { 2 | font-size: 16px; 3 | } 4 | 5 | .supplierWrapper { 6 | padding: 20px 25px; 7 | background-color: #fff; 8 | } 9 | 10 | .buttonGroup { 11 | margin: 30px 0 100px; 12 | 13 | } 14 | 15 | .confirmButton, 16 | .cancelButton { 17 | width: 200px; 18 | height: 40px; 19 | line-height: 30px; 20 | font-size: 16px 21 | } 22 | 23 | .confirmButton { 24 | margin-right: 20px; 25 | } 26 | 27 | .cancelButton { 28 | border: solid 1px #979797; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Suppliers/SupplierCommon/SupplierForm/index.css: -------------------------------------------------------------------------------- 1 | .supplierForm { 2 | width: 100%; 3 | margin: 28px 0 22px 0; 4 | } 5 | 6 | .formColumn { 7 | display: inline-block; 8 | width: 50%; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/components/Suppliers/SupplierCommon/SupplierTitle/SupplierTitle.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {supplierTitle} from './index.css'; 3 | 4 | const SupplierTitle = ({titleText}) => { 5 | 6 | return ( 7 |
8 | {titleText} 9 |
10 | ); 11 | }; 12 | 13 | export default SupplierTitle; -------------------------------------------------------------------------------- /src/components/Suppliers/SupplierCommon/SupplierTitle/index.css: -------------------------------------------------------------------------------- 1 | .supplierTitle { 2 | padding: 20px 0; 3 | border-bottom: solid 2px #979797; 4 | text-align: center; 5 | font-size: 20px; 6 | position: relative; 7 | } -------------------------------------------------------------------------------- /src/components/Suppliers/SupplierList/SupplierList.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {Table, Pagination, Popconfirm, Button} from 'antd'; 3 | import dateFormat from '../../../utils/dateFormat'; 4 | import {PAGE_SIZE} from '../../../constants/constants'; 5 | import Spliter from '../../Spliter/Spliter'; 6 | import {supplierList} from './index.css'; 7 | 8 | const SupplierList = ({ 9 | total, 10 | current, 11 | loading, 12 | dataSource, 13 | onPageChange, 14 | onModify, 15 | onDel, 16 | onDetail 17 | }) => { 18 | const columns = [ 19 | { 20 | title: '序号', 21 | dataIndex: 'serialNumber', 22 | key: 'serialNumber', 23 | render: (text, record, index)=>{index + 1} 24 | }, 25 | { 26 | title: '供应商名称', 27 | dataIndex: 'supplierName', 28 | key: 'supplierName' 29 | }, 30 | { 31 | title: '联系人', 32 | dataIndex: 'contactPeople', 33 | key: 'contactPeople' 34 | }, 35 | { 36 | title: '联系方式', 37 | dataIndex: 'contactPhone', 38 | key: 'contactPhone' 39 | }, 40 | { 41 | title: '地址', 42 | dataIndex: 'address', 43 | key: 'address', 44 | }, 45 | /*{ 46 | title: '应付金额', 47 | dataIndex: 'payment', 48 | key: 'payment', 49 | },*/ 50 | { 51 | title: '备注', 52 | dataIndex: 'mem', 53 | key: 'mem', 54 | }, 55 | { 56 | title: '操作', 57 | key: 'operation', 58 | render: (text, record)=>( 59 |

60 | onModify(record)}>编辑 61 | 62 | onDetail(record)}>详情 63 |

64 | ) 65 | } 66 | ]; 67 | 68 | const rowSelection = { 69 | onChange: (selectedRowKeys, selectedRows) => { 70 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); 71 | }, 72 | onSelect: (record, selected, selectedRows) => { 73 | console.log(record, selected, selectedRows); 74 | }, 75 | onSelectAll: (selected, selectedRows, changeRows) => { 76 | console.log(selected, selectedRows, changeRows); 77 | }, 78 | getCheckboxProps: record => ({ 79 | disabled: record.name === 'Disabled User', // Column configuration not to be checked 80 | }), 81 | }; 82 | 83 | return ( 84 |
85 |
record._id} 90 | pagination={false} 91 | rowSelection={rowSelection} 92 | /> 93 | 100 | 101 | ); 102 | }; 103 | 104 | SupplierList.propTypes = { 105 | onPageChange: PropTypes.func, 106 | onModify: PropTypes.func, 107 | onDel: PropTypes.func, 108 | dataSource: PropTypes.array, 109 | loading: PropTypes.any, 110 | total: PropTypes.any, 111 | current: PropTypes.any 112 | }; 113 | 114 | export default SupplierList; 115 | -------------------------------------------------------------------------------- /src/components/Suppliers/SupplierList/index.css: -------------------------------------------------------------------------------- 1 | .supplierList { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/SystemInfo/SystemInfo.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { Button, Avatar, Modal } from 'antd'; 3 | import {browserHistory} from 'dva/router'; 4 | import {connect} from 'dva'; 5 | import LoginModal from '../LoginModal/LoginModal'; 6 | import LogupModal from '../LogupModal/LogupModal'; 7 | import {register} from '../../../system.config.js'; 8 | import {systemInfo, systemName, userInfo, userName, loginButton, logupButton} from './index.css'; 9 | 10 | const SystemInfo = ({systemUser, dispatch}) => { 11 | const {isLogin, username, modalVisible, logupModalVisible} = systemUser; 12 | 13 | const loginClick = () => { 14 | dispatch({ 15 | type: isLogin ? 'systemUser/doLogout' : 'systemUser/login' 16 | }); 17 | browserHistory.push('/'); 18 | }; 19 | 20 | const logupClick = () => { 21 | dispatch({ 22 | type: 'systemUser/logup', 23 | }); 24 | browserHistory.push('/'); 25 | }; 26 | 27 | const loginModalProps = { 28 | visible: modalVisible, 29 | onConfirm(userData){ 30 | new Promise(function(resolve, reject){ 31 | dispatch({ 32 | type: 'systemUser/doLogin', 33 | payload: { 34 | userData, 35 | resolve, 36 | reject 37 | } 38 | }); 39 | }).then(null, (data)=>{ 40 | Modal.error({ 41 | title: '错误提示', 42 | content:

用户名 或 密码 错误!

43 | }); 44 | }); 45 | }, 46 | onCancel(){ 47 | dispatch({ 48 | type: 'systemUser/hideModal' 49 | }); 50 | } 51 | }; 52 | 53 | const logupModalProps = { 54 | visible: logupModalVisible, 55 | onConfirm(userData){ 56 | new Promise(function(resolve, reject){ 57 | dispatch({ 58 | type: 'systemUser/doLogup', 59 | payload: { 60 | userData, 61 | resolve, 62 | reject 63 | } 64 | }); 65 | }).then(null, (data)=> { 66 | //code 为 3 表示用户名已被注册 67 | if(!data.success && data.code == 3){ 68 | Modal.error({ 69 | title: '错误提示', 70 | content:

该用户名已被注册!

71 | }); 72 | } 73 | }); 74 | }, 75 | onCancel(){ 76 | dispatch({ 77 | type: 'systemUser/hideLogupModal' 78 | }); 79 | } 80 | }; 81 | 82 | const LoginModalGen = () => (); 83 | const LogupModalGen = () => (); 84 | 85 | return ( 86 |
87 | 铭帝系统门窗管理系统 88 | 89 | 90 | { 91 | isLogin ? 92 | {username} : null 93 | } 94 | 95 | 96 | { 97 | register && !isLogin ? 98 | : 99 | null 100 | } 101 | 102 | 103 | 104 |
105 | ); 106 | }; 107 | 108 | function mapStateToProps({systemUser}) { 109 | return {systemUser}; 110 | } 111 | 112 | export default connect(mapStateToProps)(SystemInfo); -------------------------------------------------------------------------------- /src/components/SystemInfo/index.css: -------------------------------------------------------------------------------- 1 | .systemInfo { 2 | height: 64px; 3 | line-height: 64px; 4 | padding:0 25px; 5 | background-color: #fafafa; 6 | display: flex; 7 | flex-direction: row; 8 | justify-content: space-between; 9 | position: fixed; 10 | top:0; 11 | right: 0; 12 | left: 300px; 13 | z-index: 999; 14 | box-shadow: 0 1px 5px rgba(0,0,0,.2); 15 | } 16 | .systemName { 17 | color: #4a4a4a; 18 | font-size: 18px; 19 | } 20 | .userInfo, 21 | .userName>span { 22 | display: flex; 23 | align-items: center; 24 | } 25 | .userName { 26 | margin-right: 10px; 27 | } 28 | .loginButton, 29 | .logupButton { 30 | cursor: pointer; 31 | margin-right: 10px; 32 | } -------------------------------------------------------------------------------- /src/constants/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/18. 3 | */ 4 | import {httpServer, defaultOptions} from '../../system.config'; 5 | 6 | export const PAGE_SIZE = 10; 7 | export const HTTP_SERVER = httpServer; 8 | export const DEFAULT_OPTIONS = defaultOptions; 9 | 10 | export const formItemLayout = { 11 | labelCol: { 12 | span: 4 13 | }, 14 | wrapperCol: { 15 | span: 16 16 | } 17 | }; -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 铭帝系统门窗管理系统 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './index.html'; 2 | import './index.less'; 3 | import dva from 'dva'; 4 | import {browserHistory} from 'dva/router'; 5 | import router from './router'; 6 | import home from './models/home'; 7 | import orders from './models/orders'; 8 | import storage from './models/storage'; 9 | import manage from './models/manage'; 10 | import systemUser from './models/systemUser'; 11 | import customers from './models/customers'; 12 | import products from './models/products'; 13 | import suppliers from './models/suppliers'; 14 | import settlement from './models/settlement'; 15 | import resource from './models/resource'; 16 | import customerBills from './models/customerBills'; 17 | import supplierBills from './models/supplierBills'; 18 | 19 | // 1. Initialize 20 | const app = dva({ 21 | history: browserHistory 22 | }); 23 | 24 | // 2. Plugins 25 | //app.use({}); 26 | 27 | // 3. Model 28 | app.model(home); 29 | app.model(orders); 30 | /*app.model(require('./models/stocks'));*/ 31 | app.model(storage); 32 | /*app.model(require('./models/funds'));*/ 33 | app.model(manage); 34 | app.model(systemUser); 35 | app.model(customers); 36 | app.model(products); 37 | app.model(suppliers); 38 | app.model(settlement); 39 | app.model(resource); 40 | app.model(customerBills); 41 | app.model(supplierBills); 42 | 43 | // 4. Router 44 | app.router(router); 45 | 46 | // 5. Start 47 | app.start('#root'); 48 | -------------------------------------------------------------------------------- /src/index.less: -------------------------------------------------------------------------------- 1 | 2 | :global { 3 | html, body, #root { 4 | height: 100%; 5 | font-size: 16px; 6 | font-family: "Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", SimSun, sans-serif; 7 | background: #ececec; 8 | } 9 | .ant-form-item-label { 10 | text-align: left !important; 11 | } 12 | .ant-table-thead > tr > th, 13 | .ant-table-tbody > tr > td, 14 | .ant-form-item-label label { 15 | font-size: 14px; 16 | } 17 | .ant-table-bordered.ant-table-small .ant-table-footer { 18 | background: #fff; 19 | border-right: 1px solid #e9e9e9 !important; 20 | border-top: 2px solid #e9e9e9 !important; 21 | text-align: right; 22 | } 23 | #root .ant-menu-vertical > .ant-menu-item, 24 | #root .ant-menu-inline > .ant-menu-item, 25 | #root .ant-menu-item-group-list > .ant-menu-item, 26 | #root .ant-menu-vertical > .ant-menu-submenu > .ant-menu-submenu-title, 27 | #root .ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title, 28 | #root .ant-menu-item-group-list > .ant-menu-submenu > .ant-menu-submenu-title { 29 | padding: 0px 16px 0 50px !important; 30 | font-size: 16px; 31 | line-height: 66px; 32 | height: 66px; 33 | overflow: hidden; 34 | text-overflow: ellipsis; 35 | text-align: left; 36 | } 37 | #root .ant-menu-inline.ant-menu-sub > .ant-menu-item { 38 | padding-left: 70px !important; 39 | } 40 | #root .ant-input[disabled], 41 | #root .ant-select-disabled, 42 | #root .ant-select-disabled .ant-select-selection { 43 | background-color: #fff; 44 | color: rgba(0, 0, 0, 0.65); 45 | } 46 | .ant-table-row-select { 47 | background: #cae9ff; 48 | } 49 | .ant-table-tbody>tr.ant-table-row-select>td, 50 | .ant-table-tbody>tr.ant-table-row-select:hover>td, 51 | .ant-table-thead>tr.ant-table-row-select>td, 52 | .ant-table-thead>tr.ant-table-row-select:hover>td { 53 | background: #cae9ff; 54 | } 55 | .ant-input[disabled] { 56 | color: #999 !important; 57 | } 58 | .ant-avatar-sm.ant-avatar-icon { 59 | margin-right: 6px; 60 | } 61 | .formTitle { 62 | font-size: 18px; 63 | margin: 30px 0 20px; 64 | } 65 | ::-webkit-scrollbar { 66 | display: none; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/models/customerBills.js: -------------------------------------------------------------------------------- 1 | import {query, doClearOrder, doClearBill} from '../services/customerBills'; 2 | import {parse} from 'qs'; 3 | import {routerRedux} from 'dva/router'; 4 | export default { 5 | 6 | namespace: 'customerBillsSpace', 7 | 8 | state: { 9 | list: [], 10 | total: null, 11 | loading: false, 12 | page: 1, 13 | customerId: '', 14 | breadcrumbItems: [ 15 | ['/customerBills', '首页'], 16 | ['/customerBills', '账单管理'], 17 | ], 18 | orders: [], 19 | customerBills: [], 20 | customers: [], 21 | visible: false, 22 | editorType: 'clearOrder', 23 | currentItem: {} 24 | }, 25 | 26 | subscriptions: { 27 | setup({dispatch, history}) { 28 | history.listen(location => { 29 | if (location.pathname == '/customerBills') { 30 | dispatch({ 31 | type: 'query', 32 | payload: location.query 33 | }); 34 | } 35 | }); 36 | }, 37 | }, 38 | 39 | effects: { 40 | *query({payload}, {select, call, put}){ 41 | const isLogin = yield select(({systemUser})=> systemUser.isLogin); 42 | if(!isLogin){ 43 | return; 44 | } 45 | yield put({type: 'showLoading'}); 46 | yield put({ 47 | type: 'updateQueryKey', 48 | payload: { 49 | page: 1, 50 | ...payload 51 | } 52 | }); 53 | let {page, customerId} = payload; 54 | customerId = customerId=='00000'?'':customerId; 55 | const {data} = yield call(query, parse({page, customerId})); 56 | if (data) { 57 | yield put({ 58 | type: 'querySuccess', 59 | payload: { 60 | orders: data.orders, 61 | customers: data.customers, 62 | customerBills: data.customerBills, 63 | total: data.page.total, 64 | current: data.page.current 65 | } 66 | }); 67 | } 68 | }, 69 | *doClearOrder({payload}, {call, put}){ 70 | let {orderId, paymentAmount} = payload; 71 | const {data} = yield call(doClearOrder, {orderId, paymentAmount}); 72 | if (data && data.success) { 73 | yield put({ 74 | type: 'hideEditor' 75 | }); 76 | yield put(routerRedux.push('/customerBills')); 77 | } 78 | }, 79 | *doClearBill({payload}, {call, put}){ 80 | const {customerId} = payload; 81 | const {data} = yield call(doClearBill, {customerId}); 82 | if (data && data.success) { 83 | yield put({ 84 | type: 'hideEditor' 85 | }); 86 | yield put(routerRedux.push('/customerBills')); 87 | } 88 | }, 89 | }, 90 | 91 | reducers: { 92 | showLoading(state, action){ 93 | return {...state, loading: true}; 94 | }, 95 | querySuccess(state, action){ 96 | const customers = action.payload.customers; 97 | customers.unshift({ 98 | _id:'00000', 99 | customerName: '全部' 100 | }); 101 | return {...state, ...action.payload, loading: false}; 102 | }, 103 | clearOrder(state, action){ 104 | const currentItem = action.payload.order; 105 | return {...state, currentItem, editorType:'clearOrder', visible: true}; 106 | }, 107 | clearBill(state, action){ 108 | const currentItem = action.payload.bill; 109 | return {...state, currentItem, editorType:'clearBill', visible: true}; 110 | }, 111 | hideEditor(state, action){ 112 | return {...state, currentItem:{}, editorType:'clearOrder', visible: false}; 113 | }, 114 | updateQueryKey(state, action){ 115 | return {...state, ...action.payload}; 116 | } 117 | }, 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/models/example.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | namespace: 'example', 4 | 5 | state: {}, 6 | 7 | subscriptions: { 8 | setup({dispatch, history}) { 9 | }, 10 | }, 11 | 12 | effects: { 13 | *fetchRemote({payload}, {call, put}) { 14 | }, 15 | }, 16 | 17 | reducers: { 18 | fetch(state, action) { 19 | return {...state, ...action.payload}; 20 | }, 21 | }, 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/models/home.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | namespace: 'home', 4 | 5 | state: { 6 | activeIndex: 0, 7 | }, 8 | 9 | subscriptions: { 10 | setup({dispatch, history}) { 11 | history.listen(location=> { 12 | dispatch({ 13 | type: 'updateActiveIndex', 14 | payload: location.pathname 15 | }); 16 | }); 17 | }, 18 | }, 19 | 20 | reducers: { 21 | updateActiveIndex(state, action){ 22 | let pathname = action.payload; 23 | let activeIndex = 0; 24 | if (/orders/.test(pathname)) { 25 | activeIndex = 1; 26 | } else if (/storage/.test(pathname)) { 27 | activeIndex = 2; 28 | } else if (/stock/.test(pathname)) { 29 | activeIndex = 3; 30 | } else if (/funds/.test(pathname)) { 31 | activeIndex = 4; 32 | } else if (/manage/.test(pathname)) { 33 | activeIndex = 5; 34 | } else { 35 | activeIndex = 0; 36 | } 37 | return {...state, activeIndex: activeIndex}; 38 | } 39 | }, 40 | 41 | } 42 | 43 | /*switch(pathname){ 44 | case '/': 45 | activeIndex=0; 46 | break; 47 | case '/orders': 48 | case '/orders/addorder': 49 | activeIndex=1; 50 | break; 51 | case '/storage': 52 | activeIndex=2; 53 | break; 54 | case '/stock': 55 | activeIndex=3; 56 | break; 57 | case '/funds': 58 | activeIndex=4; 59 | break; 60 | case '/manage': 61 | activeIndex=5; 62 | break; 63 | default: 64 | break; 65 | }*/ 66 | -------------------------------------------------------------------------------- /src/models/resource.js: -------------------------------------------------------------------------------- 1 | import {query, onSettlement} from '../services/resource'; 2 | import * as products from '../services/products'; 3 | import {parse} from 'qs'; 4 | export default { 5 | 6 | namespace: 'resource', 7 | 8 | state: { 9 | products:[], 10 | stocks: [], 11 | funds: [], 12 | loading: false, 13 | breadcrumbItems: [ 14 | ['/resource', '首页'], 15 | ['/resource', '物资管理'], 16 | ] 17 | }, 18 | 19 | subscriptions: { 20 | setup({dispatch, history}) { 21 | history.listen(location => { 22 | if(location.pathname=='/resource'){ 23 | dispatch({ 24 | type: 'queryProducts' 25 | }); 26 | dispatch({ 27 | type: 'query' 28 | }); 29 | } 30 | }); 31 | }, 32 | }, 33 | 34 | effects: { 35 | *query({payload}, {select, call, put}){ 36 | const isLogin = yield select(({systemUser})=> systemUser.isLogin); 37 | if(!isLogin){ 38 | return; 39 | } 40 | yield put({ 41 | type: 'showLoading' 42 | }); 43 | let productId = payload && payload.productId!='00000'?payload.productId:''; 44 | const {data} = yield call(query, {productId}); 45 | if(data && data.success) { 46 | yield put({ 47 | type: 'querySuccess', 48 | stocks: [...data.products], 49 | funds: [...data.products] 50 | }); 51 | } 52 | }, 53 | *queryProducts({payload}, {select, call, put}){ 54 | const isLogin = yield select(({systemUser})=> systemUser.isLogin); 55 | if(!isLogin){ 56 | return; 57 | } 58 | const {data} = yield call(products.query, {}); 59 | if(data && data.success){ 60 | yield put({ 61 | type: 'queryProductsSuccess', 62 | products: data.products 63 | }); 64 | } 65 | }, 66 | *onSettlement({payload}, {select, call, put}){ 67 | const {data} = yield call(onSettlement); 68 | if(data && data.success){ 69 | const {data} = yield call(query, {}); 70 | if(data && data.success) { 71 | yield put({ 72 | type: 'querySuccess', 73 | stocks: [...data.products], 74 | funds: [...data.products] 75 | }); 76 | } 77 | } 78 | } 79 | }, 80 | 81 | reducers: { 82 | querySuccess(state, action){ 83 | return {...state, stocks: action.stocks, funds: action.funds, loading: false}; 84 | }, 85 | queryProductsSuccess(state, action){ 86 | const products = action.products; 87 | products.unshift({'_id':'00000', productName:'全部'}); 88 | return {...state, products}; 89 | }, 90 | settlementSuccess(state, action){ 91 | return {...state, ...action.payload}; 92 | }, 93 | showLoading(state, action){ 94 | return {...state, loading: true}; 95 | }, 96 | }, 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/models/settlement.js: -------------------------------------------------------------------------------- 1 | import {query, querySettlementItem} from '../services/settlement'; 2 | import {parse} from 'qs'; 3 | export default { 4 | 5 | namespace: 'settlement', 6 | 7 | state: { 8 | list: [], 9 | total: null, 10 | timeRange: [], 11 | settlementItems: [], 12 | loading: false, 13 | current: null, 14 | breadcrumbItems: [ 15 | ['/settlement', '首页'], 16 | ['/settlement', '结算管理'], 17 | ], 18 | settlementId:'' 19 | }, 20 | 21 | subscriptions: { 22 | setup({dispatch, history}) { 23 | history.listen(location => { 24 | if (location.pathname == '/settlement') { 25 | dispatch({ 26 | type: 'query', 27 | payload: location.query 28 | }); 29 | } 30 | }); 31 | }, 32 | }, 33 | 34 | effects: { 35 | *query({payload}, {select, call, put}){ 36 | const isLogin = yield select(({systemUser})=> systemUser.isLogin); 37 | if(!isLogin){ 38 | return; 39 | } 40 | yield put({type: 'showLoading'}); 41 | yield put({ 42 | type: 'updateQueryKey', 43 | payload: { 44 | page: 1, 45 | timeRange: '', 46 | ...payload 47 | } 48 | }); 49 | const {data} = yield call(query, parse(payload)); 50 | if (data) { 51 | yield put({ 52 | type: 'querySuccess', 53 | payload: { 54 | list: data.settlements, 55 | total: data.page.total, 56 | current: data.page.current 57 | } 58 | }); 59 | } 60 | }, 61 | *settlementSelect({payload}, {call, put}){ 62 | const {data} = yield call(querySettlementItem, {settlementId: payload.settlementId}); 63 | if (data && data.success) { 64 | yield put({ 65 | type: 'querySettlementItemSuccess', 66 | settlementItems: data.settlementItems, 67 | settlementId: payload.settlementId 68 | }); 69 | } 70 | } 71 | }, 72 | 73 | reducers: { 74 | showLoading(state, action){ 75 | return {...state, loading: true}; 76 | }, 77 | querySuccess(state, action){ 78 | const settlementId = action.payload.list.length>0 ? action.payload.list[0]._id:''; 79 | return {...state, ...action.payload, settlementId, loading: false}; 80 | }, 81 | querySettlementItemSuccess(state, action){ 82 | return {...state, settlementItems:action.settlementItems, settlementId:action.settlementId, loading: false}; 83 | }, 84 | updateQueryKey(state, action){ 85 | return {...state, ...action.payload}; 86 | }, 87 | }, 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/models/supplierBills.js: -------------------------------------------------------------------------------- 1 | import {query, doClearStorage, doClearBill} from '../services/supplierBills'; 2 | import {parse} from 'qs'; 3 | import {routerRedux} from 'dva/router'; 4 | export default { 5 | 6 | namespace: 'supplierBillsSpace', 7 | 8 | state: { 9 | list: [], 10 | total: null, 11 | loading: false, 12 | page: 1, 13 | supplierId: '', 14 | breadcrumbItems: [ 15 | ['/supplierBills', '首页'], 16 | ['/supplierBills', '账单管理'], 17 | ], 18 | storage: [], 19 | supplierBills: [], 20 | suppliers: [], 21 | visible: false, 22 | editorType: 'clearStorage', 23 | currentItem: {} 24 | }, 25 | 26 | subscriptions: { 27 | setup({dispatch, history}) { 28 | history.listen(location => { 29 | if (location.pathname == '/supplierBills') { 30 | dispatch({ 31 | type: 'query', 32 | payload: location.query 33 | }); 34 | } 35 | }); 36 | }, 37 | }, 38 | 39 | effects: { 40 | *query({payload}, {select, call, put}){ 41 | const isLogin = yield select(({systemUser})=> systemUser.isLogin); 42 | if(!isLogin){ 43 | return; 44 | } 45 | yield put({type: 'showLoading'}); 46 | yield put({ 47 | type: 'updateQueryKey', 48 | payload: { 49 | page: 1, 50 | ...payload 51 | } 52 | }); 53 | let {page, supplierId} = payload; 54 | supplierId = supplierId=='00000'?'':supplierId; 55 | const {data} = yield call(query, parse({page, supplierId})); 56 | if (data) { 57 | yield put({ 58 | type: 'querySuccess', 59 | payload: { 60 | storage: data.storage, 61 | suppliers: data.suppliers, 62 | supplierBills: data.supplierBills, 63 | total: data.page.total, 64 | current: data.page.current 65 | } 66 | }); 67 | } 68 | }, 69 | *doClearStorage({payload}, {call, put}){ 70 | let {storageId, paymentAmount} = payload; 71 | const {data} = yield call(doClearStorage, {storageId, paymentAmount}); 72 | if (data && data.success) { 73 | yield put({ 74 | type: 'hideEditor' 75 | }); 76 | yield put(routerRedux.push('/supplierBills')); 77 | } 78 | }, 79 | *doClearBill({payload}, {call, put}){ 80 | const {supplierId} = payload; 81 | const {data} = yield call(doClearBill, {supplierId}); 82 | if (data && data.success) { 83 | yield put({ 84 | type: 'hideEditor' 85 | }); 86 | yield put(routerRedux.push('/supplierBills')); 87 | } 88 | }, 89 | }, 90 | 91 | reducers: { 92 | showLoading(state, action){ 93 | return {...state, loading: true}; 94 | }, 95 | querySuccess(state, action){ 96 | const suppliers = action.payload.suppliers; 97 | suppliers.unshift({ 98 | _id:'00000', 99 | supplierName: '全部' 100 | }); 101 | return {...state, ...action.payload, loading: false}; 102 | }, 103 | clearStorage(state, action){ 104 | const currentItem = action.payload.order; 105 | return {...state, currentItem, editorType:'clearStorage', visible: true}; 106 | }, 107 | clearBill(state, action){ 108 | const currentItem = action.payload.bill; 109 | return {...state, currentItem, editorType:'clearBill', visible: true}; 110 | }, 111 | hideEditor(state, action){ 112 | return {...state, currentItem:{}, editorType:'clearStorage', visible: false}; 113 | }, 114 | updateQueryKey(state, action){ 115 | return {...state, ...action.payload}; 116 | } 117 | }, 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import {Router, Route, IndexRoute, Link} from 'dva/router'; 3 | import HomePage from './routes/HomePage/HomePage'; 4 | import IndexPage from './routes/IndexPage/IndexPage'; 5 | import Orders from './routes/Orders/Orders'; 6 | import Storage from './routes/Storage/Storage'; 7 | import Stock from './routes/Stock/Stock'; 8 | import Funds from './routes/Funds/Funds'; 9 | import Resource from './routes/Resource/Resource'; 10 | import Settlement from './routes/Settlement/Settlement'; 11 | import Bills from './routes/Bills/Bills'; 12 | import CustomerBills from './routes/CustomerBills/CustomerBills'; 13 | import SupplierBills from './routes/SupplierBills/SupplierBills'; 14 | import Manage from './routes/Manage/Manage'; 15 | import Customers from './routes/Customers/Customers'; 16 | import Products from './routes/Products/Products'; 17 | import Suppliers from './routes/Suppliers/Suppliers'; 18 | import {requireAuth} from './utils/webSessionUtils'; 19 | 20 | export default function ({history}) { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {/* 34 | 35 | */} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /src/routes/Bills/Bills.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component,PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {routerRedux} from 'dva/router'; 4 | import {Search} from '../../components/SearchBar/SearchBar'; 5 | import {redirect} from '../../utils/webSessionUtils'; 6 | require('./index.css'); 7 | 8 | class Bills extends Component{ 9 | constructor(props) { 10 | super(props); 11 | } 12 | 13 | componentWillMount(){ 14 | let {isLogin} = this.props.systemUser; 15 | if(!isLogin){ 16 | redirect(); 17 | } 18 | } 19 | 20 | render(){ 21 | return ( 22 |
23 | {this.props.children} 24 |
25 | ); 26 | } 27 | } 28 | 29 | Bills.propTypes = { 30 | orders:PropTypes.object, 31 | }; 32 | 33 | function mapStateToProps({systemUser}) { 34 | return {systemUser}; 35 | } 36 | 37 | export default connect(mapStateToProps)(Bills); -------------------------------------------------------------------------------- /src/routes/Bills/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/src/routes/Bills/index.css -------------------------------------------------------------------------------- /src/routes/CustomerBills/index.css: -------------------------------------------------------------------------------- 1 | .search { 2 | margin-bottom: 20px; 3 | padding: 20px 25px; 4 | display: flex; 5 | flex-direction: row; 6 | justify-content: space-between; 7 | align-items: center; 8 | background: #fff; 9 | } 10 | 11 | .debtOrdersListContainer, 12 | .customerBillsListContainer { 13 | padding: 20px 25px 50px; 14 | background: #fff; 15 | margin-bottom: 20px; 16 | } 17 | .debtOrdersListContainer { 18 | padding: 20px 25px 100px; 19 | } 20 | 21 | .billsList { 22 | margin-top: 20px; 23 | background-color: #fff; 24 | padding: 20px 25px 50px; 25 | } 26 | 27 | .billsListTitle { 28 | padding: 20px 0; 29 | border-bottom: solid 2px #979797; 30 | text-align: center; 31 | font-size: 20px; 32 | position: relative; 33 | margin-bottom: 30px; 34 | font-weight: normal; 35 | } -------------------------------------------------------------------------------- /src/routes/Customers/index.css: -------------------------------------------------------------------------------- 1 | .customerContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } -------------------------------------------------------------------------------- /src/routes/Funds/Funds.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {routerRedux} from 'dva/router'; 4 | import FundsSearchForm from '../../components/Funds/FundsSearchForm/FundsSearchForm'; 5 | import FundsList from '../../components/Funds/FundsList/FundsList'; 6 | import {redirect} from '../../utils/webSessionUtils'; 7 | import styles from './index.css'; 8 | 9 | function genFunds({dispatch, funds, loading}) { 10 | /*const { 11 | list, 12 | field, 13 | keyword, 14 | loading 15 | } = funds; 16 | 17 | const fundsSearch = { 18 | field, 19 | keyword, 20 | onSearch(fieldValues){ 21 | dispatch(routerRedux.push({ 22 | pathname: '/funds', 23 | query: {...fieldValues, page: 1} 24 | })); 25 | } 26 | };*/ 27 | const fundsList = { 28 | dataSource: funds, 29 | loading 30 | }; 31 | return ( 32 |
33 |

资金明细表

34 | {/**/} 35 | 36 |
37 | ); 38 | } 39 | class Funds extends Component { 40 | constructor(props) { 41 | super(props); 42 | } 43 | 44 | static propTypes = { 45 | stocks: PropTypes.array, 46 | }; 47 | 48 | render() { 49 | return genFunds(this.props); 50 | } 51 | } 52 | 53 | export default Funds; -------------------------------------------------------------------------------- /src/routes/Funds/index.css: -------------------------------------------------------------------------------- 1 | .fundsContainer { 2 | padding: 20px 25px 50px; 3 | background: #fff; 4 | } 5 | 6 | .fundsTitle { 7 | padding: 20px 0; 8 | border-bottom: solid 2px #979797; 9 | text-align: center; 10 | font-size: 20px; 11 | position: relative; 12 | margin-bottom: 30px; 13 | font-weight: normal; 14 | } -------------------------------------------------------------------------------- /src/routes/HomePage/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Header from '../../components/Header/Header'; 3 | import {connect} from 'dva'; 4 | import SystemInfo from '../../components/SystemInfo/SystemInfo'; 5 | import {homePage, container} from './index.css'; 6 | 7 | const HomePage = ({children, home})=> { 8 | const {activeIndex} = home; 9 | const height = window.innerHeight - 64; 10 | return ( 11 |
12 |
13 | 14 |
15 | {children} 16 |
17 |
18 | ); 19 | }; 20 | 21 | function mapStateToProps({home}) { 22 | return {home}; 23 | } 24 | 25 | export default connect(mapStateToProps)(HomePage); -------------------------------------------------------------------------------- /src/routes/HomePage/index.css: -------------------------------------------------------------------------------- 1 | .homePage { 2 | height: 100%; 3 | background: #ececec; 4 | padding-top: 64px; 5 | box-sizing: border-box; 6 | } 7 | .container { 8 | margin-left: 300px; 9 | padding: 25px; 10 | box-sizing: border-box; 11 | position: relative; 12 | } -------------------------------------------------------------------------------- /src/routes/IndexPage/IndexPage.js: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {Link} from 'dva/router'; 4 | import {normal, title, welcome, list} from './index.css'; 5 | 6 | function IndexPage() { 7 | return ( 8 |
9 |

欢迎光临铭帝系统门窗管理系统

10 |
11 | {/**/} 16 |
17 | ); 18 | } 19 | 20 | IndexPage.propTypes = {}; 21 | 22 | export default connect()(IndexPage); 23 | -------------------------------------------------------------------------------- /src/routes/IndexPage/index.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | text-align: center; 4 | padding-top: 150px; 5 | background-color: #fff; 6 | box-sizing: border-box; 7 | position: absolute; 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | left: 0; 12 | } 13 | 14 | .title { 15 | font-size: 24px; 16 | font-weight: normal; 17 | letter-spacing: 1px; 18 | } 19 | 20 | .welcome { 21 | height: 328px; 22 | background: url(../../assets/yay.jpg) no-repeat center 0; 23 | background-size: 388px 328px; 24 | margin-top: 50px; 25 | } 26 | 27 | .list { 28 | font-size: 1.2em; 29 | margin-top: 1.8em; 30 | list-style: none; 31 | line-height: 1.5em; 32 | } 33 | 34 | .list code { 35 | background: #f7f7f7; 36 | } 37 | -------------------------------------------------------------------------------- /src/routes/Manage/Manage.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component,PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {routerRedux} from 'dva/router'; 4 | import {Search} from '../../components/SearchBar/SearchBar'; 5 | import {redirect} from '../../utils/webSessionUtils'; 6 | require('./index.css'); 7 | 8 | class Manage extends Component{ 9 | constructor(props) { 10 | super(props); 11 | } 12 | 13 | componentWillMount(){ 14 | let {isLogin} = this.props.systemUser; 15 | if(!isLogin){ 16 | redirect(); 17 | } 18 | } 19 | 20 | render(){ 21 | return ( 22 |
23 | {this.props.children} 24 |
25 | ); 26 | } 27 | } 28 | 29 | Manage.propTypes = { 30 | orders:PropTypes.object, 31 | }; 32 | 33 | function mapStateToProps({systemUser}) { 34 | return {systemUser}; 35 | } 36 | 37 | export default connect(mapStateToProps)(Manage); -------------------------------------------------------------------------------- /src/routes/Manage/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yvanwangl/AccountSystem/93e90142fba857dad710393725d2f98136c682bd/src/routes/Manage/index.css -------------------------------------------------------------------------------- /src/routes/Orders/index.css: -------------------------------------------------------------------------------- 1 | .orderContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } -------------------------------------------------------------------------------- /src/routes/Products/index.css: -------------------------------------------------------------------------------- 1 | .productContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } -------------------------------------------------------------------------------- /src/routes/Resource/Resource.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {routerRedux} from 'dva/router'; 4 | import BreadcrumbList from '../../components/BreadcrumbList/BreadcrumbList'; 5 | import ResourceSearchForm from '../../components/Resource/ResourceSearchForm/ResourceSearchForm'; 6 | import Stock from '../Stock/Stock'; 7 | import Funds from '../Funds/Funds'; 8 | import {Button} from 'antd'; 9 | import {redirect} from '../../utils/webSessionUtils'; 10 | import styles from './index.css'; 11 | 12 | function genResource({dispatch, resource}) { 13 | 14 | const { 15 | breadcrumbItems, 16 | products, 17 | stocks, 18 | funds, 19 | loading 20 | } = resource; 21 | 22 | const onSearch = (fieldValues) => { 23 | dispatch({ 24 | type: 'resource/query', 25 | payload: fieldValues 26 | }); 27 | }; 28 | 29 | const onSettlement = ()=>{ 30 | dispatch({ 31 | type: 'resource/onSettlement' 32 | }); 33 | }; 34 | 35 | return ( 36 |
37 | 38 |
39 | 40 |
41 | 42 |
43 |
44 | 45 | 46 |
47 | ); 48 | } 49 | class Resource extends Component { 50 | constructor(props) { 51 | super(props); 52 | } 53 | 54 | componentWillMount(){ 55 | let {isLogin} = this.props.systemUser; 56 | return !isLogin && redirect(); 57 | } 58 | 59 | render() { 60 | let {isLogin} = this.props.systemUser; 61 | return isLogin && genResource(this.props); 62 | } 63 | } 64 | 65 | function mapStateToProps({resource, stocks, funds, systemUser}) { 66 | return {resource, systemUser}; 67 | } 68 | 69 | export default connect(mapStateToProps)(Resource); -------------------------------------------------------------------------------- /src/routes/Resource/index.css: -------------------------------------------------------------------------------- 1 | .resourceContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } 5 | 6 | .search { 7 | margin-bottom: 20px; 8 | padding: 0 25px; 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-between; 12 | align-items: center; 13 | background: #fff; 14 | } 15 | 16 | .settlementButton { 17 | display: inline-block; 18 | } -------------------------------------------------------------------------------- /src/routes/Settlement/Settlement.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component,PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import SearchBar from '../../components/SearchBar/SearchBar'; 4 | import SettlementSearchForm from '../../components/Settlement/SettlementSearchForm/SettlementSearchForm'; 5 | import SettlementList from '../../components/Settlement/SettlementList/SettlementList'; 6 | import ProductList from '../../components/Stocks/StockList/StockList'; 7 | import {routerRedux} from 'dva/router'; 8 | import BreadcrumbList from '../../components/BreadcrumbList/BreadcrumbList'; 9 | import {redirect} from '../../utils/webSessionUtils'; 10 | import {search, settlementClass, settlementContainer, addSettlementContainer, modifySettlementContainer, productList, productListTitle} from './index.css'; 11 | 12 | function genSettlement({dispatch, settlement}){ 13 | const { 14 | list, 15 | total, 16 | timeRange, 17 | loading, 18 | current, 19 | breadcrumbItems, 20 | settlementItems, 21 | settlementId 22 | } = settlement; 23 | 24 | const settlementListProps ={ 25 | current, 26 | total, 27 | dataSource: list, 28 | settlementId, 29 | loading, 30 | onSettlementSelect(settlementId){ 31 | dispatch({ 32 | type: 'settlement/settlementSelect', 33 | payload: { 34 | settlementId 35 | } 36 | }); 37 | }, 38 | onPageChange(page){ 39 | dispatch({ 40 | type:'settlement/query', 41 | payload: {timeRange, page} 42 | }); 43 | } 44 | }; 45 | 46 | const onSearch = (fieldValues)=>{ 47 | dispatch({ 48 | type:'settlement/query', 49 | payload:{...fieldValues, page:1} 50 | }); 51 | }; 52 | 53 | return ( 54 |
55 | 56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 |

结算商品明细

64 | 65 |
66 |
67 | ); 68 | } 69 | 70 | class Settlement extends Component { 71 | constructor(props){ 72 | super(props); 73 | } 74 | 75 | componentWillMount(){ 76 | let {isLogin} = this.props.systemUser; 77 | return !isLogin && redirect(); 78 | } 79 | 80 | render(){ 81 | let {isLogin} = this.props.systemUser; 82 | return isLogin && genSettlement(this.props); 83 | } 84 | } 85 | 86 | Settlement.propTypes = { 87 | orders:PropTypes.object, 88 | }; 89 | 90 | function mapStateToProps({settlement, systemUser}) { 91 | return {settlement, systemUser}; 92 | } 93 | 94 | 95 | export default connect(mapStateToProps)(Settlement); -------------------------------------------------------------------------------- /src/routes/Settlement/index.css: -------------------------------------------------------------------------------- 1 | .search { 2 | margin-bottom: 20px; 3 | display: flex; 4 | flex-direction: row; 5 | justify-content: space-between; 6 | align-items: center; 7 | } 8 | 9 | .settlementContainer { 10 | padding: 20px 25px 100px; 11 | background: #fff; 12 | } 13 | 14 | .productList { 15 | margin-top: 20px; 16 | background-color: #fff; 17 | padding: 20px 25px 50px; 18 | } 19 | 20 | .productListTitle { 21 | padding: 20px 0; 22 | border-bottom: solid 2px #979797; 23 | text-align: center; 24 | font-size: 20px; 25 | position: relative; 26 | margin-bottom: 30px; 27 | font-weight: normal; 28 | } -------------------------------------------------------------------------------- /src/routes/Stock/Stock.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import {connect} from 'dva'; 3 | import {routerRedux} from 'dva/router'; 4 | import StockSearchForm from '../../components/Stocks/StockSearchForm/StockSearchForm'; 5 | import StockList from '../../components/Stocks/StockList/StockList'; 6 | import {redirect} from '../../utils/webSessionUtils'; 7 | import styles from './index.css'; 8 | 9 | function genStock({dispatch, stocks, loading}) { 10 | /*const { 11 | list, 12 | field, 13 | keyword, 14 | loading 15 | } = stocks; 16 | 17 | const stockSearch = { 18 | field, 19 | keyword, 20 | onSearch(fieldValues){ 21 | dispatch(routerRedux.push({ 22 | pathname: '/stocks', 23 | query: {...fieldValues, page: 1} 24 | })); 25 | } 26 | };*/ 27 | const stockList = { 28 | dataSource: stocks, 29 | loading 30 | }; 31 | return ( 32 |
33 |

仓库明细表

34 | {/**/} 35 | 36 |
37 | ); 38 | } 39 | class Stock extends Component { 40 | constructor(props) { 41 | super(props); 42 | } 43 | 44 | static propTypes = { 45 | stocks: PropTypes.array, 46 | }; 47 | 48 | render() { 49 | return genStock(this.props); 50 | } 51 | } 52 | 53 | export default Stock; -------------------------------------------------------------------------------- /src/routes/Stock/index.css: -------------------------------------------------------------------------------- 1 | .stockContainer { 2 | padding: 20px 25px 50px; 3 | background: #fff; 4 | margin-bottom: 20px; 5 | } 6 | 7 | .stockTitle { 8 | padding: 20px 0; 9 | border-bottom: solid 2px #979797; 10 | text-align: center; 11 | font-size: 20px; 12 | position: relative; 13 | margin-bottom: 30px; 14 | font-weight: normal; 15 | } -------------------------------------------------------------------------------- /src/routes/Storage/index.css: -------------------------------------------------------------------------------- 1 | .storageContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } -------------------------------------------------------------------------------- /src/routes/SupplierBills/index.css: -------------------------------------------------------------------------------- 1 | .search { 2 | margin-bottom: 20px; 3 | padding: 20px 25px; 4 | display: flex; 5 | flex-direction: row; 6 | justify-content: space-between; 7 | align-items: center; 8 | background: #fff; 9 | } 10 | 11 | .debtStorageListContainer, 12 | .supplierBillsListContainer { 13 | padding: 20px 25px 50px; 14 | background: #fff; 15 | margin-bottom: 20px; 16 | } 17 | .debtStorageListContainer { 18 | padding: 20px 25px 100px; 19 | } 20 | 21 | .billsList { 22 | margin-top: 20px; 23 | background-color: #fff; 24 | padding: 20px 25px 50px; 25 | } 26 | 27 | .billsListTitle { 28 | padding: 20px 0; 29 | border-bottom: solid 2px #979797; 30 | text-align: center; 31 | font-size: 20px; 32 | position: relative; 33 | margin-bottom: 30px; 34 | font-weight: normal; 35 | } -------------------------------------------------------------------------------- /src/routes/Suppliers/index.css: -------------------------------------------------------------------------------- 1 | .supplierContainer { 2 | padding: 20px 25px 100px; 3 | background: #fff; 4 | } -------------------------------------------------------------------------------- /src/services/customerBills.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const ORDER_API = '/api/customerBills'; 5 | 6 | export async function query(params) { 7 | return request(`${ORDER_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function doClearOrder(params) { 11 | return request(`${ORDER_API}/doClearOrder`, { 12 | method: 'post', 13 | body: JSON.stringify(params) 14 | }); 15 | } 16 | 17 | export async function doClearBill(params) { 18 | return request(`${ORDER_API}/doClearBill`, { 19 | method: 'post', 20 | body: JSON.stringify(params) 21 | }); 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/services/customers.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const CUSTOMER_API = `/api/customers`; 5 | 6 | export async function query(params) { 7 | return request(`${CUSTOMER_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function queryAll() { 11 | return request(`${CUSTOMER_API}/all`); 12 | } 13 | 14 | export async function create(params) { 15 | return request(CUSTOMER_API, { 16 | method: 'post', 17 | body: JSON.stringify(params) 18 | }); 19 | } 20 | 21 | export async function modify(params) { 22 | return request(`${CUSTOMER_API}/${params['id']}`, { 23 | method: 'put', 24 | body: JSON.stringify(params) 25 | }); 26 | } 27 | 28 | export async function del(params) { 29 | return request(`${CUSTOMER_API}/${params['id']}`, { 30 | method: 'delete' 31 | }); 32 | } -------------------------------------------------------------------------------- /src/services/example.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | 3 | export async function query() { 4 | return request('/api/users'); 5 | } 6 | -------------------------------------------------------------------------------- /src/services/funds.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const FUND_API = '/api/resource'; 5 | 6 | export async function query(params) { 7 | console.log(params); 8 | console.log(qs.stringify(params)); 9 | return request(`${FUND_API}?${qs.stringify(params)}`); 10 | } -------------------------------------------------------------------------------- /src/services/orders.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const ORDER_API = '/api/orders'; 5 | 6 | export async function query(params) { 7 | console.log(params); 8 | console.log(qs.stringify(params)); 9 | return request(`${ORDER_API}?${qs.stringify(params)}`); 10 | } 11 | 12 | export async function create(params) { 13 | return request(ORDER_API, { 14 | method: 'post', 15 | body: JSON.stringify(params) 16 | }); 17 | } 18 | 19 | export async function modify(params) { 20 | return request(`${ORDER_API}/${params['id']}`, { 21 | method: 'put', 22 | body: JSON.stringify(params) 23 | }); 24 | } 25 | 26 | export async function del(params) { 27 | return request(`${ORDER_API}/${params['id']}`, { 28 | method: 'delete' 29 | }); 30 | } 31 | 32 | export async function getOrderNumber() { 33 | return request(`${ORDER_API}/getOrderNumber`); 34 | } 35 | 36 | export async function getCustomers() { 37 | return request(`${ORDER_API}/getCustomers`); 38 | } 39 | 40 | export async function queryOrderById(orderId) { 41 | console.log(orderId); 42 | return request(`${ORDER_API}/${orderId}`); 43 | } 44 | -------------------------------------------------------------------------------- /src/services/productStocks.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const PRODUCT_STOCKS_API = `/api/productStocks`; 5 | 6 | export async function query() { 7 | return request(`${PRODUCT_STOCKS_API}`); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/services/products.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const PRODUCT_API = `/api/products`; 5 | 6 | export async function query(params) { 7 | return request(`${PRODUCT_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function create(params) { 11 | return request(PRODUCT_API, { 12 | method: 'post', 13 | body: JSON.stringify(params) 14 | }); 15 | } 16 | 17 | export async function modify(params) { 18 | return request(`${PRODUCT_API}/${params['id']}`, { 19 | method: 'put', 20 | body: JSON.stringify(params) 21 | }); 22 | } 23 | 24 | export async function del(params) { 25 | return request(`${PRODUCT_API}/${params['id']}`, { 26 | method: 'delete' 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/services/resource.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const RESOURCE_API = '/api/resource'; 5 | 6 | export async function query(params) { 7 | return request(`${RESOURCE_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function onSettlement() { 11 | return request(RESOURCE_API,{ 12 | method: 'post', 13 | body: JSON.stringify({}) 14 | }); 15 | } -------------------------------------------------------------------------------- /src/services/serviceConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/13. 3 | */ 4 | -------------------------------------------------------------------------------- /src/services/settlement.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const SETTLEMENT_API = '/api/settlement'; 5 | 6 | export async function query(params) { 7 | return request(`${SETTLEMENT_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function querySettlementItem(params) { 11 | return request(`${SETTLEMENT_API}/${params.settlementId}`); 12 | } 13 | -------------------------------------------------------------------------------- /src/services/stocks.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const STOCK_API = '/api/stocks'; 5 | 6 | export async function query(params) { 7 | console.log(params); 8 | console.log(qs.stringify(params)); 9 | return request(`${STOCK_API}?${qs.stringify(params)}`); 10 | } -------------------------------------------------------------------------------- /src/services/storage.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const STORAGE_API = '/api/storage'; 5 | 6 | export async function query(params) { 7 | console.log(params); 8 | console.log(qs.stringify(params)); 9 | return request(`${STORAGE_API}?${qs.stringify(params)}`); 10 | } 11 | 12 | export async function create(params) { 13 | return request(STORAGE_API, { 14 | method: 'post', 15 | body: JSON.stringify(params) 16 | }); 17 | } 18 | 19 | export async function modify(params) { 20 | return request(`${STORAGE_API}/${params['id']}`, { 21 | method: 'put', 22 | body: JSON.stringify(params) 23 | }); 24 | } 25 | 26 | export async function del(params) { 27 | return request(`${STORAGE_API}/${params['id']}`, { 28 | method: 'delete' 29 | }); 30 | } 31 | 32 | export async function getNoteNumber() { 33 | return request(`${STORAGE_API}/getNoteNumber`); 34 | } 35 | 36 | export async function queryStorageById(storageId) { 37 | console.log(storageId); 38 | return request(`${STORAGE_API}/${storageId}`); 39 | } 40 | -------------------------------------------------------------------------------- /src/services/supplierBills.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const ORDER_API = '/api/supplierBills'; 5 | 6 | export async function query(params) { 7 | return request(`${ORDER_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function doClearStorage(params) { 11 | return request(`${ORDER_API}/doClearStorage`, { 12 | method: 'post', 13 | body: JSON.stringify(params) 14 | }); 15 | } 16 | 17 | export async function doClearBill(params) { 18 | return request(`${ORDER_API}/doClearBill`, { 19 | method: 'post', 20 | body: JSON.stringify(params) 21 | }); 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/services/suppliers.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | import qs from 'qs'; 3 | 4 | const SUPPLIER_API = `/api/suppliers`; 5 | 6 | export async function query(params) { 7 | return request(`${SUPPLIER_API}?${qs.stringify(params)}`); 8 | } 9 | 10 | export async function queryAll() { 11 | return request(`${SUPPLIER_API}/all`); 12 | } 13 | 14 | export async function create(params) { 15 | return request(SUPPLIER_API, { 16 | method: 'post', 17 | body: JSON.stringify(params) 18 | }); 19 | } 20 | 21 | export async function modify(params) { 22 | return request(`${SUPPLIER_API}/${params['id']}`, { 23 | method: 'put', 24 | body: JSON.stringify(params) 25 | }); 26 | } 27 | 28 | export async function del(params) { 29 | return request(`${SUPPLIER_API}/${params['id']}`, { 30 | method: 'delete' 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/services/systemUser.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | 3 | const LOGIN_API = '/system'; 4 | 5 | export async function doLogin(params) { 6 | console.log(params); 7 | return request( `${LOGIN_API}/login`,{ 8 | method: 'POST', 9 | body: JSON.stringify(params) 10 | }); 11 | } 12 | 13 | export async function doLogup(params) { 14 | return request( `${LOGIN_API}/logup`,{ 15 | method: 'POST', 16 | body: JSON.stringify(params) 17 | }); 18 | } 19 | 20 | export async function doLogout() { 21 | return request( `${LOGIN_API}/logout`,{ 22 | method: 'POST' 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/tests/models/example-test.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect'; 2 | import example from '../../models/example'; 3 | 4 | describe('example', () => { 5 | 6 | describe('reducer', () => { 7 | it('it should save', () => { 8 | expect(example.reducers['example/save']({}, {payload: {a: 1}})).toEqual({a: 1}); 9 | }); 10 | }) 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /src/utils/dateFormat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/18. 3 | */ 4 | 5 | export default function dateFormat(text, formatType) { 6 | let date = new Date(text); 7 | let year = date.getFullYear(); 8 | let month = preFixO(date.getMonth() + 1); 9 | let day = preFixO(date.getDate()); 10 | let dateStr = `${year}-${month}-${day}`; 11 | switch (formatType) { 12 | case 1: 13 | dateStr = `${year}年${month}月${day}日`; 14 | break; 15 | case 2: 16 | dateStr = `${year}/${month}/${day}`; 17 | break; 18 | default: 19 | break; 20 | } 21 | return dateStr; 22 | } 23 | 24 | function preFixO(number) { 25 | return ('0' + number).substr(-2); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/utils/numberFormat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hanlu on 2017/3/27. 3 | */ 4 | import numeral from 'numeral'; 5 | 6 | const numberFormat = (number)=> numeral(number).format('0,0.00'); 7 | 8 | export default numberFormat; -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import fetch from 'dva/fetch'; 2 | import {HTTP_SERVER, DEFAULT_OPTIONS} from '../constants/constants'; 3 | 4 | function parseJSON(response) { 5 | return response.json(); 6 | } 7 | 8 | function checkStatus(response) { 9 | if (response.status >= 200 && response.status < 300) { 10 | return response; 11 | } 12 | 13 | const error = new Error(response.statusText); 14 | error.response = response; 15 | throw error; 16 | } 17 | 18 | /** 19 | * Requests a URL, returning a promise. 20 | * 21 | * @param {string} url The URL we want to request 22 | * @param {object} [options] The options we want to pass to "fetch" 23 | * @return {object} An object containing either "data" or "err" 24 | */ 25 | //使用 express-session 时,浏览器端无法写入cookie的原因是: 26 | // 请求头设置模式为跨域,此处存在两个错误: 27 | // 1、开发模式下应该为跨域模式,而生产模式应该为同域模式 28 | // 2、开发模式下的跨越模式如果要写入 cookie 需要配置 credentials: 'include' 属性 29 | // const defaultOptions = { 30 | // mode: 'cors', 31 | // credentials: 'include', 32 | // headers: { 33 | // 'Content-Type': 'application/json' 34 | // }, 35 | // }; 36 | export default function request(url, options) { 37 | return fetch(`${HTTP_SERVER}${url}`, {...DEFAULT_OPTIONS, ...options}) 38 | .then(checkStatus) 39 | .then(parseJSON) 40 | .then((data) => ({data})) 41 | .catch((err) => ({err})); 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/webSessionUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wyf on 2017/1/16. 3 | */ 4 | import request from '../utils/request'; 5 | import {message} from 'antd'; 6 | import {browserHistory} from 'dva/router'; 7 | 8 | const AUTH_API = `/api/auth`; 9 | 10 | export function getCurrentUser() { 11 | let sessionStorage = window.sessionStorage; 12 | if (sessionStorage['userInfo']) { 13 | return JSON.parse(sessionStorage['userInfo']); 14 | } 15 | return {}; 16 | } 17 | 18 | export async function fetchIsAuth(callback) { 19 | return request(`${AUTH_API}?authToken=${getCurrentUser()['authToken']}`) 20 | .then(data => callback(data.data.isAuth)) 21 | .catch(e => console.log(e)); 22 | } 23 | 24 | export function redirect() { 25 | message.error('请登录!'); 26 | //browserHistory.push 可能会导致路由丢失,推荐使用 reduxRouter 27 | browserHistory.push('/'); 28 | return null; 29 | } 30 | 31 | /*授权验证*/ 32 | export function requireAuth(nextState, replace) { 33 | const userInfo = sessionStorage.getItem('userInfo'); 34 | if(!userInfo || userInfo=='null'){ 35 | message.error('请登录!'); 36 | replace('/'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /system.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by hanlu on 2017/3/24. 3 | */ 4 | const debug = process.env.NODE_ENV == 'development'; 5 | //服务器端口 6 | const serverPort = '4000'; 7 | 8 | //服务器地址 9 | //开发模式,服务器地址 10 | const httpServerDev = 'http://localhost:'+serverPort; 11 | //跨域请求头配置 12 | const defaultOptionsDev = { 13 | mode: 'cors', 14 | credentials: 'include', 15 | headers: { 16 | 'content-type': 'application/json' 17 | }, 18 | }; 19 | 20 | //部署模式,服务器地址 21 | const httpServerPro = ''; 22 | //同域请求头配置 23 | const defaultOptionsProd = { 24 | credentials: 'same-origin', 25 | headers: { 26 | 'content-type': 'application/json' 27 | }, 28 | }; 29 | 30 | 31 | //数据库地址 32 | //开发模式数据库地址 33 | const mongooseConnectDev = "mongodb://localhost:27017/accountSystem"; 34 | //部署模式数据库地址 35 | const mongooseConnectPro = "mongodb://localhost:27017/accountSystem"; 36 | 37 | //上传图片后返回的服务器地址 38 | //可以在 ./service/routes/uploadProductImg.js 中对返回图片的路径进行拼接修改 39 | const uploadImgServerDev = 'localhost'; 40 | const uploadImgServerPro = '192.168.195.74'; 41 | 42 | module.exports = { 43 | httpServer: debug ? httpServerDev:httpServerPro, 44 | serverPort: serverPort, 45 | defaultOptions: debug ? defaultOptionsDev : defaultOptionsProd, 46 | mongooseConnect: debug ? mongooseConnectDev:mongooseConnectPro, 47 | uploadImgServer: debug ? uploadImgServerDev:uploadImgServerPro, 48 | //是否支持注册功能 49 | register: debug 50 | }; -------------------------------------------------------------------------------- /upload/uploadfiles/.ignore: -------------------------------------------------------------------------------- 1 | #ignore file --------------------------------------------------------------------------------