├── .gitignore ├── README.md ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── paths.js ├── polyfills.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── webpackDevServer.config.js ├── mockjs └── index.js ├── package.json ├── public ├── favicon.ico ├── index.html ├── js │ ├── LodopFuncs.js │ └── printbase.js └── manifest.json ├── scripts ├── build.js ├── start.js └── test.js └── src ├── api ├── config.js ├── index.js └── resource.js ├── assets ├── imgs │ ├── avar.gif │ ├── demo.gif │ ├── express.svg │ ├── logo.png │ ├── logo.svg │ └── orders.svg └── less │ ├── app.less │ ├── dashboard.less │ ├── dialog.less │ ├── nomatch.less │ ├── order-detail.less │ ├── order-print-preview.less │ ├── sender-item.less │ └── sender-new.less ├── components ├── DropOption │ └── index.js ├── dashboard.jsx ├── loading.jsx ├── login.jsx ├── logout.jsx ├── order-assigned.jsx ├── order-detail.jsx ├── order-print-preview.jsx ├── order-unassign.jsx ├── order-vendor-list.jsx ├── orderlist-back.jsx ├── orderlist-finish.jsx ├── orderlist-msg.jsx ├── orderlist-new.jsx ├── orderlist-nomsg.jsx ├── orderlist.jsx ├── orders-back.jsx ├── printer-manager.jsx ├── return-visit-orders.jsx ├── sender-edit.jsx ├── sender-item.jsx ├── sender-new.jsx ├── sender-setting.jsx ├── unreturn-visit-orders.jsx ├── user-add.jsx └── userlist.jsx ├── containers ├── app.jsx ├── dashboard.jsx ├── express.jsx ├── nomatch.jsx ├── orders.jsx ├── print.jsx ├── scroll-to-top.jsx └── usermanager.jsx ├── http └── index.js ├── main.js ├── redux ├── actions │ ├── actionstype.js │ ├── menu.jsx │ └── user.jsx ├── reducers │ ├── index.jsx │ ├── menu.jsx │ └── user.jsx ├── sagas │ ├── fetchdata.jsx │ ├── index.js │ ├── signin.jsx │ └── signout.jsx └── store │ └── index.js ├── registerServiceWorker.js └── utils ├── city.js ├── index.js ├── init.js ├── menu.jsx └── print.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-antd-webpack2-sagas-router4 2 | 学习使用react、antd、webpack2、react-routerV4、redux-saga等技术栈Demo 3 | # 说明 4 | >该项目是为了学习react、webpack、antd等相关技术栈而搭建的,希望能在学习的过程中,记录相关的问题。 5 | # 演示 6 | ![演示](https://github.com/foxkingpk/react-antd-webpack2-sagas-router4/blob/master/src/assets/imgs/demo.gif?raw=true) 7 | ## 安装 8 | 进入源码主目录,运行`cnpm install`安装时,可能会比较慢。 9 | 10 | 这里,推荐使用[淘宝NPM镜像](http://npm.taobao.org/). 11 | 直接运行下面语句,以实现cnpm (gzip 压缩支持) 命令行工具代替默认的 npm: 12 | `npm install -g cnpm --registry=https://registry.npm.taobao.org` 13 | 14 | 然后,运行`cnpm install`,安装完相关的包后,即可进入下一步。 15 | ## 启动 16 | 运行`cnpm start`,正常情况下,会在默认浏览器中打开网址`http://localhost:3000`. 17 | 18 | 端口号可以在`scripts/start.js`中,如果已存在`process.evn.PORT`则直接修改其值`process.env.PORT = '3000';`,如果没有,则添加即可。 19 | 20 | ## RAP 21 | 使用`RAP`来模拟demo需要获取的数据,其用法参考[RAP官网](http://rapapi.org/platform/home.do) 22 | 不支持在http头里面带入`Authorization token`,所以代码中是将相关代码暂时屏蔽掉的。 23 | 24 | ## antd 25 | 蚂蚁金服出品,非常棒的UI库,使用起来还是挺方便的,其用法参考[antdesign官网](https://ant.design/index-cn) 26 | ## 其他库 27 | `axios` 28 | 29 | 基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用 30 | 31 | `redux-saga` 32 | 33 | [中文文档](http://leonshi.com/redux-saga-in-chinese/index.html) 34 | 35 | ## 打包部署 36 | 运行`cnpm run build`,将资源打包输出到`build`目录。 37 | ## 编译、运行问题 38 | 1. 如果编译出错,请删除node_modules目录,重新cnpm install再试。因为有些版本更新了依赖包,需要安装 39 | ## 初学知识点 40 | ### 一、create-react-app脚手架 41 | facebook官方推出的脚手架,功能还是很强大,对于新手开发完全够用了。具体配置请[点我](https://github.com/facebookincubator/create-react-app) 42 | 43 | ### 二、token机制 44 | 在成功登录后,将获取的token保存在redux和localStorage中, 以后每次api请求时,将token带到http头里面。具体可参考`src/http/index.js` 45 | http请求使用的事axios库,其提供了请求和响应的拦截器,这样可以方便我们进行相关处理,比如:NProgress控件的使用 46 | ```javascript 47 | axios.interceptors.request.use( 48 | (config) => { 49 | if (localStorage.token) { 50 | config.headers.Authorization = `token ${localStorage.token}`; 51 | } 52 | NProgress.start();//加载进度条显示 53 | return config; 54 | }, 55 | (err) => { 56 | return Promise.reject(err); 57 | } 58 | ); 59 | ``` 60 | 如果遇到token过期等情况根据和服务端的约定,下发相关的状态码,在响应拦截器`axios.interceptors.response.use`中进行相关处理即可。 61 | 62 | 当刷新页面或者重新进入页面时,redux中保存的信息就会丢失,此时从localStorage中读取token相关信息,调用登录接口,具体实现`src/utils/init.js`中。 63 | 64 | ### 三、打包的js文件过大,怎么办? 65 | 66 | create-react-app默认打包出来只有一个js文件,这样就会出现js文件体积过大。 67 | 这里简单的说说我的做法: 68 | 1. 配置webpack, 将第三方库打包出独立的js文件, 至于为什么会多出manifest?主要解决每次打包出来的文件hash值都会该变的问题,具体代码如下: 69 | ```javascript 70 | entry: { 71 | main: [ 72 | require.resolve('./polyfills'), 73 | paths.appIndexJs 74 | ], 75 | vendor: [ 76 | 'react', 77 | 'react-router', 78 | 'react-redux', 79 | 'redux-saga', 80 | 'react-router-dom' 81 | ] 82 | } 83 | plugins: [ 84 | new webpack.optimize.CommonsChunkPlugin({ 85 | names: ['main', 'vendor', 'manifest'] 86 | }) 87 | ] 88 | ``` 89 | 2. 使用`code split`,其原理是:按需加载,举例说明:进入登录页面,那么我们就只加载登录页面需要的资源;其它的资源先不加载,这样就加快了页面的呈现速度。网上的介绍文章也很多,这里推荐一篇[请点我](http://serverless-stack.com/chapters/code-splitting-in-create-react-app.html)。 90 | 91 | 我的代码实现是使用`react-loadable`库,具体实现在`src/components/loading.jsx`文件,调用的地方在`src/containers/express.jsx`文件,我这里只是为了演示代码分割的功能,所以只在express.jsx中使用了代码分割,并且该功能最好在`cnpm run build`打包时再使用,在开发模式中要是开启代码分割,好像会影响热加载速度(修改文件后,编译变慢)。 92 | 93 | ### 四、页面滚动后,当打开新页面时,页面还是停留在之前的位置,怎么办? 94 | 在开发spa页面时,会发现如果当前页面出现滚动条时,此时打开新的页面,如果新的页面比较长的情况下,就会发现新的页面不是从页面顶部显示。 95 | 解决办法: 切换页面时,使用`window.scroll(0, 0)`函数将页面滚动到顶部。 96 | 具体实现:参考`src/containers/scroll-to-top.jsx`组件,然后其使用是在`src/main.js`中; 97 | 98 | ### 五、前后端分离,如何解决跨域问题 99 | 1. 配置package.json 100 | 在开发模式,我们只需在`package.json`中增加字段`"proxy": 'http://serverip'`,将serverip替换成你的服务端提供的接口地址。 101 | 2. 使用Nginx反向代理 102 | 使用Nginx反向代理还是比较强大,不局限于开发模式。 103 | 使用时,只需配置Nginx的配置文件即可,但是需要注意一点:location的配置路径。 104 | 比如:服务端提供的接口地址是:http://192.168.16.200:8080, 我们的开发模式开启的端口假设是3000, 105 | ```javascript 106 | server { 107 | listen 80; 108 | server_name localhost; 109 | 110 | location / { 111 | proxy_pass http://127.0.0.1:3000; 112 | } 113 | #说明: 只有地址以'http://localhost/yq'开始的接口请求才会被Nginx代理到真正的服务器地址(http://192.168.16.200:8080),当然这里的/yq不是乱写的,和你的接口请求地址相关。其实可以这么理解: 114 | 你的代码中登录接口地址:'/yq/api/v1/user/sigin',通过Nginx访问时,请求地址成为`http://localhost/yq/api/v1/user/sigin`,Nginx检测到请求地址以'http://localhost/yq'开头,那么就会将该请求地址代理到真正的服务器接口地址上`http://192.168.16.200:8080/yq/api/v1/user/sigin` 115 | location /yq { 116 | proxy_pass http://192.168.16.200:8080; 117 | # nginx非80端口处理 118 | proxy_set_header Host $host:$server_port; 119 | # 获取真实IP 120 | proxy_set_header X-Real-IP $remote_addr; 121 | # 获取代理者的真实ip 122 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 123 | proxy_set_header X-Forwarded-Proto $scheme; 124 | } 125 | } 126 | ``` 127 | 128 | -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. 31 | // https://github.com/motdotla/dotenv 32 | dotenvFiles.forEach(dotenvFile => { 33 | if (fs.existsSync(dotenvFile)) { 34 | require('dotenv').config({ 35 | path: dotenvFile, 36 | }); 37 | } 38 | }); 39 | 40 | // We support resolving modules according to `NODE_PATH`. 41 | // This lets you use absolute paths in imports inside large monorepos: 42 | // https://github.com/facebookincubator/create-react-app/issues/253. 43 | // It works similar to `NODE_PATH` in Node itself: 44 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 45 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 46 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 47 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 48 | // We also resolve them to make sure all tools using them work consistently. 49 | const appDirectory = fs.realpathSync(process.cwd()); 50 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 51 | .split(path.delimiter) 52 | .filter(folder => folder && !path.isAbsolute(folder)) 53 | .map(folder => path.resolve(appDirectory, folder)) 54 | .join(path.delimiter); 55 | 56 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 57 | // injected into the application via DefinePlugin in Webpack configuration. 58 | const REACT_APP = /^REACT_APP_/i; 59 | 60 | function getClientEnvironment(publicUrl) { 61 | const raw = Object.keys(process.env) 62 | .filter(key => REACT_APP.test(key)) 63 | .reduce( 64 | (env, key) => { 65 | env[key] = process.env[key]; 66 | return env; 67 | }, 68 | { 69 | // Useful for determining whether we’re running in production mode. 70 | // Most importantly, it switches React into the correct mode. 71 | NODE_ENV: process.env.NODE_ENV || 'development', 72 | // Useful for resolving the correct path to static assets in `public`. 73 | // For example, . 74 | // This should only be used as an escape hatch. Normally you would put 75 | // images into the `src` and `import` them in code to get their paths. 76 | PUBLIC_URL: publicUrl, 77 | } 78 | ); 79 | // Stringify all values so we can feed into Webpack DefinePlugin 80 | const stringified = { 81 | 'process.env': Object.keys(raw).reduce((env, key) => { 82 | env[key] = JSON.stringify(raw[key]); 83 | return env; 84 | }, {}), 85 | }; 86 | 87 | return { raw, stringified }; 88 | } 89 | 90 | module.exports = getClientEnvironment; 91 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /public/js/LodopFuncs.js: -------------------------------------------------------------------------------- 1 | var CreatedOKLodop7766=null; 2 | 3 | //====判断是否需要安装CLodop云打印服务器:==== 4 | function needCLodop(){ 5 | try{ 6 | var ua=navigator.userAgent; 7 | if (ua.match(/Windows\sPhone/i) !=null) return true; 8 | if (ua.match(/iPhone|iPod/i) != null) return true; 9 | if (ua.match(/Android/i) != null) return true; 10 | if (ua.match(/Edge\D?\d+/i) != null) return true; 11 | 12 | var verTrident=ua.match(/Trident\D?\d+/i); 13 | var verIE=ua.match(/MSIE\D?\d+/i); 14 | var verOPR=ua.match(/OPR\D?\d+/i); 15 | var verFF=ua.match(/Firefox\D?\d+/i); 16 | var x64=ua.match(/x64/i); 17 | if ((verTrident==null)&&(verIE==null)&&(x64!==null)) 18 | return true; else 19 | if ( verFF !== null) { 20 | verFF = verFF[0].match(/\d+/); 21 | if ((verFF[0]>= 42)||(x64!==null)) return true; 22 | } else 23 | if ( verOPR !== null) { 24 | verOPR = verOPR[0].match(/\d+/); 25 | if ( verOPR[0] >= 32 ) return true; 26 | } else 27 | if ((verTrident==null)&&(verIE==null)) { 28 | var verChrome=ua.match(/Chrome\D?\d+/i); 29 | if ( verChrome !== null ) { 30 | verChrome = verChrome[0].match(/\d+/); 31 | if (verChrome[0]>=42) return true; 32 | }; 33 | }; 34 | return false; 35 | } catch(err) {return true;}; 36 | }; 37 | 38 | //====获取LODOP对象的主过程:==== 39 | function getLodop(oOBJECT,oEMBED){ 40 | var strHtmInstall="打印控件未安装!"; 41 | var strHtmUpdate="打印控件需要升级!"; 42 | var strHtm64_Install="打印控件未安装"; 43 | var strHtm64_Update="打印控件需要升级!"; 44 | var strHtmFireFox="注意:如曾安装过Lodop旧版附件npActiveXPLugin,请在【工具】->【附加组件】->【扩展】中先卸它)"; 45 | var strHtmChrome="如果此前正常,仅因浏览器升级或重安装而出问题,需重新执行以上安装"; 46 | var strCLodopInstall="CLodop云打印服务(localhost本地)未安装启动!"; 47 | var strCLodopUpdate="CLodop云打印服务需升级!"; 48 | var LODOP; 49 | try{ 50 | var isIE = (navigator.userAgent.indexOf('MSIE')>=0) || (navigator.userAgent.indexOf('Trident')>=0); 51 | if (needCLodop()) { 52 | try{ LODOP = window.getCLodop();} catch(err) {}; 53 | if (!LODOP && document.readyState!=="complete") { 54 | alert("C-Lodop没准备好,请稍后再试!"); 55 | return; 56 | }; 57 | if (!LODOP) { 58 | if (isIE) 59 | ;//alert(strCLodopInstall+' 请到打印管理页面进行检测或者更新!'); 60 | else 61 | // alert(strCLodopInstall+' 请到打印管理页面进行检测或者更新!'); 62 | return; 63 | } else { 64 | if (CLODOP.CVERSION<"2.1.0.2") { 65 | if (isIE) 66 | ;//alert(strCLodopUpdate+' 请到打印管理页面进行检测或者更新!'); 67 | else 68 | ;//alert(strCLodopUpdate+' 请到打印管理页面进行检测或者更新!'); 69 | return; 70 | } 71 | if (oEMBED && oEMBED.parentNode) 72 | oEMBED.parentNode.removeChild(oEMBED); 73 | if (oOBJECT && oOBJECT.parentNode) 74 | oOBJECT.parentNode.removeChild(oOBJECT); 75 | }; 76 | } else { 77 | var is64IE = isIE && (navigator.userAgent.indexOf('x64')>=0); 78 | //=====如果页面有Lodop就直接使用,没有则新建:========== 79 | if (oOBJECT!=undefined || oEMBED!=undefined) { 80 | if (isIE) 81 | LODOP=oOBJECT; 82 | else 83 | LODOP=oEMBED; 84 | } else if (CreatedOKLodop7766==null){ 85 | LODOP=document.createElement("object"); 86 | LODOP.setAttribute("width",0); 87 | LODOP.setAttribute("height",0); 88 | LODOP.setAttribute("style","position:absolute;left:0px;top:-100px;width:0px;height:0px;"); 89 | if (isIE) 90 | LODOP.setAttribute("classid","clsid:2105C259-1E0C-4534-8141-A753534CB4CA"); 91 | else 92 | LODOP.setAttribute("type","application/x-print-lodop"); 93 | document.documentElement.appendChild(LODOP); 94 | CreatedOKLodop7766=LODOP; 95 | } else { 96 | LODOP=CreatedOKLodop7766; 97 | } 98 | //=====Lodop插件未安装时提示下载地址:========== 99 | if ((LODOP==null)||(typeof(LODOP.VERSION)=="undefined")) { 100 | if (navigator.userAgent.indexOf('Chrome')>=0) 101 | ;//alert(strHtmChrome+' 请到打印管理页面进行检测或者更新!'); 102 | if (navigator.userAgent.indexOf('Firefox')>=0) 103 | ;//alert(strHtmFireFox+' 请到打印管理页面进行检测或者更新!'); 104 | if (is64IE) 105 | ;//alert(strHtm64_Install+' 请到打印管理页面进行检测或者更新!'); 106 | else if (isIE) 107 | ;//alert(strHtmInstall+' 请到打印管理页面进行检测或者更新!'); 108 | else 109 | ;//alert(strHtmInstall+' 请到打印管理页面进行检测或者更新!'); 110 | return LODOP; 111 | }; 112 | }; 113 | if (LODOP.VERSION<"6.2.1.7") { 114 | if (needCLodop()) 115 | ;//alert(strCLodopUpdate+' 请到打印管理页面进行检测或者更新!'); 116 | else if (is64IE) 117 | ;//alert(strHtm64_Update+' 请到打印管理页面进行检测或者更新!'); 118 | else if (isIE) 119 | ;// alert(strHtmUpdate+' 请到打印管理页面进行检测或者更新!'); 120 | else 121 | ;//alert(strHtmUpdate+' 请到打印管理页面进行检测或者更新!'); 122 | return LODOP; 123 | }; 124 | //===如下空白位置适合调用统一功能(如注册语句、语言选择等):=== 125 | LODOP.SET_LICENSES("","0F5C1D2039BECACD5CA4909E737625D4","C94CEE276DB2187AE6B65D56B3FC2848",""); 126 | //=========================================================== 127 | return LODOP; 128 | } catch(err) {alert("getLodop出错:"+err);}; 129 | }; 130 | 131 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'production'; 5 | process.env.NODE_ENV = 'production'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const path = require('path'); 18 | const chalk = require('chalk'); 19 | const fs = require('fs-extra'); 20 | const webpack = require('webpack'); 21 | const config = require('../config/webpack.config.prod'); 22 | const paths = require('../config/paths'); 23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 24 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); 25 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); 26 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); 27 | 28 | const measureFileSizesBeforeBuild = 29 | FileSizeReporter.measureFileSizesBeforeBuild; 30 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; 31 | const useYarn = fs.existsSync(paths.yarnLockFile); 32 | 33 | // These sizes are pretty large. We'll warn for bundles exceeding them. 34 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; 35 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; 36 | 37 | // Warn and crash if required files are missing 38 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 39 | process.exit(1); 40 | } 41 | 42 | // First, read the current file sizes in build directory. 43 | // This lets us display how much they changed later. 44 | measureFileSizesBeforeBuild(paths.appBuild) 45 | .then(previousFileSizes => { 46 | // Remove all content but keep the directory so that 47 | // if you're in it, you don't end up in Trash 48 | fs.emptyDirSync(paths.appBuild); 49 | // Merge with the public folder 50 | copyPublicFolder(); 51 | // Start the webpack build 52 | return build(previousFileSizes); 53 | }) 54 | .then( 55 | ({ stats, previousFileSizes, warnings }) => { 56 | if (warnings.length) { 57 | console.log(chalk.yellow('Compiled with warnings.\n')); 58 | console.log(warnings.join('\n\n')); 59 | console.log( 60 | '\nSearch for the ' + 61 | chalk.underline(chalk.yellow('keywords')) + 62 | ' to learn more about each warning.' 63 | ); 64 | console.log( 65 | 'To ignore, add ' + 66 | chalk.cyan('// eslint-disable-next-line') + 67 | ' to the line before.\n' 68 | ); 69 | } else { 70 | console.log(chalk.green('Compiled successfully.\n')); 71 | } 72 | 73 | console.log('File sizes after gzip:\n'); 74 | printFileSizesAfterBuild( 75 | stats, 76 | previousFileSizes, 77 | paths.appBuild, 78 | WARN_AFTER_BUNDLE_GZIP_SIZE, 79 | WARN_AFTER_CHUNK_GZIP_SIZE 80 | ); 81 | console.log(); 82 | 83 | const appPackage = require(paths.appPackageJson); 84 | const publicUrl = paths.publicUrl; 85 | const publicPath = config.output.publicPath; 86 | const buildFolder = path.relative(process.cwd(), paths.appBuild); 87 | printHostingInstructions( 88 | appPackage, 89 | publicUrl, 90 | publicPath, 91 | buildFolder, 92 | useYarn 93 | ); 94 | }, 95 | err => { 96 | console.log(chalk.red('Failed to compile.\n')); 97 | console.log((err.message || err) + '\n'); 98 | process.exit(1); 99 | } 100 | ); 101 | 102 | // Create the production build and print the deployment instructions. 103 | function build(previousFileSizes) { 104 | console.log('Creating an optimized production build...'); 105 | 106 | let compiler = webpack(config); 107 | return new Promise((resolve, reject) => { 108 | compiler.run((err, stats) => { 109 | if (err) { 110 | return reject(err); 111 | } 112 | const messages = formatWebpackMessages(stats.toJson({}, true)); 113 | if (messages.errors.length) { 114 | return reject(new Error(messages.errors.join('\n\n'))); 115 | } 116 | if ( 117 | process.env.CI && 118 | (typeof process.env.CI !== 'string' || 119 | process.env.CI.toLowerCase() !== 'false') && 120 | messages.warnings.length 121 | ) { 122 | console.log( 123 | chalk.yellow( 124 | '\nTreating warnings as errors because process.env.CI = true.\n' + 125 | 'Most CI servers set it automatically.\n' 126 | ) 127 | ); 128 | return reject(new Error(messages.warnings.join('\n\n'))); 129 | } 130 | return resolve({ 131 | stats, 132 | previousFileSizes, 133 | warnings: messages.warnings, 134 | }); 135 | }); 136 | }); 137 | } 138 | 139 | function copyPublicFolder() { 140 | fs.copySync(paths.appPublic, paths.appBuild, { 141 | dereference: true, 142 | filter: file => file !== paths.appHtml, 143 | }); 144 | } 145 | -------------------------------------------------------------------------------- /scripts/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'development'; 5 | process.env.NODE_ENV = 'development'; 6 | process.env.PORT = '3000'; 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const fs = require('fs'); 18 | const chalk = require('chalk'); 19 | const webpack = require('webpack'); 20 | const WebpackDevServer = require('webpack-dev-server'); 21 | const clearConsole = require('react-dev-utils/clearConsole'); 22 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 23 | const { 24 | choosePort, 25 | createCompiler, 26 | prepareProxy, 27 | prepareUrls, 28 | } = require('react-dev-utils/WebpackDevServerUtils'); 29 | const openBrowser = require('react-dev-utils/openBrowser'); 30 | const paths = require('../config/paths'); 31 | const config = require('../config/webpack.config.dev'); 32 | const createDevServerConfig = require('../config/webpackDevServer.config'); 33 | 34 | const useYarn = fs.existsSync(paths.yarnLockFile); 35 | const isInteractive = process.stdout.isTTY; 36 | 37 | // Warn and crash if required files are missing 38 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 39 | process.exit(1); 40 | } 41 | 42 | // Tools like Cloud9 rely on this. 43 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 44 | const HOST = process.env.HOST || '0.0.0.0'; 45 | 46 | // We attempt to use the default port but if it is busy, we offer the user to 47 | // run on a different port. `detect()` Promise resolves to the next free port. 48 | choosePort(HOST, DEFAULT_PORT) 49 | .then(port => { 50 | if (port == null) { 51 | // We have not found a port. 52 | return; 53 | } 54 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 55 | const appName = require(paths.appPackageJson).name; 56 | const urls = prepareUrls(protocol, HOST, port); 57 | // Create a webpack compiler that is configured with custom messages. 58 | const compiler = createCompiler(webpack, config, appName, urls, useYarn); 59 | // Load proxy config 60 | const proxySetting = require(paths.appPackageJson).proxy; 61 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic); 62 | // Serve webpack assets generated by the compiler over a web sever. 63 | const serverConfig = createDevServerConfig( 64 | proxyConfig, 65 | urls.lanUrlForConfig 66 | ); 67 | const devServer = new WebpackDevServer(compiler, serverConfig); 68 | // Launch WebpackDevServer. 69 | devServer.listen(port, HOST, err => { 70 | if (err) { 71 | return console.log(err); 72 | } 73 | if (isInteractive) { 74 | clearConsole(); 75 | } 76 | console.log(chalk.cyan('Starting the development server...\n')); 77 | openBrowser(urls.localUrlForBrowser); 78 | }); 79 | 80 | ['SIGINT', 'SIGTERM'].forEach(function(sig) { 81 | process.on(sig, function() { 82 | devServer.close(); 83 | process.exit(); 84 | }); 85 | }); 86 | }) 87 | .catch(err => { 88 | if (err && err.message) { 89 | console.log(err.message); 90 | } 91 | process.exit(1); 92 | }); 93 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /src/api/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | //web: 'https://bird.ioliu.cn/v1?url=http://www.xbrowser.net', 3 | web: 'http://rapapi.org/mockjsdata/18181', 4 | // web: 'http://localhost:3000', 5 | method: '' 6 | }; 7 | export const API_ROOT = ''.concat(config.web); 8 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import HTTP from '../http/index'; 2 | import { 3 | LoginResource, 4 | LogoutResource, 5 | OrderListNewResource, 6 | OrderListFinishResource, 7 | OrderStateResource, 8 | SendersResource, 9 | AddSenderResource, 10 | ChangeDefaultPrinterResource, 11 | GetDefaultPrinterResource, 12 | GetOrderPrintDataResource, 13 | GetExpressTemplateResource, 14 | UpdateOrderVendorResource, 15 | GetUnassignOrdersResource, 16 | GetAssignedOrdersResource, 17 | GetOrderVendorsResource, 18 | SaveUnassignOrderResource, 19 | GetDefaultSenderResource, 20 | DelSenderResource, 21 | UpdateSenderResource, 22 | UpdateOrdersExpressStatusResource, 23 | GetOrderDetailResource, 24 | GetAllOrdersResource, 25 | GetBackOrdersResource, 26 | SavePrintOptionResource, 27 | GetPrintOptionResource, 28 | GetPrintOrderListResource 29 | } from './resource.js'; 30 | 31 | export default { 32 | getLoginResource(data) { 33 | return HTTP.Post(LoginResource, data); 34 | }, 35 | getLogoputResource() { 36 | return HTTP.Get(LogoutResource); 37 | }, 38 | getOrderListNewResource(payload) { 39 | return HTTP.Get(OrderListNewResource, payload); 40 | }, 41 | getOrderListFinishResource(payload) { 42 | return HTTP.Get(OrderListFinishResource, payload); 43 | }, 44 | getSendersResource(payload) { 45 | return HTTP.Get(SendersResource, payload); 46 | }, 47 | addSenderResource(payload) { 48 | return HTTP.Post(AddSenderResource, payload); 49 | }, 50 | changeDefaultPrinter(payload) { 51 | return HTTP.Post(ChangeDefaultPrinterResource, payload); 52 | }, 53 | getDefaultPrinter(payload) { 54 | return HTTP.Get(GetDefaultPrinterResource, payload); 55 | }, 56 | getOrderPrintDataResource(payload) { 57 | return HTTP.Get(GetOrderPrintDataResource, payload); 58 | }, 59 | getExpressTemplateResource(payload) { 60 | return HTTP.Get(GetExpressTemplateResource, payload); 61 | }, 62 | updateOrderVendorResource(payload) { 63 | return HTTP.Get(UpdateOrderVendorResource, payload); 64 | }, 65 | getUnassignOrdersResource(payload) { 66 | return HTTP.Get(GetUnassignOrdersResource, payload); 67 | }, 68 | getAssignedOrdersResource(payload) { 69 | return HTTP.Get(GetAssignedOrdersResource, payload); 70 | }, 71 | getOrderVendorsResource(payload) { 72 | return HTTP.Get(GetOrderVendorsResource, payload); 73 | }, 74 | saveUnassignOrderResource(payload) { 75 | return HTTP.Post(SaveUnassignOrderResource, payload); 76 | }, 77 | getDefaultSenderResource(payload) { 78 | return HTTP.Get(GetDefaultSenderResource, payload); 79 | }, 80 | delSenderResource(payload) { 81 | return HTTP.Get(DelSenderResource, payload); 82 | }, 83 | updateSenderResource(payload) { 84 | return HTTP.Post(UpdateSenderResource, payload); 85 | }, 86 | updateOrdersExpressStatusResource(payload) { 87 | return HTTP.Post(UpdateOrdersExpressStatusResource, payload); 88 | }, 89 | getOrderDetailResource(payload) { 90 | return HTTP.Get(GetOrderDetailResource, payload); 91 | }, 92 | getAllOrdersResource(payload) { 93 | return HTTP.Get(GetAllOrdersResource, payload); 94 | }, 95 | getBackOrdersResource(payload) { 96 | return HTTP.Get(GetBackOrdersResource, payload); 97 | }, 98 | savePrintOptionResource(payload) { 99 | return HTTP.Post(SavePrintOptionResource, payload); 100 | }, 101 | getPrintOptionResource(payload) { 102 | return HTTP.Get(GetPrintOptionResource, payload); 103 | }, 104 | getPrintOrderListResource(payload) { 105 | return HTTP.Get(GetPrintOrderListResource, payload); 106 | }, 107 | getOrderState() { 108 | return HTTP.Get(OrderStateResource); 109 | // return HTTP.Get("http://localhost:3000/api/orderState"); 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /src/api/resource.js: -------------------------------------------------------------------------------- 1 | import { API_ROOT } from './config.js'; 2 | 3 | const apiWX = { 4 | Login: '/api/login', 5 | Logout: '/api/logout', 6 | Orders: '/api/orders', 7 | OrderListNew: '/api/orderListNew', 8 | OrderListFinish: '/api/orderListFinish', 9 | OrderState: '/api/orderstate', 10 | Senders: '/api/senders', 11 | AddSender: '/api/addSender', 12 | ChangeDefaultPrinter: '/api/changeDefaultPrinter', 13 | GetDefaultPrinter: '/api/getDefaultPrinter', 14 | GetOrderPrintData: '/api/getOrderPrintData', 15 | GetExpressTemplate: '/api/getExpressTemplate', 16 | UpdateOrderVendor: '/api/updateOrderVendor', 17 | GetUnassignOrders: '/api/getUnassignOrders', 18 | GetAssignedOrders: '/api/getAssignedOrders', 19 | GetOrderVendors: '/api/getOrderVendors', 20 | SaveUnassignOrder: '/api/saveUnassignOrder', 21 | DefaultSender: '/api/defaultSender', 22 | DelSender: '/api/delSender', 23 | UpdateSender: '/api/updateSender', 24 | GetBackOrders: '/api/orderListBack', 25 | UpdateOrdersExpressStatus: '/api/updateOrdersExpressStatus', 26 | GetOrderDetail: '/api/orderDetail', 27 | GetAllOrders: '/api/orderList', 28 | GetPrintOrderList: '/api/printOrderList', 29 | GetPrintOption: '/api/printOption', 30 | SavePrintOption: '/api/savePrintOption' 31 | }; 32 | 33 | const LoginResource = API_ROOT.concat(apiWX.Login); 34 | const LogoutResource = API_ROOT.concat(apiWX.Logout); 35 | const OrderStateResource = API_ROOT.concat(apiWX.OrderState); 36 | const OrderListNewResource = API_ROOT.concat(apiWX.OrderListNew); 37 | const OrderListFinishResource = API_ROOT.concat(apiWX.OrderListFinish); 38 | const SendersResource = API_ROOT.concat(apiWX.Senders); 39 | const AddSenderResource = API_ROOT.concat(apiWX.AddSender); 40 | const ChangeDefaultPrinterResource = API_ROOT.concat(apiWX.ChangeDefaultPrinter); 41 | const GetDefaultPrinterResource = API_ROOT.concat(apiWX.GetDefaultPrinter); 42 | const GetOrderPrintDataResource = API_ROOT.concat(apiWX.GetOrderPrintData); 43 | const GetExpressTemplateResource = API_ROOT.concat(apiWX.GetExpressTemplate); 44 | const UpdateOrderVendorResource = API_ROOT.concat(apiWX.UpdateOrderVendor); 45 | const GetUnassignOrdersResource = API_ROOT.concat(apiWX.GetUnassignOrders); 46 | const GetAssignedOrdersResource = API_ROOT.concat(apiWX.GetAssignedOrders); 47 | const GetOrderVendorsResource = API_ROOT.concat(apiWX.GetOrderVendors); 48 | const SaveUnassignOrderResource = API_ROOT.concat(apiWX.SaveUnassignOrder); 49 | const GetDefaultSenderResource = API_ROOT.concat(apiWX.DefaultSender); 50 | const DelSenderResource = API_ROOT.concat(apiWX.DelSender); 51 | const UpdateSenderResource = API_ROOT.concat(apiWX.UpdateSender); 52 | const GetBackOrdersResource = API_ROOT.concat(apiWX.GetBackOrders); 53 | const GetAllOrdersResource = API_ROOT.concat(apiWX.GetAllOrders); 54 | const UpdateOrdersExpressStatusResource = API_ROOT.concat(apiWX.UpdateOrdersExpressStatus); 55 | const GetOrderDetailResource = API_ROOT.concat(apiWX.GetOrderDetail); 56 | const SavePrintOptionResource = API_ROOT.concat(apiWX.SavePrintOption); 57 | const GetPrintOptionResource = API_ROOT.concat(apiWX.GetPrintOption); 58 | const GetPrintOrderListResource = API_ROOT.concat(apiWX.GetPrintOrderList); 59 | 60 | export { 61 | LoginResource, 62 | LogoutResource, 63 | OrderListNewResource, 64 | OrderListFinishResource, 65 | OrderStateResource, 66 | SendersResource, 67 | AddSenderResource, 68 | ChangeDefaultPrinterResource, 69 | GetDefaultPrinterResource, 70 | GetOrderPrintDataResource, 71 | GetExpressTemplateResource, 72 | UpdateOrderVendorResource, 73 | GetUnassignOrdersResource, 74 | GetAssignedOrdersResource, 75 | GetOrderVendorsResource, 76 | SaveUnassignOrderResource, 77 | GetDefaultSenderResource, 78 | DelSenderResource, 79 | UpdateSenderResource, 80 | GetAllOrdersResource, 81 | GetBackOrdersResource, 82 | UpdateOrdersExpressStatusResource, 83 | GetOrderDetailResource, 84 | SavePrintOptionResource, 85 | GetPrintOptionResource, 86 | GetPrintOrderListResource 87 | }; 88 | -------------------------------------------------------------------------------- /src/assets/imgs/avar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxkingpk/react-antd-webpack2-sagas-router4/4cd913f9a252e42d6519f6311f98e0bcfe210b6e/src/assets/imgs/avar.gif -------------------------------------------------------------------------------- /src/assets/imgs/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxkingpk/react-antd-webpack2-sagas-router4/4cd913f9a252e42d6519f6311f98e0bcfe210b6e/src/assets/imgs/demo.gif -------------------------------------------------------------------------------- /src/assets/imgs/express.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxkingpk/react-antd-webpack2-sagas-router4/4cd913f9a252e42d6519f6311f98e0bcfe210b6e/src/assets/imgs/logo.png -------------------------------------------------------------------------------- /src/assets/imgs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /src/assets/imgs/orders.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/less/app.less: -------------------------------------------------------------------------------- 1 | .logo{ 2 | vertical-align: middle; 3 | width: 50px; 4 | } 5 | .user{ 6 | float: right; 7 | font-size: 20px; 8 | .item{ 9 | line-height: 64px; 10 | height:64px; 11 | } 12 | .menu { 13 | border-bottom:0; 14 | } 15 | } 16 | .sider.ant-layout-sider-collapsed .anticon { 17 | font-size: 16px; 18 | margin-left: 8px; 19 | } 20 | 21 | .sider.ant-layout-sider-collapsed .nav-text { 22 | display: none; 23 | } 24 | 25 | .sider.ant-layout-sider-collapsed .ant-menu-submenu-vertical > .ant-menu-submenu-title:after { 26 | display: none; 27 | } 28 | 29 | .ant-menu-root.ant-menu-vertical .ant-menu-sub { 30 | height: 0; 31 | opacity: 0; 32 | transition: none; 33 | } 34 | .ant-menu-root.ant-menu-vertical .ant-menu-submenu-active .ant-menu-sub { 35 | height: auto; 36 | opacity: 1; 37 | } 38 | .menuFold{ 39 | width: 47px; 40 | height: 47px; 41 | line-height: 64px; 42 | text-align: center; 43 | font-size: 18px; 44 | cursor: pointer; 45 | transition: all 0.3s ease-out; 46 | display: inline-block; 47 | } -------------------------------------------------------------------------------- /src/assets/less/dashboard.less: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | .avar{ 3 | float:left; 4 | display:inline-block; 5 | img{ 6 | width:150px; 7 | height:150px; 8 | border-radius: 150px; 9 | transition: transform 0.6s ease-in-out 0.4s, 10 | } 11 | } 12 | .avar:hover{ 13 | img{ 14 | transform: rotate(360deg); 15 | } 16 | } 17 | .info{ 18 | margin-left:200px; 19 | position:relative; 20 | height: 150px; 21 | line-height:110px; 22 | color:#000; 23 | .name{ 24 | font-size: 30px; 25 | } 26 | .other{ 27 | font-size:20px; 28 | 29 | position:absolute; 30 | line-height:40px; 31 | bottom:0; 32 | left:0; 33 | .tip{ 34 | color:#f00; 35 | margin:0 5px; 36 | } 37 | } 38 | } 39 | .shortcut{ 40 | margin: 20px 0; 41 | } 42 | 43 | .btn { 44 | font-size: 14px; 45 | color: #fff; 46 | padding: 14px 36px; 47 | border-radius: 8px; 48 | border: none; 49 | outline: none; 50 | margin-right: 25px; 51 | transition: all .6s ease; 52 | position: relative; 53 | display: inline-block 54 | } 55 | 56 | .btn:hover { 57 | background: #ececec; 58 | } 59 | 60 | // .btn:hover:after,.btn:hover:before { 61 | // width: 100%; 62 | // transition: all .6s ease 63 | // } 64 | 65 | // .btn:after,.btn:before { 66 | // content: ""; 67 | // position: absolute; 68 | // top: 0; 69 | // right: 0; 70 | // height: 2px; 71 | // width: 0; 72 | // transition: all .4s ease 73 | // } 74 | 75 | // .btn:after { 76 | // right: inherit; 77 | // top: inherit; 78 | // left: 0; 79 | // bottom: 0 80 | // } 81 | 82 | .blue-btn { 83 | background: #324157 84 | } 85 | 86 | .blue-btn:hover { 87 | color: #324157 88 | } 89 | 90 | .blue-btn:hover:after,.blue-btn:hover:before { 91 | background: #324157 92 | } 93 | 94 | .light-blue-btn { 95 | background: #3a71a8 96 | } 97 | 98 | .light-blue-btn:hover { 99 | color: #3a71a8 100 | } 101 | 102 | .light-blue-btn:hover:after,.light-blue-btn:hover:before { 103 | background: #3a71a8 104 | } 105 | 106 | .red-btn { 107 | background: #c03639 108 | } 109 | 110 | .red-btn:hover { 111 | color: #c03639 112 | } 113 | 114 | .red-btn:hover:after,.red-btn:hover:before { 115 | background: #c03639 116 | } 117 | 118 | .pink-btn { 119 | background: #e65d6e 120 | } 121 | 122 | .pink-btn:hover { 123 | color: #e65d6e 124 | } 125 | 126 | .pink-btn:hover:after,.pink-btn:hover:before { 127 | background: #e65d6e 128 | } 129 | 130 | .green-btn { 131 | background: #30b08f 132 | } 133 | 134 | .green-btn:hover { 135 | color: #30b08f 136 | } 137 | 138 | .green-btn:hover:after,.green-btn:hover:before { 139 | background: #30b08f 140 | } 141 | 142 | .tiffany-btn { 143 | background: #4ab7bd 144 | } 145 | 146 | .tiffany-btn:hover { 147 | color: #4ab7bd 148 | } 149 | 150 | .tiffany-btn:hover:after,.tiffany-btn:hover:before { 151 | background: #4ab7bd 152 | } 153 | 154 | .yellow-btn { 155 | background: #fec171 156 | } 157 | 158 | .yellow-btn:hover { 159 | color: #fec171 160 | } 161 | 162 | .yellow-btn:hover:after,.yellow-btn:hover:before { 163 | background: #fec171 164 | } 165 | 166 | } -------------------------------------------------------------------------------- /src/assets/less/dialog.less: -------------------------------------------------------------------------------- 1 | .vertical-center-modal { 2 | text-align: center; 3 | white-space: nowrap; 4 | } 5 | 6 | .vertical-center-modal:before { 7 | content: ''; 8 | display: inline-block; 9 | height: 100%; 10 | vertical-align: middle; 11 | width: 0; 12 | } 13 | 14 | .vertical-center-modal .ant-modal { 15 | display: inline-block; 16 | vertical-align: middle; 17 | top: 0; 18 | text-align: left; 19 | } -------------------------------------------------------------------------------- /src/assets/less/nomatch.less: -------------------------------------------------------------------------------- 1 | .nomatch{ 2 | background-image: url(https://os.alipayobjects.com/rmsportal/NOAjOBbnYCrNzrW.jpg); 3 | width: 100%; 4 | height: 100%; 5 | background-repeat: no-repeat; 6 | background-attachment: fixed; 7 | background-position: 50%; 8 | background-size: cover; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | z-index: 100; 15 | 16 | section { 17 | position: absolute; 18 | top: 48%; 19 | left: 55%; 20 | margin: -103px 0 0 -120px; 21 | text-align: center; 22 | } 23 | h1 { 24 | color: #108ee9; 25 | font-size: 120px; 26 | font-weight: 500; 27 | } 28 | p { 29 | color: rgba(0,0,0,.65); 30 | font-size: 18px; 31 | } 32 | a{ 33 | color: #108ee9; 34 | background: transparent; 35 | text-decoration: none; 36 | outline: none; 37 | cursor: pointer; 38 | -webkit-transition: color .3s ease; 39 | transition: color .3s ease; 40 | } 41 | } -------------------------------------------------------------------------------- /src/assets/less/order-detail.less: -------------------------------------------------------------------------------- 1 | #components-tabs-demo-card-top .code-box-demo { 2 | background: #ececec; 3 | overflow: hidden; 4 | padding: 24px; 5 | } 6 | 7 | .card-container > .ant-tabs-card > .ant-tabs-content { 8 | margin-top: -16px; 9 | } 10 | 11 | .card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane { 12 | background: #fff; 13 | padding: 16px; 14 | } 15 | 16 | .card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab { 17 | border-color: transparent; 18 | } 19 | 20 | .card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active { 21 | border-color: #fff; 22 | } 23 | 24 | .item { 25 | .item-detail{ 26 | display: flex; 27 | padding-right: 15px; 28 | margin-top: 15px; 29 | .title{ 30 | margin-right: 10px; 31 | width: 100px; 32 | text-align: right; 33 | } 34 | .content{ 35 | flex: 1; 36 | textarea{ 37 | position: relative; 38 | display: inline-block; 39 | padding: 4px 7px; 40 | width: 100%; 41 | cursor: text; 42 | font-size: 12px; 43 | line-height: 1.5; 44 | color: rgba(0, 0, 0, 0.65); 45 | background-color: #fff; 46 | background-image: none; 47 | border: 1px solid #d9d9d9; 48 | border-radius: 4px; 49 | transition: all .3s; 50 | &[disabled] { 51 | background-color: #f7f7f7; 52 | opacity: 1; 53 | cursor: not-allowed; 54 | color: rgba(0, 0, 0, 0.25); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | .ant-tabs-tabpane.ant-tabs-tabpane-active .basic, .ant-tabs-tabpane.ant-tabs-tabpane-active .express{ 62 | display: block; 63 | } 64 | .ant-tabs-tabpane.ant-tabs-tabpane-inactive .basic, .ant-tabs-tabpane.ant-tabs-tabpane-inactive .express{ 65 | display: none; 66 | } 67 | .card-container .show{ 68 | display: block; 69 | } 70 | .card-container .hide{ 71 | display: none; 72 | } 73 | .content .ant-form-item { 74 | margin-bottom: 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/less/order-print-preview.less: -------------------------------------------------------------------------------- 1 | .ant-input-number{ 2 | width: 63px !important; 3 | } 4 | .ant-row.ant-form-item.inline{ 5 | display: inline-block; 6 | } 7 | .ant-modal { 8 | width: 600px !important; 9 | } 10 | .ant-table-thead > tr > th, .ant-table-tbody > tr > td{ 11 | padding: 8px !important; 12 | } -------------------------------------------------------------------------------- /src/assets/less/sender-item.less: -------------------------------------------------------------------------------- 1 | .senderItem{ 2 | .ant-scroll-number{ 3 | transform: inherit !important; 4 | } 5 | } -------------------------------------------------------------------------------- /src/assets/less/sender-new.less: -------------------------------------------------------------------------------- 1 | .senderNew{ 2 | .ant-card-body{ 3 | height: calc(~"100% - 48px"); 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | font-size: 30px; 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/DropOption/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Dropdown, Button, Icon, Menu } from 'antd' 4 | 5 | const DropOption = ({ onMenuClick, menuOptions = [], buttonStyle, dropdownProps }) => { 6 | const menu = menuOptions.map(item => {item.name}) 7 | return ({menu}} 9 | {...dropdownProps} 10 | > 11 | 15 | ) 16 | } 17 | 18 | DropOption.propTypes = { 19 | onMenuClick: PropTypes.func, 20 | menuOptions: PropTypes.array.isRequired, 21 | buttonStyle: PropTypes.object, 22 | dropdownProps: PropTypes.object, 23 | } 24 | 25 | export default DropOption 26 | -------------------------------------------------------------------------------- /src/components/dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Link } from 'react-router-dom'; 4 | import '../assets/less/dashboard.less'; 5 | import avar from '../assets/imgs/avar.gif'; 6 | import store from '../redux/store/'; 7 | import { setOpenKeys } from '../redux/actions/menu'; 8 | 9 | class Dashboard extends React.Component { 10 | constructor() { 11 | super(); 12 | } 13 | 14 | render() { 15 | console.log("dashborad") 16 | if (this.props.isAdmin) { 17 | console.log("dashborad11111") 18 | store.dispatch(setOpenKeys(['orders'])); 19 | } else { 20 | console.log("dashborad22222") 21 | store.dispatch(setOpenKeys(['express', 'msgOrder'])); 22 | } 23 | // return this.props.isAdmin ? : ; 24 | return ( 25 |
26 |
27 |
28 | 29 |
30 |
31 |

32 | 尊敬的用户{this.props.userName},欢迎您! 33 |

34 |

35 | 您当前还有10条未打印订单,5条未分配订单等待处理! 36 |

37 |
38 |
39 |
40 | 未分配订单 41 | 未打印订单 42 | 打印机设置 43 | 搜索看看 44 |
45 |
46 | ); 47 | } 48 | } 49 | function mapStateToProp(state) { 50 | return { 51 | userName: state.userReducer.userName, 52 | isAdmin: state.userReducer.isAdmin 53 | }; 54 | } 55 | 56 | export default connect(mapStateToProp)(Dashboard); 57 | -------------------------------------------------------------------------------- /src/components/loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Spin } from 'antd'; 3 | import Loadable from 'react-loadable'; 4 | 5 | const LoadingComponent = (props) => { 6 | if (props.isLoading) { 7 | // While our other component is loading... 8 | if (props.timedOut) { 9 | // In case we've timed out loading our other component. 10 | return
加载超时,请重试!
; 11 | } else if (props.pastDelay) { 12 | // Display a loading screen after a set delay. 13 | return
; 14 | } else { 15 | // Don't flash "Loading..." when we don't need to. 16 | return null; 17 | } 18 | } else if (props.error) { 19 | // If we aren't loading, maybe 20 | return
加载出错,请重试!
; 21 | } else { 22 | // This case shouldn't happen... but we'll return null anyways. 23 | return null; 24 | } 25 | }; 26 | const MyLoadable = (args) => { 27 | return Loadable({ 28 | delay: 200, 29 | timeout: 30000, 30 | loading: LoadingComponent, 31 | ...args 32 | }); 33 | }; 34 | 35 | export default MyLoadable; -------------------------------------------------------------------------------- /src/components/login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Form, Button, Input, Icon } from 'antd'; 3 | import { withRouter, Redirect } from 'react-router-dom'; 4 | import { connect } from 'react-redux'; 5 | import { loginRequest } from '../redux/actions/user'; 6 | import Logo from '../assets/imgs/logo.svg'; 7 | import store from '../redux/store/'; 8 | import { setOpenKeys } from '../redux/actions/menu'; 9 | import { cryptPwd } from '../utils/index'; 10 | 11 | const FormItem = Form.Item; 12 | 13 | class Login extends React.Component { 14 | constructor() { 15 | super(); 16 | this.state = { 17 | username: '', 18 | password: '' 19 | }; 20 | } 21 | componentDidMount() { 22 | } 23 | handleSubmit(event) { 24 | event.preventDefault(); 25 | this.props.form.validateFields((err, values) => { 26 | if (!err) { 27 | this.props.login({ 28 | username: this.state.username, 29 | password: cryptPwd(this.state.password) 30 | }); 31 | } 32 | }); 33 | } 34 | handleChange(event) { 35 | const name = event.target.name; 36 | this.setState({ 37 | [name]: event.target.value 38 | }); 39 | } 40 | 41 | render() { 42 | const { from } = this.props.location.state || { from: { pathname: this.props.isAdmin ? '/orders/orderUnassign' : '/express/orderListNew/noMsgOrderList' } }; 43 | if (this.props.authenticated) { 44 | const arr = from.pathname.split('/'); 45 | if (arr[1] && arr[2] && arr[3]) { 46 | store.dispatch(setOpenKeys(['express', 'msgOrder'])); 47 | } else if (arr[1] && arr[2]) { 48 | store.dispatch(setOpenKeys([arr[1]])); 49 | } 50 | return (); 51 | } 52 | const { getFieldDecorator } = this.props.form; 53 | return ( 54 |
55 |
56 |
57 |
58 | logo 59 |
60 |
61 | 62 | { 63 | getFieldDecorator('userName', { 64 | rules: [{ required: true, message: '用户名不能为空!' }] 65 | })( 66 |
67 | } size="large" name="username" placeholder="请输入用户名" onChange={this.handleChange.bind(this)} /> 68 |
69 | )} 70 |
71 | 72 | { 73 | getFieldDecorator('password', { 74 | rules: [{ required: true, message: '密码不能为空!' }] 75 | })( 76 |
77 | } size="large" type="password" name="password" placeholder="请输入密码" onChange={this.handleChange.bind(this)} /> 78 |
79 | )} 80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 |
); 88 | } 89 | } 90 | function mapStateToProp(state) { 91 | return { 92 | authenticated: state.userReducer.authenticated, 93 | isAuthenticating: state.userReducer.isAuthenticating, 94 | isAdmin: state.userReducer.isAdmin 95 | }; 96 | } 97 | function mapDispatchToProp(dispatch) { 98 | return { 99 | login: (data) => { 100 | dispatch(loginRequest(data)); 101 | } 102 | }; 103 | } 104 | 105 | const WrappedLogin = Form.create()(Login); 106 | export default connect(mapStateToProp, mapDispatchToProp)(withRouter(WrappedLogin)); 107 | -------------------------------------------------------------------------------- /src/components/logout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Redirect } from 'react-router-dom'; 4 | 5 | class Logout extends React.Component { 6 | constructor() { 7 | super(); 8 | this.state = { someKey: 'someValue' }; 9 | } 10 | componentDidMount() { 11 | this.setState({ someKey: 'otherValue' }); 12 | console.log(this.props); 13 | } 14 | render() { 15 | return

Logout

; 16 | } 17 | } 18 | function mapStateToProp(state) { 19 | return { 20 | isLogin: state.userReducer.isLogin 21 | }; 22 | } 23 | function mapDispatchToProp(dispatch, ownprop) { 24 | return { 25 | logout: (data) => { 26 | dispatch({ type: 'logout', data }); 27 | } 28 | }; 29 | } 30 | const LogoutConnect = connect(mapStateToProp, mapDispatchToProp)(Logout); 31 | export default LogoutConnect; 32 | -------------------------------------------------------------------------------- /src/components/order-assigned.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, message, Input } from 'antd'; 4 | import OrderDetail from './order-detail'; 5 | 6 | const Search = Input.Search; 7 | 8 | const columns = [{ 9 | title: '编号', 10 | key: 'id', 11 | dataIndex: 'id' 12 | }, { 13 | title: '宝贝名称', 14 | key: 'goodsName', 15 | dataIndex: 'goodsName' 16 | }, { 17 | title: '订单编号', 18 | key: 'orderID', 19 | dataIndex: 'orderID' 20 | }, { 21 | title: '买家昵称', 22 | key: 'nickname', 23 | dataIndex: 'nickname' 24 | }, { 25 | title: '收件人', 26 | key: 'reciver', 27 | dataIndex: 'reciver' 28 | }, { 29 | title: '手机号', 30 | key: 'phone', 31 | dataIndex: 'phone' 32 | }, { 33 | title: '街道地址', 34 | key: 'address', 35 | dataIndex: 'address' 36 | }, { 37 | title: '快递', 38 | key: 'express', 39 | dataIndex: 'express' 40 | }, { 41 | title: '订单分配状态', 42 | key: 'assign', 43 | dataIndex: 'assign', 44 | width: 100, 45 | fixed: 'right' 46 | }, { 47 | title: '发货商家', 48 | key: 'vendor', 49 | dataIndex: 'vendor', 50 | width: 100, 51 | fixed: 'right' 52 | }]; 53 | class OrderAssigned extends React.Component { 54 | constructor() { 55 | super(); 56 | this.state = { 57 | collapsed: false, 58 | pageTotal: 0, 59 | selectedRowKeys: [], 60 | pagination: { 61 | current: 1, 62 | pageSize: 10 63 | }, 64 | orderDetailData: { 65 | disableEdit: false 66 | }, 67 | data: [], 68 | loading: false 69 | }; 70 | } 71 | componentDidMount() { 72 | document.title = '已分配订单'; 73 | this.request({ 74 | page: 1, 75 | pageSize: 10 76 | }); 77 | } 78 | request(payload) { 79 | this.setState({ 80 | loading: true 81 | }); 82 | 83 | API.getAssignedOrdersResource(payload).then((res) => { 84 | if (res.data.code === 200) { 85 | this.setState({ 86 | data: res.data.data, 87 | loading: false, 88 | pageTotal: res.data.total, 89 | selectedRowKeys: [] 90 | }); 91 | } else { 92 | this.setState({ 93 | data: [], 94 | loading: false 95 | }); 96 | message.error('获取未分配订单操失败!'); 97 | } 98 | }); 99 | } 100 | onRowClick(record) { 101 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 102 | if (res.data.code === 200) { 103 | this.setState({ 104 | ...this.state, 105 | orderDetailData: { 106 | ...this.state.orderDetailData, 107 | ...res.data.data 108 | } 109 | }); 110 | } else { 111 | message.error('获取订单详情失败!'); 112 | } 113 | }); 114 | } 115 | handleTableChange(pagination) { 116 | this.request({ 117 | page: pagination.current, 118 | pageSize: pagination.pageSize, 119 | orderID: this.state.queryKey 120 | }); 121 | } 122 | onSelectChange(selectedRowKeys) { 123 | this.setState({ 124 | ...this.state, 125 | selectedRowKeys 126 | }); 127 | } 128 | onShowSizeChange(current, size) { 129 | this.setState({ 130 | ...this.state, 131 | pagination: { 132 | current, 133 | pageSize: size 134 | } 135 | }); 136 | } 137 | onPaginationChange(page, pageSize) { 138 | this.setState({ 139 | ...this.state, 140 | pagination: { 141 | current: page, 142 | pageSize 143 | } 144 | }); 145 | } 146 | onSearch(value) { 147 | this.setState({ 148 | ...this.state, 149 | queryKey: value, 150 | pagination: { 151 | ...this.state.pagination, 152 | current: 1 153 | } 154 | }); 155 | this.request({ 156 | orderID: value, 157 | page: 1, 158 | pageSize: this.state.pagination.pageSize 159 | }); 160 | } 161 | render() { 162 | const pagination = { 163 | total: this.state.pageTotal, 164 | showSizeChanger: true, 165 | showQuickJumper: true, 166 | pageSizeOptions: ['10', '20', '30', '40', '100'], 167 | current: this.state.pagination.current, 168 | pageSize: this.state.pagination.pageSize, 169 | onChange: this.onPaginationChange.bind(this), 170 | onShowSizeChange: this.onShowSizeChange.bind(this) 171 | }; 172 | const rowSelection = { 173 | selectedRowKeys: this.state.selectedRowKeys, 174 | onChange: this.onSelectChange.bind(this) 175 | }; 176 | return (
177 |
178 |
179 | 180 |
181 |
182 | record.id} 185 | dataSource={this.state.data} 186 | pagination={pagination} 187 | loading={this.state.loading} 188 | onChange={this.handleTableChange.bind(this)} 189 | scroll={{ x: 1500 }} 190 | rowSelection={rowSelection} 191 | onRowClick={this.onRowClick.bind(this)} 192 | /> 193 | 194 | ); 195 | } 196 | } 197 | 198 | export default OrderAssigned; 199 | -------------------------------------------------------------------------------- /src/components/order-print-preview.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Modal, Button, Table, Form, Input, Select, InputNumber, message } from 'antd'; 4 | import PropTypes from 'prop-types'; 5 | import '../assets/less/order-print-preview.less'; 6 | 7 | const FormItem = Form.Item; 8 | const Option = Select.Option; 9 | const columns = [{ 10 | title: '订单编号', 11 | key: 'orderID', 12 | dataIndex: 'orderID', 13 | width: 50 14 | }, { 15 | title: '宝贝标题', 16 | key: 'goodsName', 17 | dataIndex: 'goodsName', 18 | width: 80 19 | }, { 20 | title: '快递公司', 21 | key: 'express', 22 | dataIndex: 'express', 23 | width: 80 24 | }, { 25 | title: '运单号', 26 | key: 'expressNum', 27 | dataIndex: 'expressNum', 28 | width: 80 29 | } 30 | ]; 31 | class OrderPrintPreview extends React.Component { 32 | constructor(props) { 33 | super(props); 34 | this.state = { 35 | tableData: null, 36 | loading: false, 37 | childrensList: [], 38 | senderList: [], 39 | paperPaddingLeft: null, 40 | paperPaddingRight: null, 41 | paperPaddingUp: null, 42 | paperPaddingDown: null, 43 | express: '', 44 | expressNum: null, 45 | sortInter: null, 46 | printer: '' 47 | }; 48 | this.handleOk = this.handleOk.bind(this); 49 | this.handlePreview = this.handlePreview.bind(this); 50 | } 51 | componentDidMount() { 52 | window.LODOP.Create_Printer_List(document.getElementById('printerChoose')); 53 | const optionList = document.getElementsByTagName('option'); 54 | const tempList = []; 55 | const length = optionList.length; 56 | for (let index = 0; index < length; index += 1) { 57 | tempList.push({ 58 | key: optionList[index].value, 59 | printer: optionList[index].text 60 | }); 61 | } 62 | this.setState({ 63 | childrensList: tempList 64 | }); 65 | this.request(); 66 | } 67 | request() { 68 | this.setState({ 69 | loading: true 70 | }); 71 | // 获取寄件人信息 72 | API.getSendersResource().then((res) => { 73 | if (res.data.code === 200) { 74 | this.setState({ 75 | senderList: res.data.data.list 76 | }); 77 | } else { 78 | this.setState({ 79 | datas: [] 80 | }); 81 | message.error('获取寄件人信息失败!'); 82 | } 83 | }); 84 | // 获取订单列表 85 | API.getPrintOrderListResource({ 86 | orderID: this.props.data.orderSelectList 87 | }).then((res) => { 88 | if (res.data.code === 200) { 89 | this.setState({ 90 | tableData: res.data.data 91 | }); 92 | } else { 93 | this.setState({ 94 | data: [] 95 | }); 96 | message.error('获取订单信息失败!'); 97 | } 98 | }); 99 | // 获取打印详情信息 100 | API.getPrintOptionResource().then((res) => { 101 | if (res.data.code === 200) { 102 | this.setState({ 103 | ...res.data.data 104 | }); 105 | } else { 106 | message.error('获取订单信息失败!'); 107 | } 108 | }); 109 | } 110 | handleOk() { 111 | const fields = this.props.form.getFieldsValue(); 112 | const newFields = { ...fields, startNum: fields.startNum + fields.sortInter }; 113 | this.props.data.handleOk(newFields); 114 | } 115 | handlePreview() { 116 | const fields = this.props.form.getFieldsValue(); 117 | const newFields = { ...fields, startNum: fields.startNum + fields.sortInter }; 118 | this.props.data.handlePreview(newFields); 119 | } 120 | render() { 121 | const { getFieldDecorator } = this.props.form; 122 | const formItemLayout = { 123 | labelCol: { 124 | xs: { span: 24 }, 125 | sm: { span: 4 } 126 | }, 127 | wrapperCol: { 128 | xs: { span: 24 }, 129 | sm: { span: 18 } 130 | } 131 | }; 132 | const formItemInlineLayout = { 133 | labelCol: { 134 | xs: { span: 24 }, 135 | sm: { span: 8 } 136 | }, 137 | wrapperCol: { 138 | xs: { span: 24 }, 139 | sm: { span: 14 } 140 | } 141 | }; 142 | const formItemInlineLayout1 = { 143 | labelCol: { 144 | xs: { span: 24 }, 145 | sm: { span: 12 } 146 | }, 147 | wrapperCol: { 148 | xs: { span: 24 }, 149 | sm: { span: 12 } 150 | } 151 | }; 152 | const childrens = this.state.childrensList.map(item => ); 153 | let defaultSender = '1'; 154 | this.state.senderList.forEach((item) => { if (item.default) defaultSender = item.id.toString(); }); 155 | return ( 165 | 打印并保存 166 | , 167 | , 175 | 178 | ]} 179 | > 180 |
record.id} 184 | dataSource={this.state.tableData} 185 | scroll={{ x: 1500, y: 160 }} 186 | /> 187 | 188 | 189 | {getFieldDecorator('printer', { 190 | initialValue: this.state.printer 191 | })( 192 | 195 | )} 196 | 197 | 198 | {getFieldDecorator('sender', { 199 | initialValue: defaultSender 200 | })( 201 | 204 | )} 205 | 206 | 207 | {getFieldDecorator('express', { 208 | initialValue: this.state.express 209 | })( 210 | 215 | )} 216 | 217 | 218 | 219 | {getFieldDecorator('startNum', { 220 | initialValue: this.state.expressNum 221 | })( 222 | 223 | )} 224 | 225 | 226 | {getFieldDecorator('sortInter', { 227 | initialValue: this.state.sortInter 228 | })( 229 | 230 | )} 231 | 232 | 233 | 234 | 235 | {getFieldDecorator('paperPaddingUp', { 236 | initialValue: this.state.paperPaddingUp 237 | })( 238 | 239 | )} 240 | 241 | 242 | {getFieldDecorator('paperPaddingDown', { 243 | initialValue: this.state.paperPaddingDown 244 | })( 245 | 246 | )} 247 | 248 | 249 | {getFieldDecorator('paperPaddingLeft', { 250 | initialValue: this.state.paperPaddingLeft 251 | })( 252 | 253 | )} 254 | 255 | 256 | {getFieldDecorator('paperPaddingRight', { 257 | initialValue: this.state.paperPaddingRight 258 | })( 259 | 260 | )} 261 | 262 | (厘米) 263 | 264 | 265 |
record.id} 261 | dataSource={this.state.data} 262 | pagination={pagination} 263 | loading={this.state.loading} 264 | onChange={this.handleTableChange} 265 | rowSelection={rowSelection} 266 | scroll={{ x: 1500 }} 267 | onRowClick={this.onRowClick} 268 | /> 269 | 270 | { this.state.showModal ? : '' } 271 | ); 272 | } 273 | } 274 | 275 | export default OrderUnassign; 276 | -------------------------------------------------------------------------------- /src/components/order-vendor-list.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Modal, Button, Table } from 'antd'; 4 | import PropTypes from 'prop-types'; 5 | 6 | class OrderVendorList extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | pagination: { total: 100 }, 11 | data: [], 12 | loading: false, 13 | selectedRowKeys: [] 14 | }; 15 | } 16 | componentDidMount() { 17 | this.request(); 18 | } 19 | onSelectChange(selectedRowKeys, selectedRows) { 20 | console.log(selectedRowKeys, selectedRows); 21 | this.setState({ 22 | ...this.state, 23 | selectedRowKeys 24 | }); 25 | } 26 | request(payload) { 27 | this.setState({ 28 | loading: true 29 | }); 30 | 31 | API.getOrderVendorsResource(payload).then((res) => { 32 | this.setState({ 33 | ...this.state, 34 | data: res.data.data, 35 | loading: false, 36 | selectedRowKeys: [] 37 | }); 38 | }); 39 | } 40 | handleTableChange(pagination) { 41 | this.request({ 42 | page: pagination.current, 43 | pageSize: pagination.pageSize 44 | }); 45 | } 46 | handleOk() { 47 | this.props.data.handleOk(this.state.selectedRowKeys); 48 | } 49 | render() { 50 | const rowSelection = { 51 | type: 'radio', 52 | selectedRowKeys: this.state.selectedRowKeys, 53 | onChange: this.onSelectChange.bind(this) 54 | }; 55 | const columns = [{ 56 | title: '编号', 57 | key: 'id', 58 | dataIndex: 'id' 59 | }, { 60 | title: '厂商名称', 61 | key: 'vendor', 62 | dataIndex: 'vendor' 63 | }, { 64 | title: '库存余量', 65 | key: 'inventory', 66 | dataIndex: 'inventory' 67 | }]; 68 | 69 | return ( 79 | 取消 80 | , 81 | 90 | ]} 91 | > 92 |
record.id} 95 | dataSource={this.state.data} 96 | rowSelection={rowSelection} 97 | pagination={this.state.pagination} 98 | loading={this.state.loading} 99 | onChange={this.handleTableChange.bind(this)} 100 | /> 101 | ); 102 | } 103 | } 104 | OrderVendorList.propTypes = { 105 | data: PropTypes.object.isRequired 106 | }; 107 | export default OrderVendorList; 108 | -------------------------------------------------------------------------------- /src/components/orderlist-back.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, Input, message, Button, notification } from 'antd'; 4 | import mLODOP from '../utils/print.js'; 5 | import OrderDetail from './order-detail'; 6 | import OrderPrintPreview from './order-print-preview'; 7 | 8 | const Search = Input.Search; 9 | 10 | class OrderListBack extends React.Component { 11 | constructor() { 12 | super(); 13 | this.handleTableChange = this.handleTableChange.bind(this); 14 | this.onPaginationChange = this.onPaginationChange.bind(this); 15 | this.onShowSizeChange = this.onShowSizeChange.bind(this); 16 | this.onSelectChange = this.onSelectChange.bind(this); 17 | this.onSearch = this.onSearch.bind(this); 18 | this.onRowClick = this.onRowClick.bind(this); 19 | this.printOrder = this.printOrder.bind(this); 20 | this.state = { 21 | selectedRowKeys: [], 22 | orderDetailData: { 23 | disableEdit: true 24 | }, 25 | queryKey: '', 26 | queryStatus: '', 27 | collapsed: false, 28 | orderID: null, 29 | pageTotal: 0, 30 | pagination: { 31 | current: 1, 32 | pageSize: 10 33 | }, 34 | data: [], 35 | loading: false, 36 | modalData: { 37 | confirmLoading: false, 38 | orderSelectList: '', 39 | handleOk: (payload) => { 40 | this.setState({ 41 | ...this.state, 42 | modalData: { 43 | ...this.state.modalData, 44 | confirmLoading: true 45 | } 46 | }); 47 | 48 | API.savePrintOptionResource({ ...payload }).then((res) => { 49 | if (res.data.code === 200) { 50 | this.setState({ 51 | ...this.state, 52 | modalData: { 53 | ...this.state.modalData, 54 | confirmLoading: false 55 | } 56 | }); 57 | this.hideDialog(); 58 | message.success('保存打印信息操作成功'); 59 | } else { 60 | this.setState({ 61 | ...this.state, 62 | modalData: { 63 | ...this.state.modalData, 64 | confirmLoading: false 65 | } 66 | }); 67 | this.hideDialog(); 68 | message.error('保存打印信息操作失败!'); 69 | } 70 | }); 71 | }, 72 | handleCancel: () => { 73 | this.hideDialog(); 74 | }, 75 | handlePreview: (payload) => { 76 | API.savePrintOptionResource({ ...payload }).then((res) => { 77 | if (res.data.code === 200) { 78 | this.setState({ 79 | ...this.state, 80 | modalData: { 81 | ...this.state.modalData, 82 | confirmLoading: false 83 | } 84 | }); 85 | this.hideDialog(); 86 | this.startPrint(); 87 | message.success('保存打印信息操作成功'); 88 | } else { 89 | this.setState({ 90 | ...this.state, 91 | modalData: { 92 | ...this.state.modalData, 93 | confirmLoading: false 94 | } 95 | }); 96 | this.hideDialog(); 97 | message.error('保存打印信息操作失败!'); 98 | } 99 | }); 100 | } 101 | } 102 | }; 103 | } 104 | componentDidMount() { 105 | document.title = '已发货订单'; 106 | this.request({ 107 | page: 1, 108 | pageSize: 10 109 | }); 110 | } 111 | onShowSizeChange(current, size) { 112 | this.setState({ 113 | ...this.state, 114 | pagination: { 115 | current, 116 | pageSize: size 117 | } 118 | }); 119 | } 120 | onPaginationChange(page, pageSize) { 121 | this.setState({ 122 | ...this.state, 123 | pagination: { 124 | current: page, 125 | pageSize 126 | } 127 | }); 128 | } 129 | hideDialog() { 130 | this.setState({ 131 | ...this.state, 132 | showModal: false 133 | }); 134 | } 135 | printOrder() { 136 | console.log(this.state.selectedRowKeys.length); 137 | if (this.state.selectedRowKeys.length > 1) { 138 | message.error('该功能不支持批量操作!'); 139 | return; 140 | } 141 | if (this.state.selectedRowKeys.length === 0) { 142 | message.error('请先选择打印的快递订单!'); 143 | return; 144 | } 145 | this.setState({ 146 | ...this.state, 147 | orderID: this.state.selectedRowKeys, 148 | modalData: { 149 | ...this.state.modalData, 150 | orderSelectList: this.state.selectedRowKeys.join() 151 | }, 152 | showModal: true 153 | }); 154 | } 155 | startPrint() { 156 | if (!mLODOP.getMLodop()) { 157 | notification.error({ 158 | message: '错误提示', 159 | description: '你还没安装打印插件,或者没有运行打印程序。请到打印机管理页面进行下载、安装、测试!', 160 | duration: 5 161 | }); 162 | } else { 163 | const tempLodop = mLODOP.getMLodop(); 164 | Promise.all([API.getDefaultPrinter(), API.getOrderPrintDataResource(), API.getDefaultSenderResource(), API.getExpressTemplateResource()]).then((values) => { 165 | const defaultPrinter = values[0].data.data.printer; 166 | const receiverData = values[1].data.data; 167 | const senderData = values[2].data.data; 168 | const tempdata = values[3].data.data; 169 | let reSendCity = ''; 170 | if (senderData && senderData.sendcity) { 171 | reSendCity = '' + senderData.sendcity[0] + senderData.sendcity[1] + senderData.sendcity[2]; 172 | } 173 | const printData = { ...receiverData, ...senderData, sendcity: reSendCity }; 174 | mLODOP.printPurge(defaultPrinter); 175 | mLODOP.printResume(defaultPrinter); 176 | const rTemplate = window.kdPrintBase.printContentReplace(tempdata.note, printData, tempdata); 177 | eval(rTemplate); 178 | if (!mLODOP.checkPrinter(defaultPrinter)) { 179 | notification.error({ 180 | message: '错误提示', 181 | description: '当前设置的默认打印机没有找到,请前往"打印设置"页面,重新设置默认打印机!' 182 | }); 183 | } else { 184 | tempLodop.SET_PRINT_PAGESIZE(1, parseFloat(tempdata.width) * 10, parseFloat(tempdata.height) * 10, ''); 185 | tempLodop.SET_SHOW_MODE('HIDE_PAPER_BOARD', true); 186 | tempLodop.SET_PREVIEW_WINDOW(2, 1, 1, 700, 440, '快递单打印'); 187 | tempLodop.SET_SHOW_MODE('PREVIEW_IN_BROWSE', true); 188 | tempLodop.SET_PRINTER_INDEX(defaultPrinter); 189 | tempLodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', 1); 190 | mLODOP.preview(); 191 | this.hideDialog(); 192 | } 193 | }).catch((reason) => { 194 | notification.error({ 195 | message: '错误提示', 196 | description: reason 197 | }); 198 | console.log(reason); 199 | }); 200 | } 201 | } 202 | onSearch(value) { 203 | this.setState({ 204 | ...this.state, 205 | queryKey: value, 206 | pagination: { 207 | ...this.state.pagination, 208 | current: 1 209 | } 210 | }); 211 | this.request({ 212 | orderID: value, 213 | page: 1, 214 | pageSize: this.state.pagination.pageSize 215 | }); 216 | } 217 | request(payload) { 218 | this.setState({ 219 | loading: true 220 | }); 221 | 222 | API.getBackOrdersResource(payload).then((res) => { 223 | this.setState({ 224 | ...this.state, 225 | selectedRowKeys: [], 226 | data: res.data.data, 227 | loading: false, 228 | pageTotal: res.data.total 229 | }); 230 | }); 231 | } 232 | handleTableChange(pagination) { 233 | this.request({ 234 | page: pagination.current, 235 | pageSize: pagination.pageSize, 236 | orderID: this.state.queryKey 237 | }); 238 | } 239 | onSelectChange(selectedRowKeys) { 240 | console.log('selectedRowKeys changed: ', selectedRowKeys); 241 | this.setState({ 242 | ...this.state, 243 | selectedRowKeys 244 | }); 245 | } 246 | onRowClick(record) { 247 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 248 | console.log(res.data.data); 249 | if (res.data.code === 200) { 250 | this.setState({ 251 | ...this.state, 252 | pageTotal: res.data.total, 253 | orderDetailData: { 254 | ...this.state.orderDetailData, 255 | ...res.data.data 256 | } 257 | }); 258 | } else { 259 | message.error('订单分配操作失败!'); 260 | } 261 | }); 262 | } 263 | render() { 264 | const pagination = { 265 | total: this.state.pageTotal, 266 | showSizeChanger: true, 267 | showQuickJumper: true, 268 | pageSizeOptions: ['10', '20', '30', '40', '100'], 269 | current: this.state.pagination.current, 270 | pageSize: this.state.pagination.pageSize, 271 | onChange: this.onPaginationChange, 272 | onShowSizeChange: this.onShowSizeChange 273 | }; 274 | const columns = [{ 275 | title: '编号', 276 | key: 'id', 277 | dataIndex: 'id', 278 | width: 50 279 | }, { 280 | title: '宝贝名称', 281 | key: 'goodsName', 282 | dataIndex: 'goodsName', 283 | width: 200 284 | }, { 285 | title: '订单编号', 286 | key: 'orderID', 287 | dataIndex: 'orderID', 288 | width: 80 289 | }, { 290 | title: '买家昵称', 291 | key: 'nickname', 292 | dataIndex: 'nickname', 293 | width: 80 294 | }, { 295 | title: '订单时间', 296 | key: 'time', 297 | dataIndex: 'time', 298 | width: 80 299 | }, { 300 | title: '街道地址', 301 | key: 'address', 302 | dataIndex: 'address', 303 | width: 150 304 | }, { 305 | title: '快递公司', 306 | key: 'express', 307 | dataIndex: 'express', 308 | width: 80 309 | }]; 310 | const rowSelection = { 311 | selectedRowKeys: this.state.selectedRowKeys, 312 | onChange: this.onSelectChange 313 | }; 314 | return (
315 |
316 |
317 | 318 |
319 |
320 | 321 |
322 |
323 |
record.id} 326 | dataSource={this.state.data} 327 | pagination={pagination} 328 | loading={this.state.loading} 329 | onChange={this.handleTableChange} 330 | rowSelection={rowSelection} 331 | scroll={{ x: 1500 }} 332 | onRowClick={this.onRowClick} 333 | /> 334 | 335 | { this.state.showModal ? : '' } 336 | ); 337 | } 338 | } 339 | 340 | export default OrderListBack; 341 | -------------------------------------------------------------------------------- /src/components/orderlist-new.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, message, Button, Input, notification } from 'antd'; 4 | import mLODOP from '../utils/print.js'; 5 | import OrderDetail from './order-detail'; 6 | import OrderPrintPreview from './order-print-preview'; 7 | 8 | const Search = Input.Search; 9 | 10 | const columns = [{ 11 | title: '编号', 12 | key: 'id', 13 | dataIndex: 'id', 14 | width: 50 15 | }, { 16 | title: '宝贝名称', 17 | key: 'goodsName', 18 | dataIndex: 'goodsName', 19 | width: 200 20 | }, { 21 | title: '订单编号', 22 | key: 'orderID', 23 | dataIndex: 'orderID', 24 | width: 80 25 | }, { 26 | title: '买家昵称', 27 | key: 'nickname', 28 | dataIndex: 'nickname', 29 | width: 80 30 | }, { 31 | title: '订单时间', 32 | key: 'time', 33 | dataIndex: 'time', 34 | width: 80 35 | }, { 36 | title: '街道地址', 37 | key: 'address', 38 | dataIndex: 'address', 39 | width: 150 40 | }, { 41 | title: '快递公司', 42 | key: 'express', 43 | dataIndex: 'express', 44 | width: 80 45 | }, { 46 | title: '快递单打印状态', 47 | key: 'printed', 48 | dataIndex: 'printed', 49 | width: 80 50 | }]; 51 | 52 | class OrderListNew extends React.Component { 53 | constructor() { 54 | super(); 55 | this.state = { 56 | selectedRowKeys: [], 57 | orderDetailData: null, 58 | orderID: null, 59 | collapsed: false, 60 | pageTotal: 0, 61 | pagination: { 62 | current: 1, 63 | pageSize: 10 64 | }, 65 | data: [], 66 | loading: false, 67 | showModal: false, 68 | modalData: { 69 | confirmLoading: false, 70 | orderSelectList: '', 71 | handleOk: (payload) => { 72 | this.setState({ 73 | ...this.state, 74 | modalData: { 75 | ...this.state.modalData, 76 | confirmLoading: true 77 | } 78 | }); 79 | 80 | API.saveUnassignOrderResource({ orderID: this.state.orderID, vendorID: payload[0] }).then((res) => { 81 | if (res.data.code === 200) { 82 | this.setState({ 83 | ...this.state, 84 | modalData: { 85 | ...this.state.modalData, 86 | confirmLoading: false 87 | } 88 | }); 89 | this.hideDialog(); 90 | message.success('订单分配操作成功'); 91 | } else { 92 | this.setState({ 93 | ...this.state, 94 | modalData: { 95 | ...this.state.modalData, 96 | confirmLoading: false 97 | } 98 | }); 99 | this.hideDialog(); 100 | message.error('订单分配操作失败!'); 101 | } 102 | }); 103 | }, 104 | handleCancel: () => { 105 | this.hideDialog(); 106 | }, 107 | handlePreview: () => { 108 | this.startPrint(); 109 | } 110 | } 111 | }; 112 | } 113 | componentDidMount() { 114 | document.title = '未发货订单'; 115 | this.request({ 116 | page: 1, 117 | pageSize: 10 118 | }); 119 | } 120 | hideDialog() { 121 | this.setState({ 122 | ...this.state, 123 | showModal: false 124 | }); 125 | } 126 | printOrder() { 127 | console.log(this.state.selectedRowKeys.length); 128 | if (this.state.selectedRowKeys.length > 1) { 129 | message.error('该功能不支持批量操作!'); 130 | return; 131 | } 132 | if (this.state.selectedRowKeys.length === 0) { 133 | message.error('请先选择打印的快递订单!'); 134 | return; 135 | } 136 | this.setState({ 137 | ...this.state, 138 | orderID: this.state.selectedRowKeys, 139 | orderSelectList: this.state.selectedRowKeys.join(), 140 | showModal: true 141 | }); 142 | } 143 | startPrint() { 144 | if (!mLODOP.getMLodop()) { 145 | notification.error({ 146 | message: '错误提示', 147 | description: '你还没安装打印插件,或者没有运行打印程序。请到打印机管理页面进行下载、安装、测试!', 148 | duration: 5 149 | }); 150 | } else { 151 | const tempLodop = mLODOP.getMLodop(); 152 | Promise.all([API.getDefaultPrinter(), API.getOrderPrintDataResource(), API.getDefaultSenderResource(), API.getExpressTemplateResource()]).then((values) => { 153 | const defaultPrinter = values[0].data.data.printer; 154 | const receiverData = values[1].data.data; 155 | const senderData = values[2].data.data; 156 | const tempdata = values[3].data.data; 157 | let reSendCity = ''; 158 | if (senderData && senderData.sendcity) { 159 | reSendCity = '' + senderData.sendcity[0] + senderData.sendcity[1] + senderData.sendcity[2]; 160 | } 161 | const printData = { ...receiverData, ...senderData, sendcity: reSendCity }; 162 | mLODOP.printPurge(defaultPrinter); 163 | mLODOP.printResume(defaultPrinter); 164 | const rTemplate = window.kdPrintBase.printContentReplace(tempdata.note, printData, tempdata); 165 | eval(rTemplate); 166 | if (!mLODOP.checkPrinter(defaultPrinter)) { 167 | notification.error({ 168 | message: '错误提示', 169 | description: '当前设置的默认打印机没有找到,请前往"打印设置"页面,重新设置默认打印机!' 170 | }); 171 | } else { 172 | tempLodop.SET_PRINT_PAGESIZE(1, parseFloat(tempdata.width) * 10, parseFloat(tempdata.height) * 10, ''); 173 | tempLodop.SET_SHOW_MODE('HIDE_PAPER_BOARD', true); 174 | tempLodop.SET_PREVIEW_WINDOW(2, 1, 1, 700, 440, '快递单打印'); 175 | tempLodop.SET_SHOW_MODE('PREVIEW_IN_BROWSE', true); 176 | tempLodop.SET_PRINTER_INDEX(defaultPrinter); 177 | tempLodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', 1); 178 | mLODOP.preview(); 179 | this.hideDialog(); 180 | } 181 | }).catch((reason) => { 182 | notification.error({ 183 | message: '错误提示', 184 | description: reason 185 | }); 186 | console.log(reason); 187 | }); 188 | } 189 | } 190 | onBackOrderItem() { 191 | API.updateOrderVendorResource({ id: this.state.selectedRowKeys.join() }).then((res) => { 192 | if (res.data.code === 200) { 193 | message.success('订单退回操作成功!'); 194 | } else { 195 | message.error('订单退回操作失败!'); 196 | } 197 | }); 198 | } 199 | updateOrdersExpressStatus() { 200 | API.updateOrdersExpressStatusResource({ orderID: this.state.selectedRowKeys.join() }).then((res) => { 201 | if (res.data.code === 200) { 202 | message.success('批量发货操作成功!'); 203 | } else { 204 | message.error('批量发货操作失败!'); 205 | } 206 | }); 207 | } 208 | request(payload) { 209 | this.setState({ 210 | loading: true 211 | }); 212 | 213 | API.getOrderListNewResource(payload).then((res) => { 214 | if (res.data.code === 200) { 215 | this.setState({ 216 | data: res.data.data, 217 | loading: false, 218 | pageTotal: res.data.total, 219 | selectedRowKeys: [] 220 | }); 221 | } else { 222 | message.error('获取未发货订单信息失败!'); 223 | this.setState({ 224 | data: [], 225 | loading: false 226 | }); 227 | } 228 | }); 229 | } 230 | handleTableChange(pagination) { 231 | this.request({ 232 | page: pagination.current, 233 | pageSize: pagination.pageSize, 234 | orderID: this.state.queryKey 235 | }); 236 | } 237 | onSelectChange(selectedRowKeys) { 238 | console.log('selectedRowKeys changed: ', selectedRowKeys); 239 | this.setState({ 240 | ...this.state, 241 | selectedRowKeys 242 | }); 243 | } 244 | onRowClick(record, index) { 245 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 246 | if (res.data.code === 200) { 247 | this.setState({ 248 | ...this.state, 249 | orderDetailData: res.data.data 250 | }); 251 | } else { 252 | message.error('订单分配操作失败!'); 253 | } 254 | }); 255 | } 256 | onShowSizeChange(current, size) { 257 | this.setState({ 258 | ...this.state, 259 | pagination: { 260 | current, 261 | pageSize: size 262 | } 263 | }); 264 | } 265 | onPaginationChange(page, pageSize) { 266 | this.setState({ 267 | ...this.state, 268 | pagination: { 269 | current: page, 270 | pageSize 271 | } 272 | }); 273 | } 274 | onSearch(value) { 275 | this.setState({ 276 | ...this.state, 277 | queryKey: value, 278 | pagination: { 279 | ...this.state.pagination, 280 | current: 1 281 | } 282 | }); 283 | this.request({ 284 | orderID: value, 285 | page: 1, 286 | pageSize: this.state.pagination.pageSize 287 | }); 288 | } 289 | render() { 290 | const rowSelection = { 291 | selectedRowKeys: this.state.selectedRowKeys, 292 | onChange: this.onSelectChange.bind(this) 293 | }; 294 | const pagination = { 295 | total: this.state.pageTotal, 296 | showSizeChanger: true, 297 | showQuickJumper: true, 298 | current: this.state.pagination.current, 299 | pageSize: this.state.pagination.pageSize, 300 | onChange: this.onPaginationChange.bind(this), 301 | onShowSizeChange: this.onShowSizeChange.bind(this) 302 | }; 303 | return
304 |
305 |
306 | 307 |
308 |
309 | 310 | 311 | 312 |
313 |
314 |
record.id} 317 | dataSource={this.state.data} 318 | pagination={pagination} 319 | loading={this.state.loading} 320 | onChange={this.handleTableChange.bind(this)} 321 | rowSelection={rowSelection} 322 | onRowClick={this.onRowClick.bind(this)} 323 | scroll={{ x: 1500 }} 324 | /> 325 | 326 | { this.state.showModal ? : '' } 327 | ; 328 | } 329 | } 330 | 331 | export default OrderListNew; 332 | -------------------------------------------------------------------------------- /src/components/orderlist.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, Input, message, Button, notification } from 'antd'; 4 | import mLODOP from '../utils/print.js'; 5 | import OrderDetail from './order-detail'; 6 | import OrderPrintPreview from './order-print-preview'; 7 | 8 | const Search = Input.Search; 9 | 10 | class OrderList extends React.Component { 11 | constructor() { 12 | super(); 13 | this.state = { 14 | selectedRowKeys: [], 15 | orderDetailData: { 16 | disableEdit: true 17 | }, 18 | queryKey: '', 19 | queryStatus: '', 20 | collapsed: false, 21 | orderID: null, 22 | pagination: { 23 | current: 1, 24 | pageSize: 10 25 | }, 26 | pageTotal: 0, 27 | data: [], 28 | loading: false, 29 | modalData: { 30 | confirmLoading: false, 31 | orderSelectList: '', 32 | handleOk: (payload) => { 33 | this.setState({ 34 | ...this.state, 35 | modalData: { 36 | ...this.state.modalData, 37 | confirmLoading: true 38 | } 39 | }); 40 | 41 | API.savePrintOptionResource({ ...payload }).then((res) => { 42 | if (res.data.code === 200) { 43 | this.setState({ 44 | ...this.state, 45 | modalData: { 46 | ...this.state.modalData, 47 | confirmLoading: false 48 | } 49 | }); 50 | this.hideDialog(); 51 | message.success('保存打印信息操作成功'); 52 | } else { 53 | this.setState({ 54 | ...this.state, 55 | modalData: { 56 | ...this.state.modalData, 57 | confirmLoading: false 58 | } 59 | }); 60 | this.hideDialog(); 61 | message.error('保存打印信息操作失败!'); 62 | } 63 | }); 64 | }, 65 | handleCancel: () => { 66 | this.hideDialog(); 67 | }, 68 | handlePreview: (payload) => { 69 | API.savePrintOptionResource({ ...payload }).then((res) => { 70 | if (res.data.code === 200) { 71 | this.setState({ 72 | ...this.state, 73 | modalData: { 74 | ...this.state.modalData, 75 | confirmLoading: false 76 | } 77 | }); 78 | this.hideDialog(); 79 | this.startPrint(); 80 | message.success('保存打印信息操作成功'); 81 | } else { 82 | this.setState({ 83 | ...this.state, 84 | modalData: { 85 | ...this.state.modalData, 86 | confirmLoading: false 87 | } 88 | }); 89 | this.hideDialog(); 90 | message.error('保存打印信息操作失败!'); 91 | } 92 | }); 93 | } 94 | } 95 | }; 96 | } 97 | componentDidMount() { 98 | document.title = '订单列表'; 99 | this.request({ 100 | page: 1, 101 | pageSize: 10 102 | }); 103 | } 104 | onShowSizeChange(current, size) { 105 | this.setState({ 106 | ...this.state, 107 | pagination: { 108 | current, 109 | pageSize: size 110 | } 111 | }); 112 | } 113 | onPaginationChange(page, pageSize) { 114 | this.setState({ 115 | ...this.state, 116 | pagination: { 117 | current: page, 118 | pageSize 119 | } 120 | }); 121 | } 122 | hideDialog() { 123 | this.setState({ 124 | ...this.state, 125 | showModal: false 126 | }); 127 | } 128 | printOrder() { 129 | console.log(this.state.selectedRowKeys.length); 130 | if (this.state.selectedRowKeys.length > 1) { 131 | message.error('该功能不支持批量操作!'); 132 | return; 133 | } 134 | if (this.state.selectedRowKeys.length === 0) { 135 | message.error('请先选择打印的快递订单!'); 136 | return; 137 | } 138 | this.setState({ 139 | ...this.state, 140 | orderID: this.state.selectedRowKeys, 141 | modalData: { 142 | ...this.state.modalData, 143 | orderSelectList: this.state.selectedRowKeys.join() 144 | }, 145 | showModal: true 146 | }); 147 | } 148 | startPrint() { 149 | if (!mLODOP.getMLodop()) { 150 | notification.error({ 151 | message: '错误提示', 152 | description: '你还没安装打印插件,或者没有运行打印程序。请到打印机管理页面进行下载、安装、测试!', 153 | duration: 5 154 | }); 155 | } else { 156 | const tempLodop = mLODOP.getMLodop(); 157 | Promise.all([API.getDefaultPrinter(), API.getOrderPrintDataResource(), API.getDefaultSenderResource(), API.getExpressTemplateResource()]).then((values) => { 158 | const defaultPrinter = values[0].data.data.printer; 159 | const receiverData = values[1].data.data; 160 | const senderData = values[2].data.data; 161 | const tempdata = values[3].data.data; 162 | let reSendCity = ''; 163 | if (senderData && senderData.sendcity) { 164 | reSendCity = '' + senderData.sendcity[0] + senderData.sendcity[1] + senderData.sendcity[2]; 165 | } 166 | const printData = { ...receiverData, ...senderData, sendcity: reSendCity }; 167 | mLODOP.printPurge(defaultPrinter); 168 | mLODOP.printResume(defaultPrinter); 169 | const rTemplate = window.kdPrintBase.printContentReplace(tempdata.note, printData, tempdata); 170 | eval(rTemplate); 171 | if (!mLODOP.checkPrinter(defaultPrinter)) { 172 | notification.error({ 173 | message: '错误提示', 174 | description: '当前设置的默认打印机没有找到,请前往"打印设置"页面,重新设置默认打印机!' 175 | }); 176 | } else { 177 | tempLodop.SET_PRINT_PAGESIZE(1, parseFloat(tempdata.width) * 10, parseFloat(tempdata.height) * 10, ''); 178 | tempLodop.SET_SHOW_MODE('HIDE_PAPER_BOARD', true); 179 | tempLodop.SET_PREVIEW_WINDOW(2, 1, 1, 700, 440, '快递单打印'); 180 | tempLodop.SET_SHOW_MODE('PREVIEW_IN_BROWSE', true); 181 | tempLodop.SET_PRINTER_INDEX(defaultPrinter); 182 | tempLodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', 1); 183 | mLODOP.preview(); 184 | this.hideDialog(); 185 | } 186 | }).catch((reason) => { 187 | notification.error({ 188 | message: '错误提示', 189 | description: reason 190 | }); 191 | console.log(reason); 192 | }); 193 | } 194 | } 195 | onSearch(value) { 196 | this.setState({ 197 | ...this.state, 198 | queryKey: value, 199 | pagination: { 200 | ...this.state.pagination, 201 | current: 1 202 | } 203 | }); 204 | this.request({ 205 | orderID: value, 206 | page: 1, 207 | pageSize: this.state.pagination.pageSize 208 | }); 209 | } 210 | request(payload) { 211 | this.setState({ 212 | loading: true 213 | }); 214 | 215 | API.getAllOrdersResource(payload).then((res) => { 216 | this.setState({ 217 | ...this.state, 218 | selectedRowKeys: [], 219 | data: res.data.data, 220 | loading: false, 221 | pageTotal: res.data.total 222 | }); 223 | }); 224 | } 225 | handleTableChange(pagination) { 226 | this.request({ 227 | page: pagination.current, 228 | pageSize: pagination.pageSize, 229 | orderID: this.state.queryKey 230 | }); 231 | } 232 | onSelectChange(selectedRowKeys) { 233 | console.log('selectedRowKeys changed: ', selectedRowKeys); 234 | this.setState({ 235 | ...this.state, 236 | selectedRowKeys 237 | }); 238 | } 239 | onRowClick(record, index) { 240 | console.log(record,index); 241 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 242 | console.log(res.data.data); 243 | if (res.data.code === 200) { 244 | this.setState({ 245 | ...this.state, 246 | orderDetailData: { 247 | ...this.state.orderDetailData, 248 | ...res.data.data 249 | } 250 | }); 251 | } else { 252 | message.error('订单分配操作失败!'); 253 | } 254 | }); 255 | } 256 | render() { 257 | const pagination = { 258 | total: this.state.pageTotal, 259 | showSizeChanger: true, 260 | showQuickJumper: true, 261 | pageSizeOptions: ['10', '20', '30', '40', '100'], 262 | current: this.state.pagination.current, 263 | pageSize: this.state.pagination.pageSize, 264 | onChange: this.onPaginationChange.bind(this), 265 | onShowSizeChange: this.onShowSizeChange.bind(this) 266 | }; 267 | const columns = [{ 268 | title: '编号', 269 | key: 'id', 270 | dataIndex: 'id', 271 | width: 50 272 | }, { 273 | title: '宝贝名称', 274 | key: 'goodsName', 275 | dataIndex: 'goodsName', 276 | width: 200 277 | }, { 278 | title: '订单编号', 279 | key: 'orderID', 280 | dataIndex: 'orderID', 281 | width: 80 282 | }, { 283 | title: '买家昵称', 284 | key: 'nickname', 285 | dataIndex: 'nickname', 286 | width: 80 287 | }, { 288 | title: '订单时间', 289 | key: 'time', 290 | dataIndex: 'time', 291 | width: 80 292 | }, { 293 | title: '街道地址', 294 | key: 'address', 295 | dataIndex: 'address', 296 | width: 150 297 | }, { 298 | title: '快递公司', 299 | key: 'express', 300 | dataIndex: 'express', 301 | width: 80 302 | }]; 303 | const rowSelection = { 304 | selectedRowKeys: this.state.selectedRowKeys, 305 | onChange: this.onSelectChange.bind(this) 306 | }; 307 | return (
308 |
309 |
310 | 311 |
312 |
313 | 314 |
315 |
316 |
record.id} 319 | dataSource={this.state.data} 320 | pagination={pagination} 321 | loading={this.state.loading} 322 | onChange={this.handleTableChange.bind(this)} 323 | rowSelection={rowSelection} 324 | scroll={{ x: 1500 }} 325 | onRowClick={this.onRowClick.bind(this)} 326 | /> 327 | 328 | { this.state.showModal ? : '' } 329 | ); 330 | } 331 | } 332 | 333 | export default OrderList; 334 | -------------------------------------------------------------------------------- /src/components/orders-back.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, Input, message, Button, notification } from 'antd'; 4 | import mLODOP from '../utils/print.js'; 5 | import OrderDetail from './order-detail'; 6 | import OrderPrintPreview from './order-print-preview'; 7 | 8 | const Search = Input.Search; 9 | 10 | class OrdersBack extends React.Component { 11 | constructor() { 12 | super(); 13 | this.handleTableChange = this.handleTableChange.bind(this); 14 | this.onPaginationChange = this.onPaginationChange.bind(this); 15 | this.onShowSizeChange = this.onShowSizeChange.bind(this); 16 | this.onSelectChange = this.onSelectChange.bind(this); 17 | this.onSearch = this.onSearch.bind(this); 18 | this.onRowClick = this.onRowClick.bind(this); 19 | this.printOrder = this.printOrder.bind(this); 20 | this.state = { 21 | selectedRowKeys: [], 22 | orderDetailData: { 23 | disableEdit: true 24 | }, 25 | queryKey: '', 26 | queryStatus: '', 27 | collapsed: false, 28 | orderID: null, 29 | pageTotal: 0, 30 | pagination: { 31 | current: 1, 32 | pageSize: 10 33 | }, 34 | data: [], 35 | loading: false, 36 | modalData: { 37 | confirmLoading: false, 38 | orderSelectList: '', 39 | handleOk: (payload) => { 40 | this.setState({ 41 | ...this.state, 42 | modalData: { 43 | ...this.state.modalData, 44 | confirmLoading: true 45 | } 46 | }); 47 | 48 | API.savePrintOptionResource({ ...payload }).then((res) => { 49 | if (res.data.code === 200) { 50 | this.setState({ 51 | ...this.state, 52 | modalData: { 53 | ...this.state.modalData, 54 | confirmLoading: false 55 | } 56 | }); 57 | this.hideDialog(); 58 | message.success('保存打印信息操作成功'); 59 | } else { 60 | this.setState({ 61 | ...this.state, 62 | modalData: { 63 | ...this.state.modalData, 64 | confirmLoading: false 65 | } 66 | }); 67 | this.hideDialog(); 68 | message.error('保存打印信息操作失败!'); 69 | } 70 | }); 71 | }, 72 | handleCancel: () => { 73 | this.hideDialog(); 74 | }, 75 | handlePreview: (payload) => { 76 | API.savePrintOptionResource({ ...payload }).then((res) => { 77 | if (res.data.code === 200) { 78 | this.setState({ 79 | ...this.state, 80 | modalData: { 81 | ...this.state.modalData, 82 | confirmLoading: false 83 | } 84 | }); 85 | this.hideDialog(); 86 | this.startPrint(); 87 | message.success('保存打印信息操作成功'); 88 | } else { 89 | this.setState({ 90 | ...this.state, 91 | modalData: { 92 | ...this.state.modalData, 93 | confirmLoading: false 94 | } 95 | }); 96 | this.hideDialog(); 97 | message.error('保存打印信息操作失败!'); 98 | } 99 | }); 100 | } 101 | } 102 | }; 103 | } 104 | componentDidMount() { 105 | document.title = '退货订单'; 106 | this.request({ 107 | page: 1, 108 | pageSize: 10 109 | }); 110 | } 111 | onShowSizeChange(current, size) { 112 | this.setState({ 113 | ...this.state, 114 | pagination: { 115 | current, 116 | pageSize: size 117 | } 118 | }); 119 | } 120 | onPaginationChange(page, pageSize) { 121 | this.setState({ 122 | ...this.state, 123 | pagination: { 124 | current: page, 125 | pageSize 126 | } 127 | }); 128 | } 129 | hideDialog() { 130 | this.setState({ 131 | ...this.state, 132 | showModal: false 133 | }); 134 | } 135 | printOrder() { 136 | console.log(this.state.selectedRowKeys.length); 137 | if (this.state.selectedRowKeys.length > 1) { 138 | message.error('该功能不支持批量操作!'); 139 | return; 140 | } 141 | if (this.state.selectedRowKeys.length === 0) { 142 | message.error('请先选择打印的快递订单!'); 143 | return; 144 | } 145 | this.setState({ 146 | ...this.state, 147 | orderID: this.state.selectedRowKeys, 148 | modalData: { 149 | ...this.state.modalData, 150 | orderSelectList: this.state.selectedRowKeys.join() 151 | }, 152 | showModal: true 153 | }); 154 | } 155 | startPrint() { 156 | if (!mLODOP.getMLodop()) { 157 | notification.error({ 158 | message: '错误提示', 159 | description: '你还没安装打印插件,或者没有运行打印程序。请到打印机管理页面进行下载、安装、测试!', 160 | duration: 5 161 | }); 162 | } else { 163 | const tempLodop = mLODOP.getMLodop(); 164 | Promise.all([API.getDefaultPrinter(), API.getOrderPrintDataResource(), API.getDefaultSenderResource(), API.getExpressTemplateResource()]).then((values) => { 165 | const defaultPrinter = values[0].data.data.printer; 166 | const receiverData = values[1].data.data; 167 | const senderData = values[2].data.data; 168 | const tempdata = values[3].data.data; 169 | let reSendCity = ''; 170 | if (senderData && senderData.sendcity) { 171 | reSendCity = '' + senderData.sendcity[0] + senderData.sendcity[1] + senderData.sendcity[2]; 172 | } 173 | const printData = { ...receiverData, ...senderData, sendcity: reSendCity }; 174 | mLODOP.printPurge(defaultPrinter); 175 | mLODOP.printResume(defaultPrinter); 176 | const rTemplate = window.kdPrintBase.printContentReplace(tempdata.note, printData, tempdata); 177 | eval(rTemplate); 178 | if (!mLODOP.checkPrinter(defaultPrinter)) { 179 | notification.error({ 180 | message: '错误提示', 181 | description: '当前设置的默认打印机没有找到,请前往"打印设置"页面,重新设置默认打印机!' 182 | }); 183 | } else { 184 | tempLodop.SET_PRINT_PAGESIZE(1, parseFloat(tempdata.width) * 10, parseFloat(tempdata.height) * 10, ''); 185 | tempLodop.SET_SHOW_MODE('HIDE_PAPER_BOARD', true); 186 | tempLodop.SET_PREVIEW_WINDOW(2, 1, 1, 700, 440, '快递单打印'); 187 | tempLodop.SET_SHOW_MODE('PREVIEW_IN_BROWSE', true); 188 | tempLodop.SET_PRINTER_INDEX(defaultPrinter); 189 | tempLodop.SET_PRINT_MODE('AUTO_CLOSE_PREWINDOW', 1); 190 | mLODOP.preview(); 191 | this.hideDialog(); 192 | } 193 | }).catch((reason) => { 194 | notification.error({ 195 | message: '错误提示', 196 | description: reason 197 | }); 198 | console.log(reason); 199 | }); 200 | } 201 | } 202 | onSearch(value) { 203 | this.setState({ 204 | ...this.state, 205 | queryKey: value, 206 | pagination: { 207 | ...this.state.pagination, 208 | current: 1 209 | } 210 | }); 211 | this.request({ 212 | orderID: value, 213 | page: 1, 214 | pageSize: this.state.pagination.pageSize 215 | }); 216 | } 217 | request(payload) { 218 | this.setState({ 219 | loading: true 220 | }); 221 | 222 | API.getBackOrdersResource(payload).then((res) => { 223 | this.setState({ 224 | ...this.state, 225 | selectedRowKeys: [], 226 | data: res.data.data, 227 | loading: false, 228 | pageTotal: res.data.total 229 | }); 230 | }); 231 | } 232 | handleTableChange(pagination) { 233 | this.request({ 234 | page: pagination.current, 235 | pageSize: pagination.pageSize, 236 | orderID: this.state.queryKey 237 | }); 238 | } 239 | onSelectChange(selectedRowKeys) { 240 | console.log('selectedRowKeys changed: ', selectedRowKeys); 241 | this.setState({ 242 | ...this.state, 243 | selectedRowKeys 244 | }); 245 | } 246 | onRowClick(record) { 247 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 248 | console.log(res.data.data); 249 | if (res.data.code === 200) { 250 | this.setState({ 251 | ...this.state, 252 | pageTotal: res.data.total, 253 | orderDetailData: { 254 | ...this.state.orderDetailData, 255 | ...res.data.data 256 | } 257 | }); 258 | } else { 259 | message.error('订单分配操作失败!'); 260 | } 261 | }); 262 | } 263 | render() { 264 | const pagination = { 265 | total: this.state.pageTotal, 266 | showSizeChanger: true, 267 | showQuickJumper: true, 268 | pageSizeOptions: ['10', '20', '30', '40', '100'], 269 | current: this.state.pagination.current, 270 | pageSize: this.state.pagination.pageSize, 271 | onChange: this.onPaginationChange, 272 | onShowSizeChange: this.onShowSizeChange 273 | }; 274 | const columns = [{ 275 | title: '编号', 276 | key: 'id', 277 | dataIndex: 'id', 278 | width: 50 279 | }, { 280 | title: '宝贝名称', 281 | key: 'goodsName', 282 | dataIndex: 'goodsName', 283 | width: 200 284 | }, { 285 | title: '订单编号', 286 | key: 'orderID', 287 | dataIndex: 'orderID', 288 | width: 80 289 | }, { 290 | title: '买家昵称', 291 | key: 'nickname', 292 | dataIndex: 'nickname', 293 | width: 80 294 | }, { 295 | title: '订单时间', 296 | key: 'time', 297 | dataIndex: 'time', 298 | width: 80 299 | }, { 300 | title: '街道地址', 301 | key: 'address', 302 | dataIndex: 'address', 303 | width: 150 304 | }, { 305 | title: '快递公司', 306 | key: 'express', 307 | dataIndex: 'express', 308 | width: 80 309 | }]; 310 | const rowSelection = { 311 | selectedRowKeys: this.state.selectedRowKeys, 312 | onChange: this.onSelectChange 313 | }; 314 | return (
315 |
316 |
317 | 318 |
319 |
320 | 321 |
322 |
323 |
record.id} 326 | dataSource={this.state.data} 327 | pagination={pagination} 328 | loading={this.state.loading} 329 | onChange={this.handleTableChange} 330 | rowSelection={rowSelection} 331 | scroll={{ x: 1500 }} 332 | onRowClick={this.onRowClick} 333 | /> 334 | 335 | { this.state.showModal ? : '' } 336 | ); 337 | } 338 | } 339 | 340 | export default OrdersBack; 341 | -------------------------------------------------------------------------------- /src/components/printer-manager.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { message, notification, Card } from 'antd'; 3 | import API from '../api/index'; 4 | import mLODOP from '../utils/print.js'; 5 | 6 | class PrinterManager extends React.Component { 7 | constructor() { 8 | super(); 9 | this.checkIsInstall = this.checkIsInstall.bind(this); 10 | this.changeDefaultPrinter = this.changeDefaultPrinter.bind(this); 11 | this.state = { 12 | printer: '', 13 | checkMsg: '', 14 | checkCMsg: '' 15 | }; 16 | } 17 | componentDidMount() { 18 | document.title = '打印机管理'; 19 | API.getDefaultPrinter().then((res) => { 20 | this.setState({ 21 | printer: res.data.data.printer 22 | }); 23 | }); 24 | // if (mLODOP.getMLodop()) { 25 | // window.LODOP.Create_Printer_List(document.getElementById('printerChoose')); 26 | // } 27 | } 28 | changeDefaultPrinter() { 29 | API.changeDefaultPrinter().then((res) => { 30 | if (res.data.code === 200) { 31 | message.success('修改默认打印机操作成功!'); 32 | } else { 33 | message.error('修改默认打印机作失败!'); 34 | } 35 | }); 36 | } 37 | checkIsInstall() { 38 | let msg = '没有检测到打印插件,请确保打印程序已安装,或者打印程序已经启动运行!如需帮助,请联系029-90879090'; 39 | let cmsg = msg; 40 | if (mLODOP.getMLodop()) { 41 | if (window.LODOP.VERSION < '6.2.1.7') { 42 | msg = '当前Lodop控件版本号:' + window.LODOP.VERSION + '需要升级至最新版本!'; 43 | } else { 44 | msg = '当前Lodop控件可用!Lodop版本号:' + window.LODOP.VERSION; 45 | } 46 | if (window.CLODOP.CVERSION < '2.1.0.2') { 47 | cmsg = '当前C-Lodop控件版本号:' + window.CLODOP.CVERSION + '需要升级至最新版本!'; 48 | } else { 49 | cmsg = '当前C-Lodop云打印可用!C-Lodop版本号:' + window.CLODOP.CVERSION; 50 | } 51 | } else { 52 | cmsg = ''; 53 | notification.error({ 54 | message: '错误提示', 55 | description: '没有检测到打印插件,请确保打印程序已安装,或者打印程序已经启动运行!' 56 | }); 57 | } 58 | 59 | this.setState({ 60 | checkMsg: msg, 61 | checkCMsg: cmsg 62 | }); 63 | } 64 | 65 | render() { 66 | return (
67 |
68 |

打印机检测

69 |
    70 | {/*
  • 71 | 修改} style={{ width: 300, height: 210 }}> 72 |

    当前设置的默认打印机是:

    73 |

    {this.state.printer}

    74 | 76 |
    77 |
  • */} 78 |
  • 79 | 检测} style={{ width: 300, height: 210 }}> 80 |

    {this.state.checkMsg}

    81 |

    {this.state.checkCMsg}

    82 |
    83 |
  • 84 |
85 |
86 |
87 |

打印机安装程序

88 |
89 |

下载地址:点我安装,安装成功后,请 { window.location.reload(); }}>刷新页面

90 |
91 |
92 |
); 93 | } 94 | } 95 | 96 | export default PrinterManager; 97 | -------------------------------------------------------------------------------- /src/components/return-visit-orders.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, message, Input } from 'antd'; 4 | import OrderDetail from './order-detail'; 5 | 6 | const Search = Input.Search; 7 | 8 | const columns = [{ 9 | title: '编号', 10 | key: 'id', 11 | dataIndex: 'id' 12 | }, { 13 | title: '宝贝名称', 14 | key: 'goodsName', 15 | dataIndex: 'goodsName' 16 | }, { 17 | title: '订单编号', 18 | key: 'orderID', 19 | dataIndex: 'orderID' 20 | }, { 21 | title: '买家昵称', 22 | key: 'nickname', 23 | dataIndex: 'nickname' 24 | }, { 25 | title: '收件人', 26 | key: 'reciver', 27 | dataIndex: 'reciver' 28 | }, { 29 | title: '手机号', 30 | key: 'phone', 31 | dataIndex: 'phone' 32 | }, { 33 | title: '街道地址', 34 | key: 'address', 35 | dataIndex: 'address' 36 | }, { 37 | title: '快递', 38 | key: 'express', 39 | dataIndex: 'express' 40 | }, { 41 | title: '订单分配状态', 42 | key: 'assign', 43 | dataIndex: 'assign', 44 | width: 100, 45 | fixed: 'right' 46 | }, { 47 | title: '发货商家', 48 | key: 'vendor', 49 | dataIndex: 'vendor', 50 | width: 100, 51 | fixed: 'right' 52 | }]; 53 | class ReturnVisitOrders extends React.Component { 54 | constructor() { 55 | super(); 56 | this.state = { 57 | collapsed: false, 58 | pageTotal: 0, 59 | selectedRowKeys: [], 60 | pagination: { 61 | current: 1, 62 | pageSize: 10 63 | }, 64 | orderDetailData: { 65 | disableEdit: false 66 | }, 67 | data: [], 68 | loading: false 69 | }; 70 | } 71 | componentDidMount() { 72 | document.title = '已回访订单'; 73 | this.request({ 74 | page: 1, 75 | pageSize: 10 76 | }); 77 | } 78 | request(payload) { 79 | this.setState({ 80 | loading: true 81 | }); 82 | 83 | API.getAssignedOrdersResource(payload).then((res) => { 84 | if (res.data.code === 200) { 85 | this.setState({ 86 | data: res.data.data, 87 | loading: false, 88 | pageTotal: res.data.total, 89 | selectedRowKeys: [] 90 | }); 91 | } else { 92 | this.setState({ 93 | data: [], 94 | loading: false 95 | }); 96 | message.error('获取未分配订单操失败!'); 97 | } 98 | }); 99 | } 100 | onRowClick(record) { 101 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 102 | if (res.data.code === 200) { 103 | this.setState({ 104 | ...this.state, 105 | orderDetailData: { 106 | ...this.state.orderDetailData, 107 | ...res.data.data 108 | } 109 | }); 110 | } else { 111 | message.error('获取订单详情失败!'); 112 | } 113 | }); 114 | } 115 | handleTableChange(pagination) { 116 | this.request({ 117 | page: pagination.current, 118 | pageSize: pagination.pageSize, 119 | orderID: this.state.queryKey 120 | }); 121 | } 122 | onSelectChange(selectedRowKeys) { 123 | this.setState({ 124 | ...this.state, 125 | selectedRowKeys 126 | }); 127 | } 128 | onShowSizeChange(current, size) { 129 | this.setState({ 130 | ...this.state, 131 | pagination: { 132 | current, 133 | pageSize: size 134 | } 135 | }); 136 | } 137 | onPaginationChange(page, pageSize) { 138 | this.setState({ 139 | ...this.state, 140 | pagination: { 141 | current: page, 142 | pageSize 143 | } 144 | }); 145 | } 146 | onSearch(value) { 147 | this.setState({ 148 | ...this.state, 149 | queryKey: value, 150 | pagination: { 151 | ...this.state.pagination, 152 | current: 1 153 | } 154 | }); 155 | this.request({ 156 | orderID: value, 157 | page: 1, 158 | pageSize: this.state.pagination.pageSize 159 | }); 160 | } 161 | render() { 162 | const pagination = { 163 | total: this.state.pageTotal, 164 | showSizeChanger: true, 165 | showQuickJumper: true, 166 | pageSizeOptions: ['10', '20', '30', '40', '100'], 167 | current: this.state.pagination.current, 168 | pageSize: this.state.pagination.pageSize, 169 | onChange: this.onPaginationChange.bind(this), 170 | onShowSizeChange: this.onShowSizeChange.bind(this) 171 | }; 172 | const rowSelection = { 173 | selectedRowKeys: this.state.selectedRowKeys, 174 | onChange: this.onSelectChange.bind(this) 175 | }; 176 | return (
177 |
178 |
179 | 180 |
181 |
182 |
record.id} 185 | dataSource={this.state.data} 186 | pagination={pagination} 187 | loading={this.state.loading} 188 | onChange={this.handleTableChange.bind(this)} 189 | scroll={{ x: 1500 }} 190 | rowSelection={rowSelection} 191 | onRowClick={this.onRowClick.bind(this)} 192 | /> 193 | 194 | ); 195 | } 196 | } 197 | 198 | export default ReturnVisitOrders; 199 | -------------------------------------------------------------------------------- /src/components/sender-edit.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, Form, Button, Input, Checkbox, Cascader } from 'antd'; 3 | import city from '../utils/city'; 4 | import '../assets/less/dialog.less'; 5 | import PropTypes from 'prop-types'; 6 | 7 | const FormItem = Form.Item; 8 | class SenderEdit extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.handleSubmit = this.handleSubmit.bind(this); 12 | } 13 | handleSubmit() { 14 | this.props.form.validateFields((err) => { 15 | if (!err) { 16 | this.props.data.handleOk(this.props.form.getFieldsValue()); 17 | } 18 | }); 19 | } 20 | render() { 21 | const { getFieldDecorator } = this.props.form; 22 | const formItemLayout = { 23 | labelCol: { 24 | xs: { span: 24 }, 25 | sm: { span: 6 } 26 | }, 27 | wrapperCol: { 28 | xs: { span: 24 }, 29 | sm: { span: 14 } 30 | } 31 | }; 32 | const formItemLayoutLast = { 33 | wrapperCol: { 34 | xs: { span: 24, offset: 0 }, 35 | sm: { span: 14, offset: 6 } 36 | } 37 | }; 38 | 39 | return ( 49 | 取消 50 | , 51 | 60 | ]} 61 | > 62 |
63 | 64 | {getFieldDecorator('sendName', { 65 | initialValue: this.props.data.sendName, 66 | rules: [ 67 | { 68 | required: true, 69 | message: '联系人不能为空!' 70 | } 71 | ] 72 | })()} 73 | 74 | 75 | {getFieldDecorator('sendcity', { 76 | initialValue: this.props.data.sendcity, 77 | rules: [ 78 | { 79 | required: true, 80 | message: '所在地不能为空!' 81 | } 82 | ] 83 | })()} 89 | 90 | 91 | {getFieldDecorator('sendaddr', { 92 | initialValue: this.props.data.sendaddr, 93 | rules: [ 94 | { 95 | required: true, 96 | message: '街道地址不能为空!' 97 | } 98 | ] 99 | })( 100 | 101 | )} 102 | 103 | 104 | {getFieldDecorator('sendcompany', { 105 | initialValue: this.props.data.sendcompany 106 | })()} 107 | 108 | 109 | {getFieldDecorator('sendzipcode', { 110 | initialValue: this.props.data.sendzipcode 111 | })()} 112 | 113 | 114 | {getFieldDecorator('sendmobile', { 115 | initialValue: this.props.data.sendmobile, 116 | rules: [ 117 | { 118 | required: true, 119 | message: '手机号不能为空!' 120 | } 121 | ] 122 | })()} 123 | 124 | 125 | {getFieldDecorator('sendtel', { 126 | initialValue: this.props.data.sendtel 127 | })()} 128 | 129 | 130 | 设置为默认发货地址 131 | 132 | 133 | {getFieldDecorator('id', { 134 | initialValue: this.props.data.id 135 | })()} 136 | 137 | 138 |
); 139 | } 140 | } 141 | 142 | SenderEdit.propTypes = { 143 | data: PropTypes.object.isRequired 144 | }; 145 | 146 | export default Form.create()(SenderEdit); 147 | -------------------------------------------------------------------------------- /src/components/sender-item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Badge } from 'antd'; 3 | import '../assets/less/sender-item.less'; 4 | import PropTypes from 'prop-types'; 5 | 6 | const SenderItem = ({ props, editSenderDlg, delDlg }) => { 7 | const style = { 8 | overflow: 'hidden', 9 | textOverflow: 'ellipsis', 10 | whiteSpace: 'nowrap' 11 | }; 12 | const title = ( 13 | {props.sendName} 14 | ); 15 | return (编辑删除} style={{ width: 300, height: 210 }}> 16 |

所在地:{props.sendcity[0]}{props.sendcity[1]}{props.sendcity[2]}

17 |

街道地址:{props.sendaddr}

18 |

单位:{props.sendcompany}

19 |

邮编:{props.sendzipcode}

20 |

手机:{props.sendmobile}

21 |

电话:{props.sendtel}

22 |
); 23 | }; 24 | 25 | SenderItem.propTypes = { 26 | props: PropTypes.object.isRequired, 27 | editSenderDlg: PropTypes.func.isRequired, 28 | delDlg: PropTypes.func.isRequired 29 | }; 30 | 31 | export default SenderItem; 32 | -------------------------------------------------------------------------------- /src/components/sender-new.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, Icon } from 'antd'; 3 | import PropTypes from 'prop-types'; 4 | import '../assets/less/sender-new.less'; 5 | 6 | const SenderNew = (props) => { 7 | return ( 8 | 新增 9 | ); 10 | }; 11 | 12 | SenderNew.propTypes = { 13 | newSenderDlg: PropTypes.func 14 | }; 15 | export default SenderNew; 16 | -------------------------------------------------------------------------------- /src/components/sender-setting.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { message, Modal } from 'antd'; 4 | import SenderItem from './sender-item'; 5 | import SenderNew from './sender-new'; 6 | import SenderEdit from './sender-edit'; 7 | 8 | const confirm = Modal.confirm; 9 | class SenderSetting extends React.Component { 10 | constructor() { 11 | super(); 12 | this.state = { 13 | showModal: false, 14 | isNewDlg: false, 15 | datas: [], 16 | modalData: { 17 | title: '新增联系人', 18 | sendName: '', 19 | sendaddr: '', 20 | sendcity: [], 21 | sendcompany: '', 22 | sendzipcode: '', 23 | sendmobile: '', 24 | sendtel: '', 25 | id: null, 26 | default: false, 27 | confirmLoading: false, 28 | handleOk: (payload) => { 29 | this.setState({ 30 | ...this.state, 31 | modalData: { 32 | ...this.state.modalData, 33 | confirmLoading: true 34 | } 35 | }); 36 | if (payload.id) { 37 | API.updateSenderResource(payload).then((res) => { 38 | if (res.data.code === 200) { 39 | message.success('修改联系人成功!'); 40 | this.setState({ 41 | ...this.state, 42 | modalData: { 43 | ...this.state.modalData, 44 | confirmLoading: false 45 | } 46 | }); 47 | this.hideDialog(); 48 | this.getData(); 49 | } else { 50 | this.setState({ 51 | ...this.state, 52 | modalData: { 53 | ...this.state.modalData, 54 | confirmLoading: false 55 | } 56 | }); 57 | message.error('修改联系人操作失败!'); 58 | } 59 | }); 60 | } else { 61 | API.addSenderResource(payload).then((res) => { 62 | if (res.data.code === 200) { 63 | message.success('新增联系人成功!'); 64 | this.setState({ 65 | ...this.state, 66 | modalData: { 67 | ...this.state.modalData, 68 | confirmLoading: false 69 | } 70 | }); 71 | this.hideDialog(); 72 | this.getData(); 73 | } else { 74 | this.setState({ 75 | ...this.state, 76 | modalData: { 77 | ...this.state.modalData, 78 | confirmLoading: false 79 | } 80 | }); 81 | message.error('新增联系人操作失败!'); 82 | } 83 | }); 84 | } 85 | }, 86 | handleCancel: () => { 87 | this.hideDialog(); 88 | }, 89 | onChange: (e) => { 90 | this.setState({ 91 | ...this.state, 92 | modalData: { 93 | ...this.state.modalData, 94 | default: e.target.checked 95 | } 96 | }); 97 | } 98 | } 99 | }; 100 | } 101 | componentDidMount() { 102 | document.title = '寄件人管理'; 103 | this.getData(); 104 | } 105 | getData() { 106 | API.getSendersResource().then((res) => { 107 | if (res.data.code === 200) { 108 | this.setState({ 109 | datas: res.data.data.list 110 | }); 111 | } else { 112 | this.setState({ 113 | datas: [] 114 | }); 115 | message.error('获取寄件人信息失败!'); 116 | } 117 | }); 118 | } 119 | hideDialog() { 120 | this.setState({ 121 | ...this.state, 122 | showModal: false 123 | }); 124 | } 125 | newSenderDlg() { 126 | this.setState({ 127 | ...this.state, 128 | modalData: { 129 | ...this.state.modalData, 130 | title: '新增联系人', 131 | sendName: '', 132 | sendaddr: '', 133 | sendcity: [], 134 | sendcompany: '', 135 | sendzipcode: '', 136 | sendmobile: '', 137 | sendtel: '', 138 | default: false, 139 | id: null 140 | }, 141 | showModal: true, 142 | isNewDlg: true 143 | }); 144 | } 145 | editSenderDlg(item) { 146 | this.setState({ 147 | ...this.state, 148 | modalData: { 149 | ...this.state.modalData, 150 | visible: true, 151 | title: '编辑联系人', 152 | sendName: item.sendName, 153 | sendaddr: item.sendaddr, 154 | sendcity: item.sendcity, 155 | sendcompany: item.sendcompany, 156 | sendzipcode: item.sendzipcode, 157 | sendmobile: item.sendmobile, 158 | sendtel: item.sendtel, 159 | default: item.default, 160 | id: item.id 161 | }, 162 | showModal: true, 163 | isNewDlg: false 164 | }); 165 | } 166 | delDlg(item) { 167 | confirm({ 168 | title: '您确定将删除该寄件人信息?', 169 | onOk: () => { 170 | API.delSenderResource({ id: item.id }).then((res) => { 171 | if (res.data.code === 200) { 172 | message.success('删除寄件人信息操作成功!'); 173 | this.getData(); 174 | } else { 175 | message.error('删除寄件人信息操作失败!'); 176 | } 177 | }); 178 | } 179 | }); 180 | } 181 | render() { 182 | const listItems = this.state.datas.map((item, index) => 183 | (
  • 184 | 185 |
  • ) 186 | ); 187 | 188 | return (
    189 |
      190 | {listItems} 191 |
    • 192 | 193 |
    • 194 |
    195 | { this.state.showModal ? : '' } 196 |
    ); 197 | } 198 | } 199 | 200 | export default SenderSetting; 201 | -------------------------------------------------------------------------------- /src/components/unreturn-visit-orders.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, message, Input } from 'antd'; 4 | import OrderDetail from './order-detail'; 5 | 6 | const Search = Input.Search; 7 | 8 | const columns = [{ 9 | title: '编号', 10 | key: 'id', 11 | dataIndex: 'id' 12 | }, { 13 | title: '宝贝名称', 14 | key: 'goodsName', 15 | dataIndex: 'goodsName' 16 | }, { 17 | title: '订单编号', 18 | key: 'orderID', 19 | dataIndex: 'orderID' 20 | }, { 21 | title: '买家昵称', 22 | key: 'nickname', 23 | dataIndex: 'nickname' 24 | }, { 25 | title: '收件人', 26 | key: 'reciver', 27 | dataIndex: 'reciver' 28 | }, { 29 | title: '手机号', 30 | key: 'phone', 31 | dataIndex: 'phone' 32 | }, { 33 | title: '街道地址', 34 | key: 'address', 35 | dataIndex: 'address' 36 | }, { 37 | title: '快递', 38 | key: 'express', 39 | dataIndex: 'express' 40 | }, { 41 | title: '订单分配状态', 42 | key: 'assign', 43 | dataIndex: 'assign', 44 | width: 100, 45 | fixed: 'right' 46 | }, { 47 | title: '发货商家', 48 | key: 'vendor', 49 | dataIndex: 'vendor', 50 | width: 100, 51 | fixed: 'right' 52 | }]; 53 | class UnReturnVisitOrders extends React.Component { 54 | constructor() { 55 | super(); 56 | this.state = { 57 | collapsed: false, 58 | pageTotal: 0, 59 | selectedRowKeys: [], 60 | pagination: { 61 | current: 1, 62 | pageSize: 10 63 | }, 64 | orderDetailData: { 65 | disableEdit: false 66 | }, 67 | data: [], 68 | loading: false 69 | }; 70 | } 71 | componentDidMount() { 72 | document.title = '未回访订单'; 73 | this.request({ 74 | page: 1, 75 | pageSize: 10 76 | }); 77 | } 78 | request(payload) { 79 | this.setState({ 80 | loading: true 81 | }); 82 | 83 | API.getAssignedOrdersResource(payload).then((res) => { 84 | if (res.data.code === 200) { 85 | this.setState({ 86 | data: res.data.data, 87 | loading: false, 88 | pageTotal: res.data.total, 89 | selectedRowKeys: [] 90 | }); 91 | } else { 92 | this.setState({ 93 | data: [], 94 | loading: false 95 | }); 96 | message.error('获取未分配订单操失败!'); 97 | } 98 | }); 99 | } 100 | onRowClick(record) { 101 | API.getOrderDetailResource({ orderID: record.id }).then((res) => { 102 | if (res.data.code === 200) { 103 | this.setState({ 104 | ...this.state, 105 | orderDetailData: { 106 | ...this.state.orderDetailData, 107 | ...res.data.data 108 | } 109 | }); 110 | } else { 111 | message.error('获取订单详情失败!'); 112 | } 113 | }); 114 | } 115 | handleTableChange(pagination) { 116 | this.request({ 117 | page: pagination.current, 118 | pageSize: pagination.pageSize, 119 | orderID: this.state.queryKey 120 | }); 121 | } 122 | onSelectChange(selectedRowKeys) { 123 | this.setState({ 124 | ...this.state, 125 | selectedRowKeys 126 | }); 127 | } 128 | onShowSizeChange(current, size) { 129 | this.setState({ 130 | ...this.state, 131 | pagination: { 132 | current, 133 | pageSize: size 134 | } 135 | }); 136 | } 137 | onPaginationChange(page, pageSize) { 138 | this.setState({ 139 | ...this.state, 140 | pagination: { 141 | current: page, 142 | pageSize 143 | } 144 | }); 145 | } 146 | onSearch(value) { 147 | this.setState({ 148 | ...this.state, 149 | queryKey: value, 150 | pagination: { 151 | ...this.state.pagination, 152 | current: 1 153 | } 154 | }); 155 | this.request({ 156 | orderID: value, 157 | page: 1, 158 | pageSize: this.state.pagination.pageSize 159 | }); 160 | } 161 | render() { 162 | const pagination = { 163 | total: this.state.pageTotal, 164 | showSizeChanger: true, 165 | showQuickJumper: true, 166 | pageSizeOptions: ['10', '20', '30', '40', '100'], 167 | current: this.state.pagination.current, 168 | pageSize: this.state.pagination.pageSize, 169 | onChange: this.onPaginationChange.bind(this), 170 | onShowSizeChange: this.onShowSizeChange.bind(this) 171 | }; 172 | const rowSelection = { 173 | selectedRowKeys: this.state.selectedRowKeys, 174 | onChange: this.onSelectChange.bind(this) 175 | }; 176 | return (
    177 |
    178 |
    179 | 180 |
    181 |
    182 |
    record.id} 185 | dataSource={this.state.data} 186 | pagination={pagination} 187 | loading={this.state.loading} 188 | onChange={this.handleTableChange.bind(this)} 189 | scroll={{ x: 1500 }} 190 | rowSelection={rowSelection} 191 | onRowClick={this.onRowClick.bind(this)} 192 | /> 193 | 194 | ); 195 | } 196 | } 197 | 198 | export default UnReturnVisitOrders; 199 | -------------------------------------------------------------------------------- /src/components/user-add.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, Button, Input, Select, Form } from 'antd'; 3 | 4 | const Option = Select.Option; 5 | const FormItem = Form.Item; 6 | class UserAdd extends React.Component { 7 | constructor() { 8 | super(); 9 | this.state = { someKey: 'someValue' }; 10 | } 11 | handleOk() { 12 | this.props.data.handleOk(); 13 | } 14 | render() { 15 | const formItemLayout = { 16 | labelCol: { span: 24 }, 17 | wrapperCol: { span: 24 } 18 | }; 19 | const { getFieldDecorator } = this.props.form; 20 | return ( 30 | 取消 31 | , 32 | 41 | ]} 42 | > 43 |
    44 | 45 | {getFieldDecorator('userName', { 46 | initialValue: this.props.userName 47 | })( 48 | 49 | )} 50 | 51 | 52 | {getFieldDecorator('userName', { 53 | initialValue: this.props.userName 54 | })( 55 | 56 | )} 57 | 58 | 59 | {getFieldDecorator('wareHouse', { 60 | initialValue: '广东省广州市科技路一号百库1号仓' 61 | })( 62 | 70 | )} 71 | 72 | 73 |
    ); 74 | } 75 | 76 | componentDidMount() { 77 | this.setState({ someKey: 'otherValue' }); 78 | } 79 | } 80 | 81 | export default Form.create()(UserAdd); 82 | -------------------------------------------------------------------------------- /src/components/userlist.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from '../api/index'; 3 | import { Table, Input, message, Button } from 'antd'; 4 | import UserAdd from './user-add'; 5 | 6 | const Search = Input.Search; 7 | class Userlist extends React.Component { 8 | constructor() { 9 | super(); 10 | this.state = { 11 | selectedRowKeys: [], 12 | pageTotal: 0, 13 | queryKey: '', 14 | collapsed: false, 15 | orderID: null, 16 | pagination: { 17 | current: 1, 18 | pageSize: 10 19 | }, 20 | data: [], 21 | loading: false, 22 | showModal: false, 23 | modalData: { 24 | confirmLoading: false, 25 | orderSelectList: '', 26 | title: '新建用户', 27 | disableUserName: false, 28 | handleOk: (payload) => { 29 | this.setState({ 30 | ...this.state, 31 | modalData: { 32 | ...this.state.modalData, 33 | confirmLoading: true 34 | } 35 | }); 36 | 37 | API.savePrintOptionResource({ ...payload }).then((res) => { 38 | if (res.data.code === 200) { 39 | this.setState({ 40 | ...this.state, 41 | modalData: { 42 | ...this.state.modalData, 43 | confirmLoading: false 44 | } 45 | }); 46 | this.hideDialog(); 47 | message.success('保存打印信息操作成功'); 48 | } else { 49 | this.setState({ 50 | ...this.state, 51 | modalData: { 52 | ...this.state.modalData, 53 | confirmLoading: false 54 | } 55 | }); 56 | this.hideDialog(); 57 | message.error('保存打印信息操作失败!'); 58 | } 59 | }); 60 | }, 61 | handleCancel: () => { 62 | console.log("11111111111") 63 | this.hideDialog(); 64 | } 65 | } 66 | }; 67 | this.addUser = this.addUser.bind(this); 68 | this.delUser = this.delUser.bind(this); 69 | this.resetPassword = this.resetPassword.bind(this); 70 | this.editUser = this.editUser.bind(this); 71 | this.onSelectChange = this.onSelectChange.bind(this); 72 | this.handleTableChange = this.handleTableChange.bind(this); 73 | this.onPaginationChange = this.onPaginationChange.bind(this); 74 | this.onShowSizeChange = this.onShowSizeChange.bind(this); 75 | } 76 | componentDidMount() { 77 | document.title = '用户列表'; 78 | this.request({ 79 | page: 1, 80 | pageSize: 10 81 | }); 82 | } 83 | hideDialog() { 84 | this.setState({ 85 | ...this.state, 86 | showModal: false 87 | }); 88 | } 89 | addUser() { 90 | this.setState({ 91 | ...this.state, 92 | showModal: true, 93 | modalData: { 94 | ...this.state.modalData, 95 | title: '新建用户', 96 | disableUserName: false 97 | } 98 | }); 99 | } 100 | delUser() { 101 | if (this.state.selectedRowKeys.length < 1) { 102 | message.error('请至少选择一项!'); 103 | return; 104 | } 105 | } 106 | editUser() { 107 | if (this.state.selectedRowKeys.length < 1) { 108 | message.error('请至少选择一项!'); 109 | return; 110 | } 111 | this.setState({ 112 | ...this.state, 113 | showModal: true, 114 | modalData: { 115 | ...this.state.modalData, 116 | title: '编辑用户', 117 | disableUserName: true 118 | } 119 | }); 120 | } 121 | resetPassword() { 122 | if (this.state.selectedRowKeys.length < 1) { 123 | message.error('请至少选择一项!'); 124 | return; 125 | } 126 | } 127 | onShowSizeChange(current, size) { 128 | this.setState({ 129 | ...this.state, 130 | pagination: { 131 | current, 132 | pageSize: size 133 | } 134 | }); 135 | } 136 | onPaginationChange(page, pageSize) { 137 | this.setState({ 138 | ...this.state, 139 | pagination: { 140 | current: page, 141 | pageSize 142 | } 143 | }); 144 | } 145 | request(payload) { 146 | this.setState({ 147 | loading: true 148 | }); 149 | 150 | API.getUnassignOrdersResource(payload).then((res) => { 151 | this.setState({ 152 | ...this.state, 153 | selectedRowKeys: [], 154 | data: res.data.data, 155 | loading: false, 156 | pageTotal: res.data.total 157 | }); 158 | }); 159 | } 160 | handleTableChange(pagination) { 161 | this.request({ 162 | page: pagination.current, 163 | pageSize: pagination.pageSize, 164 | userQuery: this.state.queryKey 165 | }); 166 | } 167 | onSelectChange(selectedRowKeys) { 168 | console.log('selectedRowKeys changed: ', selectedRowKeys); 169 | this.setState({ 170 | ...this.state, 171 | selectedRowKeys 172 | }); 173 | } 174 | render() { 175 | const pagination = { 176 | total: this.state.pageTotal, 177 | showSizeChanger: true, 178 | showQuickJumper: true, 179 | pageSizeOptions: ['10', '20', '30', '40', '100'], 180 | current: this.state.pagination.current, 181 | pageSize: this.state.pagination.pageSize, 182 | onChange: this.onPaginationChange, 183 | onShowSizeChange: this.onShowSizeChange 184 | }; 185 | const columns = [{ 186 | title: '编号', 187 | key: 'id', 188 | dataIndex: 'id', 189 | width: 20 190 | }, { 191 | title: '用户名', 192 | key: 'userName', 193 | dataIndex: 'userName', 194 | width: 80 195 | }, { 196 | title: '手机号', 197 | key: 'phone', 198 | dataIndex: 'phone', 199 | width: 80 200 | }, { 201 | title: '联系人', 202 | key: 'contact', 203 | dataIndex: 'contact', 204 | width: 80 205 | }, { 206 | title: '库位', 207 | key: 'whareHouse', 208 | dataIndex: 'wareHouse', 209 | width: 100 210 | }]; 211 | const rowSelection = { 212 | selectedRowKeys: this.state.selectedRowKeys, 213 | onChange: this.onSelectChange 214 | }; 215 | return (
    216 |
    217 |
    218 | 219 |
    220 |
    221 | 222 | 223 | 224 | 225 |
    226 |
    227 |
    record.id} 230 | dataSource={this.state.data} 231 | pagination={pagination} 232 | loading={this.state.loading} 233 | onChange={this.handleTableChange} 234 | rowSelection={rowSelection} 235 | /> 236 | { this.state.showModal ? : '' } 237 | ); 238 | } 239 | } 240 | 241 | export default Userlist; 242 | -------------------------------------------------------------------------------- /src/containers/app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Layout, Menu, Icon, Modal, Button, Input } from 'antd'; 3 | import { withRouter, Redirect } from 'react-router-dom'; 4 | import { connect } from 'react-redux'; 5 | import { requestData, logoutRequest } from '../redux/actions/user'; 6 | import { setCurrentItem, setOpenKeys, setMenuFold } from '../redux/actions/menu'; 7 | import RecursiveMenus from '../utils/menu'; 8 | import '../assets/less/app.less'; 9 | import Logo from '../assets/imgs/logo.svg'; 10 | 11 | const { Header, Sider, Footer, Content } = Layout; 12 | const SubMenu = Menu.SubMenu; 13 | 14 | 15 | class App extends React.Component { 16 | constructor() { 17 | super(); 18 | this.state = { 19 | pagination: { total: 20 }, 20 | data: [], 21 | loading: false, 22 | passwdDlg: false 23 | }; 24 | } 25 | componentDidMount() { 26 | const location = this.props.location.pathname; 27 | const arr = location.split('/'); 28 | if (arr[1] && arr[2] && arr[3]) { 29 | this.props.setOpenKeys([arr[1], arr[2]]); 30 | } else if (arr[1] && arr[2]) { 31 | this.props.setOpenKeys([arr[1]]); 32 | } 33 | } 34 | onOpenChange(openKeys) { 35 | const latestOpenKey = openKeys.find(key => !(this.props.openKeys.indexOf(key) > -1)); 36 | const latestCloseKey = this.props.openKeys.find(key => !(openKeys.indexOf(key) > -1)); 37 | 38 | let nextOpenKeys = []; 39 | if (latestOpenKey) { 40 | nextOpenKeys = this.getAncestorKeys(latestOpenKey).concat(latestOpenKey); 41 | } 42 | if (latestCloseKey) { 43 | nextOpenKeys = this.getAncestorKeys(latestCloseKey); 44 | } 45 | 46 | this.props.setOpenKeys(nextOpenKeys); 47 | } 48 | onCollapse(collapsed) { 49 | this.props.setMenuFold(collapsed); 50 | } 51 | getAncestorKeys(key) { 52 | const map = { 53 | msgOrder: ['express'] 54 | }; 55 | return map[key] || []; 56 | } 57 | menuClick(e) { 58 | console.log(e.key); 59 | this.props.setCurrentItem(e.key); 60 | } 61 | toggle() { 62 | this.props.setMenuFold(!this.props.menuFold); 63 | } 64 | logout() { 65 | this.props.logout(); 66 | } 67 | handleOk() { 68 | this.setState({ loading: true }); 69 | setTimeout(() => { 70 | this.setState({ loading: false, passwdDlg: false }); 71 | }, 3000); 72 | } 73 | handleCancel() { 74 | this.setState({ passwdDlg: false }); 75 | } 76 | modifyPasswd() { 77 | this.setState({ 78 | passwdDlg: true 79 | }); 80 | } 81 | request(pageNumber) { 82 | 83 | } 84 | send() { 85 | this.props.sendData(); 86 | } 87 | render() { 88 | if (!this.props.authenticated) { 89 | return (); 98 | } 99 | const menuProps = !this.props.menuFold ? { 100 | onOpenChange: this.onOpenChange.bind(this), 101 | openKeys: this.props.openKeys 102 | } : {}; 103 | const location = this.props.location.pathname; 104 | const arr = location.split('/'); 105 | const defaultSelectedKeys = arr[1] && arr[2] && arr[3] ? [arr[3]] : arr[1] && arr[2] ? [arr[2]] : arr[1] ? [arr[1]] : ['']; 106 | const notAdminMenu=[{ 107 | key: 'orderListNew', 108 | path: '/express/orderListNew', 109 | name: '未发货订单', 110 | icon: 'meh-o', 111 | children: [{ 112 | key: 'noMsgOrderList', 113 | path: '/express/orderListNew/noMsgOrderList', 114 | name: '无留言/备注' 115 | }, { 116 | key: 'msgOrderList', 117 | path: '/express/orderListNew/msgOrderList', 118 | name: '有留言/备注' 119 | }] 120 | }, { 121 | key: 'orderListFinish', 122 | path: '/express/orderListFinish', 123 | name: '已发货订单', 124 | icon: 'smile-o' 125 | }, { 126 | key: 'orderListBack', 127 | path: '/express/orderListBack', 128 | name: '退货订单', 129 | icon: 'rollback' 130 | }, { 131 | key: 'orderList', 132 | path: '/express/orderList', 133 | name: '所有订单', 134 | icon: 'solution' 135 | }, { 136 | key: 'senderSetting', 137 | path: '/print/senderSetting', 138 | name: '寄件人设置', 139 | icon: 'user' 140 | }, { 141 | key: 'printerManager', 142 | path: '/print/printerManager', 143 | name: '打印机管理', 144 | icon: 'printer' 145 | }]; 146 | const adminMenu=[{ 147 | key: 'orders', 148 | path: '/orders/list', 149 | name: '我的订单', 150 | icon: 'printer', 151 | children: [{ 152 | key: 'orderUnassign', 153 | path: '/orders/orderUnassign', 154 | name: '未分配订单' 155 | }, { 156 | key: 'orderAssigned', 157 | path: '/orders/orderAssigned', 158 | name: '已分配订单' 159 | },{ 160 | key: 'UnreturnVisitOrders', 161 | path: '/orders/UnreturnVisitOrders', 162 | name: '未回访订单' 163 | }, { 164 | key: 'ReturnVisitOrders', 165 | path: '/orders/ReturnVisitOrders', 166 | name: '已回访订单' 167 | }, { 168 | key: 'OrdersBack', 169 | path: '/orders/OrdersBack', 170 | name: '退货订单' 171 | }] 172 | }, { 173 | key: 'userList', 174 | path: '/user/userList', 175 | name: '用户管理', 176 | level: 0, 177 | icon: 'user' 178 | }, { 179 | key: 'orderListBack', 180 | path: '/express/orderListBack', 181 | name: '已发货订单', 182 | icon: 'phone' 183 | }]; 184 | return (
    185 | 186 | 187 |
    188 | logo 189 |
    190 | 191 | { this.props.isAdmin ? RecursiveMenus(adminMenu,false) : RecursiveMenus(notAdminMenu,false)} 192 | 193 |
    194 | 195 |
    196 |
    197 | 202 |
    203 |
    204 | 205 | {this.props.userName}}> 206 | 207 |
    修改密码
    208 |
    209 | 210 |
    退出
    211 |
    212 |
    213 |
    214 |
    215 |
    216 | 217 |
    218 | {this.props.children} 219 | {/*
    220 |

    数据:{this.props.customData ? this.props.customData.data.payload.orderstate : '无数据' }

    221 | 222 |
    */} 223 |
    224 |
    225 | 235 | 取消 236 | , 237 | 243 | ]} 244 | > 245 | 246 | 247 | 248 | 249 |
    250 |
    251 |
    252 | XXX订单管理系统 版权所有 © 2017 由 XXX科技有限责任公司 支持 253 |
    254 |
    255 |
    256 |
    ); 257 | } 258 | } 259 | 260 | function mapStateToProp(state) { 261 | return { 262 | authenticated: state.userReducer.authenticated, 263 | isAuthenticating: state.userReducer.isAuthenticating, 264 | customData: state.userReducer.customData, 265 | current: state.menuReducer.currentItem, 266 | openKeys: state.menuReducer.openKeys, 267 | menuFold: state.menuReducer.menuFold, 268 | isAdmin: state.userReducer.isAdmin, 269 | userName: state.userReducer.userName 270 | }; 271 | } 272 | 273 | function mapDispatchToProp(dispatch) { 274 | return { 275 | logout: () => { 276 | dispatch(logoutRequest()); 277 | }, 278 | sendData: (data) => { 279 | dispatch(requestData(data)); 280 | }, 281 | setOpenKeys: (data) => { 282 | dispatch(setOpenKeys(data)); 283 | }, 284 | setMenuFold: (data) => { 285 | dispatch(setMenuFold(data)); 286 | }, 287 | setCurrentItem: (data) => { 288 | dispatch(setCurrentItem(data)); 289 | } 290 | }; 291 | } 292 | 293 | export default connect(mapStateToProp, mapDispatchToProp)(withRouter(App)); 294 | -------------------------------------------------------------------------------- /src/containers/dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './app'; 3 | import CDashBoard from '../components/dashboard'; 4 | 5 | const DashBoard = () => { 6 | return (
    7 | 8 | 9 | 10 |
    ); 11 | }; 12 | 13 | export default DashBoard; 14 | -------------------------------------------------------------------------------- /src/containers/express.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './app'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | // import OrderList from '../components/orderlist'; 5 | // import OrderListBack from '../components/orderlist-back'; 6 | // import OrderListNew from '../components/orderlist-new'; 7 | // import OrderListFinish from '../components/orderlist-finish'; 8 | // import MsgOrderList from '../components/orderlist-msg'; 9 | // import NoMsgOrderList from '../components/orderlist-nomsg'; 10 | import NoMatch from './nomatch'; 11 | 12 | import Loadable from '../components/loading'; 13 | 14 | const OrderList = Loadable({ 15 | loader: () => import('../components/orderlist') 16 | }); 17 | const OrderListBack = Loadable({ 18 | loader: () => import('../components/orderlist-back') 19 | }); 20 | const OrderListFinish = Loadable({ 21 | loader: () => import('../components/orderlist-finish') 22 | }); 23 | const MsgOrderList = Loadable({ 24 | loader: () => import('../components/orderlist-msg') 25 | }); 26 | const NoMsgOrderList = Loadable({ 27 | loader: () => import('../components/orderlist-nomsg') 28 | }); 29 | const OrderListNew = Loadable({ 30 | loader: () => import('../components/orderlist-new') 31 | }); 32 | 33 | 34 | 35 | const Express = () => { 36 | return (
    37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
    ); 49 | }; 50 | 51 | export default Express; 52 | -------------------------------------------------------------------------------- /src/containers/nomatch.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import '../assets/less/nomatch.less'; 4 | 5 | const Nomatch = () => { 6 | return (
    7 |
    8 |

    404

    9 |

    10 | 返回首页 11 |

    12 |
    13 |
    ); 14 | }; 15 | 16 | export default Nomatch; 17 | -------------------------------------------------------------------------------- /src/containers/orders.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './app'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | import OrderUnassign from '../components/order-unassign.jsx'; 5 | import OrderAssigned from '../components/order-assigned.jsx'; 6 | import OrdersBack from '../components/orders-back.jsx'; 7 | import UnReturnVisitOrders from '../components/unreturn-visit-orders.jsx'; 8 | import ReturnVisitOrders from '../components/return-visit-orders.jsx'; 9 | import NoMatch from './nomatch'; 10 | 11 | const Orders = () => { 12 | return (
    13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
    ); 24 | }; 25 | 26 | export default Orders; 27 | -------------------------------------------------------------------------------- /src/containers/print.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './app'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | import PrinterManager from '../components/printer-manager.jsx'; 5 | import SenderSetting from '../components/sender-setting.jsx'; 6 | import NoMatch from './nomatch'; 7 | 8 | const Print = () => { 9 | return (
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    ); 18 | }; 19 | 20 | export default Print; 21 | -------------------------------------------------------------------------------- /src/containers/scroll-to-top.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {withRouter} from 'react-router-dom'; 3 | 4 | 5 | class ScrollToTop extends React.Component { 6 | componentDidUpdate(prevProps) { 7 | if (this.props.location !== prevProps.location) { 8 | window.scroll(0, 0); 9 | } 10 | } 11 | render() { 12 | return (
    13 | {this.props.children} 14 |
    ); 15 | } 16 | } 17 | export default withRouter(ScrollToTop); -------------------------------------------------------------------------------- /src/containers/usermanager.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './app'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | import NewUser from '../components/user-add.jsx'; 5 | import UserList from '../components/userlist.jsx'; 6 | import NoMatch from './nomatch'; 7 | 8 | const UserManager = () => { 9 | return (
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    ); 18 | }; 19 | 20 | export default UserManager; 21 | -------------------------------------------------------------------------------- /src/http/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import qs from 'qs'; 3 | import store from '../redux/store/'; 4 | import { loginFailure } from '../redux/actions/user'; 5 | import NProgress from 'nprogress'; 6 | import 'nprogress/nprogress.css'; 7 | 8 | axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; 9 | // http request 拦截器 10 | axios.interceptors.request.use( 11 | (config) => { 12 | // if (localStorage.token) { 13 | // config.headers.Authorization = `token ${localStorage.token}`; 14 | // } 15 | NProgress.start(); 16 | return config; 17 | }, 18 | (err) => { 19 | return Promise.reject(err); 20 | } 21 | ); 22 | 23 | // http response 拦截器 24 | axios.interceptors.response.use( 25 | (response) => { 26 | NProgress.done(); 27 | return response; 28 | }, 29 | (error) => { 30 | console.log('response interceptors:', error.response); 31 | if (error.response) { 32 | switch (error.response.status) { 33 | case 401: 34 | store.dispatch(loginFailure({ 35 | code: 401, 36 | msg: error.response.data 37 | })); 38 | break; 39 | default: 40 | break; 41 | } 42 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 43 | return Promise.reject(error); 44 | } 45 | } 46 | ); 47 | 48 | const Get = (url, payload) => { 49 | const obj = { 50 | params: null 51 | }; 52 | if (payload) { 53 | obj.params = payload; 54 | } 55 | return new Promise((resolve, reject) => { 56 | axios.get(url, obj).then((response) => { 57 | resolve({ 58 | status: response.status, 59 | data: response.data 60 | }); 61 | }).catch((error) => { 62 | reject(error); 63 | }); 64 | }); 65 | }; 66 | 67 | const Post = (url, payload) => { 68 | return new Promise((resolve, reject) => { 69 | axios.post(url, qs.stringify( 70 | payload 71 | )).then((response) => { 72 | resolve({ 73 | status: response.status, 74 | data: response.data 75 | }); 76 | }).catch((error) => { 77 | reject(error); 78 | }); 79 | }); 80 | }; 81 | 82 | export default { 83 | Get, 84 | Post 85 | }; 86 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { BrowserRouter, Route, Switch } from 'react-router-dom'; 5 | import Dashboard from './containers/dashboard'; 6 | import Orders from './containers/orders'; 7 | import UserManger from './containers/usermanager'; 8 | import Express from './containers/express'; 9 | import Print from './containers/print'; 10 | import Login from './components/login'; 11 | import NoMatch from './containers/nomatch'; 12 | import store from './redux/store/'; 13 | import { init } from './utils/init.js'; 14 | import ScrollToTop from './containers/scroll-to-top'; 15 | init(store); 16 | 17 | ReactDOM.render( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | , 34 | document.getElementById('box') 35 | ); 36 | -------------------------------------------------------------------------------- /src/redux/actions/actionstype.js: -------------------------------------------------------------------------------- 1 | export const LOGIN_REQUEST = 'LOGIN_REQUEST'; 2 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 3 | export const LOGIN_FAILURE = 'LOGIN_FAILURE'; 4 | 5 | export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'; 6 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; 7 | 8 | export const REQUEST_DATA = 'REQUEST_DATA'; 9 | export const RECIVE_DATA = 'RECIVE_DATA'; 10 | 11 | export const TOGGLE_ORDER_DETAIL = 'TOGGLE_ORDER_DETAIL'; 12 | 13 | //设置menu菜单选中 14 | export const DEFAULTITEM = 'DEFAULTITEM'; 15 | export const OPENKEYS = 'OPENKEYS'; 16 | export const MENUFOLD = 'MENUFOLD'; 17 | -------------------------------------------------------------------------------- /src/redux/actions/menu.jsx: -------------------------------------------------------------------------------- 1 | import { OPENKEYS, DEFAULTITEM, MENUFOLD } from '../actions/actionstype.js'; 2 | 3 | export const setCurrentItem = (data) => { 4 | return { 5 | type: DEFAULTITEM, 6 | payload: data 7 | }; 8 | }; 9 | export const setOpenKeys = (data) => { 10 | return { 11 | type: OPENKEYS, 12 | payload: data 13 | }; 14 | }; 15 | export const setMenuFold = (data) => { 16 | localStorage.setItem('menuFold', data); 17 | return { 18 | type: MENUFOLD, 19 | payload: data 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/redux/actions/user.jsx: -------------------------------------------------------------------------------- 1 | import { REQUEST_DATA, RECIVE_DATA, LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT_REQUEST, LOGOUT_SUCCESS, TOGGLE_ORDER_DETAIL } from './actionstype.js'; 2 | 3 | export const loginRequest = (data) => { 4 | return { type: LOGIN_REQUEST, payload: data }; 5 | }; 6 | export const loginSuccess = (token, isAdmin, userName) => { 7 | localStorage.setItem('token', token); 8 | localStorage.setItem('isAdmin', isAdmin); 9 | localStorage.setItem('userName', userName); 10 | return { type: LOGIN_SUCCESS, payload: { token, isAdmin, userName } }; 11 | }; 12 | export const loginFailure = (data) => { 13 | localStorage.removeItem('token'); 14 | localStorage.removeItem('isAdmin'); 15 | localStorage.removeItem('userName'); 16 | return { type: LOGIN_FAILURE, payload: { status: data.status, msg: data.msg } }; 17 | }; 18 | export const logoutRequest = () => { 19 | return { type: LOGOUT_REQUEST }; 20 | }; 21 | export const logoutSuccess = () => { 22 | localStorage.removeItem('token'); 23 | localStorage.removeItem('isAdmin'); 24 | localStorage.removeItem('userName'); 25 | return { type: LOGOUT_SUCCESS }; 26 | }; 27 | export const requestData = (data) => { 28 | return { type: REQUEST_DATA, payload: data }; 29 | }; 30 | export const reciveData = (data) => { 31 | return { type: RECIVE_DATA, payload: data }; 32 | }; 33 | export const toggleOrderDetail = (data) => { 34 | return { type: TOGGLE_ORDER_DETAIL, payload: data }; 35 | }; 36 | -------------------------------------------------------------------------------- /src/redux/reducers/index.jsx: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import userReducer from './user'; 3 | import menuReducer from './menu'; 4 | 5 | const rootReducer = combineReducers({ 6 | userReducer, 7 | menuReducer 8 | }); 9 | export default rootReducer; 10 | -------------------------------------------------------------------------------- /src/redux/reducers/menu.jsx: -------------------------------------------------------------------------------- 1 | import { DEFAULTITEM, OPENKEYS, MENUFOLD } from '../actions/actionstype.js'; 2 | 3 | const menuReducer = (state = { 4 | currentItem: '', 5 | openKeys: [''], 6 | menuFold: false 7 | }, action) => { 8 | switch (action.type) { 9 | case DEFAULTITEM: 10 | return { 11 | ...state, 12 | currentItem: action.payload 13 | }; 14 | case OPENKEYS: 15 | return { 16 | ...state, 17 | openKeys: action.payload 18 | }; 19 | case MENUFOLD: 20 | return { 21 | ...state, 22 | menuFold: action.payload 23 | }; 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default menuReducer; 30 | -------------------------------------------------------------------------------- /src/redux/reducers/user.jsx: -------------------------------------------------------------------------------- 1 | import { message } from 'antd'; 2 | import { LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT_REQUEST, LOGOUT_SUCCESS, RECIVE_DATA, TOGGLE_ORDER_DETAIL } from '../actions/actionstype.js'; 3 | 4 | 5 | const userReducer = (state = { 6 | authenticated: false, 7 | isAuthenticating: false, 8 | customData: '', 9 | token: '', 10 | statusText: '', 11 | isAdmin: false, 12 | userName: '', 13 | showOrderDetail: true 14 | }, action) => { 15 | switch (action.type) { 16 | case LOGIN_REQUEST: 17 | return { 18 | ...state, 19 | isAuthenticating: true 20 | }; 21 | case LOGIN_SUCCESS: 22 | return { 23 | ...state, 24 | authenticated: true, 25 | isAuthenticating: false, 26 | token: action.payload.token, 27 | isAdmin: action.payload.isAdmin, 28 | userName: action.payload.userName 29 | }; 30 | case LOGIN_FAILURE: 31 | message.error(action.payload.msg); 32 | return { 33 | ...state, 34 | authenticated: false, 35 | isAuthenticating: false, 36 | token: '', 37 | statusText: action.payload.msg, 38 | isAdmin: false, 39 | userName: '' 40 | }; 41 | case LOGOUT_REQUEST: 42 | return state; 43 | case LOGOUT_SUCCESS: 44 | return { 45 | ...state, 46 | authenticated: false, 47 | isAuthenticating: false, 48 | token: null, 49 | statusText: '', 50 | isAdmin: false, 51 | userName: '' 52 | }; 53 | case RECIVE_DATA: 54 | return { 55 | ...state, 56 | customData: action.payload 57 | }; 58 | case TOGGLE_ORDER_DETAIL: 59 | return { 60 | ...state, 61 | showOrderDetail: !state.showOrderDetail 62 | }; 63 | default: 64 | return state; 65 | } 66 | }; 67 | export default userReducer; 68 | -------------------------------------------------------------------------------- /src/redux/sagas/fetchdata.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | put, 3 | call, 4 | takeEvery 5 | } from 'redux-saga/effects'; 6 | 7 | import API from '../../api/index'; 8 | import { REQUEST_DATA } from '../actions/actionstype'; 9 | import { reciveData } from '../actions/user'; 10 | 11 | 12 | function fetchdata() { 13 | return API.getOrderState(); 14 | } 15 | 16 | function* fetchDataRequest() { 17 | const res = yield call(fetchdata); 18 | yield put(reciveData(res)); 19 | } 20 | 21 | export function* fetchData() { 22 | yield takeEvery(REQUEST_DATA, fetchDataRequest); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/redux/sagas/index.js: -------------------------------------------------------------------------------- 1 | // saga 模块化引入 2 | import { fork, all } from 'redux-saga/effects'; 3 | 4 | // 异步逻辑 5 | import { signin } from './signin'; 6 | import { signout } from './signout'; 7 | import { fetchData } from './fetchdata'; 8 | 9 | // 单一进入点,一次启动所有 Saga 10 | export default function* rootSaga() { 11 | yield all([fork(signin), fork(signout), fork(fetchData)]); 12 | } 13 | -------------------------------------------------------------------------------- /src/redux/sagas/signin.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foxkingpk/react-antd-webpack2-sagas-router4/4cd913f9a252e42d6519f6311f98e0bcfe210b6e/src/redux/sagas/signin.jsx -------------------------------------------------------------------------------- /src/redux/sagas/signout.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | put, 3 | call, 4 | takeEvery 5 | } from 'redux-saga/effects'; 6 | import API from '../../api/index'; 7 | import { LOGOUT_REQUEST } from '../actions/actionstype'; 8 | import { logoutSuccess } from '../actions/user'; 9 | import { setCurrentItem, setOpenKeys } from '../actions/menu'; 10 | 11 | function logout() { 12 | return API.getLogoputResource(); 13 | } 14 | 15 | function* logoutRequest() { 16 | const key = ['']; 17 | const item = 'orders'; 18 | yield call(logout); 19 | yield put(logoutSuccess()); 20 | yield put(setOpenKeys(key)); 21 | yield put(setCurrentItem(item)); 22 | } 23 | 24 | export function* signout() { 25 | yield takeEvery(LOGOUT_REQUEST, logoutRequest); 26 | } 27 | -------------------------------------------------------------------------------- /src/redux/store/index.js: -------------------------------------------------------------------------------- 1 | import reducers from '../reducers'; 2 | import saga from '../sagas'; 3 | import createSagaMiddleware from 'redux-saga'; 4 | import { createStore, applyMiddleware, compose } from 'redux'; 5 | const sagaMiddleware = createSagaMiddleware(); 6 | let store; 7 | if (window.__REDUX_DEVTOOLS_EXTENSION__) { 8 | store = createStore(reducers, compose(applyMiddleware(sagaMiddleware), 9 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())); 10 | } else { 11 | store = createStore(reducers, applyMiddleware(sagaMiddleware)); 12 | } 13 | sagaMiddleware.run(saga); 14 | 15 | export default store; 16 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (!isLocalhost) { 36 | // Is not local host. Just register service worker 37 | registerValidSW(swUrl); 38 | } else { 39 | // This is running on localhost. Lets check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto-browserify'; 2 | 3 | // 使用随机值加盐,MD5加密 4 | export function cryptPwd(password) { 5 | // const salt = Math.random().toString().slice(2, 5); 6 | // 密码“加盐” 7 | // const saltPassword = password + ':' + salt; 8 | const saltPassword = password; 9 | // 加盐密码的md5值 10 | const md5 = crypto.createHash('md5'); 11 | const result = md5.update(saltPassword).digest('hex'); 12 | return result; 13 | } 14 | // 获取url参数 15 | export function getUrlPara(key) { 16 | const queryStr = window.location.search.slice(1); 17 | const reg = new RegExp(`(^|&)${key}=([^&]*)(&|$)`, 'i'); 18 | const matchRes = queryStr.match(reg); 19 | if (matchRes && matchRes[2]) { 20 | return decodeURIComponent(matchRes[2]); 21 | } else { 22 | return ''; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/init.js: -------------------------------------------------------------------------------- 1 | import { setMenuFold } from '../redux/actions/menu'; 2 | import { loginSuccess } from '../redux/actions/user'; 3 | 4 | export const init = (store) => { 5 | //刷新页面或者首次访问时,判断本地是否存在token 6 | const token = localStorage.token; 7 | const isAdmin = localStorage.isAdmin === 'true' ? true : false; 8 | 9 | if (token) { 10 | store.dispatch(loginSuccess(token, isAdmin, localStorage.userName)); 11 | } 12 | if (localStorage.menuFold) { 13 | store.dispatch(setMenuFold(localStorage.menuFold === 'true' ? true : false)); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/utils/menu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Menu, Icon } from 'antd'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | const SubMenu = Menu.SubMenu; 6 | 7 | const RecursiveMenus = (menuTreeN, siderFoldN) => { 8 | return menuTreeN.map(item => { 9 | if (item.children && item.children.length>0) { 10 | return ( 11 | 14 | {item.icon && } 15 | {item.name} 16 | } 17 | > 18 | {RecursiveMenus(item.children, siderFoldN)} 19 | 20 | ) 21 | } 22 | return ( 23 | 24 | 25 | {item.icon && } 26 | {item.name} 27 | 28 | 29 | ); 30 | }); 31 | } 32 | export default RecursiveMenus; -------------------------------------------------------------------------------- /src/utils/print.js: -------------------------------------------------------------------------------- 1 | 2 | const getMLodop = () => { 3 | let mLODOP; 4 | try { 5 | mLODOP = window.getCLodop(); 6 | } catch (err) { 7 | 8 | }; 9 | 10 | if ((mLODOP != null) && (typeof (mLODOP.VERSION) !== 'undefined')) { 11 | return mLODOP; 12 | } else { 13 | return null; 14 | } 15 | }; 16 | 17 | const init = () => { 18 | const mLODOP = getMLodop(); 19 | if (mLODOP) { 20 | return true; 21 | } else { 22 | return false; 23 | } 24 | }; 25 | 26 | const preview = () => { 27 | const mLODOP = getMLodop(); 28 | if (mLODOP) { 29 | mLODOP.PREVIEW(); 30 | } 31 | }; 32 | const checkPrinter = (printerName) => { 33 | const mLODOP = getMLodop(); 34 | const n = mLODOP.GET_PRINTER_COUNT(); 35 | if (n > 0) { 36 | let isExist = false; 37 | for (let i = 0; i < n; i++) { 38 | const name = window.LODOP.GET_PRINTER_NAME(i); 39 | if (name === printerName) { 40 | isExist = true; 41 | break; 42 | } 43 | } 44 | return isExist; 45 | } else { 46 | return false; 47 | } 48 | }; 49 | const printResume = (id) => { 50 | return window.LODOP.SET_PRINT_MODE('CONTROL_PRINTER:' + id, 'RESUME'); 51 | } 52 | const printPurge = (id) => { 53 | return window.LODOP.SET_PRINT_MODE('CONTROL_PRINTER:' + id, 'PURGE'); 54 | }; 55 | export default { 56 | getMLodop, 57 | init, 58 | preview, 59 | checkPrinter, 60 | printResume, 61 | printPurge 62 | }; 63 | --------------------------------------------------------------------------------