├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .reduxrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build ├── compile.js ├── compile.server.js ├── forever_deamon.js ├── karma.conf.js ├── node-forever ├── webpack-compiler.js ├── webpack.config.js └── webpack.servercfg.js ├── config ├── apiclient_cert_1411146202.p12 ├── cfg.coin.lancertech.js ├── environments.js └── index.js ├── doc ├── RESTful_Web_Services_Cookbook-cn.pdf ├── images │ └── working-with-data-in-wordpress-introduction-database-tables.jpg ├── index.md ├── tip_aliyun_server.md ├── tip_app.md ├── tip_bootstrap.md ├── tip_boundaries.md ├── tip_code_protection.md ├── tip_d3.md ├── tip_database.md ├── tip_energy.md ├── tip_errcode.md ├── tip_exclamation.md ├── tip_forever.md ├── tip_ftp.md ├── tip_image_upload.md ├── tip_map.md ├── tip_nginx.md ├── tip_npm.md ├── tip_others.md ├── tip_pm2.md ├── tip_preview.md ├── tip_promise.md ├── tip_react_router.md ├── tip_redis.md ├── tip_redux.md ├── tip_restful.md ├── tip_singleton.md ├── tip_upload.md └── tip_weinre.md ├── nodemon.json ├── package.json ├── server ├── Errcode.js ├── coin │ ├── dbBase.js │ ├── dbmodels.js │ ├── doc │ │ └── restclient.json │ ├── index.js │ ├── middleware.js │ ├── models │ │ ├── jsonField.js │ │ ├── order.js │ │ ├── post.js │ │ ├── refund.js │ │ └── user.js │ ├── postLoop.js │ ├── refundLoop.js │ └── roles.js ├── index.js ├── lib │ ├── .gitignore │ └── apply-express-middleware.js ├── lottery │ └── base.js ├── mainserver.js ├── middleware │ ├── webpack-dev.js │ └── webpack-hmr.js ├── mosca.js ├── sockserver.js ├── uploader │ └── index.js └── wechat │ ├── emitter.js │ └── index.js ├── src ├── Errcode.js ├── components │ ├── Counter │ │ ├── Counter.js │ │ ├── Counter.scss │ │ └── index.js │ ├── Cropper │ │ ├── Cropper.js │ │ ├── Cropper.scss │ │ └── CropperDemo.js │ ├── Forms │ │ ├── Login.js │ │ └── Login.scss │ ├── Header │ │ ├── Header.js │ │ ├── Header.scss │ │ ├── HeaderAirbnb.js │ │ ├── HeaderAirbnb.scss │ │ └── index.js │ ├── Hoe │ │ ├── HoeLayout.js │ │ └── HoeLayout.v1.js │ ├── Link.js │ ├── Touchtop │ │ ├── TouchtopLayout.js │ │ └── TouchtopLayout.scss │ ├── Wordpress │ │ ├── FileUploader.js │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── LeftMenu.js │ │ ├── Login.js │ │ ├── Login.scss │ │ └── Styles.scss │ └── widgets │ │ ├── FileExplorer.js │ │ ├── FileExplorer.scss │ │ ├── FiveStar.js │ │ ├── FiveStar.scss │ │ ├── ImageGallery.js │ │ ├── ImageUploader.js │ │ ├── ImageUploader.scss │ │ └── Widgets.scss ├── containers │ └── AppContainer.js ├── get-weixin-code.html ├── index.html ├── layouts │ ├── CoreLayout │ │ ├── CoreLayout.js │ │ ├── CoreLayout.scss │ │ └── index.js │ └── EmptyLayout │ │ ├── EmptyLayout.js │ │ ├── EmptyLayout.scss │ │ └── index.js ├── main.js ├── routes │ ├── Coin │ │ ├── authedRoutes.js │ │ ├── common.js │ │ ├── components │ │ │ ├── Artwork.js │ │ │ ├── ArtworkDetail.js │ │ │ ├── ArtworkEditor.js │ │ │ ├── ArtworkList.js │ │ │ ├── HomeLayout.js │ │ │ ├── LoginPage.js │ │ │ ├── Mine.js │ │ │ ├── MyAddress.js │ │ │ ├── MyArtList.js │ │ │ ├── MyOrderList.js │ │ │ ├── MyRefundList.js │ │ │ ├── Qrcode.js │ │ │ ├── RootArtList.js │ │ │ ├── Sandbox.js │ │ │ ├── Search.js │ │ │ ├── ShopCenter.js │ │ │ ├── UserList.js │ │ │ ├── Weinre.js │ │ │ └── styles.scss │ │ ├── config.js │ │ ├── containers │ │ │ ├── ArtworkContainer.js │ │ │ ├── ArtworkDetailContainer.js │ │ │ ├── ArtworkEditorContainer.js │ │ │ ├── ArtworkListContainer.js │ │ │ ├── HomeLayoutContainer.js │ │ │ ├── LoginContainer.js │ │ │ ├── MineContainer.js │ │ │ ├── MyAddressContainer.js │ │ │ ├── MyArtListContainer.js │ │ │ ├── MyLuckContainer.js │ │ │ ├── MyRefundListContainer.js │ │ │ ├── MyVoteContainer.js │ │ │ ├── QrcodeContainer.js │ │ │ ├── RootArtListContainer.js │ │ │ ├── RootRefundListContainer.js │ │ │ ├── SandboxContainer.js │ │ │ ├── SearchContainer.js │ │ │ ├── ShopCenterContainer.js │ │ │ ├── UserListContainer.js │ │ │ └── WeinreContainer.js │ │ ├── index.js │ │ ├── modules │ │ │ ├── order.js │ │ │ ├── post.js │ │ │ ├── qrcode.js │ │ │ ├── refund.js │ │ │ ├── user.js │ │ │ └── utils.js │ │ └── shopRoutes.js │ ├── Pay │ │ ├── components │ │ │ ├── JsPay.js │ │ │ ├── OnlyWechat.js │ │ │ └── styles.scss │ │ ├── config.js │ │ ├── containers │ │ │ ├── JsPayContainer.js │ │ │ └── OnlyWechatContainer.js │ │ ├── index.js │ │ └── modules │ │ │ └── pay.js │ ├── components │ │ ├── LoginPage.js │ │ └── LoginPage.scss │ ├── containers │ │ └── LoginContainer.js │ └── index.js ├── static │ ├── MP_verify_xzc12yoaLMDUKzaP.txt │ ├── example.css │ ├── favicon.ico │ ├── humans.txt │ ├── images │ │ ├── 1.jpg │ │ ├── art0.jpg │ │ ├── art1.jpg │ │ ├── art2.jpg │ │ ├── art3.jpg │ │ ├── art4.jpg │ │ ├── art5.jpg │ │ ├── art6.jpg │ │ ├── art7.jpg │ │ ├── art8.jpg │ │ ├── art9.jpg │ │ ├── avatar-1.jpg │ │ ├── avatar-1.png │ │ ├── avatar-2.jpg │ │ ├── avatar-3.jpg │ │ ├── avatar-4.jpg │ │ ├── avatar-5.jpg │ │ ├── baseinfo.png │ │ ├── file.png │ │ ├── folder1.png │ │ ├── folder2.png │ │ ├── folder3.png │ │ ├── logo128.png │ │ ├── logo32.png │ │ ├── logo48.png │ │ ├── logo64.png │ │ ├── none.jpg │ │ ├── qrcode_for_gh_14b79043d0a3_430.jpg │ │ ├── qrcode_for_gh_9e62dd855eff_430.jpg │ │ ├── solar.jpg │ │ └── wechat_menu.png │ ├── jssdk │ │ ├── assets │ │ │ ├── demo.js │ │ │ ├── jweixin-1.js │ │ │ ├── jweixin-1.js.orig │ │ │ ├── style.css │ │ │ ├── widget.zip │ │ │ └── zepto.js │ │ └── index.html │ ├── robots.txt │ ├── weui.css │ └── weui.min.css ├── store │ ├── createStore.js │ ├── lib │ │ ├── database.js │ │ ├── funlib.js │ │ ├── jssdk.js │ │ ├── loading.js │ │ ├── preview.js │ │ ├── signPackage.js │ │ ├── tempdata.js │ │ ├── timely.js │ │ └── upload.js │ └── reducers.js ├── styles │ ├── _base.scss │ ├── core.scss │ ├── global │ │ ├── _bootstrap.scss │ │ ├── global.scss │ │ ├── hoe │ │ │ ├── extra.css │ │ │ ├── header.css │ │ │ ├── hoe-horizontal-navigation.css │ │ │ ├── hoe-overlay-effect.css │ │ │ ├── hoe-push-effect.css │ │ │ ├── hoe-rightsidebar.css │ │ │ ├── hoe-shrink-effect.css │ │ │ ├── hoe-theme-color.css │ │ │ └── hoe.css │ │ ├── images │ │ │ ├── bg1.png │ │ │ ├── bg2.png │ │ │ ├── bg3.png │ │ │ ├── bg4.png │ │ │ ├── bg5.png │ │ │ ├── bg6.png │ │ │ ├── bg7.png │ │ │ ├── bg8.png │ │ │ └── bg9.png │ │ └── slick │ │ │ ├── ajax-loader.gif │ │ │ ├── fonts │ │ │ ├── slick.eot │ │ │ ├── slick.svg │ │ │ ├── slick.ttf │ │ │ └── slick.woff │ │ │ ├── slick-theme.scss │ │ │ └── slick.scss │ └── slick.css └── utils │ ├── dataURL2Blob.js │ ├── imageScale.js │ ├── jweixin-1.2.0.js │ ├── upload.js │ ├── urlParams.js │ └── userAgent.js └── tests ├── .eslintrc ├── components ├── Counter │ └── Counter.spec.js └── Header │ └── Header.spec.js ├── db.sh ├── framework.spec.js ├── layouts └── CoreLayout.spec.js ├── routes ├── Counter │ ├── components │ │ └── CounterView.spec.js │ ├── index.spec.js │ └── modules │ │ └── counter.spec.js └── Home │ ├── components │ └── HomeView.spec.js │ └── index.spec.js ├── run.sh ├── server └── core │ └── WpRoles.spec.js ├── test-bundler.js └── test_client_upgrade.c /.babelrc: -------------------------------------------------------------------------------- 1 | // NOTE: These options are overriden by the babel-loader configuration 2 | // for webpack, which can be found in ~/build/webpack.config. 3 | // 4 | // Why? The react-transform-hmr plugin depends on HMR (and throws if 5 | // module.hot is disabled), so keeping it and related plugins contained 6 | // within webpack helps prevent unexpected errors. 7 | { 8 | "presets": ["es2015", "react", "stage-0"], 9 | "plugins": ["transform-runtime", "transform-class-properties"] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # A special property that should be specified at the top of the file outside of 4 | # any sections. Set to true to stop .editor config file search on current file 5 | root = true 6 | 7 | [*] 8 | # Indentation style 9 | # Possible values - tab, space 10 | indent_style = space 11 | 12 | # Indentation size in single-spaced characters 13 | # Possible values - an integer, tab 14 | indent_size = 2 15 | 16 | # Line ending file format 17 | # Possible values - lf, crlf, cr 18 | end_of_line = lf 19 | 20 | # File character encoding 21 | # Possible values - latin1, utf-8, utf-16be, utf-16le 22 | charset = utf-8 23 | 24 | # Denotes whether to trim whitespace at the end of lines 25 | # Possible values - true, false 26 | trim_trailing_whitespace = true 27 | 28 | # Denotes whether file should end with a newline 29 | # Possible values - true, false 30 | insert_final_newline = true 31 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | blueprints/**/files/** 2 | coverage/** 3 | node_modules/** 4 | dist/** 5 | *.spec.js 6 | src/index.html 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser" : "babel-eslint", 3 | "extends" : [ 4 | "standard", 5 | "standard-react" 6 | ], 7 | "plugins": [ 8 | "babel", 9 | "react", 10 | "promise" 11 | ], 12 | "env" : { 13 | "browser" : true 14 | }, 15 | "globals" : { 16 | "__DEV__" : false, 17 | "__PROD__" : false, 18 | "__DEBUG__" : false, 19 | "__COVERAGE__" : false, 20 | "__BASENAME__" : false 21 | }, 22 | "rules": { 23 | "semi" : [2, "never"], 24 | "max-len": [2, 120, 2], 25 | "generator-star-spacing": 0, 26 | "babel/generator-star-spacing": 1, 27 | "jsx-quotes": [2, "prefer-single"] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | *.log 3 | *~ 4 | *.swp 5 | *.bak 6 | 7 | node_modules 8 | 9 | dist 10 | sdist 11 | coverage 12 | 13 | .idea/ 14 | 15 | access_token*.json 16 | jsapi_ticket*.json 17 | db.*.sqlite 18 | 19 | tmp 20 | temp 21 | uploads 22 | -------------------------------------------------------------------------------- /.reduxrc: -------------------------------------------------------------------------------- 1 | { 2 | "sourceBase":"src", 3 | "testBase":"tests", 4 | "smartPath":"containers", 5 | "dumbPath":"components", 6 | "fileCasing":"pascal" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "6" 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | install: 11 | - npm install 12 | 13 | script: 14 | - npm run lint 15 | - npm run test 16 | - NODE_ENV=development npm run deploy 17 | - NODE_ENV=staging npm run deploy 18 | - NODE_ENV=production npm run deploy 19 | 20 | after_success: 21 | - npm run codecov 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 0.0.4 4 | ------------- 5 | ### Features 6 | * database change. 7 | alter table Users ADD wechat text DEFAULT NULL; 8 | alter table Users ADD visited text DEFAULT NULL; 9 | alter table Users ADD subscribe int(11) NOT NULL DEFAULT 0; 10 | ALTER TABLE Users CHANGE `caps` `caps` text DEFAULT NULL; 11 | ALTER TABLE Posts CHANGE `desc` `desc` text DEFAULT NULL; 12 | ALTER TABLE Posts CHANGE `images` `images` text DEFAULT NULL; 13 | ### Improvements 14 | * 15 | 16 | 0.0.3 17 | ------------- 18 | ### Features 19 | * database change. 20 | alter table Orders ADD serial int(11) NOT NULL DEFAULT 0; 21 | alter table Posts ADD paid int(11) NOT NULL DEFAULT 0; 22 | alter table Posts ADD publish datetime NOT NULL DEFAULT 0; 23 | update Users set caps='ROOT,POST_PUBLISH' where nicename='root' 24 | ### Improvements 25 | * MyVoteOrders, MyLuckyOrders, See https://github.com/windsome/one for details and discussion. 26 | 27 | 0.0.2 28 | ------------- 29 | ### Features 30 | * database change. 31 | alter table Posts ADD random1 int(11) NOT NULL DEFAULT 0, ADD random2 varchar(255) NOT NULL DEFAULT '', ADD accomplish datetime NOT NULL; 32 | ### Improvements 33 | * Change database for calculate lucky number of post. 34 | 35 | 0.0.1 36 | ----- 37 | ### Features 38 | * Upgraded `eslint-plugin-react` to `^5.0.0` 39 | * Upgraded `fs-extra` to `^0.28.0` 40 | 41 | ### Improvements 42 | * Updated syntax used for `createStore` to match `redux@^3.1.0` 43 | * Cleaned up `connect` decorator in `HomeView` 44 | * Cleaned up flow types in `HomeView` 45 | 46 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Some basic conventions for contributing to this project. 4 | 5 | ### General 6 | 7 | Please make sure that there aren't existing pull requests attempting to address the issue mentioned. Likewise, please check for issues related to update, as someone else may be working on the issue in a branch or fork. 8 | 9 | * Non-trivial changes should be discussed in an issue first 10 | * Develop in a topic branch, not master 11 | * Squash your commits 12 | 13 | ### Linting 14 | 15 | Please check your code using `npm run lint` before submitting your pull requests, as the CI build will fail if `eslint` fails. 16 | 17 | ### Commit Message Format 18 | 19 | Each commit message should include a **type**, a **scope** and a **subject**: 20 | 21 | ``` 22 | (): 23 | ``` 24 | 25 | Lines should not exceed 100 characters. This allows the message to be easier to read on github as well as in various git tools and produces a nice, neat commit log ie: 26 | 27 | ``` 28 | #271 feat(standard): add style config and refactor to match 29 | #270 fix(config): only override publicPath when served by webpack 30 | #269 feat(eslint-config-defaults): replace eslint-config-airbnb 31 | #268 feat(config): allow user to configure webpack stats output 32 | ``` 33 | 34 | #### Type 35 | 36 | Must be one of the following: 37 | 38 | * **feat**: A new feature 39 | * **fix**: A bug fix 40 | * **docs**: Documentation only changes 41 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing 42 | semi-colons, etc) 43 | * **refactor**: A code change that neither fixes a bug or adds a feature 44 | * **test**: Adding missing tests 45 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation 46 | generation 47 | 48 | #### Scope 49 | 50 | The scope could be anything specifying place of the commit change. For example `webpack`, 51 | `babel`, `redux` etc... 52 | 53 | #### Subject 54 | 55 | The subject contains succinct description of the change: 56 | 57 | * use the imperative, present tense: "change" not "changed" nor "changes" 58 | * don't capitalize first letter 59 | * no dot (.) at the end 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Zukowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Project Introduction 2 | The front-end code is located in src/routes/Coin, the backend uses the post to store the product information, order stores the order information, the corresponding lottery information for each item (lottery random number, the serial number should be placed in the postmeta table), see [ Treasure] (doc/api_coin.md) 3 | 4 | ## Setting 5 | 6 | Change db, wechat, wechatPay in config/cfg.coin.lancertech.js 7 | 8 | ## Run 9 | 10 | ``` 11 | npm install 12 | redis-server 13 | npm start 14 | ``` 15 | 16 | then, you can open browser, and visit http://localhost:3000, http://yourlocalip:3000 is better. 17 | 18 | ## Problems encountered 19 | 1. Use npm to install some packages failed to see over (npm domestic mirror introduction) 20 |      See [Taobao npm mirror registry and cnpm] (doc / tip_cnpm.md) 21 | 2. How to use node-inspector for nodejs debugging 22 | 3. React component attributes sometimes do not appear to see if the name of the property is not the size of the problem? 23 | 24 | 25 | # 中文介绍 26 | 27 | ## 项目介绍 28 | 前端代码位于 src/routes/Coin, 后端使用post存储商品信息,order存储订单信息,每个商品对应的抽奖信息(抽奖随机数,抽中序号,应该放在postmeta表中),见 [一元夺宝](doc/api_coin.md) 29 | 30 | ## 设置 31 | 在config/cfg.coin.lancertech.js设置数据库,微信,微信支付 32 | 33 | ## 运行 34 | 35 | ``` 36 | npm install 37 | redis-server 38 | npm start 39 | ``` 40 | 41 | 然后,你打开浏览器,浏览http://localhost:3000,最好用http://yourlocalip:3000。 42 | 43 | ## 遇到的问题 44 | 1. 使用npm安装一些包失败了的看过来(npm国内镜像介绍) 45 | 见 [淘宝npm镜像registry及cnpm](doc/tip_cnpm.md) 46 | 2. 如何使用 node-inspector进行nodejs的调试 47 | 3. react组件属性有时总不出现,看看是不是属性名大小写出了问题? 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /build/compile.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import _debug from 'debug' 3 | import webpackCompiler from './webpack-compiler' 4 | import webpackConfig from './webpack.config' 5 | //import webpackServerConfig from './webpack.servercfg' 6 | import config from '../config' 7 | import _ from 'lodash' 8 | 9 | const debug = _debug('app:bin:compile') 10 | const paths = config.utils_paths 11 | 12 | ;(async function () { 13 | try { 14 | let startTime = new Date().getTime() / 1000; 15 | debug('Start client compile at ', startTime, ", time:", new Date()) 16 | const stats = await webpackCompiler(webpackConfig) 17 | if (stats.warnings.length && config.compiler_fail_on_warning) { 18 | debug('Config set to fail on warning, exiting with status code "1".') 19 | process.exit(1) 20 | } 21 | debug('Copy static assets to dist folder.') 22 | fs.copySync(paths.client('static'), paths.dist()) 23 | let endTime = new Date().getTime() / 1000; 24 | debug ('Finish compile at ', endTime, ", time:", new Date(), ', time elapse ', _.round(endTime - startTime, 3)); 25 | } catch (e) { 26 | debug('Compiler encountered an error.', e) 27 | process.exit(1) 28 | } 29 | })() 30 | -------------------------------------------------------------------------------- /build/compile.server.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import _debug from 'debug' 3 | import webpackCompiler from './webpack-compiler' 4 | import webpackServerConfig from './webpack.servercfg' 5 | import config from '../config' 6 | import _ from 'lodash' 7 | 8 | const debug = _debug('app:bin:compile') 9 | const paths = config.utils_paths 10 | 11 | ;(async function () { 12 | try { 13 | let startTime = new Date().getTime() / 1000; 14 | debug('Start server compile at ', startTime) 15 | const stats2 = await webpackCompiler(webpackServerConfig) 16 | if (stats2.warnings.length && config.compiler_fail_on_warning) { 17 | debug('Config set to fail on warning, exiting with status code "1".') 18 | process.exit(1) 19 | } 20 | debug('Copy static assets to sdist folder.') 21 | fs.copySync(paths.base('config/1_mp.lancertech.net_cert.crt'), paths.sdist('1_mp.lancertech.net_cert.crt')) 22 | fs.copySync(paths.base('config/2_mp.lancertech.net.key'), paths.sdist('2_mp.lancertech.net.key')) 23 | fs.copySync(paths.base('config/ca.mqtt.lock.cer'), paths.sdist('ca.mqtt.lock.cer')) 24 | fs.copySync(paths.base('config/apiclient_cert_1440629702.p12'), paths.sdist('apiclient_cert_1440629702.p12')) 25 | fs.copySync(paths.base('config/apiclient_cert_1454160902.p12'), paths.sdist('apiclient_cert_1454160902.p12')) 26 | fs.copySync(paths.base('config/apiclient_cert_1411146202.p12'), paths.sdist('apiclient_cert_1411146202.p12')) 27 | 28 | //fs.copySync(paths.server('windpress/models'), paths.sdist('models/')) 29 | //fs.copySync(paths.server('smartlock/slmodels'), paths.sdist('slmodels/')) 30 | //fs.copySync(paths.server('xxenergy/xxemodels'), paths.sdist('xxemodels/')) 31 | //fs.copySync(paths.server('coin/coinmodels'), paths.sdist('coinmodels/')) 32 | let endTime = new Date().getTime() / 1000; 33 | debug ('Finish compile at ', endTime, ', time elapse ', _.round(endTime - startTime, 3)); 34 | 35 | } catch (e) { 36 | debug('Compiler encountered an error.', e) 37 | process.exit(1) 38 | } 39 | })() 40 | -------------------------------------------------------------------------------- /build/forever_deamon.js: -------------------------------------------------------------------------------- 1 | //require('babel-register'); 2 | require('../sdist'); 3 | -------------------------------------------------------------------------------- /build/karma.conf.js: -------------------------------------------------------------------------------- 1 | import { argv } from 'yargs' 2 | import config from '../config' 3 | import webpackConfig from './webpack.config' 4 | import _debug from 'debug' 5 | 6 | const debug = _debug('app:karma') 7 | debug('Create configuration.') 8 | 9 | const karmaConfig = { 10 | basePath: '../', // project root in relation to bin/karma.js 11 | files: [ 12 | { 13 | pattern: `./${config.dir_test}/test-bundler.js`, 14 | watched: false, 15 | served: true, 16 | included: true 17 | } 18 | ], 19 | singleRun: !argv.watch, 20 | frameworks: ['mocha'], 21 | reporters: ['mocha'], 22 | preprocessors: { 23 | [`${config.dir_test}/test-bundler.js`]: ['webpack'] 24 | }, 25 | browsers: ['PhantomJS'], 26 | webpack: { 27 | devtool: 'cheap-module-source-map', 28 | resolve: { 29 | ...webpackConfig.resolve, 30 | alias: { 31 | ...webpackConfig.resolve.alias, 32 | sinon: 'sinon/pkg/sinon.js' 33 | } 34 | }, 35 | plugins: webpackConfig.plugins, 36 | module: { 37 | noParse: [ 38 | /\/sinon\.js/ 39 | ], 40 | loaders: webpackConfig.module.loaders.concat([ 41 | { 42 | test: /sinon(\\|\/)pkg(\\|\/)sinon\.js/, 43 | loader: 'imports?define=>false,require=>false' 44 | } 45 | ]) 46 | }, 47 | // Enzyme fix, see: 48 | // https://github.com/airbnb/enzyme/issues/47 49 | externals: { 50 | ...webpackConfig.externals, 51 | 'react/addons': true, 52 | 'react/lib/ExecutionEnvironment': true, 53 | 'react/lib/ReactContext': 'window' 54 | }, 55 | sassLoader: webpackConfig.sassLoader 56 | }, 57 | webpackMiddleware: { 58 | noInfo: true 59 | }, 60 | coverageReporter: { 61 | reporters: config.coverage_reporters 62 | } 63 | } 64 | 65 | if (config.globals.__COVERAGE__) { 66 | karmaConfig.reporters.push('coverage') 67 | karmaConfig.webpack.module.preLoaders = [{ 68 | test: /\.(js|jsx)$/, 69 | include: [new RegExp(config.dir_client),new RegExp(config.dir_server)], 70 | loader: 'isparta', 71 | exclude: /node_modules/ 72 | }] 73 | } 74 | 75 | // cannot use `export default` because of Karma. 76 | module.exports = (cfg) => cfg.set(karmaConfig) 77 | -------------------------------------------------------------------------------- /build/webpack-compiler.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack' 2 | import _debug from 'debug' 3 | import config from '../config' 4 | 5 | const debug = _debug('app:build:webpack-compiler') 6 | const DEFAULT_STATS_FORMAT = config.compiler_stats 7 | 8 | export default function webpackCompiler (webpackConfig, statsFormat = DEFAULT_STATS_FORMAT) { 9 | return new Promise((resolve, reject) => { 10 | const compiler = webpack(webpackConfig) 11 | 12 | compiler.run((err, stats) => { 13 | const jsonStats = stats.toJson() 14 | 15 | debug('Webpack compile completed.') 16 | debug(stats.toString(statsFormat)) 17 | 18 | if (err) { 19 | debug('Webpack compiler encountered a fatal error.', err) 20 | return reject(err) 21 | } else if (jsonStats.errors.length > 0) { 22 | debug('Webpack compiler encountered errors.') 23 | debug(jsonStats.errors.join('\n')) 24 | return reject(new Error('Webpack compiler encountered errors')) 25 | } else if (jsonStats.warnings.length > 0) { 26 | debug('Webpack compiler encountered warnings.') 27 | debug(jsonStats.warnings.join('\n')) 28 | } else { 29 | debug('No errors or warnings encountered.') 30 | } 31 | resolve(jsonStats) 32 | }) 33 | }) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /build/webpack.servercfg.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import webpack from 'webpack' 3 | import config from '../config' 4 | import _debug from 'debug' 5 | var nodeExternals = require('webpack-node-externals'); 6 | 7 | const debug = _debug('app:webpack:config') 8 | const paths = config.utils_paths 9 | const {__DEV__, __PROD__, __TEST__} = config.globals 10 | 11 | debug('Create configuration. __DEV__='+__DEV__+',__PROD__='+__PROD__+',__TEST__='+__TEST__) 12 | const webpackConfig = { 13 | name: 'server', 14 | target: 'node', 15 | node: { 16 | __dirname: false, 17 | __filename: false, 18 | }, 19 | devtool: config.compiler_devtool, 20 | resolve: { 21 | root: paths.server(), 22 | extensions: ['', '.js', '.jsx'] 23 | }, 24 | externals: [nodeExternals()], // in order to ignore all modules in node_modules folder 25 | module: {} 26 | } 27 | // ------------------------------------ 28 | // Entry Points 29 | // ------------------------------------ 30 | const SERVER_ENTRY_PATHS = [ 31 | paths.server('index.js') 32 | ] 33 | 34 | webpackConfig.entry = { 35 | server: SERVER_ENTRY_PATHS, 36 | } 37 | 38 | // ------------------------------------ 39 | // Bundle Output 40 | // ------------------------------------ 41 | webpackConfig.output = { 42 | // filename: `[name].[${config.compiler_hash_type}].js`, 43 | filename: "index.js", 44 | path: paths.sdist(), 45 | publicPath: config.compiler_public_path 46 | } 47 | debug('windsome:filename='+webpackConfig.output.filename+",path="+webpackConfig.output.path+",publicPath="+webpackConfig.output.publicPath); 48 | 49 | // ------------------------------------ 50 | // Plugins 51 | // ------------------------------------ 52 | webpackConfig.plugins = [ 53 | new webpack.DefinePlugin(config.globals), 54 | new webpack.optimize.OccurrenceOrderPlugin(), 55 | new webpack.optimize.DedupePlugin(), 56 | new webpack.optimize.UglifyJsPlugin({ 57 | compress: { 58 | unused: true, 59 | dead_code: true, 60 | warnings: false 61 | } 62 | }) 63 | ] 64 | 65 | 66 | // ------------------------------------ 67 | // Loaders 68 | // ------------------------------------ 69 | // JavaScript / JSON 70 | webpackConfig.module.loaders = [{ 71 | test: /\.(js|jsx)$/, 72 | exclude: [/node_modules/, 'src'], 73 | //include: [paths.server()], 74 | loader: 'babel', 75 | query: { 76 | cacheDirectory: true, 77 | plugins: ['transform-decorators-legacy', 'transform-runtime'], 78 | presets: ['es2015', 'react', 'stage-0'] 79 | } 80 | }, 81 | { 82 | test: /\.json$/, 83 | exclude: ['package.json'], 84 | //include: [paths.server()], 85 | loader: 'json' 86 | }] 87 | 88 | 89 | export default webpackConfig 90 | -------------------------------------------------------------------------------- /config/apiclient_cert_1411146202.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/config/apiclient_cert_1411146202.p12 -------------------------------------------------------------------------------- /config/cfg.coin.lancertech.js: -------------------------------------------------------------------------------- 1 | // Here is where you can define configuration overrides based on the execution environment. 2 | // Supply a key to the default export matching the NODE_ENV that you wish to target, and 3 | // the base configuration will apply your overrides before exporting itself. 4 | import fs from 'fs' 5 | 6 | export default (config) => ({ 7 | ...config, 8 | //lancertech wechat. 9 | cfg: 'coin', 10 | domain: 'mp.qingshansi.cn', //本站域名 11 | db: { 12 | database: "wpcoin", //数据库名 13 | username: "", //数据库用户名 14 | password: "", //数据库用户密码 15 | options: { 16 | host: "localhost", //数据库服务器地址 17 | port: 3306, //端口 18 | dialect: "mysql", 19 | logging: function () {}, 20 | pool: { 21 | max: 5, 22 | min: 0, 23 | idle: 10000 24 | }, 25 | dialectOptions: { 26 | collate: 'utf8mb4_unicode_ci', //支持表情字符 27 | charset: 'utf8mb4', //支持表情字符 28 | socketPath: "/var/run/mysqld/mysqld.sock" 29 | }, 30 | define: { 31 | paranoid: true 32 | } 33 | } 34 | }, 35 | wechat : { 36 | origin:'<本公众号原始id>', 37 | appId:'<本公众号APPID>', 38 | appSecret:'<本公众号appSecret>', 39 | encodingAESKey:'<本公众号encodingAESKey>', 40 | token:'<本公众号token>', 41 | 42 | templateLuckyResult: '6P0gkBJlimIBW5GDFFwNgvauakQLM4f0VHHJkL2zHWk', //模板消息的模板id,增加模板消息功能后,可以去微信mp后台添加 43 | templatePaySucess: 'bPKQz3bJqZ_LUdmcUTBqLcZ60w1t_aunCuopWCzoMrw', 44 | templateRefundNotify: '-P2mc2E88Te-Ml-xc7WhNNM7nAm0hRE0MiHCpF_ESdc', 45 | templateOrderCancel: 'q4jpFEjimEHoUK0Pzf7PRAva3mgWdHFAXM8eRCAQX60', 46 | }, 47 | wechatPay : { 48 | appId:'<服务商APPID>', //服务商APPID,邮件中 49 | mchId: '<服务商商户号>', 50 | notifyUrl: '<服务商微信支付通知URL地址>', 51 | partnerKey: '<服务商partnerKey>', 52 | subAppId:'<特约商户APPID>', 53 | subMchId: '<特约商户号>', 54 | pfx: fs.readFileSync(__dirname + '/apiclient_cert_1411146202.p12'), //服务商的apiclient_cert_1411146202 55 | passphrase:'1411146202',//密码,默认为服务商商户号 56 | }, 57 | }) 58 | -------------------------------------------------------------------------------- /config/environments.js: -------------------------------------------------------------------------------- 1 | // Here is where you can define configuration overrides based on the execution environment. 2 | // Supply a key to the default export matching the NODE_ENV that you wish to target, and 3 | // the base configuration will apply your overrides before exporting itself. 4 | export default { 5 | // ====================================================== 6 | // Overrides when NODE_ENV === 'development' 7 | // ====================================================== 8 | // NOTE: In development, we use an explicit public path when the assets 9 | // are served webpack by to fix this issue: 10 | // http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809 11 | development: (config) => ({ 12 | compiler_public_path: `http://${config.server_host}:${config.server_port}/`, 13 | proxy: { 14 | enabled: true, 15 | options: { 16 | host: 'http://lancertech.net', 17 | match: /^\/bpi\/.*/, 18 | map: function(path) { 19 | var re = /(^\/bpi\/)(.*)/; 20 | var newstr = path.replace(re, "/farm/douchat/index.php?s=/Home/$2"); 21 | // TODO: every request url comes here! why? 22 | // match & map, which priority is higher? 23 | //console.log ("windsome: newpath="+newstr); 24 | return newstr; 25 | } 26 | } 27 | } 28 | }), 29 | 30 | // ====================================================== 31 | // Overrides when NODE_ENV === 'production' 32 | // ====================================================== 33 | production: (config) => ({ 34 | compiler_public_path: '/', 35 | compiler_fail_on_warning: false, 36 | compiler_hash_type: 'chunkhash', 37 | compiler_devtool: null, 38 | compiler_stats: { 39 | chunks: true, 40 | chunkModules: true, 41 | colors: true 42 | } 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /doc/RESTful_Web_Services_Cookbook-cn.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/doc/RESTful_Web_Services_Cookbook-cn.pdf -------------------------------------------------------------------------------- /doc/images/working-with-data-in-wordpress-introduction-database-tables.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/doc/images/working-with-data-in-wordpress-introduction-database-tables.jpg -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 此项目是一个微信智能硬件第三方平台,包含公司网站,微信第三方平台及其他业务逻辑。 3 | 4 | 5 | # 环境搭建 6 | 1. [使用nvm搭建npm环境,可以自由切换npm版本](tip_nvm.md) 7 | 2. [使用淘宝镜像作为npm镜像](tip_cnpm.md) 8 | 3. [使用forever作为linux开机服务](tip_forever.md) 9 | 4. 使用redis作为session缓存, ```apt-get install redis-server``` 10 | 11 | # TIPS 12 | 1. [JS中!, !!, !!! 的使用. 叹号取反的使用](tip_exclamation.md) 13 | 2. [使用redux-form时,使用initialValues作为初始值,但初始值在redux-form组件中改变后并不更新?](tip_redux.md) 14 | 3. [使用node-redis时,默认是使用callback的异步模式,怎么改成async/await模式?](tip_redis.md) 15 | 4. [bootstrap-scss 如何以全局方式编译](tip_bootstrap.md) 16 | 5. [bootstrap-sass 字体文件未生成问题](tip_bootstrap.md) 17 | 6. 比较好的markdown编辑器retext ```apt-get install retext``` 18 | 7. [使用react-router时,首页跳转到某个子路由的页面去,要注意写法](tip_react_router.md) 19 | 8. [图片预览及上传相关](tip_image_upload.md) 20 | 9. [大文件分块上传及md5sum校验](tip_file_upload.md) 21 | 10. [可以被取消的promise,fetch](tip_promise.md) 22 | 11. [javascript中的singleton](tip_singleton.md) 23 | 12. [nodejs端拓展Error接口:Errcode](tip_errcode.md) 24 | 13. [通过apache/nginx代理访问nodejs](tip_nginx.md) 25 | 14. [wordpress 分析](api_wordpress.md) 26 | 15. [RESTful API 开发](tip_restful.md) 27 | 16. [更多涉及的js知识](tip_others.md) 28 | 17. [js代码的压缩混淆](tip_code_protection.md) 29 | 18. [nodejs端代码的压缩混淆保护](tip_code_protection.md) 30 | 19. [d3画图表相关](tip_d3.md) 31 | 20. [vsftp虚拟用户配置](tip_ftp.md) 32 | 21. [手机APP的构建](tip_app.md) 33 | 22. [react-map-gl](tip_map.md) 34 | 23. [移动端页面到chrome上调试](tip_weinre.md) 35 | 36 | # APIS 37 | 1. [能源管理相关](tip_energy.md) 38 | 2. [智能微信锁相关](api_smartlock.md) 39 | 40 | -------------------------------------------------------------------------------- /doc/tip_app.md: -------------------------------------------------------------------------------- 1 | # APP HTML5 Hybrid. 2 | + ionic 3 | + apicloud 4 | + react-native 5 | 6 | 7 | -------------------------------------------------------------------------------- /doc/tip_code_protection.md: -------------------------------------------------------------------------------- 1 | # 使用webpack可以对代码进行压缩混淆,也支持node端代码的压缩 2 | 3 | 1. 对于本项目,复制webpack.config.js为webpack.servercfg.js,修改其内容为node端内容 4 | 2. 修改compile.js,加入对node端代码编译的支持。注意,node端部分文件是动态import进来的,需要拷贝到目的目录,还有__dirname相关的内容,改变量改为了sdist目录,需要改相关代码 5 | 3. 运行npm run deploy:prod,会同时创建dist和sdist目录,分别是客户端代码和服务器端代码 6 | 4. 修改package.json中npm run startprod命令,将server改成sdist,则直接从sdist启动服务器端 7 | 8 | 9 | # 参考 10 | 1. [__dirname returns '/' when js file is built with webpack](https://github.com/webpack/webpack/issues/1599) 11 | 2. [Webpack not excluding node_modules](http://stackoverflow.com/questions/33001237/webpack-not-excluding-node-modules) 12 | 3. [Webpack: How to don't bundle node_modules, but use them normally in node.js?](https://github.com/webpack/webpack/issues/603) 13 | -------------------------------------------------------------------------------- /doc/tip_d3.md: -------------------------------------------------------------------------------- 1 | # SVG primer 2 | 1. [SVG primer](http://alignedleft.com/tutorials/d3/an-svg-primer/) 3 | 2. [SVG中文教程](http://www.cnblogs.com/duanhuajian/archive/2013/07/31/3227410.html) 4 | 3. [Mozilla path详细介绍](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) 5 | + SVG图形: rect, circle, elipse, line, polyline, polygon, path, 其中path为重点,可以实现任何其他图形 6 | ```path中具有的指令 7 | M = moveto 8 | L = lineto 9 | H = horizontal lineto 10 | V = vertical lineto 11 | C = curveto 12 | S = smooth curveto 13 | Q = quadratic Belzier curve 14 | T = smooth quadratic Belzier curveto 15 | A = elliptical Arc 16 | Z = closepath 17 | ``` 18 | + SVG中文本与图像:text, tspan, tref, textPath, image等 19 | + SVG属性:笔画与填充: fill, stroke(stroke-linecap, stroke-linejoin, stroke-dasharray), 也可使用css,只是background-color和border换成了fill和stroke 20 | + SVG颜色及渐变:线性渐变linearGradient,环形渐变radialGradient,纹理填充pattern 21 | + SVG重用与引用:组合g,模板symbol,定义defs,引用use 22 | + SVG元素汇总: 23 | > 动画元素:animate, animateColor, animateMotion, animateTransform, set; 24 | > 解释元素:desc, metadata, title; 25 | > 图形元素:circle, ellipse, line, path, polygon, polyline, rect; 26 | > 结构元素:defs, g, svg, symbol, use; 27 | > 渐变元素:linearGradient, radialGradient; 28 | > 其他元素:a,altGlyphDef,clipPath,color-profile,cursor,filter,font,font-face,foreignObject,image,marker,mask,pattern,script,style,switch,text,view等。 29 | 30 | # d3的工作原理 31 | 1. selection, 能高效的使用原由的dom选择方法,与jquery之类的一样 32 | 比如: 33 | ```d3.selectAll("p").style("color", "white"); 34 | d3.select("body").style("background-color", "black"); 35 | ``` 36 | 2. dynamic properties,动态属性,可以将dom某个属性设置成函数。 37 | 比如: 38 | ```d3.selectAll("p").style("color", function() { 39 | return "hsl(" + Math.random() * 360 + ",100%,50%)"; 40 | }); 41 | 42 | d3.selectAll("p").style("color", function(d, i) { 43 | return i % 2 ? "#fff" : "#eee"; 44 | }); 45 | 46 | d3.selectAll("p") 47 | .data([4, 8, 15, 16, 23, 42]) 48 | .style("font-size", function(d) { return d + "px"; }); 49 | ``` 50 | 3. Enter & Exit,当data与selection绑定时,data数组中每一个元素对应一个dom元素,enter()之后,不够的会创建,多的会在exit().remove()中删除。 51 | ```// Update… 52 | var p = d3.select("body") 53 | .selectAll("p") 54 | .data([4, 8, 15, 16, 23, 42]) 55 | .text(function(d) { return d; }); 56 | 57 | // Enter… 58 | p.enter().append("p") 59 | .text(function(d) { return d; }); 60 | 61 | // Exit… 62 | p.exit().remove(); 63 | ``` 64 | # react d3现有架构 65 | 1. [react-faux-dom](https://github.com/Olical/react-faux-dom) ,利用ref直接更新在dom上。如果以前用d3开发过代码库,可以以这种方式迁移。 66 | 2. [recharts](http://recharts.org/#/en-US/examples/JointLineScatterChart) 67 | 68 | -------------------------------------------------------------------------------- /doc/tip_database.md: -------------------------------------------------------------------------------- 1 | #数据库操作 2 | 1. 命令行登录数据库 3 | `mysql -u USERNAME -pPASSWORD -h HOST -P PORT DBNAME` 4 | 2. 备份数据库 5 | `mysqldump -u USERNAME -pPASSWORD -h HOST -P PORT DBNAME > DBNAME.sql` 6 | 3. 恢复数据库 7 | ``` 8 | 自动创建数据库 9 | mysql -u root -p123456 < init.sql 10 | 已创建的数据库导入数据 11 | mysql -u root -p123456 com_safety999_www < db.sql 12 | ``` 13 | 4. 创建数据库 14 | ``` 15 | mysql -u root -p123456 16 | CREATE DATABASE `com_safety999_www` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 17 | ``` 18 | 5. 删除数据库 19 | ``` 20 | mysql -u root -p123456 21 | drop database com_safety999_www; 22 | ``` 23 | 6. 创建超级用户 24 | GRANT ALL PRIVILEGES ON *.* TO admin@localhost IDENTIFIED BY '' WITH GRANT OPTION; 25 | FLUSH PRIVILEGES; 26 | 27 | -------------------------------------------------------------------------------- /doc/tip_energy.md: -------------------------------------------------------------------------------- 1 | # 能源管理 2 | 参考: 3 | [GOODWE 1](http://www.goodwe.com.cn/Uploads/image/20160909/20160909091336_66044.jpg) 4 | [GOODWE 2](http://www.goodwe.com.cn/Uploads/image/20160909/20160909091334_74028.jpg) 5 | [GOODWE 3](http://www.goodwe.com.cn/Uploads/image/20160909/20160909091332_77686.jpg) 6 | -------------------------------------------------------------------------------- /doc/tip_errcode.md: -------------------------------------------------------------------------------- 1 | ## nodejs端拓展Error接口 2 | [mozilla-Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) 3 | ``` 4 | // Create a new object, that prototypically inherits from the Error constructor 5 | function MyError(message) { 6 | this.name = 'MyError'; 7 | this.message = message || 'Default Message'; 8 | this.stack = (new Error()).stack; 9 | } 10 | MyError.prototype = Object.create(Error.prototype); 11 | MyError.prototype.constructor = MyError; 12 | 13 | try { 14 | throw new MyError(); 15 | } catch (e) { 16 | console.log(e.name); // 'MyError' 17 | console.log(e.message); // 'Default Message' 18 | } 19 | 20 | try { 21 | throw new MyError('custom message'); 22 | } catch (e) { 23 | console.log(e.name); // 'MyError' 24 | console.log(e.message); // 'custom message' 25 | } 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /doc/tip_exclamation.md: -------------------------------------------------------------------------------- 1 | # JS中!, !!, !!! 的使用 2 | ``` 3 | 227 debug ("undefined expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 4 | 228 expire = null; 5 | 229 debug ("null expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 6 | 230 expire = 0; 7 | 231 debug ("0 expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 8 | 232 expire = ""; 9 | 233 debug ("\"\" expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 10 | 234 expire = 1; 11 | 235 debug ("1 expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 12 | 236 expire = "11"; 13 | 237 debug ("\"11\" expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 14 | 238 expire = {}; 15 | 239 debug ("{} expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 16 | 240 expire = {a:'a'}; 17 | 241 debug ("{a} expire", expire, "!expire", !expire, "!!expire", !!expire, "!!!expire", !!!expire); 18 | 结果: 19 | app:server:apis undefined expire +18s undefined !expire true !!expire false !!!expire true 20 | app:server:apis null expire +0ms null !expire true !!expire false !!!expire true 21 | app:server:apis 0 expire +0ms 0 !expire true !!expire false !!!expire true 22 | app:server:apis "" expire +0ms !expire true !!expire false !!!expire true 23 | app:server:apis 1 expire +0ms 1 !expire false !!expire true !!!expire false 24 | app:server:apis "11" expire +0ms 11 !expire false !!expire true !!!expire false 25 | app:server:apis {} expire +0ms {} !expire false !!expire true !!!expire false 26 | app:server:apis {a} expire +16ms { a: 'a' } !expire false !!expire true !!!expire false 27 | ``` 28 | 29 | 30 | -------------------------------------------------------------------------------- /doc/tip_forever.md: -------------------------------------------------------------------------------- 1 | # 使用forever作为linux开机服务 2 | 可以参考:[使用forever将nodejs设置成开机服务](https://www.exratione.com/2011/07/running-a-nodejs-server-as-a-service-using-forever/) 3 | 在bin/下有一个文件node-forever,根据自己的目录不同适当修改后拷贝到/etc/init.d/下,并链接到rc*.d中控制在开机时启动,关机时关闭,如下所示: 4 | ``` 5 | +edit node-forever 6 | sudo cp node-forever /etc/init.d/ 7 | cd /etc/rc2.d 8 | ln -s ../init.d/node-forever S23node 9 | cd /etc/rc3.d 10 | ln -s ../init.d/node-forever S23node 11 | cd /etc/rc4.d 12 | ln -s ../init.d/node-forever S23node 13 | cd /etc/rc5.d 14 | ln -s ../init.d/node-forever S23node 15 | cd /etc/rc0.d 16 | ln -s ../init.d/node-forever K23node 17 | cd /etc/rc1.d 18 | ln -s ../init.d/node-forever K23node 19 | cd /etc/rc6.d 20 | ln -s ../init.d/node-forever K23node 21 | ``` 22 | 注意要安装forever, ``` sudo npm install -g forever ```,用sudo可能不成功,可以su -,进入root再操作 23 | 24 | 附 /etc/init.d/node-forever内容: 25 | ``` 26 | NAME=IOT_WECHAT 27 | SOURCE_DIR=/home/ubuntu/node/iot-starter 28 | SOURCE_FILE=bin/forever_deamon.js 29 | 30 | user=ubuntu 31 | pidfile=/var/run/$NAME.pid 32 | logfile=/var/log/$NAME.log 33 | forever_dir=/var/run/forever 34 | 35 | node=node 36 | forever=forever 37 | sed=sed 38 | 39 | #PATH=$PATH:/home/node/local/node/bin 40 | #NODE_PATH=$NODE_PATH:/home/node/local/node/lib/node_modules 41 | 42 | WORKING_DIR=$SOURCE_DIR 43 | export NODE_ENV='production' 44 | export DEBUG='app:*' 45 | export PORT=3000 46 | export PORT_HTTPS=443 47 | 48 | #mkdir $forever_dir 49 | 50 | start() { 51 | echo "Starting $NAME node instance: " 52 | 53 | if [ "$foreverid" == "" ]; then 54 | # Create the log and pid files, making sure that 55 | # the target use has access to them 56 | touch $logfile 57 | chown $user $logfile 58 | 59 | touch $pidfile 60 | chown $user $pidfile 61 | 62 | echo "$forever start -p $forever_dir --pidFile $pidfile -l $logfile -a -d --sourceDir=$SOURCE_DIR $SOURCE_FILE" 63 | # Launch the application 64 | #daemon --user=root \ 65 | $forever start -p $forever_dir --pidFile $pidfile -l $logfile -a -d --sourceDir=$SOURCE_DIR --workingDir=$WORKING_DIR $SOURCE_FILE 66 | RETVAL=$? 67 | else 68 | echo "Instance already running" 69 | RETVAL=0 70 | fi 71 | } 72 | 73 | stop() { 74 | echo -n "Shutting down $NAME node instance : " 75 | #$forever stop $SOURCE_FILE 76 | if [ "$foreverid" != "" ]; then 77 | $forever stop $foreverid 78 | # #$node $SOURCE_DIR/prepareForStop.js 79 | # $forever stop -p $forever_dir $id 80 | else 81 | echo "Instance is not running"; 82 | fi 83 | RETVAL=$? 84 | } 85 | 86 | if [ -f $pidfile ]; then 87 | read pid < $pidfile 88 | foreverid=$pid; 89 | else 90 | pid="" 91 | foreverid="" 92 | fi 93 | echo "foreverid=$foreverid" 94 | 95 | case "$1" in 96 | start) 97 | start 98 | ;; 99 | stop) 100 | stop 101 | ;; 102 | status) 103 | status -p ${pidfile} 104 | ;; 105 | *) 106 | echo "Usage: {start|stop|status}" 107 | exit 1 108 | ;; 109 | esac 110 | exit $RETVAL 111 | ``` 112 | 113 | -------------------------------------------------------------------------------- /doc/tip_ftp.md: -------------------------------------------------------------------------------- 1 | # vsftp虚拟用户配置 2 | [vsftp虚拟用户配置](http://www.cnblogs.com/allenjin/archive/2011/12/03/2274542.html) 3 | 4 | 本文主要介绍一下VSFTP虚拟用户模式配置方法: 5 | 安装VSFTP:sudo apt-get install vsftpd 6 | 安装DB软件包:sudo apt-get install db-util 7 | 配置虚拟用户(进入/etc/vsftpd下操作) 8 | 1. 建立虚拟用户口令库文件 9 | # vim vusers.list (第一行写 用户名,第二行写 密码,保存退出) 10 | user1 11 | user1pwd 12 | user2 13 | user2pwd 14 | 2. 生成vsftpd的认证文件 15 | # db_load -T -t hash -f vusers.list /etc/vsftpd/vsftpd_login.db (生成认证文件) 16 | # chmod 600 /etc/vsftpd/vsftpd_login.db (赋权) 17 | 3. 建立虚拟用户所需的PAM配置文件 18 | # vim /etc/pam.d/vsftpd (加入下面内容,其他全部注释。) 19 | auth required pam_userdb.so db=/etc/vsftpd/vsftpd_login 20 | account required pam_userdb.so db=/etc/vsftpd/vsftpd_login 21 | 4. 建立虚拟用户要访问的目录并设置权限 22 | # useradd -d /home/ftp -s /sbin/nologin virtual 23 | # chmod 777 /home/ftp/ 24 | 在 vsftpd.conf 添加以下参数配置项: 25 | guest_enable=YES 26 | guest_username=virtual 27 | 5. 对不同虚拟用户设置不同权限 28 | # mkdir /etc/vsftpd/vsftpd_user_conf 29 | # vim /etc/vsftpd/vsftpd_user_conf/user1 (建立用户单独配置文件,文件名就是用户名) 30 | local_root=/home/ftp/user1 #这里的虚拟用户目录可以根据实际情况修改 31 | write_enable=YES 32 | virtual_use_local_privs=YES #虚拟用户具有写权限(上传、下载、删除、重命名) 33 | 在 vsftpd.conf 添加以下参数配置项: 34 | user_config_dir=/etc/vsftpd/vsftpd_user_conf 35 | 6. 禁锢FTP用户在宿主目录 36 | 将需要禁锢的用户名写入“vsftpd.chroot_list”文件 37 | # vim /etc/vsftpd.chroot_list 38 | user1 39 | user2 40 | 在 vsftpd.conf 添加以下参数配置项: 41 | chroot_list_enable=YES 42 | chroot_list_file=/etc/vsftpd.chroot_list 43 | -------------------------------------------------------------------------------- /doc/tip_image_upload.md: -------------------------------------------------------------------------------- 1 | ## 图片上传 2 | 使用formdata方式上传或使用``` Content-Type: application/octet-stream ```方式上传 3 | 参考 4 | [理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型](http://www.zhangxinxu.com/wordpress/2013/10/understand-domstring-document-formdata-blob-file-arraybuffer/) 5 | [Ajax file upload with pure JavaScript](http://igstan.ro/posts/2009-01-11-ajax-file-upload-with-pure-javascript.html) 6 | [FileReader.readAsDataURL()](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL) 7 | [使用 JavaScript File API 实现文件上传](http://www.ibm.com/developerworks/cn/web/1101_hanbf_fileupload/index.html) 8 | [Canvas API](http://javascript.ruanyifeng.com/htmlapi/canvas.html) 9 | [js图片压缩上传](http://www.cnblogs.com/tonyjude/p/4261930.html) 10 | [Using files from web applications](https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications) 11 | [File Uploading over AJAX using HTML5](http://www.nickdesteffen.com/blog/file-uploading-over-ajax-using-html5) 12 | ### 使用FormData对象 13 | 测试例子见 src/static/demos/fileupload_FormData.html 14 | 后台见 apis/upload/form 15 | [FormData](https://developer.mozilla.org/zh-CN/docs/Web/API/FormData) 16 | [HTMLFormElement](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLFormElement) 17 | ### 使用DataUrl来上传图片 18 | 测试例子见 src/static/demos/fileupload_base64.html 19 | 后台见 apis/upload/base64/a.png 20 | [FileReader.readAsDataURL upload to express.js](http://stackoverflow.com/questions/13069769/filereader-readasdataurl-upload-to-express-js) 21 | ### 许多图片预览时,加载慢导致浏览器卡死问题 22 | [许多图片预览时,加载慢导致浏览器卡死问题](tip_preview.md) 23 | 24 | ### 微信浏览器input[type='file']控件不支持multiple属性,不支持多文件上传 25 | 得使用微信jssdk的wx.chooseImage方法实现多图片上传 26 | ### XMLHttpRequest详细用法 27 | [mozilla XMLHttpRequest用法](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) 28 | 29 | -------------------------------------------------------------------------------- /doc/tip_map.md: -------------------------------------------------------------------------------- 1 | # uber/react-map-gl 2 | + it use mapbox-gl.js, encount some webpack errors. 3 | 4 | # ERROR in ./~/react-map-gl/dist/utils/transform.js 5 | Module not found: Error: Cannot resolve 'file' or 'directory' /home/dev/frontend/windpress/node_modules/mapbox-gl/dist/mapbox-gl.js/js/geo/transform in /home/dev/frontend/windpress/node_modules/react-map-gl/dist/utils 6 | @ ./~/react-map-gl/dist/utils/transform.js 11:17-54 7 | 8 | 9 | # webpack.config 10 | ``` 11 | resolve: { 12 | root: paths.client(), 13 | extensions: ['', '.js', '.jsx', '.json'], 14 | alias: { 15 | webworkify: 'webworkify-webpack', 16 | 'mapbox-gl/js/geo/transform': path.resolve(__dirname, '../node_modules/mapbox-gl/js/geo/transform'), 17 | 'mapbox-gl': path.resolve(__dirname, '../node_modules/mapbox-gl/dist/mapbox-gl.js') 18 | } 19 | }, 20 | module: {}, 21 | node: { 22 | fs: "empty" 23 | } 24 | 25 | webpackConfig.module.loaders = [ 26 | { 27 | test: /\.js$/, 28 | include: path.resolve(__dirname, '../node_modules/webworkify/index.js'), 29 | loader: 'worker' 30 | }, 31 | { 32 | test: /mapbox-gl.+\.js$/, 33 | loader: 'transform/cacheable?brfs' 34 | }, 35 | { 36 | test: /\.(js|jsx)$/, 37 | exclude: /node_modules/, 38 | loader: 'babel', 39 | query: { 40 | cacheDirectory: true, 41 | plugins: ['transform-runtime'], 42 | presets: ['es2015', 'react', 'stage-0'] 43 | } 44 | }, 45 | { 46 | test: /\.json$/, 47 | loader: 'json' 48 | }] 49 | 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /doc/tip_nginx.md: -------------------------------------------------------------------------------- 1 | ## 通过apache代理访问nodejs 2 | 新建一个配置文件,将域名指向nodejs服务器 3 | ``` 4 | 5 | ServerName mp.lancertech.net 6 | ProxyPass / http://localhost:3000/ 7 | ProxyPassReverse / http://localhost:3000/ 8 | 9 | ServerAdmin webmaster@localhost 10 | #DocumentRoot /home/dev/frontend/test/ranzhi 11 | 12 | ErrorLog ${APACHE_LOG_DIR}/error.log 13 | CustomLog ${APACHE_LOG_DIR}/access.log combined 14 | 15 | 16 | Options FollowSymLinks 17 | AllowOverride None 18 | Order allow,deny 19 | Allow from all 20 | #Require all granted 21 | 22 | 23 | ``` 24 | 注意:如果在局域网测试,则需要在/etc/hosts内增加一个域名。如: 25 | ``` 26 | 127.0.0.1 mp.lancertech.net 27 | 127.0.0.1 mp.lancertech.net 28 | ``` 29 | ## 通过ngnix代理访问nodejs 30 | 在/etc/nginx/sites-available/下建立一个配置文件nodejs.conf,并ln链接到/etc/nginx/sites-enabled/下,重启nginx即可,内容: 31 | ``` 32 | upstream nodejs__upstream { 33 | server 127.0.0.1:3000; 34 | keepalive 64; 35 | } 36 | 37 | server { 38 | listen 8000; 39 | server_name mp.lancertech.net; 40 | 41 | location / { 42 | proxy_set_header X-Real-IP $remote_addr; 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 44 | proxy_set_header Host $http_host; 45 | proxy_set_header X-NginX-Proxy true; 46 | proxy_set_header Connection ""; 47 | proxy_http_version 1.1; 48 | proxy_pass http://nodejs__upstream; 49 | } 50 | } 51 | ``` 52 | ## nginx代理nodejs时,上传文件报错 53 | ``` 54 | 55 | 413 Request Entity Too Large 56 | 57 |

413 Request Entity Too Large

58 |
nginx/1.4.6 (Ubuntu)
59 | 60 | 61 | ``` 62 | 解决: 在 nginx/nginx.conf 中的 http 下加入 63 | 64 | http { 65 | ... 66 | client_max_body_size 64M; 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /doc/tip_npm.md: -------------------------------------------------------------------------------- 1 | ### npm操作 2 | 1. npm发布包到www.npmjs.com 3 | + `npm adduser`注册用户 4 | + `npm login`登录 5 | + `npm init` 初始化项目 6 | + `npm publish` 发布项目到npmjs 7 | 8 | 2. npm使用github上的包 9 | + `git+https://git@github.com/visionmedia/express.git` 使用https方式 10 | + `git+ssh://git@github.com/visionmedia/express.git` 使用ssh方式 11 | -------------------------------------------------------------------------------- /doc/tip_preview.md: -------------------------------------------------------------------------------- 1 | # 许多图片预览时,加载慢导致浏览器卡死问题 2 | 主要原因在于许多图片一起加载导致内存消耗太大,可以使用promise或者回调函数中调用函数方式,一张张顺序加载图片。 3 | [Reading the files sequentially using callbacks](http://stevehanov.ca/blog/index.php?id=127) 4 | [Iteration with .reduce() that Resolves With Array](http://stackoverflow.com/questions/29880715/how-to-synchronize-a-sequence-of-promises) 5 | ``` 6 | function processArray(array, fn) { 7 | var results = []; 8 | return array.reduce(function(p, item) { 9 | return p.then(function(data) { 10 | return fn(item).then(function(data) { 11 | results.push(data); 12 | return results; 13 | }); 14 | }); 15 | }, Promise.resolve()); 16 | } 17 | 18 | var arr = [0,1,2,3]; 19 | 20 | function factory(idx) { 21 | 22 | return new Promise(function(resolve, reject) { 23 | // delayResolve it just here to make this actually be async for testing 24 | function delayResolve(data) { 25 | setTimeout(function() { 26 | resolve(data); 27 | }, 500); 28 | } 29 | 30 | switch (idx) { 31 | case 0: 32 | delayResolve("one"); 33 | break; 34 | case 1: 35 | delayResolve("two"); 36 | break; 37 | case 2: 38 | delayResolve("three"); 39 | break; 40 | default: 41 | reject("invalid arg " + idx + " to factory"); 42 | break; 43 | } 44 | 45 | }); 46 | } 47 | 48 | processArray(arr, factory).then(function(result) { 49 | log(result); 50 | }, function(reason) { 51 | log(reason); 52 | }); 53 | ``` 54 | 55 | -------------------------------------------------------------------------------- /doc/tip_promise.md: -------------------------------------------------------------------------------- 1 | ## 可以被取消的promise,fetch 2 | [isMounted is an Antipattern](https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html) 3 | [create a cancelable fetch](https://github.com/facebook/react/issues/5465#issuecomment-157888325) 4 | ``` 5 | const cancelablePromise = makeCancelable( 6 | new Promise(r => component.setState({...}})) 7 | ); 8 | 9 | cancelablePromise 10 | .promise 11 | .then(() => console.log('resolved')) 12 | .catch((reason) => console.log('isCanceled', reason.isCanceled)); 13 | 14 | cancelablePromise.cancel(); // Cancel the promise 15 | 16 | Where makeCancelable is defined by @istarkov as: 17 | 18 | const makeCancelable = (promise) => { 19 | let hasCanceled_ = false; 20 | 21 | const wrappedPromise = new Promise((resolve, reject) => { 22 | promise.then((val) => 23 | hasCanceled_ ? reject({isCanceled: true}) : resolve(val) 24 | ); 25 | promise.catch((error) => 26 | hasCanceled_ ? reject({isCanceled: true}) : reject(error) 27 | ); 28 | }); 29 | 30 | return { 31 | promise: wrappedPromise, 32 | cancel() { 33 | hasCanceled_ = true; 34 | }, 35 | }; 36 | }; 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /doc/tip_react_router.md: -------------------------------------------------------------------------------- 1 | # 使用react-router时,首页跳转到某个子路由的页面去,要注意写法 2 | 当是jsx风格时,是用Redirect或IndexRedirect完成。当使用plain route时,是用onEnter()完成的,注意这个函数所写的位置,应该在indexRoute下。 3 | ``` 4 | 正确: 5 | export const createRoutes = (store) => ({ 6 | path: '/', 7 | //component: EmptyLayout, 8 | //indexRoute: BlogRoute(store), 9 | indexRoute: { 10 | onEnter: (nextState, replace) => { 11 | replace ('/iot/'); 12 | } 13 | }, 14 | childRoutes: [ 15 | ... 16 | ... 17 | 18 | 错误写法(直接写到根路径上,会造成无限循环): 19 | export const createRoutes = (store) => ({ 20 | path: '/', 21 | //component: EmptyLayout, 22 | //indexRoute: BlogRoute(store), 23 | onEnter: (nextState, replace) => { 24 | replace ('/iot/'); 25 | }, 26 | childRoutes: [ 27 | ... 28 | ... 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /doc/tip_redis.md: -------------------------------------------------------------------------------- 1 | ### redis-cli用法 2 | 1. 运行 redis-cli进入redis命令行 3 | 2. keys * 查看当前所有键值 4 | 3. get KEY,得到某个键值的内容 5 | 4. hgetall KEY, 得到某个以哈希存储的值。 6 | 7 | 8 | ### 使用node-redis时,默认是使用callback的异步模式,怎么改成async/await模式? 9 | 参考 [极品好文:Tips for using async functions](http://www.2ality.com/2016/10/async-function-tips.html) 10 | ``` 11 | 1, callback模式: 12 | redis._redisClient.hget ([hkey], [key], function(err,res){ 13 | if (err) { 14 | console.log('Error:'+ err); 15 | return; 16 | } 17 | console.dir(res); 18 | }); 19 | 20 | redis._redisClient.del ([hkey],key); 21 | 这种模式查询或处理结果从异步的callback返回,对于koa2的服务器来说编程很困难。 22 | 2, async/await模式,写一个通用的用来处理redis方法的函数,并调用此函数返回一个Promise,特别适合koa2 23 | const redis_fn = function (fn, arg) { 24 | if (arguments.length > 2) arg = Array.prototype.slice.call(arguments, 1); 25 | return new Promise(function (resolve, reject) { 26 | fn.apply (redis._redisClient, arg.concat( function (err, res) { 27 | console.log ("redis_fn result", err, res); 28 | if (err) return reject(err); 29 | if (arguments.length > 2) res = slice.call(arguments, 1); 30 | resolve(res); 31 | })); 32 | }); 33 | } 34 | 用法: 35 | var scene_value = await redis_fn(redis._redisClient.hget, 'qrscene',qrscene); 36 | console.log ("scene_value", scene_value); 37 | await redis_fn(redis._redisClient.del, 'qrscene',qrscene); 38 | ``` 39 | 参考: 40 | [thunkify和co的结合](http://www.cnblogs.com/wofeiwofei/p/5462387.html) 41 | [JavaScript里function函数实现可变参数(多态)](http://www.oschina.net/question/54100_15938) 42 | 43 | ### Error: session store is unavailable 44 | ``` 45 | Error: session store is unavailable 46 | at Object.getSession (/data/nodejs/windpress/node_modules/.1.11.4@koa-generic-session/lib/session.js:181:13) 47 | at next (native) 48 | at onFulfilled (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:65:19) 49 | at /data/nodejs/windpress/node_modules/.4.6.0@co/index.js:54:5 50 | at new Promise (/data/nodejs/windpress/node_modules/.2.4.1@core-js/modules/es6.promise.js:191:7) 51 | at Object.co (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:50:10) 52 | at Object.toPromise (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:118:63) 53 | at next (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:99:29) 54 | at onFulfilled (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:69:7) 55 | at /data/nodejs/windpress/node_modules/.4.6.0@co/index.js:54:5 56 | at new Promise (/data/nodejs/windpress/node_modules/.2.4.1@core-js/modules/es6.promise.js:191:7) 57 | at Object.co (/data/nodejs/windpress/node_modules/.4.6.0@co/index.js:50:10) 58 | at converted (/data/nodejs/windpress/node_modules/.1.2.0@koa-convert/index.js:17:15) 59 | at dispatch (/data/nodejs/windpress/node_modules/.3.2.1@koa-compose/index.js:44:32) 60 | at /data/nodejs/windpress/node_modules/.3.2.1@koa-compose/index.js:36:12 61 | at Server. (/data/nodejs/windpress/node_modules/.2.0.0@koa/lib/application.js:135:7) 62 | ``` 63 | 需要安装redis,```apt-get install redis-server``` 64 | 65 | -------------------------------------------------------------------------------- /doc/tip_restful.md: -------------------------------------------------------------------------------- 1 | ## RESTful API 开发 2 | [RESTful API URI 设计: 查询(Query)和标识(Identify)](http://www.cnblogs.com/xishuai/p/designing-rest-api-uri-query-and-identify.html) 3 | [REST简介](http://www.cnblogs.com/loveis715/p/4669091.html) 4 | 用户列表:(用正则表达式进行匹配分解解析字符串) 5 | GET /wpapis/v1/users 6 | GET /wpapis/v1/users/1 7 | GET /wpapis/v1/users/,+,20; GET /wpapis/v1/users/,-,20; GET /wpapis/v1/users/100,+,20; GET /wpapis/v1/users/100,-,20 8 | POST /wpapis/v1/users 9 | PUT /wpapis/v1/users/1 10 | DELETE /wpapis/v1/users/1 11 | ### 使用restclient插件进行API调试 12 | 如果是POST,PUT json数据,则需要在Headers菜单中加入custom header, Content-Type: application/json 13 | 1: users: 14 | { 15 | "email":"222", 16 | "pass":"123456", 17 | "displayName":"1" 18 | } 19 | 2: roles: 20 | { 21 | "role": "author1", 22 | "name": "author1", 23 | "capabilities": 24 | [ 25 | "upload_files", 26 | "edit_posts", 27 | "edit_published_posts", 28 | "publish_posts", 29 | "read", 30 | "delete_posts", 31 | "delete_published_posts" 32 | ] 33 | } 34 | ## 35 | 36 | -------------------------------------------------------------------------------- /doc/tip_singleton.md: -------------------------------------------------------------------------------- 1 | ## javascript中的singleton 2 | [JS中singleton](https://www.sitepoint.com/javascript-design-patterns-singleton/) 3 | ``` 4 | ES5中写法: 5 | var UserStore = (function(){ 6 | var _data = []; 7 | 8 | function add(item){ 9 | _data.push(item); 10 | } 11 | 12 | function get(id){ 13 | return _data.find((d) => { 14 | return d.id === id; 15 | }); 16 | } 17 | 18 | return { 19 | add: add, 20 | get: get 21 | }; 22 | }()); 23 | 24 | ES6写法1: 25 | const _data = []; 26 | 27 | const UserStore = { 28 | add: item => _data.push(item), 29 | get: id => _data.find(d => d.id === id) 30 | } 31 | 32 | Object.freeze(UserStore); 33 | export default UserStore; 34 | 35 | ES6写法2: 36 | class UserStore { 37 | constructor(){ 38 | this._data = []; 39 | } 40 | 41 | add(item){ 42 | this._data.push(item); 43 | } 44 | 45 | get(id){ 46 | return this._data.find(d => d.id === id); 47 | } 48 | } 49 | 50 | const instance = new UserStore(); 51 | Object.freeze(instance); 52 | 53 | export default instance; 54 | ES6写法3(更加OO化): 55 | class UserStore { 56 | constructor(){ 57 | if(! UserStore.instance){ 58 | this._data = []; 59 | UserStore.instance = this; 60 | } 61 | 62 | return UserStore.instance; 63 | } 64 | 65 | //rest is the same code as preceding example 66 | 67 | } 68 | 69 | const instance = new UserStore(); 70 | Object.freeze(instance); // 这一步可以不要,只是确保不修改instance下属性,但子属性的子属性可以修改。 71 | 72 | export default instance; 73 | ``` 74 | [ES6中singleton写法](http://amanvirk.me/singleton-classes-in-es6/) 75 | ``` 76 | let instance = null; 77 | 78 | class Cache{ 79 | constructor() { 80 | if(!instance){ 81 | instance = this; 82 | } 83 | 84 | // to test whether we have singleton or not 85 | this.time = new Date() 86 | 87 | return instance; 88 | } 89 | } 90 | 注意 instance不能写在Cache内部 91 | ``` 92 | ES7中singleton(ES7中能用static,用babel可以转成ES5), 93 | ``` 94 | export class FileBufferManager { 95 | static gInstance; 96 | constructor () { 97 | if (gInstance) return gInstance; 98 | debug ("FileBufferManager constructor! must be singleton!"); 99 | this.filename = null; 100 | this.data = null; 101 | this.version = 0; 102 | this.gInstance = this; 103 | } 104 | static getInstance () { 105 | return new FileBufferManager(); 106 | } 107 | ... 108 | } 109 | ``` 110 | 111 | -------------------------------------------------------------------------------- /doc/tip_upload.md: -------------------------------------------------------------------------------- 1 | # 大文件分块上传的库 2 | PHP版本打文件分块上传库: 3 | [ajaxuploader](http://www.albanx.com/ajaxuploader/pricing.php) 4 | [plupload](http://www.plupload.com/download/) 5 | Nodejs未找到好的 6 | [nodejs 大文件断点续传](http://www.qdfuns.com/notes/18061/401bd2e8689bcc1ca3003873b06d4ec3:storey-3.html) 7 | js-spark-md5用做前台md5计算, crypto用在后台 8 | [FILE API 之 FileReader](https://developer.mozilla.org/en-US/docs/Web/API/FileReader) 9 | [JS Buffer之atob](https://developer.mozilla.org/zh-CN/docs/Web/API/WindowBase64/atob) 10 | 11 | 12 | # XMLHttpRequest详细用法 13 | [mozilla XMLHttpRequest用法](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) 14 | 15 | # 将base64转为binary. 16 | ## browser端将dataURL(base64编码)转为Blob,一般用于上传,比base64上传会省一部分流量 17 | ``` 18 | export const dataURL2Blob = (dataURL) => { 19 | var dataArray = dataURL.split(','); 20 | 21 | // convert base64 to raw binary data held in a string 22 | // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this 23 | var byteString = atob(dataArray[1]); 24 | 25 | // separate out the mime component 26 | var mimeString = dataArray[0].split(':')[1].split(';')[0]; 27 | 28 | // write the bytes of the string to an ArrayBuffer 29 | var ab = new ArrayBuffer(byteString.length); 30 | var ia = new Uint8Array(ab); 31 | for (var i = 0; i < byteString.length; i++) { 32 | ia[i] = byteString.charCodeAt(i); 33 | } 34 | 35 | //Old Code 36 | //write the ArrayBuffer to a blob, and you're done 37 | //var bb = new BlobBuilder(); 38 | //bb.append(ab); 39 | //return bb.getBlob(mimeString); 40 | 41 | //New Code 42 | return new Blob([ab], {type: mimeString}); 43 | } 44 | ``` 45 | 46 | ## nodejs端一般用file解析库busboy解析到一个Buffer 47 | ``` 48 | async uploadBase64 (ctx, next) { 49 | //console.log (ctx.req); 50 | var filename = ctx.params.filename; 51 | var charset = 'utf-8'; 52 | try { 53 | charset = contentType.parse(ctx.req).parameters.charset; 54 | //console.log ("get charset", charset); 55 | } catch (e) { 56 | console.log ("parse charset error!", e); 57 | charset = 'utf-8'; 58 | } 59 | var rawText = await getRawBody(ctx.req, { 60 | encoding: charset 61 | }); 62 | var destfile = this.genFileName(filename); 63 | 64 | // get image base64 data. 65 | var pos1 = rawText.indexOf (";base64,"); 66 | if (pos1 < 0) { 67 | ctx.body = { errcode:-1, errmsg: "image content wrong!" }; 68 | return; 69 | } 70 | pos1 = pos1 + ";base64,".length; 71 | var base64_data = rawText.substr (pos1); 72 | // // care!!! regular match() expend too much time, change to indexOf(). 73 | // var matches = rawText.match(/^data:.+\/(.+);base64,(.*)$/); 74 | // var ext = matches[1]; 75 | // var base64_data = matches[2]; 76 | var buffer = new Buffer(base64_data, 'base64'); 77 | 78 | const filepath = path.join(this.uploads, destfile); 79 | fs.writeFileSync (filepath, buffer); 80 | ctx.body = { errcode:0, file: destfile }; 81 | 82 | return; 83 | } 84 | ``` 85 | 86 | ## 后续工作,尝试使用 canvas.toBlob()得到图片的二进制数据,减少从base64到blob的开销试试。 87 | 88 | -------------------------------------------------------------------------------- /doc/tip_weinre.md: -------------------------------------------------------------------------------- 1 | # 移动端页面调试 2 | 3 | + 首先安装weinre 4 | + 调试步骤: 5 | 1. 终端运行: 6 | weinre --boundHost -all- --httpPort 9090 7 | or 8 | weinre --boundHost 192.168.1.7 --httpPort 9090 9 | 10 | 2. 用基于webkit的浏览器如chrome打开如下链接 11 | http://192.168.1.7:9090/ 12 | 13 | 3. 点击如下连接进行调试 14 | http://192.168.1.7:9090/client/#anonymous 15 | 16 | 4. 移动端页面需加入 17 | 18 | 19 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "ignore": ["dist", "coverage", "tests", "src"] 4 | } 5 | -------------------------------------------------------------------------------- /server/Errcode.js: -------------------------------------------------------------------------------- 1 | export default from 'errcode' 2 | 3 | // COMMON ERROR CODE 4 | export const ERR_UNKNOWN = 40001; 5 | export const ERR_BUSY = 40002; 6 | export const ERR_PARAM_ERROR = 40003; 7 | export const ERR_NO_SUCH_ENTITY = 40004; 8 | export const ERR_INSERT_DB_FAIL = 40005; 9 | export const ERR_UPDATE_DB_FAIL = 40006; 10 | export const ERR_ALREADY_EXIST = 40007; 11 | export const ERR_MISS_REQUIRE = 40008; 12 | export const ERR_SYSTEM_ERROR = 40009; 13 | export const ERR_NOT_ALLOW = 40018; 14 | export const ERR_NOT_OWNER = 40019; 15 | export const ERR_USER_NO_BASEINFO = 40020; 16 | export const ERR_USER_DUP_LOGIN = 40021; 17 | export const ERR_USER_DUP_EMAIL = 40022; 18 | export const ERR_USER_CREATE_FAIL = 40023; 19 | export const ERR_USER_UPDATE_FAIL = 40024; 20 | export const ERR_USER_UPD_NO_USER = 40025; 21 | export const ERR_USER_AUTH_NO_USER = 40026; 22 | export const ERR_USER_AUTH_WRONG_PASS = 40027; 23 | export const ERR_USER_NO_CAP = 40028; 24 | export const ERR_USER_NOT_LOGIN = 40029; 25 | export const ERR_TAXONOMY_DUP_TERM = 40030; 26 | export const ERR_POST_TYPE_CHANGE_NOT_ALLOW = 40040; 27 | export const ERR_POST_MISS_REQUIRE = 40041; 28 | export const ERR_ORDER_COUNT_OVERFLOW = 40050; 29 | 30 | // XIEXIN ENERGY SPECIAL ERROR CODE 31 | export const XXE_UNKNOWN = 40101; 32 | export const XXE_BIND_EXIST = 40102; 33 | export const XXE_UNBIND_NOT_EXIST = 40103; 34 | -------------------------------------------------------------------------------- /server/coin/dbmodels.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Sequelize = require("sequelize"); 3 | import config from '../../config'; 4 | 5 | import UserModel from './models/user' 6 | import PostModel from './models/post' 7 | import OrderModel from './models/order' 8 | import RefundModel from './models/refund' 9 | 10 | var dbcfg = config.db; 11 | 12 | var sequelize = new Sequelize(dbcfg.database, dbcfg.username, dbcfg.password, dbcfg.options); 13 | 14 | var db = {}; 15 | var model = UserModel(sequelize, Sequelize); 16 | db[model.name] = model; 17 | model = PostModel(sequelize, Sequelize); 18 | db[model.name] = model; 19 | model = OrderModel(sequelize, Sequelize); 20 | db[model.name] = model; 21 | model = RefundModel(sequelize, Sequelize); 22 | db[model.name] = model; 23 | 24 | Object.keys(db).forEach(function(modelName) { 25 | if ("associate" in db[modelName]) { 26 | db[modelName].associate(db); 27 | } 28 | }); 29 | 30 | db.sequelize = sequelize; 31 | db.Sequelize = Sequelize; 32 | 33 | module.exports = db; 34 | -------------------------------------------------------------------------------- /server/coin/models/jsonField.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | module.exports = { 3 | getAsJson: function(fieldName) { 4 | return function() { 5 | var content = this.getDataValue(fieldName); 6 | try { 7 | return content && JSON.parse(content) || content; 8 | } catch (e) { 9 | console.log ("warning: field '"+fieldName+"', json parse:", e.message); 10 | return content; 11 | } 12 | } 13 | }, 14 | setFromJson: function(fieldName) { 15 | return function(val) { 16 | var content = val; 17 | try { 18 | content = val && JSON.stringify (val) || val; 19 | } catch (e) { 20 | console.log ("warning: field '"+fieldName+"', json stringify:", e.message); 21 | content = val; 22 | } 23 | this.setDataValue(fieldName, content); 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /server/coin/models/order.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var jsonField = require ("./jsonField"); 3 | module.exports = function(sequelize, DataTypes) { 4 | var table = sequelize.define("Order", { 5 | id: { type: DataTypes.BIGINT(20), primaryKey: true, autoIncrement: true }, 6 | uuid: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 7 | count: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 8 | status: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 9 | serial: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 10 | userId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 11 | postId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 12 | fee: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 13 | desc: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 14 | get: jsonField.getAsJson('desc'), 15 | set: jsonField.setFromJson('desc'), 16 | }, 17 | // rongyu. 18 | //openid: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 19 | }, { 20 | underscore: true, 21 | indexes: [ 22 | { fields: ['userId'] }, 23 | { fields: ['postId'] }, 24 | { fields: ['status'] }, 25 | ], 26 | classMethods: { 27 | associate: function(models) { 28 | //User.hasMany(models.Lock); 29 | //User.belongsToMany(models.Lock, {through: 'UserLock', timestamps: false}); 30 | } 31 | } 32 | }); 33 | 34 | return table; 35 | }; 36 | -------------------------------------------------------------------------------- /server/coin/models/post.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var jsonField = require ("./jsonField"); 3 | module.exports = function(sequelize, DataTypes) { 4 | var table = sequelize.define("Post", { 5 | id: { type: DataTypes.BIGINT(20), primaryKey: true, autoIncrement: true }, 6 | status: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 7 | unit: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 8 | count: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0}, 9 | paid : { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0}, 10 | random1:{ type: DataTypes.INTEGER, allowNull: false, defaultValue: 0}, 11 | random2:{ type: DataTypes.STRING, allowNull: false, defaultValue: ''}, 12 | publish:{ type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW}, 13 | accomplish:{ type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW}, 14 | lucky: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0}, 15 | owner: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0}, 16 | favor: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0}, 17 | images: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 18 | get: jsonField.getAsJson('images'), 19 | set: jsonField.setFromJson('images'), 20 | }, 21 | desc: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 22 | get: jsonField.getAsJson('desc'), 23 | set: jsonField.setFromJson('desc'), 24 | }, 25 | }, { 26 | //timestamps: false, 27 | underscore: true, 28 | indexes: [ 29 | { fields: ['status'] }, 30 | { fields: ['owner'] }, 31 | ], 32 | classMethods: { 33 | associate: function(models) { 34 | //Device.hasMany(models.Lock); 35 | } 36 | } 37 | }); 38 | 39 | return table; 40 | }; 41 | -------------------------------------------------------------------------------- /server/coin/models/refund.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var jsonField = require ("./jsonField"); 3 | module.exports = function(sequelize, DataTypes) { 4 | var table = sequelize.define("Refund", { 5 | id: { type: DataTypes.BIGINT(20), primaryKey: true, autoIncrement: true }, 6 | uuid: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 7 | totalFee: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 8 | refundFee: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 9 | orderUuid: { type: DataTypes.STRING(255), allowNull: false, defaultValue: 0 }, 10 | orderId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 11 | userId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 12 | postId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 13 | status: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 14 | detail: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 15 | get: jsonField.getAsJson('detail'), 16 | set: jsonField.setFromJson('detail'), 17 | }, 18 | requestUserId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 19 | requestTime: { type: DataTypes.DATE, allowNull: true, defaultValue: null }, 20 | issueUserId: { type: DataTypes.BIGINT(20), allowNull: false, defaultValue: 0 }, 21 | issueTime: { type: DataTypes.DATE, allowNull: true, defaultValue: null }, 22 | }, { 23 | underscore: true, 24 | indexes: [ 25 | { fields: ['uuid'] }, 26 | { fields: ['orderUuid'] }, 27 | { fields: ['orderId'] }, 28 | { fields: ['postId'] }, 29 | { fields: ['userId'] }, 30 | ], 31 | classMethods: { 32 | associate: function(models) { 33 | //User.hasMany(models.Lock); 34 | //User.belongsToMany(models.Lock, {through: 'UserLock', timestamps: false}); 35 | } 36 | } 37 | }); 38 | 39 | return table; 40 | }; 41 | -------------------------------------------------------------------------------- /server/coin/models/user.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var jsonField = require ("./jsonField"); 3 | module.exports = function(sequelize, DataTypes) { 4 | var table = sequelize.define("User", { 5 | id: { type: DataTypes.BIGINT(20), primaryKey: true, autoIncrement: true }, 6 | openid: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 7 | pass: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 8 | nicename: { type: DataTypes.STRING(50), allowNull: false, defaultValue: '' }, 9 | avatar: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 10 | phone: { type: DataTypes.STRING(50), allowNull: false, defaultValue: '' }, 11 | address: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' }, 12 | caps: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 13 | get: jsonField.getAsJson('caps'), 14 | set: jsonField.setFromJson('caps'), 15 | }, 16 | status: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 17 | subscribe:{ type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, 18 | wechat: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 19 | get: jsonField.getAsJson('wechat'), 20 | set: jsonField.setFromJson('wechat'), 21 | }, 22 | visited: { type: DataTypes.TEXT, allowNull: true, defaultValue: null, 23 | get: jsonField.getAsJson('visited'), 24 | set: jsonField.setFromJson('visited'), 25 | }, 26 | }, { 27 | underscore: true, 28 | indexes: [ 29 | { fields: ['openid'] }, 30 | { fields: ['nicename'] }, 31 | { fields: ['phone'] }, 32 | { fields: ['address'] }, 33 | ], 34 | classMethods: { 35 | associate: function(models) { 36 | //User.hasMany(models.Lock); 37 | //User.belongsToMany(models.Lock, {through: 'UserLock', timestamps: false}); 38 | } 39 | } 40 | }); 41 | 42 | return table; 43 | }; 44 | -------------------------------------------------------------------------------- /server/coin/roles.js: -------------------------------------------------------------------------------- 1 | import _debug from 'debug' 2 | const debug = _debug('server:coin:role') 3 | import _ from 'lodash'; 4 | import Errcode, * as EC from '../Errcode' 5 | 6 | const Roles = { 7 | root: ['manage_users','edit_users','manage_posts','edit_posts','manage_orders','edit_orders'], 8 | agent: ['manage_users','edit_users','manage_posts','edit_posts'], 9 | customer: ['manage_users','edit_users','manage_posts','edit_posts'], 10 | }; 11 | 12 | export function hasCaps ($roleName, $caps) { 13 | if (!$roleName || !$caps) { 14 | debug ("param error: ", $roleName, $caps); 15 | return false; 16 | } 17 | 18 | var roleCaps = Roles[$roleName]; 19 | if(_.isInteger($roleName)) { 20 | switch ($roleName) { 21 | case 1: roleCaps = Roles['root']; break; 22 | case 2: roleCaps = Roles['agent']; break; 23 | case 3: roleCaps = Roles['customer']; break; 24 | } 25 | } 26 | if (!roleCaps || _.isEmpty(roleCaps)) { 27 | debug ("roleCaps is empty ", roleCaps); 28 | return false; 29 | } 30 | var caps = $caps; 31 | if (!_.isArray($caps)) caps = [$caps]; 32 | if (_.isEmpty (caps)) { 33 | debug ("caps is empty ", caps); 34 | return false; 35 | } 36 | 37 | return caps.reduce ( (result, cap ) => { 38 | if (_.indexOf(roleCaps, cap) >= 0) 39 | return result; 40 | else 41 | return false; 42 | }, true); 43 | } 44 | 45 | export default function userHasCaps ($user, $caps) { 46 | return true; 47 | if (!$user || !$user.status) { 48 | debug ("user is empty ", $user); 49 | return false; 50 | } 51 | return hasCaps ($user.status, $caps); 52 | } 53 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | import config from '../config' 3 | import server from '../server/main' 4 | import _debug from 'debug' 5 | 6 | const debug = _debug('app:bin:server') 7 | const port = config.server_port 8 | const host = config.server_host 9 | 10 | server.listen(port) 11 | debug(`Server is now running at http://${host}:${port}.`) 12 | debug(`Server accessible via localhost:${port} if you are using the project defaults.`) 13 | */ 14 | 15 | /////////////////////////////////////////////////// 16 | // koa web server for all APPs. 17 | /////////////////////////////////////////////////// 18 | var http = require("http"); 19 | var https = require('https'); 20 | 21 | import config from '../config' 22 | import server from './mainserver' 23 | import _debug from 'debug' 24 | const debug = _debug('app:bin:server') 25 | 26 | const host = config.server_host 27 | const port = config.server_port 28 | const port_https = config.server_port_https 29 | 30 | http.createServer(server.callback()).listen(port); 31 | //https.createServer( config.https, server.callback() ).listen(port_https); 32 | 33 | debug(`Server is now running at http://${host}:${port}.`) 34 | debug(`Https Server is now running at http://${host}:${port_https}.`) 35 | debug(`Server accessible via localhost:${port} and localhost:${port_https} if you are using the project defaults.`) 36 | 37 | /////////////////////////////////////////////////// 38 | // socket server for firmware update of Xiexin Energy. 39 | /////////////////////////////////////////////////// 40 | var net = require('net'); 41 | import sockserver, {webSocketServer} from './sockserver'; 42 | const PORT = 6969; 43 | const HOST = '127.0.0.1'; 44 | 45 | net.createServer(sockserver).listen(PORT); 46 | debug('sockServer listening on ' + HOST +':'+ PORT); 47 | 48 | var ws = require("nodejs-websocket"); 49 | const PORT2 = 6970; 50 | var serverWS = ws.createServer(webSocketServer).listen(PORT2); 51 | debug('webSocketServer listening on ' + HOST +':'+ PORT2); 52 | 53 | /////////////////////////////////////////////////// 54 | // mosac broker. 55 | /////////////////////////////////////////////////// 56 | import createMosac from './mosca' 57 | createMosac (); 58 | debug ('mosca broker created!'); 59 | -------------------------------------------------------------------------------- /server/lib/.gitignore: -------------------------------------------------------------------------------- 1 | WordPress 2 | -------------------------------------------------------------------------------- /server/lib/apply-express-middleware.js: -------------------------------------------------------------------------------- 1 | // Based on: https://github.com/dayAlone/koa-webpack-hot-middleware/blob/master/index.js 2 | export default function applyExpressMiddleware (fn, req, res) { 3 | const originalEnd = res.end 4 | 5 | return new Promise((resolve) => { 6 | res.end = function () { 7 | originalEnd.apply(this, arguments) 8 | resolve(false) 9 | } 10 | fn(req, res, function () { 11 | resolve(true) 12 | }) 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /server/middleware/webpack-dev.js: -------------------------------------------------------------------------------- 1 | import WebpackDevMiddleware from 'webpack-dev-middleware' 2 | import applyExpressMiddleware from '../lib/apply-express-middleware' 3 | import _debug from 'debug' 4 | import config from '../../config' 5 | 6 | const paths = config.utils_paths 7 | const debug = _debug('app:server:webpack-dev') 8 | 9 | export default function (compiler, publicPath) { 10 | debug('Enable webpack dev middleware.') 11 | 12 | const middleware = WebpackDevMiddleware(compiler, { 13 | publicPath, 14 | contentBase: paths.client(), 15 | hot: true, 16 | quiet: config.compiler_quiet, 17 | noInfo: config.compiler_quiet, 18 | lazy: false, 19 | stats: config.compiler_stats 20 | }) 21 | 22 | return async function koaWebpackDevMiddleware (ctx, next) { 23 | let hasNext = await applyExpressMiddleware(middleware, ctx.req, { 24 | end: (content) => (ctx.body = content), 25 | setHeader: function () { 26 | ctx.set.apply(ctx, arguments) 27 | } 28 | }) 29 | 30 | if (hasNext) { 31 | await next() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/middleware/webpack-hmr.js: -------------------------------------------------------------------------------- 1 | import WebpackHotMiddleware from 'webpack-hot-middleware' 2 | import applyExpressMiddleware from '../lib/apply-express-middleware' 3 | import _debug from 'debug' 4 | 5 | const debug = _debug('app:server:webpack-hmr') 6 | 7 | export default function (compiler, opts) { 8 | debug('Enable Webpack Hot Module Replacement (HMR).') 9 | 10 | const middleware = WebpackHotMiddleware(compiler, opts) 11 | return async function koaWebpackHMR (ctx, next) { 12 | let hasNext = await applyExpressMiddleware(middleware, ctx.req, ctx.res) 13 | 14 | if (hasNext && next) { 15 | await next() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/mosca.js: -------------------------------------------------------------------------------- 1 | import _debug from 'debug' 2 | const debug = _debug('app:mosca') 3 | 4 | var mosca = require('mosca') 5 | 6 | var ascoltatore = { 7 | type: 'redis', 8 | redis: require('redis'), 9 | db: 12, 10 | port: 6379, 11 | return_buffers: true, // to handle binary payloads 12 | host: "localhost" 13 | }; 14 | 15 | var moscaSettings = { 16 | port: 3883, 17 | backend: ascoltatore, 18 | persistence: { 19 | factory: mosca.persistence.Redis 20 | } 21 | }; 22 | 23 | const createServer = () => { 24 | var server = new mosca.Server(moscaSettings); 25 | server.on('ready', () => { 26 | debug('Mosca server is up and running') 27 | }); 28 | 29 | server.on('clientConnected', function(client) { 30 | debug('client connected', client.id); 31 | }); 32 | 33 | // fired when a message is received 34 | server.on('published', function(packet, client) { 35 | debug('Published', packet.topic, packet.payload); 36 | }); 37 | } 38 | 39 | export default createServer; 40 | -------------------------------------------------------------------------------- /server/wechat/emitter.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | 3 | export class SvrEmitter extends EventEmitter { 4 | constructor () { 5 | super (); 6 | } 7 | 8 | wechatSendOut (obj) { 9 | this.emit('wechatout', obj); 10 | } 11 | onFromWechat (fn) { 12 | this.on('wechatout', fn); 13 | } 14 | 15 | sendToWechat (obj) { 16 | this.emit('intowechat', obj); 17 | } 18 | onWechatRecv (fn) { 19 | this.on('intowechat', fn); 20 | } 21 | } 22 | 23 | const globalEmitter = new SvrEmitter (); 24 | 25 | export default globalEmitter; 26 | -------------------------------------------------------------------------------- /src/Errcode.js: -------------------------------------------------------------------------------- 1 | /*export default class Errcode1 extends Error { 2 | static ERR_UNKNOWN = 40001; 3 | static ERR_CODE1 = 40002; 4 | static ERR_PARAM_ERROR = 40003; 5 | static ERR_NO_SUCH_ENTITY = 40004; 6 | static ERR_ALREADY_EXIST = 40005; 7 | static ERR_USER_NO_BASEINFO = 40006; 8 | static ERR_USER_DUP_LOGIN = 40007; 9 | static ERR_USER_DUP_EMAIL = 40008; 10 | static ERR_USER_CREATE_FAIL = 40009; 11 | static ERR_USER_UPDATE_FAIL = 40010; 12 | 13 | 14 | constructor(message, errcode=ERR_UNKNOWN) { 15 | super(message); 16 | this.name = this.constructor.name; 17 | this.message = message; 18 | this.errcode = errcode; 19 | if (typeof Error.captureStackTrace === 'function') { 20 | Error.captureStackTrace(this, this.constructor); 21 | } else { 22 | this.stack = (new Error(message)).stack; 23 | } 24 | } 25 | toJSON () { 26 | return { errcode: this.errcode, msg: this.message }; 27 | } 28 | } 29 | 30 | export class Errcode extends Errcode1 {} 31 | */ 32 | 33 | 34 | /* 35 | export const ERR_UNKNOWN = 40001; 36 | export const ERR_CODE1 = 40002; 37 | export const ERR_PARAM_ERROR = 40003; 38 | export const ERR_NO_SUCH_ENTITY = 40004; 39 | export const ERR_ALREADY_EXIST = 40005; 40 | */ 41 | /* 42 | // now I can extend 43 | class MyError extends Errcode {} 44 | 45 | //var myerror = new MyError("ll"); 46 | var myerror = new Errcode("ll"); 47 | console.log(myerror.message); 48 | console.log(myerror.errcode); 49 | console.log(myerror instanceof Error); 50 | console.log(myerror.name); 51 | console.log(myerror.stack); 52 | */ 53 | 54 | export const ERR_UNKNOWN = 40001; 55 | export const ERR_CODE1 = 40002; 56 | export const ERR_PARAM_ERROR = 40003; 57 | export const ERR_NO_SUCH_ENTITY = 40004; 58 | export const ERR_INSERT_DB_FAIL = 40005; 59 | export const ERR_UPDATE_DB_FAIL = 40006; 60 | export const ERR_ALREADY_EXIST = 40007; 61 | export const ERR_MISS_REQUIRE = 40008; 62 | export const ERR_USER_NO_BASEINFO = 40020; 63 | export const ERR_USER_DUP_LOGIN = 40021; 64 | export const ERR_USER_DUP_EMAIL = 40022; 65 | export const ERR_USER_CREATE_FAIL = 40023; 66 | export const ERR_USER_UPDATE_FAIL = 40024; 67 | export const ERR_USER_UPD_NO_USER = 40025; 68 | export const ERR_TAXONOMY_DUP_TERM = 40030; 69 | export const ERR_POST_TYPE_CHANGE_NOT_ALLOW = 40040; 70 | export const ERR_POST_MISS_REQUIRE = 40041; 71 | 72 | export const ERR_FILE_READ_FAIL = 44001; 73 | export const ERR_UPLOAD_FAIL = 44002; 74 | export const ERR_UPLOAD_USER_ABORT = 44003; 75 | 76 | export default function Errcode(message, errcode=ERR_UNKNOWN) { 77 | this.name = 'Errcode'; 78 | this.message = message || ''; 79 | this.errcode = errcode; 80 | this.stack = (new Error()).stack; 81 | this.toJSON = () => { 82 | return { errcode: this.errcode, msg: this.message }; 83 | } 84 | } 85 | Errcode.prototype = Object.create(Error.prototype); 86 | Errcode.prototype.constructor = Errcode; 87 | -------------------------------------------------------------------------------- /src/components/Counter/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './Counter.scss' 3 | 4 | export const Counter = (props) => ( 5 |
6 |

7 | Counter: 8 | {' '} 9 | 10 | {props.counter} 11 | 12 |

13 | 16 | {' '} 17 | 20 |
21 | ) 22 | 23 | Counter.propTypes = { 24 | counter: React.PropTypes.number.isRequired, 25 | doubleAsync: React.PropTypes.func.isRequired, 26 | increment: React.PropTypes.func.isRequired 27 | } 28 | 29 | export default Counter 30 | -------------------------------------------------------------------------------- /src/components/Counter/Counter.scss: -------------------------------------------------------------------------------- 1 | .counter { 2 | font-weight: bold; 3 | } 4 | 5 | .counter--green { 6 | composes: counter; 7 | color: rgb(25,200,25); 8 | } 9 | 10 | .counterContainer { 11 | margin: 1em auto; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Counter/index.js: -------------------------------------------------------------------------------- 1 | import Counter from './Counter' 2 | 3 | export default Counter 4 | -------------------------------------------------------------------------------- /src/components/Cropper/CropperDemo.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import Cropper from './Cropper' 3 | 4 | export class CropperDemo extends Component { 5 | constructor (props) { 6 | super (props); 7 | this.state = { 8 | canvas:null 9 | } 10 | } 11 | 12 | componentDidMount() { 13 | } 14 | 15 | componentWillUnmount() { 16 | } 17 | 18 | render2 () { 19 | return ( 20 |
21 | 22 |
23 | ) 24 | } 25 | render () { 26 | var dataurl = this.state.canvas && this.state.canvas.toDataURL(); 27 | //console.log ("canvas:", this.state.canvas, ", dataurl:", dataurl); 28 | return ( 29 |
30 | this.setState({canvas:e})}/> 31 |
32 |
33 | ) 34 | } 35 | } 36 | 37 | 38 | export default CropperDemo 39 | 40 | -------------------------------------------------------------------------------- /src/components/Forms/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Field, reduxForm } from 'redux-form' 3 | import classes from './Login.scss' 4 | 5 | const LoginForm = (props) => { 6 | const { onSubmit, handleSubmit, pristine, reset, submitting } = props; 7 | return ( 8 |
9 | 10 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | ); 23 | }; 24 | 25 | LoginForm.propTypes = { 26 | onSubmit: React.PropTypes.func.isRequired 27 | }; 28 | 29 | export default reduxForm({ 30 | form: 'login' // a unique name for this form 31 | })(LoginForm); 32 | -------------------------------------------------------------------------------- /src/components/Forms/Login.scss: -------------------------------------------------------------------------------- 1 | .savedWisdoms { 2 | margin-top: 4em; 3 | color: red; 4 | > ul { 5 | padding: 0; 6 | list-style: none; 7 | font-style: italic; 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import classes from './Header.scss' 4 | 5 | export const Header = () => ( 6 |
7 |

React Redux Starter Kit

8 | 9 | Home 10 | 11 | {' · '} 12 | 13 | Counter 14 | 15 | {' · '} 16 | 17 | Zen 18 | 19 |
20 | ) 21 | 22 | export default Header 23 | -------------------------------------------------------------------------------- /src/components/Header/Header.scss: -------------------------------------------------------------------------------- 1 | .activeRoute { 2 | font-weight: bold; 3 | text-decoration: underline; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/components/Header/HeaderAirbnb.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import classes from './HeaderAirbnb.scss' 4 | import classNames from 'classnames'; 5 | 6 | const Logo = () => ( 7 |
8 | 9 | {/* 'Home' */} 10 |
11 | ) 12 | 13 | const Tour = () => ( 14 |
15 | { 'Tour' } 16 | 17 |
18 | ) 19 | 20 | const Message = () => ( 21 |
22 | { 'Msg' } 23 | {/**/} 24 | 25 | 1 26 | 27 |
28 | ) 29 | 30 | const Help = () => ( 31 |
32 | Help 33 | 34 |
35 | ) 36 | 37 | const User = (props) => ( 38 |
39 | {props.userName || 'Login'} 40 | ... 41 |
42 | ) 43 | 44 | const Auth2Url = (url) => { 45 | //var redirect_uri = url || "http://www.lancertech.net"; 46 | var redirect_uri = location.href; 47 | redirect_uri = encodeURIComponent(redirect_uri); 48 | var state = "1"; 49 | var next_url = "http://lancertech.net/get-weixin-code.html?appid=wx1a6eca02cffc398c&scope=snsapi_base&state="+state+"&redirect_uri="+redirect_uri; 50 | console.log ("next_url:", next_url); 51 | return next_url; 52 | } 53 | 54 | export const Header = (props) => ( 55 |
56 |
57 | 58 | 59 | 60 |
61 | 62 |
63 | 64 | 65 | 66 |
67 | 68 |
69 | 70 | 71 | 72 |
73 | 74 |
75 | 76 | 77 | 78 |
79 | 80 |
81 | 82 | 83 | 84 |
85 | 86 |
87 | ) 88 | 89 | export default Header 90 | -------------------------------------------------------------------------------- /src/components/Header/HeaderAirbnb.scss: -------------------------------------------------------------------------------- 1 | .activeRoute { 2 | font-weight: bold; 3 | text-decoration: underline; 4 | } 5 | 6 | .airbnbHeader { 7 | height: 61px; 8 | line-height: 61px; 9 | display: block; 10 | position: relative; 11 | background-color: rgba(0,0,0,0.05); 12 | z-index: 1000; 13 | .comp { 14 | font-size: 16px; 15 | height: 100%; 16 | box-sizing: border-box; 17 | border-left: 1px solid #dce0e0; 18 | padding: 0 19px; 19 | .icon { 20 | margin-left: 5px; 21 | font-size: 24px; 22 | height: 24px; 23 | } 24 | .avatar { 25 | height: 24px; 26 | width: 24px; 27 | margin-bottom: 20px; 28 | } 29 | } 30 | } 31 | 32 | .numberBadge { 33 | position: absolute; 34 | top: -5px; 35 | right: -15px; 36 | padding: 0 4px; 37 | border: { 38 | style: solid; 39 | width: 2px; 40 | color: red; 41 | } 42 | background-color: transparent; 43 | height: 16px; 44 | line-height:10px; 45 | font-size: 10px; 46 | color: red; 47 | } -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './HeaderAirbnb' 2 | 3 | export default Header 4 | -------------------------------------------------------------------------------- /src/components/Touchtop/TouchtopLayout.scss: -------------------------------------------------------------------------------- 1 | .activeRoute { 2 | font-weight: bold; 3 | text-decoration: underline; 4 | } 5 | .navChange { 6 | background-color: #fff; 7 | padding: 12px 0; 8 | box-shadow: 0 0 3px #ccc; 9 | font-weight: 500; 10 | border-color: #e7e7e7; 11 | top: 0; 12 | border-width: 0 0 1px; 13 | } 14 | 15 | .logo { 16 | height: 50px; 17 | width: 199px; 18 | padding: 0px; 19 | margin: 0px; 20 | margin-left: 15px !important; 21 | } 22 | .onlineLayer { 23 | position: fixed; 24 | right: -140px; 25 | top: 30%; 26 | z-index: 20; 27 | } 28 | 29 | .services { 30 | background-color: #00a7ea; 31 | padding: 60px 0; 32 | text-align: center; 33 | background-size: cover; 34 | } 35 | .services H2 { 36 | color: #fff; 37 | } 38 | .services H3 { 39 | color: #fff; 40 | } 41 | .services hr { 42 | display: block; 43 | height: 3px; 44 | border: 0; 45 | margin: 3em 0; 46 | padding: 0; 47 | background-image: -moz-linear-gradient(left,rgba(255,255,255,0),#71c9d6,rgba(255,255,255,0)); 48 | width: 50%; 49 | left: 25%; 50 | position: relative; 51 | border: none; 52 | } 53 | 54 | .contact { 55 | background-color: #00a7ea; 56 | color: #fff; 57 | padding: 60px 0; 58 | text-align: center; 59 | } 60 | 61 | .footerwrap { 62 | background: #2b2b2b; 63 | border-bottom: 1px solid #ddd; 64 | text-align: center; 65 | } 66 | .copyright { 67 | line-height: 40px; 68 | color: #888; 69 | font-weight: 400; 70 | } 71 | 72 | :global { 73 | .row { 74 | margin-left:0px; 75 | margin-right:0px; 76 | } 77 | .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:focus, .navbar-default .navbar-nav > .active > a:hover { 78 | color: #00a7ea; 79 | background-color: transparent; 80 | } 81 | } -------------------------------------------------------------------------------- /src/components/Wordpress/FileUploader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './Styles.scss'; 3 | import classNames from 'classnames/bind'; 4 | const cx = classNames.bind(styles); 5 | 6 | const MIME_TYPE_IMGS = "image/jpg,image/jpeg,image/png,image/gif"; 7 | const MIME_TYPE_BINS = "*.bin,*.hex"; 8 | 9 | export const FileInput = (props) => ( 10 |
11 | 12 |
13 | ) 14 | FileInput.propTypes = { 15 | accept: React.PropTypes.string, 16 | addFile: React.PropTypes.func 17 | } 18 | 19 | export default FileInput; 20 | -------------------------------------------------------------------------------- /src/components/Wordpress/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import classes from './Styles.scss' 4 | import classNames from 'classnames'; 5 | 6 | export const Footer = (props) => ( 7 |
8 |
9 | 15 |
16 |
17 |

18 | Designed and built with all the love in the world by @mdo and @fat. Maintained by the core team with the help of our contributors. 19 |

20 |

21 | Code licensed MIT, docs CC BY 3.0. 22 |

23 |
24 |
25 | ) 26 | 27 | export default Footer 28 | -------------------------------------------------------------------------------- /src/components/Wordpress/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import classes from './Styles.scss' 4 | import classNames from 'classnames'; 5 | 6 | const Menu1 = () => ( 7 | 33 | ) 34 | 35 | const Menu2 = () => ( 36 | 42 | ) 43 | 44 | export const Header = (props) => ( 45 |
46 |
47 |
48 |
49 | ) 50 | 51 | export default Header 52 | -------------------------------------------------------------------------------- /src/components/Wordpress/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Field, reduxForm } from 'redux-form' 3 | import classes from './Styles.scss' 4 | 5 | 6 | const LoginForm = (props) => { 7 | const { onSubmit, handleSubmit, pristine, reset, submitting } = props; 8 | return ( 9 |
10 |

Please sign in

11 | 12 | 13 | 14 | 15 |
16 | 19 |
20 | 21 | 22 | ); 23 | }; 24 | /* 25 | const LoginForm = (props) => { 26 | const { onSubmit, handleSubmit, pristine, reset, submitting } = props; 27 | return ( 28 |
29 | 30 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | ); 43 | }; 44 | */ 45 | LoginForm.propTypes = { 46 | onSubmit: React.PropTypes.func.isRequired 47 | }; 48 | 49 | export default reduxForm({ 50 | form: 'login' // a unique name for this form 51 | })(LoginForm); 52 | -------------------------------------------------------------------------------- /src/components/Wordpress/Login.scss: -------------------------------------------------------------------------------- 1 | .savedWisdoms { 2 | margin-top: 4em; 3 | color: red; 4 | > ul { 5 | padding: 0; 6 | list-style: none; 7 | font-style: italic; 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/Wordpress/Styles.scss: -------------------------------------------------------------------------------- 1 | $menu-bgcolor: #aaa; 2 | 3 | :global .wp-menu-container { 4 | background-color:$menu-bgcolor; 5 | width:100%; 6 | margin:0; 7 | } 8 | 9 | :global .wp-menu { 10 | display: inline-block; 11 | font-size: 1em; 12 | line-height: 1.2em; 13 | border-radius: 0px; 14 | } 15 | 16 | :global .wp-menu-icon { 17 | font-size: 1.2em; 18 | } 19 | 20 | :global .wp-sidebar-container { 21 | height: 100%; 22 | background-color:$menu-bgcolor; 23 | border-width: 0; 24 | margin:0; 25 | padding:0; 26 | float:left; 27 | } 28 | 29 | :global .wp-sidebar-text-show { 30 | display:inline; 31 | } 32 | 33 | :global .wp-footer-container { 34 | background-color:$menu-bgcolor; 35 | width:100%; 36 | margin:0; 37 | } 38 | 39 | :global .form-signin { 40 | max-width: 330px; 41 | padding: 15px; 42 | margin: 0 auto; 43 | .form-signin-heading, .checkbox { 44 | margin-bottom: 10px; 45 | } 46 | .checkbox { 47 | font-weight: normal; 48 | } 49 | .form-control { 50 | position: relative; 51 | height: auto; 52 | -webkit-box-sizing: border-box; 53 | -moz-box-sizing: border-box; 54 | box-sizing: border-box; 55 | padding: 10px; 56 | font-size: 16px; 57 | } 58 | .form-control:focus { 59 | z-index: 2; 60 | } 61 | input[type="text"] { 62 | margin-bottom: -1px; 63 | border-bottom-right-radius: 0; 64 | border-bottom-left-radius: 0; 65 | } 66 | input[type="password"] { 67 | margin-bottom: 10px; 68 | border-top-left-radius: 0; 69 | border-top-right-radius: 0; 70 | } 71 | } 72 | 73 | :global { 74 | .weui_uploader_input_wrp { 75 | float: left; 76 | position: relative; 77 | margin: 9px; 78 | width: 100px; 79 | height: 100px; 80 | border: 1px solid #d9d9d9; 81 | &:after, &:before { 82 | content: " "; 83 | position: absolute; 84 | top: 50%; 85 | left: 50%; 86 | -webkit-transform: translate(-50%,-50%); 87 | transform: translate(-50%,-50%); 88 | background-color: #d9d9d9; 89 | } 90 | &:before { 91 | width: 2px; 92 | height: 39.5px; 93 | } 94 | &:after { 95 | width: 39.5px; 96 | height: 2px; 97 | } 98 | } 99 | .weui_uploader_input { 100 | position: absolute; 101 | z-index: 1; 102 | top: 0; 103 | left: 0; 104 | width: 100%; 105 | height: 100%; 106 | opacity: 0; 107 | -webkit-tap-highlight-color: rgba(0,0,0,0); 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /src/components/widgets/FileExplorer.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | padding: 15px; 3 | } 4 | .pathinfo { 5 | font-size: 1.4em; 6 | } 7 | :global { 8 | .fe-toolbar.ul { 9 | list-style:none; 10 | } 11 | .fe-toolbar li { 12 | display: inline; 13 | margin-left: 10px; 14 | float:left; 15 | } 16 | } 17 | .filetable { 18 | background-color:#fff; 19 | } 20 | 21 | .fileicon { 22 | width:30px; 23 | } 24 | 25 | .itemName { 26 | } 27 | .itemName .fileOperate { 28 | display:none; 29 | } 30 | .itemName:hover .fileOperate { 31 | display:block; 32 | } 33 | 34 | .cardContainer { 35 | padding:10px; 36 | } 37 | .imgWrapper { 38 | position:relative; 39 | padding-bottom: 67%; 40 | } 41 | .imgInner { 42 | position: absolute; 43 | max-height:100%; 44 | max-width:100%; 45 | } 46 | .infoInner { 47 | display:none; 48 | position: absolute; 49 | width: 200px; 50 | height:100px; 51 | } 52 | .imgWrapper:hover .infoInner { 53 | display:block; 54 | } 55 | .fileName { 56 | white-space: nowrap; 57 | text-overflow:ellipsis; 58 | overflow: hidden; 59 | } 60 | -------------------------------------------------------------------------------- /src/components/widgets/FiveStar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './FiveStar.scss'; 3 | import classNames from 'classnames/bind'; 4 | const cx = classNames.bind(styles); 5 | 6 | const StarFull = () => ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | const StarHalf = () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | const StarEmpty = () => ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | const FiveStar = (props) => { 39 | var star = parseFloat(props.star); 40 | var full = parseInt(star); 41 | var half = (star - full) == 0 ? 0 : 1; 42 | var empty = 5 - full - half; 43 | var flags = []; 44 | for (var i = 0; i < 5; i++) flags[i] = 0; 45 | for (var i = 0; i < full; i++) flags[i] = 2; 46 | for (var i = 0; i < half; i++) flags[full+i] = 1; 47 | var objs = flags.map ( (item, index) => { 48 | if (item == 2) return (); 49 | else if (item == 1) return (); 50 | else return (); 51 | }); 52 | 53 | return ( 54 | {objs} 55 | ) 56 | } 57 | 58 | FiveStar.propTypes = { 59 | star: React.PropTypes.number 60 | } 61 | 62 | export default FiveStar 63 | -------------------------------------------------------------------------------- /src/components/widgets/FiveStar.scss: -------------------------------------------------------------------------------- 1 | .starContainer_starContainer_sizeSmall { 2 | display: inline-block !important; 3 | position: relative !important; 4 | height: 12px !important; 5 | width: 12px !important; 6 | margin-right: 3px !important; 7 | } 8 | .star { 9 | position: absolute !important; 10 | } -------------------------------------------------------------------------------- /src/components/widgets/ImageUploader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | //import classes from './Widgets.scss' 3 | //import classNames from 'classnames'; 4 | import styles from './ImageUploader.scss'; 5 | import classNames from 'classnames/bind'; 6 | const cx = classNames.bind(styles); 7 | 8 | /* 9 | const PreviewImage = (props) => ( 10 |
11 | {props.alt}/ 12 |
13 |
14 | ) 15 | */ 16 | const PreviewImage = (props) => ( 17 |
18 | { !props.src && 19 |
{props.alt}
20 | } 21 |
22 |
23 |
24 | ) 25 | 26 | const FileInput = (props) => ( 27 |
28 | 29 |
30 | ) 31 | 32 | const ImageUploader = (props) => { 33 | var data = props.data; 34 | var imgs = props.info && props.info.map( (image, index) => { 35 | return {props.removeImage(image)} } /> 36 | }); 37 | return ( 38 |
39 | { imgs } 40 | 41 |
42 | ) 43 | } 44 | 45 | ImageUploader.propTypes = { 46 | addImage: React.PropTypes.func, 47 | removeImage: React.PropTypes.func, 48 | info: React.PropTypes.array, 49 | data: React.PropTypes.object, 50 | } 51 | 52 | export default ImageUploader 53 | -------------------------------------------------------------------------------- /src/components/widgets/ImageUploader.scss: -------------------------------------------------------------------------------- 1 | $cell-width: 100px !default; 2 | $cell-height: 100px !default; 3 | 4 | .weui_uploader_input_wrp { 5 | float: left; 6 | position: relative; 7 | margin-right: 9px; 8 | margin-bottom: 9px; 9 | width: $cell-width; 10 | height: $cell-height; 11 | border: 1px solid #d9d9d9; 12 | &:after, &:before { 13 | content: " "; 14 | position: absolute; 15 | top: 50%; 16 | left: 50%; 17 | -webkit-transform: translate(-50%,-50%); 18 | transform: translate(-50%,-50%); 19 | background-color: #d9d9d9; 20 | } 21 | &:before { 22 | width: 2px; 23 | height: 39.5px; 24 | } 25 | &:after { 26 | width: 39.5px; 27 | height: 2px; 28 | } 29 | } 30 | .weui_uploader_input { 31 | position: absolute; 32 | z-index: 1; 33 | top: 0; 34 | left: 0; 35 | width: 100%; 36 | height: 100%; 37 | opacity: 0; 38 | -webkit-tap-highlight-color: rgba(0,0,0,0); 39 | } 40 | .weui_uploader_file { 41 | position: relative; 42 | float: left; 43 | margin-right: 9px; 44 | margin-bottom: 9px; 45 | width: $cell-width; 46 | height: $cell-height; 47 | background: no-repeat 50%; 48 | background-size: cover; 49 | text-align:center; 50 | } 51 | .weui_uploader_file_alt { 52 | position: absolute; 53 | width: 100%; 54 | height: 100%; 55 | text-align:left; 56 | } 57 | .weui_uploader_image_outer { 58 | position: absolute; 59 | width: 100%; 60 | height: 100%; 61 | } 62 | 63 | .previewImageContainer { 64 | display: block; 65 | width:100px; 66 | height:100px; 67 | text-align:center; 68 | float:left; 69 | } 70 | .responsive-imgwh { 71 | max-height:100%; 72 | max-width:100%; 73 | } 74 | 75 | .responsive-imgw { 76 | width:100%; 77 | height:auto; 78 | } 79 | .responsive-imgh { 80 | height:100%; 81 | width:auto; 82 | } 83 | .imgRemove { 84 | position: absolute; 85 | cursor: point; 86 | left:0px; 87 | top:0px; 88 | height:30px; 89 | width:30px; 90 | background-color: #d9d9d9; 91 | &:before 92 | { 93 | content: "X"; 94 | position: absolute; 95 | top: 50%; 96 | left: 50%; 97 | -webkit-transform: translate(-50%,-50%); 98 | transform: translate(-50%,-50%); 99 | } 100 | } 101 | .responsive-img-wrapper { 102 | position:absolute; 103 | height:100%; 104 | width:100%; 105 | text-align:center; 106 | } 107 | .responsive-img-outer { 108 | position:relative; 109 | padding-bottom:67%; 110 | width:100%; 111 | } 112 | -------------------------------------------------------------------------------- /src/components/widgets/Widgets.scss: -------------------------------------------------------------------------------- 1 | .responsive-imgwh { 2 | max-height:100%; 3 | max-width:100%; 4 | } 5 | .responsive-imgw { 6 | width:100%; 7 | height:auto; 8 | } 9 | .responsive-imgh { 10 | height:100%; 11 | width:auto; 12 | } 13 | .responsive-img-wrapper { 14 | position:absolute; 15 | height:100%; 16 | width:100%; 17 | text-align:center; 18 | } 19 | .responsive-img-outer { 20 | position:relative; 21 | padding-bottom:67%; 22 | width:100%; 23 | } 24 | .text-center { 25 | text-align: center; 26 | } 27 | .listing-img-container { 28 | // z-index: 1; 29 | overflow: hidden; 30 | } 31 | .media-photo { 32 | -webkit-backface-visibility: hidden; 33 | -moz-backface-visibility: hidden; 34 | backface-visibility: hidden; 35 | position: relative; 36 | display: inline-block; 37 | vertical-align: bottom; 38 | overflow: hidden; 39 | background-color: #bbb; 40 | } 41 | .media-cover, .media-cover-dark::after { 42 | position: absolute; 43 | top: 0; 44 | bottom: 0; 45 | left: 0; 46 | right: 0; 47 | } 48 | .target-control { 49 | position: absolute; 50 | height: 100%; 51 | cursor: pointer; 52 | // z-index: 1; 53 | background: none; 54 | border: 0; 55 | } 56 | .target-next { 57 | right: 0; 58 | width: 25%; 59 | } 60 | .target-prev { 61 | left: 0; 62 | width: 25%; 63 | } 64 | .imageContainer { 65 | width: 100% !important; 66 | position: relative !important; 67 | padding-bottom: 67% !important; 68 | min-height: 200px !important; 69 | background-color: #D8D8D8 !important; 70 | } 71 | .icon { 72 | font-style: normal; 73 | font-weight: normal; 74 | line-height: 1; 75 | } 76 | .icon-size-2 { 77 | font-size: 2.4em; 78 | } 79 | .icon-white { 80 | color: #fff; 81 | } 82 | 83 | .infoContainer { 84 | padding-top: 12px !important; 85 | padding-left: 12px; 86 | text-align: left; 87 | } 88 | .linkContainer { 89 | display: block !important; 90 | } 91 | .text_size_regular_weight_bold_inline { 92 | font-weight: 700 !important; 93 | color: #484848 !important; 94 | font-family: Circular !important; 95 | margin: 0px !important; 96 | word-wrap: break-word !important; 97 | font-size: 19px !important; 98 | line-height: 24px !important; 99 | letter-spacing: undefined !important; 100 | padding-top: 0px !important; 101 | padding-bottom: 0px !important; 102 | display: inline !important; 103 | } 104 | .text_size_small_weight_light_inline { 105 | font-weight: 200 !important; 106 | color: #484848 !important; 107 | font-family: Circular !important; 108 | margin: 0px !important; 109 | word-wrap: break-word !important; 110 | font-size: 15px !important; 111 | line-height: 18px !important; 112 | letter-spacing: 0.2px !important; 113 | padding-top: 0px !important; 114 | padding-bottom: 0px !important; 115 | display: inline !important; 116 | } 117 | .detailContainer { 118 | white-space: nowrap !important; 119 | } 120 | -------------------------------------------------------------------------------- /src/containers/AppContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Router } from 'react-router' 3 | import { Provider } from 'react-redux' 4 | 5 | class AppContainer extends Component { 6 | static propTypes = { 7 | history: PropTypes.object.isRequired, 8 | routes: PropTypes.object.isRequired, 9 | store: PropTypes.object.isRequired 10 | } 11 | 12 | render () { 13 | const { history, routes, store } = this.props 14 | 15 | return ( 16 | 17 | 18 | 19 | ) 20 | } 21 | } 22 | 23 | export default AppContainer 24 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 帝利文化圈 5 | 6 | 7 | 8 | 11 | 12 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /src/layouts/CoreLayout/CoreLayout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from '../../components/Header' 3 | import classes from './CoreLayout.scss' 4 | import '../../styles/core.scss' 5 | //import '../../styles/slick.css' 6 | 7 | export const CoreLayout = ({ children }) => ( 8 |
9 |
10 |
11 | {children} 12 |
13 |
14 | ) 15 | 16 | CoreLayout.propTypes = { 17 | children: React.PropTypes.element.isRequired 18 | } 19 | 20 | export default CoreLayout 21 | -------------------------------------------------------------------------------- /src/layouts/CoreLayout/CoreLayout.scss: -------------------------------------------------------------------------------- 1 | .mainContainer { 2 | padding-top:20px; 3 | } 4 | -------------------------------------------------------------------------------- /src/layouts/CoreLayout/index.js: -------------------------------------------------------------------------------- 1 | import CoreLayout from './CoreLayout' 2 | 3 | export default CoreLayout 4 | -------------------------------------------------------------------------------- /src/layouts/EmptyLayout/EmptyLayout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classes from './EmptyLayout.scss' 3 | 4 | export const EmptyLayout = ({ children }) => ( 5 |
6 | {children} 7 |
8 | ) 9 | 10 | EmptyLayout.propTypes = { 11 | children: React.PropTypes.element.isRequired 12 | } 13 | 14 | export default EmptyLayout 15 | -------------------------------------------------------------------------------- /src/layouts/EmptyLayout/EmptyLayout.scss: -------------------------------------------------------------------------------- 1 | .emptyContainer { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | -------------------------------------------------------------------------------- /src/layouts/EmptyLayout/index.js: -------------------------------------------------------------------------------- 1 | import EmptyLayout from './EmptyLayout' 2 | 3 | export default EmptyLayout 4 | -------------------------------------------------------------------------------- /src/routes/Coin/common.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('COIN:common'); 2 | import _ from 'lodash'; 3 | 4 | export const userHasCap = (user, cap) => { 5 | if (!cap) return true; 6 | if (!user || !user.caps) return false; 7 | 8 | var userCaps = user.caps; 9 | if (_.isString(userCaps)) 10 | userCaps = userCaps.split(','); 11 | userCaps = userCaps.map (item => { 12 | return item.toUpperCase(); 13 | }) 14 | 15 | var isRoot = userCaps && (userCaps.indexOf('ROOT') >= 0); 16 | if (isRoot) return true; 17 | 18 | var hasCapShopAgent = userCaps && (userCaps.indexOf(cap) >= 0); 19 | return !!hasCapShopAgent; 20 | } 21 | 22 | export const addCap = (caps, cap) => { 23 | if (!cap) return caps; 24 | 25 | cap = cap.toUpperCase(); 26 | if (!caps) return [cap]; 27 | 28 | if (_.isString(caps)) 29 | caps = caps.split(','); 30 | caps = caps.map (item => { 31 | return item.toUpperCase(); 32 | }) 33 | 34 | var capSet = new Set (caps); 35 | capSet.add (cap); 36 | 37 | return [...capSet]; 38 | } 39 | 40 | export const delCap = (caps, cap) => { 41 | if (!caps || !cap) return caps; 42 | 43 | cap = cap.toUpperCase(); 44 | 45 | if (_.isString(caps)) 46 | caps = caps.split(','); 47 | caps = caps.map (item => { 48 | return item.toUpperCase(); 49 | }) 50 | 51 | var capSet = new Set (caps); 52 | capSet.delete (cap); 53 | 54 | return [...capSet]; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/routes/Coin/components/ArtworkDetail.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import _ from 'lodash'; 4 | var xdebug = window.myDebug('COIN:ArtworkDetail'); 5 | 6 | export default class Page extends Component { 7 | static propTypes = { 8 | cpost: PropTypes.object.isRequired, 9 | goBack: PropTypes.func.isRequired, 10 | retrievePosts: PropTypes.func.isRequired, 11 | } 12 | 13 | componentDidMount() { 14 | var { params, cpost, retrievePosts } = this.props; 15 | if (params && params.postId) { 16 | if (cpost && cpost.db && cpost.db[params.postId]) { 17 | xdebug ("already have this post, no need to retrieve again!"); 18 | } else 19 | retrievePosts (params.postId); 20 | } 21 | } 22 | 23 | componentWillUnmount() { 24 | } 25 | 26 | render () { 27 | var {goBack, cpost, params} = this.props; 28 | var postId = params && params.postId; 29 | var post = cpost && cpost.db && cpost.db[postId]; 30 | var images = post && post.images || ['/images/none.jpg']; 31 | var imgList = images && images.map ((item, index)=>{ 32 | return ( 33 |
  • {'none'}/
  • 34 | ) 35 | }); 36 | 37 | return ( 38 |
    39 | 47 | 48 |
    49 |
    50 |
      51 | {imgList} 52 |
    53 |
    54 |
    55 |
    56 | ); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/routes/Coin/components/LoginPage.js: -------------------------------------------------------------------------------- 1 | import { IndexLink, Link } from 'react-router' 2 | import React, { Component, PropTypes } from 'react' 3 | import { Field, reduxForm } from 'redux-form' 4 | import LoginForm from 'components/Wordpress/Login'; 5 | 6 | var xdebug = window.myDebug('Coin:LoginPage') 7 | 8 | export default class LoginPage extends Component { 9 | static propTypes = { 10 | goBack: PropTypes.func.isRequired, 11 | push: PropTypes.func.isRequired, 12 | replace: PropTypes.func.isRequired, 13 | 14 | cuser: PropTypes.object.isRequired, 15 | handleLogin: PropTypes.func.isRequired, 16 | } 17 | 18 | constructor (props) { 19 | xdebug ("constructor"); 20 | super (props); 21 | } 22 | 23 | componentDidMount () { 24 | const { location, replace, push, cuser } = this.props; 25 | var nextPathname = location && location.state && location.state.nextPathname; 26 | var userId = cuser && cuser.user && cuser.id; 27 | xdebug ("componentDidMount userId:"+userId+", nextPathname:"+nextPathname); 28 | if( userId ) { 29 | xdebug ("componentDidMount already login! go to previous page!"); 30 | replace(nextPathname || '/coin/message'); 31 | } 32 | } 33 | /* 34 | componentWillReceiveProps (nextProps) { 35 | //xdebug ("componentWillReceiveProps"); 36 | const { location, push, replace, cuser } = nextProps; 37 | var nextPathname = location && location.state && location.state.nextPathname; 38 | var userId = cuser && cuser.user && cuser.user.id; 39 | xdebug ("componentWillReceiveProps userId:"+userId+", nextPathname:"+nextPathname); 40 | 41 | if( userId ) { 42 | xdebug ("already login! go to previous page!"); 43 | replace(nextPathname || '/coin'); 44 | } 45 | } 46 | */ 47 | render () { 48 | const { handleLogin, cuser, replace, location } = this.props; 49 | var nextPathname = location && location.state && location.state.nextPathname; 50 | var submit = (values) => { 51 | // Do something with the form values 52 | xdebug("login form:", values); 53 | var userdata = {nicename:values.username, pass: values.password}; 54 | handleLogin (userdata, cuser.user).then(retobj => { 55 | if (retobj && retobj.id) { 56 | var next = nextPathname || '/coin'; 57 | xdebug ("already login! go to previous page! url="+next); 58 | replace(next); 59 | } else { 60 | xdebug("error! login fail! not get user!"); 61 | } 62 | }).catch (error => { 63 | xdebug("error! login fail:"+error.message, error); 64 | }); 65 | } 66 | 67 | return ( 68 |
    69 | 70 |
    { cuser.error && cuser.error.message }
    71 |
    72 | ); 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/routes/Coin/components/Qrcode.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('COIN:MyAddress'); 2 | import React, { Component, PropTypes } from 'react' 3 | import { IndexLink, Link } from 'react-router' 4 | import _ from 'lodash'; 5 | import config from '../config'; 6 | 7 | export default class Page extends Component { 8 | static propTypes = { 9 | goBack: PropTypes.func.isRequired, 10 | 11 | cuser: PropTypes.object.isRequired, 12 | } 13 | 14 | constructor (props) { 15 | super (props); 16 | this.state = { 17 | } 18 | } 19 | 20 | componentDidMount() { 21 | wx && wx.showAllNonBaseMenuItem(); 22 | } 23 | 24 | render () { 25 | var {goBack, cuser} = this.props; 26 | var qrcode = config.qrcode; 27 | var user1 = cuser.user || {}; 28 | var userId = user1.id; 29 | 30 | return ( 31 |
    32 | 38 | 39 |
    40 |
    41 | 42 |
    43 | 44 |
    45 | 46 |
    47 |

    方法一:长按二维码图片弹出菜单,点击《识别图中二维码》,即可关注

    48 |

    方法二:请保存二维码图片,并打开微信扫一扫,点击右上角菜单,选择《从相册选取二维码》

    49 |

    方法三:添加朋友方式,搜索公众号《中农物联》,并关注

    50 |
    51 | 52 |
    53 | Responsive image 54 |
    55 | 56 |
    57 |
    58 | 59 |
    60 | ); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/routes/Coin/components/Weinre.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { IndexLink, Link } from 'react-router' 3 | import _ from 'lodash'; 4 | var xdebug = window.myDebug('COIN:MyAddress'); 5 | 6 | export default class Page extends Component { 7 | static propTypes = { 8 | goBack: PropTypes.func.isRequired, 9 | } 10 | 11 | constructor (props) { 12 | super (props); 13 | this.state = { 14 | ip:'192.168.2.109', 15 | settled:false, 16 | } 17 | } 18 | 19 | componentDidMount () { 20 | } 21 | 22 | injectScript() { 23 | var ip = this.state.ip || '192.168.2.109'; 24 | const script = document.createElement("script"); 25 | script.src = "http://"+ip+":9090/target/target-script-min.js#anonymous"; 26 | script.async = true; 27 | document.body.appendChild(script); 28 | this.setState({settled:true}); 29 | } 30 | 31 | render () { 32 | var {goBack} = this.props; 33 | var disabled ={}; 34 | if (this.state.settled) disabled = {disabled:'disabled'}; 35 | return ( 36 |
    37 | 43 | 44 |
    45 |
    46 | 47 |
    48 |
    49 | IP 50 | {this.setState({ip: e.target.value})}} value={this.state.ip}/> 51 |
    52 |
    53 | 54 |
    55 | 56 |
    57 | 58 |
    59 |
    60 | 61 |
    62 | ); 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/routes/Coin/components/styles.scss: -------------------------------------------------------------------------------- 1 | .slideWrap { 2 | position: relative; 3 | background-color: #d8d8d8; 4 | min-height: 200px; 5 | width: 100%; 6 | padding-bottom: 100%; 7 | } 8 | 9 | .slideImageWrap { 10 | position: absolute; 11 | display: inline-block; 12 | background-color: #bbb; 13 | overflow: hidden; 14 | top: 0; 15 | bottom: 0; 16 | left: 0; 17 | right: 0; 18 | // z-index: 1; 19 | overflow: hidden; 20 | text-align: center; 21 | vertical-align: bottom; 22 | } 23 | 24 | .slideCtrl { 25 | position: absolute; 26 | height: 100%; 27 | cursor: pointer; 28 | // z-index: 1; 29 | background: none; 30 | border: 0; 31 | } 32 | .slideCtrlNext { 33 | right: 0; 34 | width: 25%; 35 | } 36 | .slideCtrlPrev { 37 | left: 0; 38 | width: 25%; 39 | } 40 | 41 | :global .ReactModal__Body--open { 42 | overflow: hidden; 43 | } 44 | 45 | .modalOverlay { 46 | position : fixed; 47 | top : 0; 48 | left : 0; 49 | right : 0; 50 | bottom : 0; 51 | background-color : rgba(255, 255, 255, 0.75); 52 | z-index : 2017; 53 | } 54 | 55 | .modalContent { 56 | position: absolute; 57 | top: auto; 58 | left: 40px; 59 | right: 40px; 60 | bottom: 5px; 61 | background-color: papayawhip; 62 | } 63 | 64 | .w-number { 65 | padding: 0 24px; 66 | position: relative; 67 | display: inline-block; 68 | vertical-align: middle; 69 | } 70 | .w-number-btn { 71 | box-sizing: content-box; 72 | font-size: 22px; 73 | width: 30px; 74 | height: 30px; 75 | line-height: 28px; 76 | text-decoration: none; 77 | position: absolute; 78 | right: 0; 79 | text-align: center; 80 | overflow: hidden; 81 | background: #fff; 82 | color: #999; 83 | border: 1px solid #e0e0e0; 84 | } 85 | .w-number-btn-plus { 86 | right: 0; 87 | top: 0; 88 | } 89 | .w-number-btn-minus { 90 | right: auto; 91 | left: 0; 92 | top: 0; 93 | } 94 | .w-number-input { 95 | box-sizing: content-box; 96 | font-size: 16px; 97 | text-align: center; 98 | width: 74px; 99 | border: 1px solid #e0e0e0; 100 | height: 26px; 101 | padding: 2px 4px; 102 | outline: none; 103 | } 104 | 105 | .userDetailOpenid .detail { 106 | display:none 107 | } 108 | .userDetailOpenid:hover .detail { 109 | display:block; 110 | } 111 | -------------------------------------------------------------------------------- /src/routes/Coin/config.js: -------------------------------------------------------------------------------- 1 | export const configZdili = { 2 | qrcode: '/images/qrcode_for_gh_14b79043d0a3_430.jpg', 3 | }; 4 | 5 | export const configQingShanSi = { 6 | qrcode: '/images/qrcode_for_gh_9e62dd855eff_430.jpg', 7 | }; 8 | 9 | export default configZdili; 10 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/ArtworkContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { retrievePosts } from '../modules/post' 4 | import { updateUser } from '../modules/user' 5 | import { retrieveQrcode } from '../modules/qrcode' 6 | import { retrieveOrders, retrieveOrdersLater, createOrder } from '../modules/order' 7 | import { jsPayOrders } from 'routes/Pay/modules/pay' 8 | 9 | const mapDispatchToProps = { 10 | goBack, 11 | replace, 12 | push, 13 | 14 | retrievePosts, 15 | retrieveOrdersLater, 16 | createOrder, 17 | 18 | jsPayOrders, 19 | 20 | updateUser, 21 | 22 | retrieveQrcode, 23 | } 24 | 25 | const mapStateToProps = (state) => ({ 26 | cpost: state.cpost, 27 | corder: state.corder, 28 | cuser: state.cuser, 29 | jssdk: state.jssdk, 30 | qrcode: state.qrcode, 31 | }) 32 | 33 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Artwork').default) 34 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/ArtworkDetailContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { retrievePosts } from '../modules/post' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | retrievePosts, 8 | } 9 | 10 | const mapStateToProps = (state) => ({ 11 | cpost: state.cpost, 12 | }) 13 | 14 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/ArtworkDetail').default) 15 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/ArtworkEditorContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, replace } from 'react-router-redux' 3 | import { retrievePosts, createPost, updatePost, setPostEditor, updatePostEditor } from '../modules/post' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | replace, 8 | 9 | retrievePosts, 10 | createPost, 11 | updatePost, 12 | setPostEditor, 13 | updatePostEditor, 14 | } 15 | 16 | const mapStateToProps = (state) => ({ 17 | cpost: state.cpost, 18 | editor: state.cpost && state.cpost.editor || {}, 19 | }) 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/ArtworkEditor').default) 22 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/ArtworkListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { retrieveOwnerPublishedPosts } from '../modules/post' 3 | import { updateUser, retrieveSomeUsers } from '../modules/user' 4 | 5 | const mapDispatchToProps = { 6 | retrievePosts: retrieveOwnerPublishedPosts, 7 | updateUser, 8 | retrieveSomeUsers, 9 | } 10 | 11 | const mapStateToProps = (state) => ({ 12 | postStatus: state.cpost && state.cpost.retrieve, 13 | postDb: state.cpost && state.cpost.db, 14 | cpost: state.cpost, 15 | 16 | cuser: state.cuser, 17 | }) 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/ArtworkList').default) 20 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/HomeLayoutContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import Layout from '../components/HomeLayout' 3 | import { doLogout } from 'store/lib/user' 4 | 5 | const mapDispatchToProps = { 6 | doLogout, 7 | } 8 | 9 | const mapStateToProps = (state) => ({ 10 | login: state.login, 11 | }) 12 | 13 | export default connect(mapStateToProps, mapDispatchToProps)(Layout) 14 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/LoginContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { loginByNicename } from '../modules/user' 3 | import { goBack, push, replace } from 'react-router-redux' 4 | 5 | const mapDispatchToProps = { 6 | handleLogin: loginByNicename, 7 | 8 | goBack, 9 | push, 10 | replace, 11 | } 12 | 13 | const mapStateToProps = (state) => ({ 14 | cuser: state.cuser, 15 | nextPathname: state.nextPathname 16 | }) 17 | 18 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/LoginPage').default) 19 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MineContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { doLogout } from '../modules/user' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | push, 8 | replace, 9 | 10 | doLogout, 11 | } 12 | 13 | const mapStateToProps = (state) => ({ 14 | cuser: state.cuser, 15 | }) 16 | 17 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Mine').default) 18 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MyAddressContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { updateUser, setUserEditor } from '../modules/user' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | 8 | updateUser, 9 | setUserEditor, 10 | } 11 | 12 | const mapStateToProps = (state) => ({ 13 | cuser: state.cuser, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyAddress').default) 17 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MyArtListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { retrieveMyPosts, removePost, updatePost, refundPost, retrieveSomePosts } from '../modules/post' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | push, 8 | replace, 9 | 10 | retrievePosts: retrieveMyPosts, 11 | removePost, 12 | updatePost, 13 | refundPost, 14 | retrieveSomePosts, 15 | } 16 | 17 | const mapStateToProps = (state) => ({ 18 | pageName: '我发布的宝贝', 19 | postStatus: state.cpost && state.cpost.retrieveMine, 20 | postDb: state.cpost && state.cpost.db, 21 | refund: state.cpost && state.cpost.refund, 22 | 23 | cuser: state.cuser, 24 | }) 25 | 26 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyArtList').default) 27 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MyLuckContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { retrieveMyLuckyOrders } from '../modules/order' 4 | import { retrieveSomePosts } from '../modules/post' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | retrieveMyOrders: retrieveMyLuckyOrders, 9 | retrieveSomePosts, 10 | } 11 | 12 | const mapStateToProps = (state) => ({ 13 | pageName: '我的幸运记录', 14 | 15 | postDb: state.cpost.db || {}, 16 | orderDb: state.corder.db || {}, 17 | orderStatus: state.corder.retrieveMyLucky || {}, 18 | }) 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyOrderList').default) 21 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MyRefundListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { retrieveMyRefunds } from '../modules/refund' 4 | import { retrieveSomePosts } from '../modules/post' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | retrieveRefunds: retrieveMyRefunds, 9 | retrieveSomePosts, 10 | } 11 | 12 | const mapStateToProps = (state) => ({ 13 | pageName: '我的退款记录', 14 | 15 | postDb: state.cpost.db || {}, 16 | refundDb: state.crefund.db || {}, 17 | refundStatus: state.crefund.retrieveMine || {}, 18 | }) 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyRefundList').default) 21 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/MyVoteContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { retrieveMyOrders } from '../modules/order' 4 | import { retrieveSomePosts } from '../modules/post' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | retrieveMyOrders: retrieveMyOrders, 9 | retrieveSomePosts, 10 | } 11 | 12 | const mapStateToProps = (state) => ({ 13 | pageName: '我的夺宝记录', 14 | 15 | postDb: state.cpost.db || {}, 16 | orderDb: state.corder.db || {}, 17 | orderStatus: state.corder.retrieveMine || {}, 18 | }) 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyOrderList').default) 21 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/QrcodeContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | 4 | const mapDispatchToProps = { 5 | goBack, 6 | } 7 | 8 | const mapStateToProps = (state) => ({ 9 | cuser: state.cuser, 10 | }) 11 | 12 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Qrcode').default) 13 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/RootArtListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { retrieveRootPosts, removePost, updatePost, refundPost, retrieveSomePosts } from '../modules/post' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | push, 8 | replace, 9 | 10 | retrievePosts: retrieveRootPosts, 11 | removePost, 12 | updatePost, 13 | refundPost, 14 | retrieveSomePosts, 15 | } 16 | 17 | const mapStateToProps = (state) => ({ 18 | pageName: '管理所有宝贝', 19 | postStatus: state.cpost && state.cpost.retrieveRoot, 20 | postDb: state.cpost && state.cpost.db, 21 | refund: state.cpost && state.cpost.refund, 22 | 23 | cuser: state.cuser, 24 | }) 25 | 26 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyArtList').default) 27 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/RootRefundListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | import { retrieveRootRefunds } from '../modules/refund' 4 | import { retrieveSomePosts } from '../modules/post' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | retrieveRefunds: retrieveRootRefunds, 9 | retrieveSomePosts, 10 | } 11 | 12 | const mapStateToProps = (state) => ({ 13 | pageName: '我的退款记录', 14 | 15 | postDb: state.cpost.db || {}, 16 | refundDb: state.crefund.db || {}, 17 | refundStatus: state.crefund.retrieveMine || {}, 18 | }) 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/MyRefundList').default) 21 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/SandboxContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push } from 'react-router-redux' 3 | import { createOrder } from '../modules/order' 4 | import { jsPayOrders, acquireSandboxKey } from 'routes/Pay/modules/pay' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | push, 9 | 10 | createOrder, 11 | 12 | jsPayOrders, 13 | acquireSandboxKey, 14 | } 15 | 16 | const mapStateToProps = (state) => ({ 17 | corder: state.corder, 18 | pay: state.pay, 19 | }) 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Sandbox').default) 22 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/SearchContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { searchPosts } from '../modules/post' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | push, 8 | replace, 9 | 10 | retrievePosts: searchPosts, 11 | } 12 | 13 | const mapStateToProps = (state) => ({ 14 | pageName: '查找', 15 | postStatus: state.cpost && state.cpost.search, 16 | postDb: state.cpost && state.cpost.db, 17 | 18 | cuser: state.cuser, 19 | }) 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Search').default) 22 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/ShopCenterContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { retrieveUsersByIds } from '../modules/user' 4 | import { retrieveFavorPosts } from '../modules/post' 5 | 6 | const mapDispatchToProps = { 7 | goBack, 8 | push, 9 | replace, 10 | 11 | retrieveItems: retrieveUsersByIds, 12 | 13 | retrieveFavorPosts, 14 | } 15 | 16 | const mapStateToProps = (state) => ({ 17 | itemStatus: state.cuser && state.cuser.retrieve || {}, 18 | itemDb: state.cuser && state.cuser.db || {}, 19 | cuser: state.cuser, 20 | 21 | postStatus: state.cpost && state.cpost.retrieveFavor, 22 | postDb: state.cpost && state.cpost.db, 23 | cpost: state.cpost, 24 | 25 | }) 26 | 27 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/ShopCenter').default) 28 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/UserListContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, push, replace } from 'react-router-redux' 3 | import { searchUsers, retrieveSomeUsers, updateUser } from '../modules/user' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | push, 8 | replace, 9 | 10 | retrieveItems: searchUsers, 11 | retrieveSomeUsers, 12 | updateUser, 13 | } 14 | 15 | const mapStateToProps = (state) => ({ 16 | pageName: '查找', 17 | itemStatus: state.cuser && state.cuser.retrieve || {}, 18 | itemDb: state.cuser && state.cuser.db || {}, 19 | 20 | cuser: state.cuser, 21 | }) 22 | 23 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/UserList').default) 24 | -------------------------------------------------------------------------------- /src/routes/Coin/containers/WeinreContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | 4 | const mapDispatchToProps = { 5 | goBack, 6 | } 7 | 8 | const mapStateToProps = (state) => ({ 9 | 10 | }) 11 | 12 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/Weinre').default) 13 | -------------------------------------------------------------------------------- /src/routes/Coin/modules/qrcode.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('COIN:reducer:qrcode'); 2 | import { createActions } from 'redux-actions'; 3 | const { retrieveQrcodeRequest, retrieveQrcodeSuccess, retrieveQrcodeFailure } = createActions('RETRIEVE_QRCODE_REQUEST', 'RETRIEVE_QRCODE_SUCCESS', 'RETRIEVE_QRCODE_FAILURE'); 4 | 5 | export const retrieveQrcode = (info) => { 6 | var url = "/wcapis/v1/get_temp_qrcode"; 7 | return (dispatch) => { 8 | dispatch(retrieveQrcodeRequest()) 9 | return fetch(url, { 10 | credentials: 'include', 11 | method: 'POST', 12 | headers: { 13 | "Content-Type": "application/json; charset=utf-8" 14 | }, 15 | //mode: 'cors', 16 | cache: 'default', 17 | body: JSON.stringify(info), 18 | }) 19 | .then(response => response.json().then(json => ({ json, response})) ) 20 | .then(({ json, response }) => { 21 | //xdebug ("retrieve:", json); 22 | if (!response.ok) { 23 | throw new Error ("fetch() error: "+response.statusText+", ret="+JSON.stringify(json)); 24 | } 25 | if (response.ok && (!json.errcode || json.errcode == 0)) { 26 | dispatch(retrieveQrcodeSuccess(json.qrcode)); 27 | } else { 28 | dispatch(retrieveQrcodeFailure(json)); 29 | } 30 | }) 31 | .catch(error => { 32 | xdebug('error! retrieveQrcodes fetch() fail: '+error.message, error); 33 | dispatch(retrieveQrcodeFailure(error)) 34 | return null; 35 | }); 36 | } 37 | } 38 | 39 | const ACTION_HANDLERS = { 40 | ['RETRIEVE_QRCODE_REQUEST']: (state, action) => ({ ...state, fetching: true, error: null, qrcode: null }), 41 | ['RETRIEVE_QRCODE_SUCCESS']: (state, action) => ({ ...state, fetching: false, error: null, qrcode: action.payload }), 42 | ['RETRIEVE_QRCODE_FAILURE']: (state, action) => ({ ...state, fetching: false, error: action.payload, qrcode: null }), 43 | } 44 | 45 | const initialState = { fetching: false, qrcode: null, error: null } 46 | export default function Reducer(state = initialState, action) { 47 | const handler = ACTION_HANDLERS[action.type] 48 | //console.debug ("action:", action); 49 | return handler ? handler(state, action) : state 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/routes/Coin/modules/utils.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('COIN:reducer:post'); 2 | 3 | export const utilPostStatusToString = (status) => { 4 | var statusString = ''; 5 | switch (status) { 6 | case 0: statusString = '草案'; break; 7 | case 1: statusString = '等待审核'; break; 8 | case 2: statusString = '已发布'; break; 9 | case 3: statusString = '审核失败'; break; 10 | case 4: statusString = '已下架'; break; 11 | case 5: statusString = '等待中签号'; break; 12 | case 6: statusString = '已结束'; break; 13 | default: statusString = '未知'; break; 14 | } 15 | return statusString; 16 | } 17 | -------------------------------------------------------------------------------- /src/routes/Coin/shopRoutes.js: -------------------------------------------------------------------------------- 1 | import { injectReducer } from 'store/reducers'; 2 | var xdebug = window.myDebug('COIN:shopRoutes'); 3 | import jssdkReducer, { fetchSign, wxConfig } from 'store/lib/jssdk'; 4 | 5 | const signAndConfigJssdk = (url) => { 6 | return (dispatch, getState) => { 7 | return dispatch (fetchSign (url)).then ((pkg)=>{ 8 | if (pkg) { 9 | return dispatch(wxConfig(pkg)).then (retobj => { 10 | return retobj; 11 | }); 12 | } else { 13 | xdebug ("error! signAndConfigJssdk: fetchSign none!"); 14 | return null; 15 | } 16 | }) 17 | } 18 | } 19 | 20 | 21 | export default (store) => ({ 22 | path: 'shop', 23 | indexRoute: { 24 | onEnter: (nextState, replace) => { 25 | var xState = store.getState(); 26 | var loginUser = xState && xState.cuser && xState.cuser.user; 27 | if (loginUser && loginUser.visited && loginUser.visited.length == 1) { 28 | var nextUrl = '/coin/shop/'+loginUser.visited[0]; 29 | xdebug ("has only visit one shop, enter it!"); 30 | replace (nextUrl); 31 | } 32 | }, 33 | getComponent(nextState, cb) { 34 | require.ensure([], (require) => { 35 | cb(null, require('./containers/ShopCenterContainer').default) 36 | }) 37 | } 38 | }, 39 | childRoutes: [ 40 | { 41 | path: ':userId', 42 | getComponent(nextState, cb) { 43 | require.ensure([], (require) => { 44 | cb(null, require('./containers/ArtworkListContainer').default) 45 | }) 46 | } 47 | }, 48 | { 49 | path: ':userId/:postId', 50 | /*onEnter: (nextState, replace) => { 51 | xdebug ("shop/:userId/:postId onEnter, inject jssdk! "+location.href); 52 | injectReducer(store, { key:'jssdk', reducer:jssdkReducer }) 53 | store.dispatch(signAndConfigJssdk (location.href)); 54 | }, 55 | onChange: (prevState, nextState, replace) => { 56 | xdebug ("shop/:userId/:postId onChange, inject jssdk! "+location.href); 57 | injectReducer(store, { key:'jssdk', reducer:jssdkReducer }) 58 | store.dispatch(signAndConfigJssdk (location.href)); 59 | },*/ 60 | getComponent(nextState, cb) { 61 | require.ensure([], (require) => { 62 | cb(null, require('./containers/ArtworkContainer').default) 63 | }) 64 | } 65 | }, 66 | { 67 | path: ':userId/:postId/detail', 68 | getComponent(nextState, cb) { 69 | require.ensure([], (require) => { 70 | cb(null, require('./containers/ArtworkDetailContainer').default) 71 | }) 72 | } 73 | }, 74 | ] 75 | }) 76 | 77 | -------------------------------------------------------------------------------- /src/routes/Pay/components/OnlyWechat.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('Pay:OnlyWechat'); 2 | import React, { Component, PropTypes } from 'react' 3 | import { IndexLink, Link } from 'react-router' 4 | import _ from 'lodash'; 5 | import config from '../config'; 6 | 7 | export default class Page extends Component { 8 | static propTypes = { 9 | goBack: PropTypes.func.isRequired, 10 | } 11 | 12 | constructor (props) { 13 | super (props); 14 | this.state = { 15 | ip:'192.168.2.109', 16 | settled:false, 17 | } 18 | } 19 | 20 | componentDidMount () { 21 | wx && wx.hideAllNonBaseMenuItem(); 22 | } 23 | 24 | render () { 25 | var {goBack} = this.props; 26 | var qrcode = config.qrcode; 27 | return ( 28 |
    29 | 35 | 36 |
    37 |
    38 | 39 |
    40 |
    请用微信扫描关注公众号
    41 |
    42 |
    43 | Responsive image 44 |
    45 | 46 |
    47 |
    48 | 49 |
    50 | ); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/routes/Pay/components/styles.scss: -------------------------------------------------------------------------------- 1 | .slideWrap { 2 | position: relative; 3 | background-color: #d8d8d8; 4 | min-height: 200px; 5 | width: 100%; 6 | padding-bottom: 100%; 7 | } 8 | 9 | .slideImageWrap { 10 | position: absolute; 11 | display: inline-block; 12 | background-color: #bbb; 13 | overflow: hidden; 14 | top: 0; 15 | bottom: 0; 16 | left: 0; 17 | right: 0; 18 | // z-index: 1; 19 | overflow: hidden; 20 | text-align: center; 21 | vertical-align: bottom; 22 | } 23 | 24 | .slideCtrl { 25 | position: absolute; 26 | height: 100%; 27 | cursor: pointer; 28 | // z-index: 1; 29 | background: none; 30 | border: 0; 31 | } 32 | .slideCtrlNext { 33 | right: 0; 34 | width: 25%; 35 | } 36 | .slideCtrlPrev { 37 | left: 0; 38 | width: 25%; 39 | } 40 | 41 | :global .ReactModal__Body--open { 42 | overflow: hidden; 43 | } 44 | 45 | .modalOverlay { 46 | position : fixed; 47 | top : 0; 48 | left : 0; 49 | right : 0; 50 | bottom : 0; 51 | background-color : rgba(255, 255, 255, 0.75); 52 | z-index : 2017; 53 | } 54 | 55 | .modalContent { 56 | position: absolute; 57 | top: auto; 58 | left: 40px; 59 | right: 40px; 60 | bottom: 5px; 61 | background-color: papayawhip; 62 | } 63 | 64 | .w-number { 65 | padding: 0 24px; 66 | position: relative; 67 | display: inline-block; 68 | vertical-align: middle; 69 | } 70 | .w-number-btn { 71 | box-sizing: content-box; 72 | font-size: 22px; 73 | width: 30px; 74 | height: 30px; 75 | line-height: 28px; 76 | text-decoration: none; 77 | position: absolute; 78 | right: 0; 79 | text-align: center; 80 | overflow: hidden; 81 | background: #fff; 82 | color: #999; 83 | border: 1px solid #e0e0e0; 84 | } 85 | .w-number-btn-plus { 86 | right: 0; 87 | top: 0; 88 | } 89 | .w-number-btn-minus { 90 | right: auto; 91 | left: 0; 92 | top: 0; 93 | } 94 | .w-number-input { 95 | box-sizing: content-box; 96 | font-size: 16px; 97 | text-align: center; 98 | width: 74px; 99 | border: 1px solid #e0e0e0; 100 | height: 26px; 101 | padding: 2px 4px; 102 | outline: none; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/routes/Pay/config.js: -------------------------------------------------------------------------------- 1 | export const configZdili = { 2 | qrcode: '/images/qrcode_for_gh_14b79043d0a3_430.jpg', 3 | }; 4 | 5 | export const configQingShanSi = { 6 | qrcode: '/images/qrcode_for_gh_9e62dd855eff_430.jpg', 7 | }; 8 | 9 | export default configZdili; 10 | -------------------------------------------------------------------------------- /src/routes/Pay/containers/JsPayContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack, replace, go } from 'react-router-redux' 3 | import { invokePayment, checkOrderStatus, retrieveOrder } from '../modules/pay' 4 | 5 | const mapDispatchToProps = { 6 | goBack, 7 | go, 8 | replace, 9 | 10 | invokePayment, 11 | checkOrderStatus, 12 | retrieveOrder, 13 | } 14 | 15 | const mapStateToProps = (state) => ({ 16 | cuser: state.cuser, 17 | pay: state.pay, 18 | }) 19 | 20 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/JsPay').default) 21 | -------------------------------------------------------------------------------- /src/routes/Pay/containers/OnlyWechatContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { goBack } from 'react-router-redux' 3 | 4 | const mapDispatchToProps = { 5 | goBack, 6 | } 7 | 8 | const mapStateToProps = (state) => ({ 9 | }) 10 | 11 | export default connect(mapStateToProps, mapDispatchToProps)(require('../components/OnlyWechat').default) 12 | -------------------------------------------------------------------------------- /src/routes/Pay/index.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('Pay:routes'); 2 | import { injectReducer } from 'store/reducers'; 3 | import { signAndConfigJssdk } from 'store/lib/jssdk'; 4 | import { loginByCookie } from '../Coin/modules/user'; 5 | import parseUserAgent from 'utils/userAgent'; 6 | 7 | export default (store) => ({ 8 | //path: 'pay', 9 | onEnter: (nextState, replace, callback) => { 10 | injectReducer(store, { key:'cuser', reducer: require('../Coin/modules/user').default }) 11 | injectReducer(store, { key:'jssdk', reducer:require('store/lib/jssdk').default }) 12 | // if authorized, go to dash page, else goto login 13 | store.dispatch(loginByCookie ()).then (ret => { 14 | store.dispatch(signAndConfigJssdk (location.href)); 15 | callback (); 16 | }).catch(error => { 17 | xdebug ("error! loginByCookie fail! error=", error); 18 | callback (); 19 | }); 20 | }, 21 | onChange: (prevState, nextState, replace, callback) => { 22 | var ua = parseUserAgent(); 23 | if (ua && ua.wechat) { 24 | xdebug ("onChange, inject jssdk! "+location.href); 25 | store.dispatch(signAndConfigJssdk (location.href)).then (ret => { 26 | callback (); 27 | }).catch(error => { 28 | xdebug ("error! signAndConfigJssdk in onChange() fail! error=", error); 29 | callback (); 30 | }); 31 | } else { 32 | callback (); 33 | } 34 | }, 35 | childRoutes: [ 36 | { 37 | path: 'pay', 38 | childRoutes: [ 39 | { 40 | path: 'jspay', 41 | onEnter: (nextState, replace) => { 42 | var ua = parseUserAgent(); 43 | if (!(ua && ua.wechat)) { 44 | replace ('/pay/onlywechat'); 45 | } 46 | }, 47 | getComponent(nextState, cb) { 48 | require.ensure([], (require) => { 49 | injectReducer(store, { key:'pay', reducer:require('./modules/pay').default }) 50 | cb(null, require('./containers/JsPayContainer').default) 51 | }) 52 | } 53 | }, 54 | { 55 | path: 'onlywechat', 56 | getComponent(nextState, cb) { 57 | require.ensure([], (require) => { 58 | cb(null, require('./containers/OnlyWechatContainer').default) 59 | }) 60 | } 61 | }, 62 | ] 63 | }, 64 | { 65 | path: 'paytest', 66 | childRoutes: [ 67 | { 68 | path: 'jspay', 69 | getComponent(nextState, cb) { 70 | require.ensure([], (require) => { 71 | injectReducer(store, { key:'pay', reducer:require('./modules/pay').default }) 72 | cb(null, require('./containers/JsPayContainer').default) 73 | }) 74 | } 75 | }, 76 | ] 77 | }, 78 | ] 79 | }) 80 | 81 | -------------------------------------------------------------------------------- /src/routes/components/LoginPage.js: -------------------------------------------------------------------------------- 1 | import { IndexLink, Link } from 'react-router' 2 | import React, { Component, PropTypes } from 'react' 3 | import { Field, reduxForm } from 'redux-form' 4 | import classes from './LoginPage.scss' 5 | import LoginForm from 'components/Wordpress/Login'; 6 | 7 | import _debug from 'debug' 8 | const debug = _debug('app:server:LoginPage') 9 | 10 | export default class LoginPage extends Component { 11 | 12 | componentDidMount () { 13 | //debug ("componentDidMount"); 14 | const { location, push, login } = this.props; 15 | var nextPathname = location && location.state && location.state.nextPathname; 16 | console.log ("componentDidMount login:", login, ", nextPathname:", nextPathname); 17 | if( login && login.user && login.user.username ) { 18 | console.log ("already login! go to previous page!"); 19 | push(nextPathname || '/'); 20 | } 21 | } 22 | 23 | componentWillReceiveProps (nextProps) { 24 | //console.log ("componentWillReceiveProps"); 25 | const { location, push, login } = nextProps; 26 | var nextPathname = location && location.state && location.state.nextPathname; 27 | console.log ("componentWillReceiveProps login:", login, ", nextPathname:", nextPathname); 28 | if( login && login.user && login.user.username ) { 29 | console.log ("already login! go to previous page!"); 30 | push(nextPathname || '/'); 31 | } 32 | } 33 | 34 | render () { 35 | //const { pristine, reset, submitting, handleLogin, login } = this.props; 36 | const { handleLogin, login } = this.props; 37 | var submit = (values) => { 38 | // Do something with the form values 39 | console.log("login form:", values); 40 | handleLogin (values, login.user); 41 | } 42 | 43 | return ( 44 |
    45 | 46 |
    { (login.errcode != 0) && login.msg }
    47 |
    48 | ); 49 | } 50 | } 51 | 52 | LoginPage.propTypes = { 53 | handleLogin: React.PropTypes.func.isRequired, 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/routes/components/LoginPage.scss: -------------------------------------------------------------------------------- 1 | .savedWisdoms { 2 | margin-top: 4em; 3 | color: red; 4 | > ul { 5 | padding: 0; 6 | list-style: none; 7 | font-style: italic; 8 | } 9 | } -------------------------------------------------------------------------------- /src/routes/containers/LoginContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { loginIfNeeded } from 'store/lib/user' 3 | import LoginPage from '../components/LoginPage' 4 | import { push } from 'react-router-redux' 5 | 6 | const mapDispatchToProps = { 7 | handleLogin: loginIfNeeded, 8 | push 9 | } 10 | 11 | const mapStateToProps = (state) => ({ 12 | login: state.login, 13 | nextPathname: state.nextPathname 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(LoginPage) 17 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | /* Note: Instead of using JSX, we recommend using react-router 2 | PlainRoute objects to build route definitions. */ 3 | 4 | // We only need to import the modules necessary for initial render 5 | import CoinRoute from './Coin' 6 | import PayRoute from './Pay' 7 | 8 | export const createRoutes = (store) => ({ 9 | path: '/', 10 | indexRoute: { 11 | onEnter: (nextState, replace) => { 12 | replace ('/coin/'); 13 | } 14 | }, 15 | 16 | childRoutes: [ 17 | CoinRoute(store), 18 | PayRoute(store), 19 | ] 20 | }) 21 | 22 | export default createRoutes 23 | -------------------------------------------------------------------------------- /src/static/MP_verify_xzc12yoaLMDUKzaP.txt: -------------------------------------------------------------------------------- 1 | xzc12yoaLMDUKzaP -------------------------------------------------------------------------------- /src/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/favicon.ico -------------------------------------------------------------------------------- /src/static/humans.txt: -------------------------------------------------------------------------------- 1 | # Check it out: http://humanstxt.org/ 2 | 3 | # TEAM 4 | 5 | -- -- 6 | 7 | # THANKS 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/static/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/1.jpg -------------------------------------------------------------------------------- /src/static/images/art0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art0.jpg -------------------------------------------------------------------------------- /src/static/images/art1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art1.jpg -------------------------------------------------------------------------------- /src/static/images/art2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art2.jpg -------------------------------------------------------------------------------- /src/static/images/art3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art3.jpg -------------------------------------------------------------------------------- /src/static/images/art4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art4.jpg -------------------------------------------------------------------------------- /src/static/images/art5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art5.jpg -------------------------------------------------------------------------------- /src/static/images/art6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art6.jpg -------------------------------------------------------------------------------- /src/static/images/art7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art7.jpg -------------------------------------------------------------------------------- /src/static/images/art8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art8.jpg -------------------------------------------------------------------------------- /src/static/images/art9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/art9.jpg -------------------------------------------------------------------------------- /src/static/images/avatar-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-1.jpg -------------------------------------------------------------------------------- /src/static/images/avatar-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-1.png -------------------------------------------------------------------------------- /src/static/images/avatar-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-2.jpg -------------------------------------------------------------------------------- /src/static/images/avatar-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-3.jpg -------------------------------------------------------------------------------- /src/static/images/avatar-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-4.jpg -------------------------------------------------------------------------------- /src/static/images/avatar-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/avatar-5.jpg -------------------------------------------------------------------------------- /src/static/images/baseinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/baseinfo.png -------------------------------------------------------------------------------- /src/static/images/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/file.png -------------------------------------------------------------------------------- /src/static/images/folder1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/folder1.png -------------------------------------------------------------------------------- /src/static/images/folder2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/folder2.png -------------------------------------------------------------------------------- /src/static/images/folder3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/folder3.png -------------------------------------------------------------------------------- /src/static/images/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/logo128.png -------------------------------------------------------------------------------- /src/static/images/logo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/logo32.png -------------------------------------------------------------------------------- /src/static/images/logo48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/logo48.png -------------------------------------------------------------------------------- /src/static/images/logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/logo64.png -------------------------------------------------------------------------------- /src/static/images/none.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/none.jpg -------------------------------------------------------------------------------- /src/static/images/qrcode_for_gh_14b79043d0a3_430.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/qrcode_for_gh_14b79043d0a3_430.jpg -------------------------------------------------------------------------------- /src/static/images/qrcode_for_gh_9e62dd855eff_430.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/qrcode_for_gh_9e62dd855eff_430.jpg -------------------------------------------------------------------------------- /src/static/images/solar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/solar.jpg -------------------------------------------------------------------------------- /src/static/images/wechat_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/images/wechat_menu.png -------------------------------------------------------------------------------- /src/static/jssdk/assets/widget.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/static/jssdk/assets/widget.zip -------------------------------------------------------------------------------- /src/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/store/createStore.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, compose, createStore } from 'redux' 2 | import { routerMiddleware } from 'react-router-redux' 3 | import thunk from 'redux-thunk' 4 | //import promiseMiddleware from 'redux-promise'; 5 | import makeRootReducer from './reducers' 6 | import logger from 'redux-diff-logger'; 7 | 8 | export default (initialState = {}, history) => { 9 | // ====================================================== 10 | // Middleware Configuration 11 | // ====================================================== 12 | //const middleware = [logger, thunk, routerMiddleware(history)] 13 | //const middleware = [thunk, promiseMiddleware, routerMiddleware(history)] 14 | const middleware = [thunk, routerMiddleware(history)] 15 | 16 | // ====================================================== 17 | // Store Enhancers 18 | // ====================================================== 19 | const enhancers = [] 20 | if (__DEBUG__) { 21 | const devToolsExtension = window.devToolsExtension 22 | if (typeof devToolsExtension === 'function') { 23 | enhancers.push(devToolsExtension()) 24 | } 25 | } 26 | 27 | // ====================================================== 28 | // Store Instantiation and HMR Setup 29 | // ====================================================== 30 | const store = createStore( 31 | makeRootReducer(), 32 | initialState, 33 | compose( 34 | applyMiddleware(...middleware), 35 | ...enhancers 36 | ) 37 | ) 38 | store.asyncReducers = {} 39 | 40 | if (module.hot) { 41 | module.hot.accept('./reducers', () => { 42 | const reducers = require('./reducers').default 43 | store.replaceReducer(reducers(store.asyncReducers)) 44 | }) 45 | } 46 | 47 | return store 48 | } 49 | -------------------------------------------------------------------------------- /src/store/lib/database.js: -------------------------------------------------------------------------------- 1 | import { Schema, arrayOf, normalize } from 'normalizr' 2 | import { camelizeKeys } from 'humps' 3 | import merge from 'lodash/merge' 4 | 5 | export { camelizeKeys } from 'humps'; 6 | export { normalize } from 'normalizr'; 7 | 8 | const userSchema = new Schema('users', { 9 | idAttribute: 'id' 10 | }); 11 | const lockSchema = new Schema('locks', { 12 | idAttribute: 'id' 13 | }); 14 | export const Schemas = { 15 | USER: userSchema, 16 | USER_ARRAY: arrayOf(userSchema), 17 | LOCK: lockSchema, 18 | LOCK_ARRAY: arrayOf(lockSchema) 19 | } 20 | 21 | export const DATABASE_MERGE = 'DATABASE_MERGE' 22 | export const DATABASE_REPLACE = 'DATABASE_REPLACE' 23 | 24 | export function dataMerge (data) { 25 | return { 26 | type: DATABASE_MERGE, 27 | payload: data 28 | } 29 | } 30 | 31 | export function dataReplace (data) { 32 | return { 33 | type: DATABASE_REPLACE, 34 | payload: data 35 | } 36 | } 37 | 38 | export const actions = { 39 | dataMerge, 40 | dataReplace 41 | } 42 | 43 | const DATABASE_ACTION_HANDLERS = { 44 | [DATABASE_MERGE]: (state, action) => { return merge( {}, state, action.payload )}, 45 | [DATABASE_REPLACE]: (state, action) => { return ({ ...state, ...action.payload }) } 46 | } 47 | 48 | const initialState = { } 49 | export default function databaseReducer(state = initialState, action) { 50 | const handler = DATABASE_ACTION_HANDLERS[action.type] 51 | return handler ? handler(state, action) : state 52 | } 53 | -------------------------------------------------------------------------------- /src/store/lib/loading.js: -------------------------------------------------------------------------------- 1 | import { createActions } from 'redux-actions'; 2 | 3 | export const { pageLoadRequest, pageLoadSuccess, pageLoadFailure } = createActions('PAGE_LOAD_REQUEST', 'PAGE_LOAD_SUCCESS', 'PAGE_LOAD_FAILURE'); 4 | 5 | const ACTION_HANDLERS = { 6 | ['PAGE_LOAD_REQUEST']: (state, action) => ({ ...state, pageLoading: true }), 7 | ['PAGE_LOAD_SUCCESS']: (state, action) => ({ ...state, pageLoading: false }), 8 | ['PAGE_LOAD_FAILURE']: (state, action) => ({ ...state, pageLoading: false }), 9 | } 10 | 11 | const initialState = { pageLoading:false } 12 | export default function Reducer(state = initialState, action) { 13 | const handler = ACTION_HANDLERS[action.type] 14 | return handler ? handler(state, action) : state 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/store/lib/tempdata.js: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/merge' 2 | 3 | export const TEMPDATA_SET = 'TEMPDATA_SET' 4 | 5 | export function tempdataSet (data) { 6 | return { 7 | type: TEMPDATA_SET, 8 | payload: data 9 | } 10 | } 11 | 12 | export const actions = { 13 | tempdataSet 14 | } 15 | 16 | const TEMPDATA_ACTION_HANDLERS = { 17 | [TEMPDATA_SET]: (state, action) => ({ data: action.payload }) 18 | //[TEMPDATA_SET]: (state, action) => { return merge( {}, action.payload )} 19 | //[TEMPDATA_SET]: (state, action) => { console.log ("reach here?", action); return ({ ...state, data: action.payload }) } 20 | } 21 | 22 | const initialState = { } 23 | export default function tempdataReducer(state = initialState, action) { 24 | const handler = TEMPDATA_ACTION_HANDLERS[action.type] 25 | /*console.log ("oldState", state, JSON.stringify(state), action, JSON.stringify(action), handler); 26 | var newState = handler ? handler(state, action) : state; 27 | console.log ("newState", newState, JSON.stringify(newState), action, JSON.stringify(action)); 28 | return newState;*/ 29 | return handler ? handler(state, action) : state 30 | } 31 | -------------------------------------------------------------------------------- /src/store/lib/timely.js: -------------------------------------------------------------------------------- 1 | import { createActions } from 'redux-actions'; 2 | 3 | export const { intervalStart, intervalStop } = createActions('INTERVAL_START', 'INTERVAL_STOP'); 4 | 5 | export const startInterval = (key, cb, interval) => { 6 | return (dispatch) => { 7 | var intervalId = setInterval (cb, interval * 1000); 8 | return dispatch(intervalStart({ key, interval, intervalId })) 9 | } 10 | } 11 | 12 | export const stopInterval = (obj) => { 13 | if (!obj || !obj.key) return; 14 | return (dispatch) => { 15 | obj.intervalId && clearInterval (obj.intervalId); 16 | return dispatch(intervalStop(obj)) 17 | } 18 | } 19 | 20 | const ACTION_HANDLERS = { 21 | ['INTERVAL_START']: (state, action) => ({ ...state, [action.payload.key]: action.payload }), 22 | ['INTERVAL_STOP']: (state, action) => { 23 | console.log ("action:", action); 24 | return ({ ...state, [action.payload.key]: null }) 25 | }, 26 | } 27 | 28 | const initialState = { } 29 | export default function Reducer(state = initialState, action) { 30 | const handler = ACTION_HANDLERS[action.type] 31 | //console.log ("timely action:",action); 32 | return handler ? handler(state, action) : state 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/store/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import { routerReducer as router } from 'react-router-redux' 3 | import { reducer as reduxFormReducer } from 'redux-form' 4 | import databaseReducer from './lib/database' 5 | import tempdataReducer from './lib/tempdata' 6 | import timelyReducer from './lib/timely' 7 | import loadingReducer from './lib/loading' 8 | 9 | export const makeRootReducer = (asyncReducers) => { 10 | //console.log ("windsome makeRootReducer", oauth2Reducer); 11 | return combineReducers({ 12 | // Add sync reducers here 13 | router, 14 | loading: loadingReducer, 15 | timely: timelyReducer, 16 | form: reduxFormReducer, 17 | 18 | database: databaseReducer, 19 | //tempdata: tempdataReducer, 20 | ...asyncReducers 21 | }) 22 | } 23 | 24 | export const injectReducer = (store, { key, reducer }) => { 25 | store.asyncReducers[key] = reducer 26 | store.replaceReducer(makeRootReducer(store.asyncReducers)) 27 | } 28 | 29 | export default makeRootReducer 30 | -------------------------------------------------------------------------------- /src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Application Settings Go Here 3 | ------------------------------------ 4 | This file acts as a bundler for all variables/mixins/themes, so they 5 | can easily be swapped out without `core.scss` ever having to know. 6 | 7 | For example: 8 | 9 | @import './variables/colors'; 10 | @import './variables/components'; 11 | @import './themes/default'; 12 | */ 13 | -------------------------------------------------------------------------------- /src/styles/core.scss: -------------------------------------------------------------------------------- 1 | :global { 2 | @import 'base'; 3 | @import '~normalize.css/normalize'; 4 | 5 | // Some best-practice CSS that's useful for most apps 6 | // Just remove them if they're not what you want 7 | html { 8 | box-sizing: border-box; 9 | } 10 | 11 | html, 12 | body { 13 | margin: 0; 14 | padding: 0; 15 | height: 100%; 16 | } 17 | 18 | *, 19 | *:before, 20 | *:after { 21 | box-sizing: inherit; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/styles/global/_bootstrap.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | // Core variables and mixins 8 | @import "bootstrap/variables"; 9 | @import "bootstrap/mixins"; 10 | 11 | // Reset and dependencies 12 | @import "bootstrap/normalize"; 13 | @import "bootstrap/print"; 14 | @import "bootstrap/glyphicons"; 15 | 16 | // Core CSS 17 | @import "bootstrap/scaffolding"; 18 | @import "bootstrap/type"; 19 | @import "bootstrap/code"; 20 | @import "bootstrap/grid"; 21 | @import "bootstrap/tables"; 22 | @import "bootstrap/forms"; 23 | @import "bootstrap/buttons"; 24 | 25 | // Components 26 | @import "bootstrap/component-animations"; 27 | @import "bootstrap/dropdowns"; 28 | @import "bootstrap/button-groups"; 29 | @import "bootstrap/input-groups"; 30 | @import "bootstrap/navs"; 31 | @import "bootstrap/navbar"; 32 | @import "bootstrap/breadcrumbs"; 33 | @import "bootstrap/pagination"; 34 | @import "bootstrap/pager"; 35 | @import "bootstrap/labels"; 36 | @import "bootstrap/badges"; 37 | @import "bootstrap/jumbotron"; 38 | @import "bootstrap/thumbnails"; 39 | @import "bootstrap/alerts"; 40 | @import "bootstrap/progress-bars"; 41 | @import "bootstrap/media"; 42 | @import "bootstrap/list-group"; 43 | @import "bootstrap/panels"; 44 | @import "bootstrap/responsive-embed"; 45 | @import "bootstrap/wells"; 46 | @import "bootstrap/close"; 47 | 48 | // Components w/ JavaScript 49 | @import "bootstrap/modals"; 50 | @import "bootstrap/tooltip"; 51 | @import "bootstrap/popovers"; 52 | @import "bootstrap/carousel"; 53 | 54 | // Utility classes 55 | @import "bootstrap/utilities"; 56 | @import "bootstrap/responsive-utilities"; 57 | -------------------------------------------------------------------------------- /src/styles/global/global.scss: -------------------------------------------------------------------------------- 1 | @function font-path($path) { 2 | @return $path; 3 | } 4 | 5 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 6 | @import "~bootstrap-sass/assets/stylesheets/_bootstrap-sprockets.scss"; 7 | @import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss"; 8 | 9 | $fa-font-path: "~font-awesome/fonts/"; 10 | @import '~font-awesome/scss/font-awesome.scss'; 11 | 12 | // for react-slick, slick-carousel. 13 | @function font-url($url) { 14 | @return './slick/font/'+$url; 15 | } 16 | @function image-url($url) { 17 | @return './slick/'+$url; 18 | } 19 | @import './slick/slick.scss'; 20 | @import './slick/slick-theme.scss'; 21 | 22 | /* 23 | html { 24 | font-size: 1rem; 25 | } 26 | html,body { 27 | height:100%; 28 | } 29 | 30 | .row { 31 | margin:0px; 32 | } 33 | */ -------------------------------------------------------------------------------- /src/styles/global/hoe/hoe-overlay-effect.css: -------------------------------------------------------------------------------- 1 | /*################ Theme Header With Content Overlay Effect Start #####################*/ 2 | #hoe-header[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="fixed"] { 3 | margin-left: 0px; 4 | padding-right: 0px; 5 | } 6 | #hoe-header[data-hoe-lpanel-effect="overlay"].hoe-minimized-lpanel .hoe-right-header[data-hoe-position-type="fixed"] { 7 | margin-left: 50px; 8 | padding-right: 50px; 9 | } 10 | #hoe-header[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="relative"] { 11 | margin-left: 0px; 12 | } 13 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="relative"] { 14 | margin-left: 50px; 15 | } 16 | #hoe-header[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 17 | margin-left: 230px; 18 | 19 | } 20 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 21 | margin-left: 0px; 22 | } 23 | #hoe-header[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 24 | margin-left: 230px; 25 | } 26 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="overlay"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 27 | margin-left: 0px; 28 | } 29 | 30 | 31 | #hoeapp-container[data-hoe-lpanel-effect="overlay"] #main-content{ 32 | margin-left: 50px; 33 | } 34 | #hoeapp-container[data-hoe-lpanel-effect="overlay"].hoe-minimized-lpanel #main-content{ 35 | margin-left: 50px; 36 | } 37 | /*################ Theme Header With Content Overlay Effect Close #####################*/ -------------------------------------------------------------------------------- /src/styles/global/hoe/hoe-push-effect.css: -------------------------------------------------------------------------------- 1 | /*################ Theme Header With Content Push Effect Start #####################*/ 2 | #hoe-header[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="fixed"] { 3 | margin-left: 230px; 4 | margin-right: -230px; 5 | padding-right:0px 6 | } 7 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="fixed"] { 8 | margin-left: 50px; 9 | margin-right: 0px; 10 | padding-right:50px 11 | } 12 | #hoe-header[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="relative"] { 13 | margin-left: 230px; 14 | margin-right: -230px; 15 | } 16 | #hoe-header[data-hoe-lpanel-effect="push"].hoe-minimized-lpanel .hoe-right-header[data-hoe-position-type="relative"] { 17 | margin-left: 50px; 18 | margin-right: 0px; 19 | } 20 | #hoe-header[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 21 | margin-left: 0px; 22 | } 23 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 24 | margin-left: 0px; 25 | } 26 | #hoe-header[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 27 | margin-left: 0px; 28 | } 29 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="push"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 30 | margin-left: 0px; 31 | } 32 | 33 | #hoeapp-container[data-hoe-lpanel-effect="push"] #main-content{ 34 | margin-left: 230px; 35 | margin-right: -230px; 36 | } 37 | #hoeapp-container[data-hoe-lpanel-effect="push"].hoe-minimized-lpanel #main-content{ 38 | margin-left: 50px; 39 | margin-right: 0px; 40 | } 41 | 42 | /*################ Theme Header With Content Push Effect Close #####################*/ -------------------------------------------------------------------------------- /src/styles/global/hoe/hoe-shrink-effect.css: -------------------------------------------------------------------------------- 1 | /*################ Theme Header With shrink Effect Start #####################*/ 2 | #hoe-header[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="relative"] { 3 | margin-left: 230px; 4 | padding-right: 0; 5 | } 6 | #hoe-header[data-hoe-lpanel-effect="shrink"].hoe-minimized-lpanel .hoe-right-header[data-hoe-position-type="relative"] { 7 | margin-left: 50px; 8 | padding-right: 0; 9 | } 10 | #hoe-header[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="fixed"] { 11 | padding-right: 230px; 12 | margin-left: 230px; 13 | } 14 | #hoe-header[data-hoe-lpanel-effect="shrink"].hoe-minimized-lpanel .hoe-right-header[data-hoe-position-type="fixed"] { 15 | padding-right: 50px; 16 | margin-left: 50px; 17 | } 18 | #hoe-header[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 19 | margin-left: 0px; 20 | } 21 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="relative"] .hoe-sidebar-toggle a:after { 22 | margin-left: 0px; 23 | } 24 | #hoe-header[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 25 | left: 0px; 26 | } 27 | #hoe-header.hoe-minimized-lpanel[data-hoe-lpanel-effect="shrink"] .hoe-right-header[data-hoe-position-type="fixed"] .hoe-sidebar-toggle a:after { 28 | left: 0px; 29 | } 30 | 31 | #hoeapp-container[data-hoe-lpanel-effect="shrink"] #main-content{ 32 | margin-left: 230px; 33 | } 34 | #hoeapp-container[data-hoe-lpanel-effect="shrink"].hoe-minimized-lpanel #main-content{ 35 | margin-left: 50px; 36 | } 37 | /*################ Theme Header With shrink Effect Close #####################*/ -------------------------------------------------------------------------------- /src/styles/global/images/bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg1.png -------------------------------------------------------------------------------- /src/styles/global/images/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg2.png -------------------------------------------------------------------------------- /src/styles/global/images/bg3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg3.png -------------------------------------------------------------------------------- /src/styles/global/images/bg4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg4.png -------------------------------------------------------------------------------- /src/styles/global/images/bg5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg5.png -------------------------------------------------------------------------------- /src/styles/global/images/bg6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg6.png -------------------------------------------------------------------------------- /src/styles/global/images/bg7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg7.png -------------------------------------------------------------------------------- /src/styles/global/images/bg8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg8.png -------------------------------------------------------------------------------- /src/styles/global/images/bg9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/images/bg9.png -------------------------------------------------------------------------------- /src/styles/global/slick/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/slick/ajax-loader.gif -------------------------------------------------------------------------------- /src/styles/global/slick/fonts/slick.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/slick/fonts/slick.eot -------------------------------------------------------------------------------- /src/styles/global/slick/fonts/slick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by Fontastic.me 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/styles/global/slick/fonts/slick.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/slick/fonts/slick.ttf -------------------------------------------------------------------------------- /src/styles/global/slick/fonts/slick.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/src/styles/global/slick/fonts/slick.woff -------------------------------------------------------------------------------- /src/styles/global/slick/slick.scss: -------------------------------------------------------------------------------- 1 | /* Slider */ 2 | 3 | .slick-slider { 4 | position: relative; 5 | display: block; 6 | box-sizing: border-box; 7 | -webkit-touch-callout: none; 8 | -webkit-user-select: none; 9 | -khtml-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | -ms-touch-action: pan-y; 14 | touch-action: pan-y; 15 | -webkit-tap-highlight-color: transparent; 16 | } 17 | .slick-list { 18 | position: relative; 19 | overflow: hidden; 20 | display: block; 21 | margin: 0; 22 | padding: 0; 23 | 24 | &:focus { 25 | outline: none; 26 | } 27 | 28 | &.dragging { 29 | cursor: pointer; 30 | cursor: hand; 31 | } 32 | } 33 | .slick-slider .slick-track, 34 | .slick-slider .slick-list { 35 | -webkit-transform: translate3d(0, 0, 0); 36 | -moz-transform: translate3d(0, 0, 0); 37 | -ms-transform: translate3d(0, 0, 0); 38 | -o-transform: translate3d(0, 0, 0); 39 | transform: translate3d(0, 0, 0); 40 | } 41 | 42 | .slick-track { 43 | position: relative; 44 | left: 0; 45 | top: 0; 46 | display: block; 47 | 48 | &:before, 49 | &:after { 50 | content: ""; 51 | display: table; 52 | } 53 | 54 | &:after { 55 | clear: both; 56 | } 57 | 58 | .slick-loading & { 59 | visibility: hidden; 60 | } 61 | } 62 | .slick-slide { 63 | float: left; 64 | height: 100%; 65 | min-height: 1px; 66 | [dir="rtl"] & { 67 | float: right; 68 | } 69 | img { 70 | display: block; 71 | } 72 | &.slick-loading img { 73 | display: none; 74 | } 75 | 76 | display: none; 77 | 78 | &.dragging img { 79 | pointer-events: none; 80 | } 81 | 82 | .slick-initialized & { 83 | display: block; 84 | } 85 | 86 | .slick-loading & { 87 | visibility: hidden; 88 | } 89 | 90 | .slick-vertical & { 91 | display: block; 92 | height: auto; 93 | border: 1px solid transparent; 94 | } 95 | } 96 | .slick-arrow.slick-hidden { 97 | display: none; 98 | } 99 | -------------------------------------------------------------------------------- /src/styles/slick.css: -------------------------------------------------------------------------------- 1 | /* Slider */ 2 | .slick-slider 3 | { 4 | position: relative; 5 | 6 | display: block; 7 | box-sizing: border-box; 8 | 9 | -webkit-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | 14 | -webkit-touch-callout: none; 15 | -khtml-user-select: none; 16 | -ms-touch-action: pan-y; 17 | touch-action: pan-y; 18 | -webkit-tap-highlight-color: transparent; 19 | } 20 | 21 | .slick-list 22 | { 23 | position: relative; 24 | 25 | display: block; 26 | overflow: hidden; 27 | 28 | margin: 0; 29 | padding: 0; 30 | } 31 | .slick-list:focus 32 | { 33 | outline: none; 34 | } 35 | .slick-list.dragging 36 | { 37 | cursor: pointer; 38 | cursor: hand; 39 | } 40 | 41 | .slick-slider .slick-track, 42 | .slick-slider .slick-list 43 | { 44 | -webkit-transform: translate3d(0, 0, 0); 45 | -moz-transform: translate3d(0, 0, 0); 46 | -ms-transform: translate3d(0, 0, 0); 47 | -o-transform: translate3d(0, 0, 0); 48 | transform: translate3d(0, 0, 0); 49 | } 50 | 51 | .slick-track 52 | { 53 | position: relative; 54 | top: 0; 55 | left: 0; 56 | 57 | display: block; 58 | } 59 | .slick-track:before, 60 | .slick-track:after 61 | { 62 | display: table; 63 | 64 | content: ''; 65 | } 66 | .slick-track:after 67 | { 68 | clear: both; 69 | } 70 | .slick-loading .slick-track 71 | { 72 | visibility: hidden; 73 | } 74 | 75 | .slick-slide 76 | { 77 | display: none; 78 | float: left; 79 | 80 | height: 100%; 81 | min-height: 1px; 82 | } 83 | [dir='rtl'] .slick-slide 84 | { 85 | float: right; 86 | } 87 | .slick-slide img 88 | { 89 | display: block; 90 | } 91 | .slick-slide.slick-loading img 92 | { 93 | display: none; 94 | } 95 | .slick-slide.dragging img 96 | { 97 | pointer-events: none; 98 | } 99 | .slick-initialized .slick-slide 100 | { 101 | display: block; 102 | } 103 | .slick-loading .slick-slide 104 | { 105 | visibility: hidden; 106 | } 107 | .slick-vertical .slick-slide 108 | { 109 | display: block; 110 | 111 | height: auto; 112 | 113 | border: 1px solid transparent; 114 | } 115 | .slick-arrow.slick-hidden { 116 | display: none; 117 | } 118 | -------------------------------------------------------------------------------- /src/utils/dataURL2Blob.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('Utils:dataURL2Blob'); 2 | 3 | export const dataURL2Blob = (dataURL) => { 4 | var dataArray = dataURL.split(','); 5 | 6 | // convert base64 to raw binary data held in a string 7 | // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this 8 | var byteString = atob(dataArray[1]); 9 | 10 | // separate out the mime component 11 | var mimeString = dataArray[0].split(':')[1].split(';')[0]; 12 | 13 | // write the bytes of the string to an ArrayBuffer 14 | var ab = new ArrayBuffer(byteString.length); 15 | var ia = new Uint8Array(ab); 16 | for (var i = 0; i < byteString.length; i++) { 17 | ia[i] = byteString.charCodeAt(i); 18 | } 19 | 20 | //Old Code 21 | //write the ArrayBuffer to a blob, and you're done 22 | //var bb = new BlobBuilder(); 23 | //bb.append(ab); 24 | //return bb.getBlob(mimeString); 25 | 26 | //New Code 27 | return new Blob([ab], {type: mimeString}); 28 | } 29 | 30 | export default dataURL2Blob; 31 | -------------------------------------------------------------------------------- /src/utils/urlParams.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('Utils:urlParams'); 2 | 3 | export const urlParams = (queryString) => { 4 | /* 5 | see: https://developer.mozilla.org/en-US/docs/Web/API/Location 6 | Location.search 7 | eg: ?a=1&b=2&c=3 8 | */ 9 | var qsObj = {}; 10 | var qs = queryString.substring(1); 11 | var qsArr = qs && qs.split('&'); 12 | for (var i = 0; i < qsArr.length; i++) { 13 | var arr2 = qsArr[i].split('='); 14 | var name = arr2[0]; 15 | qsObj[name] = arr2[1]; 16 | } 17 | return qsObj; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/utils/userAgent.js: -------------------------------------------------------------------------------- 1 | var xdebug = window.myDebug('utils:userAgent'); 2 | 3 | export const parseUserAgent = () => { 4 | // Wechat UserAgent: "Mozilla/5.0(iphone;CPU iphone OS 5_1_1 like Mac OS X) AppleWebKit/534.46(KHTML,like Geocko) Mobile/9B206 MicroMessenger/5.0" 5 | var Sys = {}; 6 | var ua = navigator.userAgent.toLowerCase(); 7 | var s; 8 | (s = ua.match(/micromessenger\/([\d.]+)/)) ? Sys.wechat = s[1] : 9 | (s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] : 10 | (s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] : 11 | (s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] : 12 | (s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] : 13 | (s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0; 14 | //xdebug ("UserAgent:", navigator.userAgent, ", parsed:", Sys); 15 | return Sys; 16 | } 17 | 18 | export default parseUserAgent; 19 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : "../.eslintrc", 3 | "env" : { 4 | "mocha" : true 5 | }, 6 | "globals" : { 7 | "expect" : false, 8 | "should" : false, 9 | "sinon" : false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/components/Counter/Counter.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { bindActionCreators } from 'redux' 3 | import { Counter } from 'components/Counter/Counter' 4 | import { shallow } from 'enzyme' 5 | 6 | describe('(Component) Counter', () => { 7 | let _props, _spies, _wrapper 8 | 9 | beforeEach(() => { 10 | _spies = {} 11 | _props = { 12 | counter: 5, 13 | ...bindActionCreators({ 14 | doubleAsync: (_spies.doubleAsync = sinon.spy()), 15 | increment: (_spies.increment = sinon.spy()) 16 | }, _spies.dispatch = sinon.spy()) 17 | } 18 | _wrapper = shallow() 19 | }) 20 | 21 | it('Should render as a
    .', () => { 22 | expect(_wrapper.is('div')).to.equal(true) 23 | }) 24 | 25 | it('Should render with an

    that includes Sample Counter text.', () => { 26 | expect(_wrapper.find('h2').text()).to.match(/Counter:/) 27 | }) 28 | 29 | it('Should render props.counter at the end of the sample counter

    .', () => { 30 | expect(_wrapper.find('h2').text()).to.match(/5$/) 31 | _wrapper.setProps({ counter: 8 }) 32 | expect(_wrapper.find('h2').text()).to.match(/8$/) 33 | }) 34 | 35 | it('Should render exactly two buttons.', () => { 36 | expect(_wrapper.find('button')).to.have.length(2) 37 | }) 38 | 39 | describe('An increment button...', () => { 40 | let _button 41 | 42 | beforeEach(() => { 43 | _button = _wrapper.find('button').filterWhere(a => a.text() === 'Increment') 44 | }) 45 | 46 | it('has bootstrap classes', () => { 47 | expect(_button.hasClass('btn btn-default')).to.be.true 48 | }) 49 | 50 | it('Should dispatch a `increment` action when clicked', () => { 51 | _spies.dispatch.should.have.not.been.called 52 | 53 | _button.simulate('click') 54 | 55 | _spies.dispatch.should.have.been.called 56 | _spies.increment.should.have.been.called 57 | }); 58 | }) 59 | 60 | describe('A Double (Async) button...', () => { 61 | let _button 62 | 63 | beforeEach(() => { 64 | _button = _wrapper.find('button').filterWhere(a => a.text() === 'Double (Async)') 65 | }) 66 | 67 | it('has bootstrap classes', () => { 68 | expect(_button.hasClass('btn btn-default')).to.be.true 69 | }) 70 | 71 | it('Should dispatch a `doubleAsync` action when clicked', () => { 72 | _spies.dispatch.should.have.not.been.called 73 | 74 | _button.simulate('click') 75 | 76 | _spies.dispatch.should.have.been.called 77 | _spies.doubleAsync.should.have.been.called 78 | }); 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /tests/components/Header/Header.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Header } from 'components/Header/Header' 3 | import classes from 'components/Header/Header.scss' 4 | import { IndexLink, Link } from 'react-router' 5 | import { shallow } from 'enzyme' 6 | 7 | describe('(Component) Header', () => { 8 | let _wrapper 9 | 10 | beforeEach(() => { 11 | _wrapper = shallow(
    ) 12 | }) 13 | 14 | it('Renders a welcome message', () => { 15 | const welcome = _wrapper.find('h1') 16 | expect(welcome).to.exist 17 | expect(welcome.text()).to.match(/React Redux Starter Kit/) 18 | }) 19 | 20 | describe('Navigation links...', () => { 21 | 22 | it('Should render a Link to Home route', () => { 23 | expect(_wrapper.contains( 24 | 25 | Home 26 | 27 | )).to.be.true 28 | }) 29 | 30 | it('Should render a Link to Counter route', () => { 31 | expect(_wrapper.contains( 32 | 33 | Counter 34 | 35 | )).to.be.true 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /tests/db.sh: -------------------------------------------------------------------------------- 1 | curl 'http://localhost:3000/apis/db/create_user?openid=1&info=a' 2 | echo ' ' 3 | curl 'http://localhost:3000/apis/db/create_user?openid=2&info=b' 4 | echo ' ' 5 | curl 'http://localhost:3000/apis/db/create_user?openid=3&info=c' 6 | echo ' ' 7 | curl 'http://localhost:3000/apis/db/create_user?openid=4&info=d' 8 | echo ' ' 9 | 10 | #curl http://localhost:3000/apis/db/update_user?id=29b1cda2-09a5-46dd-88ad-e3e6ea280fe2&info=f 11 | #curl http://localhost:3000/apis/db/list_user 12 | 13 | echo 'create lock: ' 14 | curl 'http://localhost:3000/apis/db/create_lock?thing=\{"info":"d","owner":"247f215e-31cc-4648-8eb4-b3c913ac67b6"\}' 15 | 16 | echo 'update lock: ' 17 | curl 'http://localhost:3000/apis/db/update_lock?thing=\{"id":"fcacd15d-7fdf-406d-a82e-e50864fa0e9a","info":"d"\}' 18 | 19 | echo 'list all locks ' 20 | curl 'http://localhost:3000/apis/db/list_lock' 21 | 22 | echo 'list lock of user:' 23 | curl 'http://localhost:3000/apis/db/list_lock?where=\{"owner":"247f215e-31cc-4648-8eb4-b3c913ac67b6"\}' 24 | echo ' ' 25 | echo ' ' 26 | -------------------------------------------------------------------------------- /tests/framework.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import React from 'react' 3 | import {mount, render, shallow} from 'enzyme' 4 | 5 | class Fixture extends React.Component { 6 | render () { 7 | return ( 8 |
    9 | 10 | 11 |
    12 | ) 13 | } 14 | } 15 | 16 | describe('(Framework) Karma Plugins', function () { 17 | it('Should expose "expect" globally.', function () { 18 | assert.ok(expect) 19 | }) 20 | 21 | it('Should expose "should" globally.', function () { 22 | assert.ok(should) 23 | }) 24 | 25 | it('Should have chai-as-promised helpers.', function () { 26 | const pass = new Promise(res => res('test')) 27 | const fail = new Promise((res, rej) => rej()) 28 | 29 | return Promise.all([ 30 | expect(pass).to.be.fulfilled, 31 | expect(fail).to.not.be.fulfilled 32 | ]) 33 | }) 34 | 35 | it('should have chai-enzyme working', function() { 36 | let wrapper = shallow() 37 | expect(wrapper.find('#checked')).to.be.checked() 38 | 39 | wrapper = mount() 40 | expect(wrapper.find('#checked')).to.be.checked() 41 | 42 | wrapper = render() 43 | expect(wrapper.find('#checked')).to.be.checked() 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /tests/layouts/CoreLayout.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TestUtils from 'react-addons-test-utils' 3 | import CoreLayout from 'layouts/CoreLayout/CoreLayout' 4 | 5 | function shallowRender (component) { 6 | const renderer = TestUtils.createRenderer() 7 | 8 | renderer.render(component) 9 | return renderer.getRenderOutput() 10 | } 11 | 12 | function shallowRenderWithProps (props = {}) { 13 | return shallowRender() 14 | } 15 | 16 | describe('(Layout) Core', function () { 17 | let _component 18 | let _props 19 | let _child 20 | 21 | beforeEach(function () { 22 | _child =

    Child

    23 | _props = { 24 | children: _child 25 | } 26 | 27 | _component = shallowRenderWithProps(_props) 28 | }) 29 | 30 | it('Should render as a
    .', function () { 31 | expect(_component.type).to.equal('div') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/routes/Counter/components/CounterView.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsome/windpress/1619f7ec01e2cb1e85857702d381304e79713e53/tests/routes/Counter/components/CounterView.spec.js -------------------------------------------------------------------------------- /tests/routes/Counter/index.spec.js: -------------------------------------------------------------------------------- 1 | import CounterRoute from 'routes/Counter' 2 | 3 | describe('(Route) Counter', () => { 4 | let _route 5 | 6 | beforeEach(() => { 7 | _route = CounterRoute({}) 8 | }) 9 | 10 | it('Should return a route configuration object', () => { 11 | expect(typeof(_route)).to.equal('object') 12 | }) 13 | 14 | it('Configuration should contain path `counter`', () => { 15 | expect(_route.path).to.equal('counter') 16 | }) 17 | 18 | }) 19 | -------------------------------------------------------------------------------- /tests/routes/Home/components/HomeView.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { HomeView } from 'routes/Home/components/HomeView' 3 | import { render } from 'enzyme' 4 | 5 | describe('(View) Home', () => { 6 | let _component 7 | 8 | beforeEach(() => { 9 | _component = render() 10 | }) 11 | 12 | it('Renders a welcome message', () => { 13 | const welcome = _component.find('h4') 14 | expect(welcome).to.exist 15 | expect(welcome.text()).to.match(/Welcome!/) 16 | }) 17 | 18 | it('Renders an awesome duck image', () => { 19 | const duck = _component.find('img') 20 | expect(duck).to.exist 21 | expect(duck.attr('alt')).to.match(/This is a duck, because Redux!/) 22 | }) 23 | 24 | }) 25 | -------------------------------------------------------------------------------- /tests/routes/Home/index.spec.js: -------------------------------------------------------------------------------- 1 | import HomeRoute from 'routes/Home' 2 | 3 | describe('(Route) Home', () => { 4 | let _component 5 | 6 | beforeEach(() => { 7 | _component = HomeRoute.component() 8 | }) 9 | 10 | it('Should return a route configuration object', () => { 11 | expect(typeof(HomeRoute)).to.equal('object') 12 | }) 13 | 14 | it('Should define a route component', () => { 15 | expect(_component.type).to.equal('div') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | COUNT=100 2 | if [ $# -gt 0 ];then 3 | COUNT=$1 4 | fi 5 | I=1 6 | while [ $I -le $COUNT ]; do 7 | # ./a.out 121.42.167.160 & 8 | ./a.out 127.0.0.1 & 9 | I=$(($I+1)) 10 | d=$(echo "$RANDOM" | awk '{print 1213.12/$1}') 11 | 12 | sleep $d 13 | done 14 | 15 | echo "done!" 16 | 17 | -------------------------------------------------------------------------------- /tests/server/core/WpRoles.spec.js: -------------------------------------------------------------------------------- 1 | import WpRoles from 'server/lib/core/class_roles' 2 | 3 | describe('WpRoles', () => { 4 | 5 | it('Renders a welcome message', () => { 6 | const welcome = WpRoles.initRolesFromDefault() 7 | expect(welcome).to.exist 8 | expect(welcome.text()).to.match(/Welcome!/) 9 | }) 10 | 11 | it('Renders an awesome duck image', () => { 12 | const duck = WpRoles.initRolesFromDefault('img') 13 | expect(duck).to.exist 14 | expect(duck.attr('alt')).to.match(/This is a duck, because Redux!/) 15 | }) 16 | 17 | }) 18 | -------------------------------------------------------------------------------- /tests/test-bundler.js: -------------------------------------------------------------------------------- 1 | // --------------------------------------- 2 | // Test Environment Setup 3 | // --------------------------------------- 4 | import 'babel-polyfill' 5 | import sinon from 'sinon' 6 | import chai from 'chai' 7 | import sinonChai from 'sinon-chai' 8 | import chaiAsPromised from 'chai-as-promised' 9 | import chaiEnzyme from 'chai-enzyme' 10 | 11 | chai.use(sinonChai) 12 | chai.use(chaiAsPromised) 13 | chai.use(chaiEnzyme()) 14 | 15 | global.chai = chai 16 | global.sinon = sinon 17 | global.expect = chai.expect 18 | global.should = chai.should() 19 | 20 | // --------------------------------------- 21 | // Require Tests 22 | // --------------------------------------- 23 | // for use with karma-webpack-with-fast-source-maps 24 | const __karmaWebpackManifest__ = []; // eslint-disable-line 25 | const inManifest = (path) => ~__karmaWebpackManifest__.indexOf(path) 26 | 27 | // require all `tests/**/*.spec.js` 28 | const testsContext = require.context('./', true, /\.spec\.js$/) 29 | 30 | // only run tests that have changed after the first pass. 31 | const testsToRun = testsContext.keys().filter(inManifest) 32 | ;(testsToRun.length ? testsToRun : testsContext.keys()).forEach(testsContext) 33 | 34 | // require all `src/**/*.js` except for `main.js` (for isparta coverage reporting) 35 | if (__COVERAGE__) { 36 | const componentsContext = require.context('../src/', true, /^((?!main).)*\.js$/) 37 | componentsContext.keys().forEach(componentsContext) 38 | } 39 | --------------------------------------------------------------------------------