Commit 2a04d736 by zhanghaozhe

Merge branch 'master' into study

parents 6fccff3b c3c32002
......@@ -971,6 +971,86 @@
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw=="
},
"@emotion/cache": {
"version": "10.0.9",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz",
"integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==",
"requires": {
"@emotion/sheet": "0.9.2",
"@emotion/stylis": "0.8.3",
"@emotion/utils": "0.11.1",
"@emotion/weak-memoize": "0.2.2"
}
},
"@emotion/core": {
"version": "10.0.10",
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.10.tgz",
"integrity": "sha512-U1aE2cOWUscjc8ZJ3Cx32udOzLeRoJwGxBH93xQD850oQFpwPKZARzdUtdc9SByUOwzSFYxhDhrpXnV34FJmWg==",
"requires": {
"@emotion/cache": "^10.0.9",
"@emotion/css": "^10.0.9",
"@emotion/serialize": "^0.11.6",
"@emotion/sheet": "0.9.2",
"@emotion/utils": "0.11.1"
}
},
"@emotion/css": {
"version": "10.0.12",
"resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.12.tgz",
"integrity": "sha512-esET/v6AwYIw5YVo0e1L/bUik7bIMWyK32BudsC/PE5O1rLK3rjiLCOoMVv5GY6+ssuwWVzooGbz79hPvkkmsw==",
"requires": {
"@emotion/serialize": "^0.11.7",
"@emotion/utils": "0.11.1",
"babel-plugin-emotion": "^10.0.9"
}
},
"@emotion/hash": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz",
"integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA=="
},
"@emotion/memoize": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz",
"integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg=="
},
"@emotion/serialize": {
"version": "0.11.7",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.7.tgz",
"integrity": "sha512-GfzJIMue9eIEPFgBL340hBbjfki11vjYkfmY2LXoCDAFPuG6S+hkOlfinRXLnPVlXnKu7WWp587cVa6/xQriNQ==",
"requires": {
"@emotion/hash": "0.7.1",
"@emotion/memoize": "0.7.1",
"@emotion/unitless": "0.7.3",
"@emotion/utils": "0.11.1",
"csstype": "^2.5.7"
}
},
"@emotion/sheet": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz",
"integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A=="
},
"@emotion/stylis": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz",
"integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q=="
},
"@emotion/unitless": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz",
"integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg=="
},
"@emotion/utils": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz",
"integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg=="
},
"@emotion/weak-memoize": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz",
"integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA=="
},
"@loadable/component": {
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.10.1.tgz",
......@@ -2290,6 +2370,30 @@
"object.assign": "^4.1.0"
}
},
"babel-plugin-emotion": {
"version": "10.0.13",
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.13.tgz",
"integrity": "sha512-w8yukWIYDw2ZUzBo7B9t5jh7wsM4NQWqvuZadW4MhVokgw5wsoBRJ59Sa1hMc3UZiatwb0iBNufmRQZVl77I5Q==",
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@emotion/hash": "0.7.1",
"@emotion/memoize": "0.7.1",
"@emotion/serialize": "^0.11.6",
"babel-plugin-macros": "^2.0.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^1.0.5",
"find-root": "^1.1.0",
"source-map": "^0.5.7"
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
"babel-plugin-import": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.11.0.tgz",
......@@ -2340,6 +2444,11 @@
"resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.1.tgz",
"integrity": "sha512-vzZlo+yEB5YHqI6CRRTDojeT43J3Wf3C/MVkZW5UlbSeIIVUYRKtxaFT2L/VTv9mbIyatCW39+9g/SZolvwRUQ=="
},
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
},
"babel-plugin-syntax-object-rest-spread": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
......@@ -3199,6 +3308,14 @@
}
}
},
"can-promise": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz",
"integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==",
"requires": {
"window-or-global": "^1.0.1"
}
},
"caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
......@@ -3248,6 +3365,11 @@
"supports-color": "^5.3.0"
}
},
"change-emitter": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
"integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
},
"chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
......@@ -4073,6 +4195,11 @@
"cssom": "0.3.x"
}
},
"csstype": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz",
"integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA=="
},
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
......@@ -4357,6 +4484,11 @@
"randombytes": "^2.0.0"
}
},
"dijkstrajs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
"integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
},
"dir-glob": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz",
......@@ -5608,6 +5740,11 @@
}
}
},
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
......@@ -8915,6 +9052,14 @@
"tmpl": "1.0.x"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
"requires": {
"p-defer": "^1.0.0"
}
},
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
......@@ -9963,6 +10108,11 @@
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
"integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg=="
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
......@@ -10189,6 +10339,11 @@
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
},
"pngjs": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
},
"pnp-webpack-plugin": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.2.1.tgz",
......@@ -11178,6 +11333,156 @@
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
},
"qrcode": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.3.tgz",
"integrity": "sha512-SH7V13AcJusH3GT8bMNOGz4w0L+LjcpNOU/NiOgtBhT/5DoWeZE6D5ntMJnJ84AMkoaM4kjJJoHoh9g++8lWFg==",
"requires": {
"can-promise": "0.0.1",
"dijkstrajs": "^1.0.1",
"isarray": "^2.0.1",
"pngjs": "^3.3.0",
"yargs": "^12.0.5"
},
"dependencies": {
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"requires": {
"pump": "^3.0.0"
}
},
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA=="
},
"isarray": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
"integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA=="
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"requires": {
"invert-kv": "^2.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"mem": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
"integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
}
},
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"p-limit": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
"integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"yargs": {
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
"os-locale": "^3.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1 || ^4.0.0",
"yargs-parser": "^11.1.1"
}
},
"yargs-parser": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
......@@ -11658,6 +11963,16 @@
"tiny-warning": "^1.0.0"
}
},
"react-spinners": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.4.tgz",
"integrity": "sha512-jo7BE8prvnZNL7xNrQL16geVXH6LmaWrhvvXnmUwz2MhFO14bhlAdCLuKCwqmUJ37kGphH0C2CJRThwkjfpVzw==",
"requires": {
"@emotion/core": "^10.0.4",
"prop-types": "^15.5.10",
"recompose": "0.27.1 - 0.30.0"
}
},
"react-tween-state": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/react-tween-state/-/react-tween-state-0.1.5.tgz",
......@@ -11728,6 +12043,26 @@
"util.promisify": "^1.0.0"
}
},
"recompose": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
"integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
"requires": {
"@babel/runtime": "^7.0.0",
"change-emitter": "^0.1.2",
"fbjs": "^0.8.1",
"hoist-non-react-statics": "^2.3.1",
"react-lifecycles-compat": "^3.0.2",
"symbol-observable": "^1.0.4"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
}
}
},
"recursive-readdir": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
......@@ -14699,6 +15034,11 @@
"string-width": "^1.0.2 || 2"
}
},
"window-or-global": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz",
"integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4="
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
......
......@@ -61,6 +61,7 @@
"react-mobile-swiper": "^1.1.4",
"react-redux": "^7.0.2",
"react-router-dom": "^5.0.1",
"react-spinners": "^0.5.4",
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-logger": "^3.0.6",
......
import React, { Component } from 'react'
import Routes from './router'
import cookie from 'js-cookie'
import { http, api } from '@/utils';
import { connect } from 'react-redux';
import { setCurrentUser } from '@/store/userAction';
import { connect } from "react-redux";
import { setCurrentUser, startFetchUser } from "@/store/userAction";
import { withRouter } from 'react-router-dom'
import { compose } from 'redux'
import { api, getParam, http } from "@/utils";
import { Toast } from "antd-mobile";
import jsCookie from 'js-cookie'
import { addDays } from 'date-fns'
//拦截ajax请求,返回mock数据
......@@ -24,12 +29,45 @@ class App extends Component {
//平台信息
cookie.set('plat', '5')
this.props.startFetchUser()
http.get(`${api.home}/m/user_info`).then(res => {
this.props.setCurrentUser(this.storeUser(res))
this.props.setCurrentUser(this.transformUser(res))
})
let code = getParam('code')
if (code) {
http.get(`${api['home']}/m/wx_loginInfo/code/${code}`)
.then(res => {
let data = res.data
console.log(res)
if (data.errno == 200) {
if (data.data['is_bind_mobile']) {
window.location.assign(data.data.url)
} else {
let user = this.transformWxUser(res)
let {role, uid, token} = data.data
let expires = {expires: addDays(new Date(), 90)}
jsCookie.set('role', role, expires)
jsCookie.set('uid', uid, expires)
jsCookie.set('token', token, expires)
this.props.receiveUser(user)
}
} else {
Toast.info(data.msg)
}
})
}
}
storeUser = res => {
transformUser = res => {
let payload
if (res.data.code === 200) {
const {
......@@ -64,13 +102,41 @@ class App extends Component {
return payload
}
transformWxUser = res => {
let data = res.data
if (data.errno == 200) {
let {uid, token, avatar_file: avatar, uname: username,} = data.data
return {
hasError: false,
data: {
uid,
token,
avatar,
username
},
msg: data.msg
}
} else {
let {code, msg} = data.data
return {
code,
msg,
hasError: true,
data: {}
}
}
}
render() {
return <Routes/>
}
}
export default connect(
export default compose(
connect(
null,
{setCurrentUser}
{setCurrentUser, startFetchUser}
),
withRouter
)(App)
\ No newline at end of file
......@@ -40,6 +40,7 @@ $font_12: 12px;
*/
$bg_active: #09f;
$bg_0078FF: #0078FF;
$bg_0080FF: #0080FF;
$bg_fff: #fff;
$bg_000: #000;
$bg_f4f4f4: #f4f4f4;
......
import React, { Component } from 'react'
import { ActivityIndicator } from 'antd-mobile'
import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { HashLoader } from "react-spinners";
import './loading.scss'
export default class componentName extends Component {
const container = document.body
class Loading extends Component {
static defaultProps = {
text: '加载中',
fake: 0
}
state = {
isLoading: true
}
componentDidUpdate(prevProps) {
let {isLoading, fake} = this.props
if (!isLoading) {
if(fake){
setTimeout(() => {
this.setState({
isLoading
})
}, fake)
}else {
if(prevProps.isLoading != isLoading){
this.setState({
isLoading
})
}
}
}
}
render() {
return (
const innerLoading =
<div className="loading">
<ActivityIndicator></ActivityIndicator>
<span>加载中</span>
<div className="loading-wrapper">
<HashLoader
css={{
display: 'block',
marginTop: '-100px'
}}
size={50}
color={'#09f'}
/>
<p>{this.props.text}</p>
</div>
)
</div>
return (
this.state.isLoading ? ReactDOM.createPortal(innerLoading, container) : this.props.children
);
}
}
export default Loading;
\ No newline at end of file
.loading{
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.loading-wrapper {
display: flex;
width:100%;
height:44px;
line-height: 44px;
flex-flow: column;
justify-content: center;
align-items: center;
span{
margin-left:5px;
p {
font-size: 14px;
margin-top: 12px;
color: $active;
}
}
}
\ No newline at end of file
......@@ -153,7 +153,8 @@ class Index extends Component {
{/* 直播间预约 */}
{
this.state.islive &&
<LiveRoom isShow={this.state.isShow} colseBox={this.colseBox} roomMess={this.state.roomMess}></LiveRoom>
<LiveRoom isShow={this.state.isShow} colseBox={this.colseBox}
roomMess={this.state.roomMess}></LiveRoom>
}
</div>
......@@ -258,10 +259,13 @@ function ScrollBox(props) {
<div className="item-content">
<h2 className="item-title">{item.live_title}</h2>
<p className="item-teacher">讲师:{item.live_teacher_name}</p>
{
item.is_prepare &&
<p className="item-time">时间:{item.live_start_time}</p>
}
{
item.live_status === 1 &&
<span>已预约</span>
!item.is_prepare &&
<p className='item-btn'>预约</p>
}
</div>
</div>
......
......@@ -185,10 +185,12 @@
text-align: center;
border-radius: 0 20px 20px 0;
}
.no-start{
.no-start {
background-color: $red;
}
.start{
.start {
background-color: $bg_active;
}
......@@ -205,6 +207,18 @@
white-space: nowrap
}
.item-btn {
width: 49px;
height: 20px;
text-align: center;
line-height: 20px;
background: linear-gradient(60deg, $bg_active 0%, $bg_0080FF 100%);
border-radius: 10px;
font-size: 14px;
color: $white;
margin-top: 10px;
}
.item-title {
font-size: 16px;
color: $color_333;
......
......@@ -39,10 +39,13 @@ function OrderList(props) {
onClick={() => { }}
>
<Link to={{
pathname: `/coupons?id=${course_id}`,
pathname: `/coupons`,
search: `?id=${course_id}`,
state: {
from: '/order',
a: 'sldfasldjfsl'
}}}>
{/* <Link to='coupons' query={{id: course_id}} state={{from: '/order'}}> */}
<Flex justify='between'>
<span style={{ color: '#333', fontSize: '15px' }}>优惠券</span>
<span style={{ fontSize: '14px', color: '#999999' }}>{!coupon_desc ? (coupon_num === 0 ? '无' : `${coupon_num}张可用`) : (coupon_desc)}</span>
......@@ -90,10 +93,11 @@ class Order extends Component {
if(res.data.code !== 200) {
return;
}
this.props.history.replace(`/payorder?oid=${res.data.data.oid}`);
sessionStorage.removeItem('orderUseCacheObj');
this.props.history.replace(`/payorder?oid=${res.data.data.order_id}`);
});
}
// 使用余额
// 勾选取消勾选 是否使用余额
useBalance = () => {
let useBalanceFlag = this.state.useBalance;
this.setState({
......@@ -103,22 +107,29 @@ class Order extends Component {
this.cacheObj = {
...this.state
}
sessionStorage.setItem('orderUseCacheObj', JSON.stringify(this.state));
}else{
sessionStorage.removeItem('orderUseCacheObj');
}
// console.log(useBalance);
this.computedMoney(useBalanceFlag);
}
// 勾选取消勾选时:计算金额、优惠金额、优惠券等
computedMoney = (useBalanceFlag) => {
let totalSale = parseFloat(this.cacheObj.total),
userAccount = parseFloat(this.cacheObj.user_account);
const { discount } = this.state;
if (!useBalanceFlag) {
if (totalSale > userAccount) {
this.setState({
offset: userAccount.toFixed(2),
total: (totalSale - userAccount).toFixed(2),
discount: userAccount.toFixed(2),
discount: (userAccount + parseFloat(discount)).toFixed(2),
});
} else {
this.setState({
offset: totalSale.toFixed(2),
total: 0,
discount: totalSale.toFixed(2),
discount: (totalSale + parseFloat(discount)).toFixed(2),
});
}
} else {
......@@ -128,11 +139,25 @@ class Order extends Component {
discount: this.cacheObj.discount,
});
}
this.computedMoney();
};
// 选择优惠券返回时根据是否勾选计算
computedMoneyByCache = () => {
let totalSale = parseFloat(this.cacheObj.total),
userAccount = parseFloat(this.cacheObj.user_account),
discount = parseFloat(this.cacheObj.discount);
if(totalSale > userAccount) {
this.setState({
offset: userAccount.toFixed(2),
total: (totalSale - userAccount).toFixed(2),
discount: (discount + userAccount).toFixed(2),
});
}else{
this.setState({
offset: totalSale.toFixed(2),
total: 0,
discount: totalSale.toFixed(2),
});
}
// 计算金额、优惠金额、优惠券等
computedMoney = () => {
};
// 展示余额抵扣规则
showInfo = () => {
......@@ -146,7 +171,13 @@ class Order extends Component {
return;
}
const { course, total, user_account, user_info, discount } = res.data.data;
this.dataCache = JSON.parse(JSON.stringify(res.data.data));
this.cacheObj = {
perfect: user_info,
orderList: course,
user_account,
total,
discount,
}
this.setState({
perfect: user_info,
orderList: course,
......@@ -154,12 +185,19 @@ class Order extends Component {
total,
discount,
});
if(this.props.history.action === 'PUSH') {
sessionStorage.removeItem('orderUseCacheObj');
}
else{
const cacheObj = sessionStorage.getItem('orderUseCacheObj');
if(cacheObj !== null) {
this.setState({
useBalance: true
});
this.computedMoneyByCache();
}
}
})
// // 获取报名信息 获取课程列表 获取用户账户余额
// Promise.race([http.get(), http.get(), http.get()]).then(res=>{
// });
};
render() {
const {
......@@ -224,17 +262,21 @@ class Order extends Component {
<Flex align='center'>
<span>余额抵扣</span>
<span className="order-balanceprice"> (余额: <i className="order-money">{`${user_account}元`}</i>)</span>
<i className="iconfont iconiconfront-22" onClick={this.showInfo}></i>
<i className="iconfont iconiconfront-22 question-mark" onClick={this.showInfo}></i>
</Flex>
<div>
<Flex>
{
useBalance ? (
<>
<span style={{ color: '#FF2121', fontSize: '15px', marginRight: "6px" }}>{`-${offset}`}</span>
) : null
<i className={`iconfont icondanseshixintubiao-5 balance-used`} onClick={throttle(this.useBalance, 600)}></i>
</>
) : (
<i className='circle-icon' onClick={throttle(this.useBalance, 600)}></i>
)
}
<i className={`iconfont ${useBalance ? 'icondanseshixintubiao-5' : 'iconiconfront-3'} `} onClick={throttle(this.useBalance, 600)}></i>
</div>
</Flex>
</Flex>
</Item>
</List>
......
......@@ -40,6 +40,7 @@
.user-icon {
font-size: 28px;
flex-basis: 40px;
}
.order-cell {
......@@ -47,6 +48,7 @@
font-size: 13px;
color: $color_333;
line-height: 20px;
flex: 1 1 auto;
.name {
margin-bottom: 10px;
......@@ -212,6 +214,7 @@
}
}
.order-balance{
.order-balanceprice {
color: $color_666;
font-size: $font_12;
......@@ -225,6 +228,21 @@
margin: 0;
}
}
.am-list-content .iconiconfront-22.question-mark {
font-size: 24px;
}
.balance-used {
font-size: 24px;
color: #0099FF;
}
.circle-icon {
width: 20px;
height: 20px;
border: 1px solid #BFBFBF;
border-radius: 50%;
margin-right: 2px;
}
}
.am-list-item .am-list-line .am-list-content {
color: $color_333;
......@@ -232,7 +250,6 @@
.order-list {
.am-list-item {
padding-left: 0 !important;
}
}
......
......@@ -6,6 +6,7 @@ import { compose } from 'redux';
import { accountLogin } from '@/store/userAction';
import { connect } from "react-redux";
import { isEmpty } from 'lodash'
import { HeaderBar } from "@/common";
import Header from "../common/Header";
......@@ -16,11 +17,6 @@ import { Toast } from "antd-mobile";
class AccountLogin extends PureComponent {
componentDidMount() {
console.log(this.props.values);
}
render() {
const {
errors,
......@@ -28,6 +24,7 @@ class AccountLogin extends PureComponent {
} = this.props
return (
<div className={'account-login'}>
<HeaderBar arrow={true} title={'登录'}/>
<Header/>
<Form className="login-info">
<FastField
......@@ -75,10 +72,9 @@ const formikConfig = {
props.accountLogin({
username, password
}).then(res => {
console.log(res);
if (!res.hasError) {
let {from} = props.location.state || {from: {pathname: '/'}}
history.push(from.pathname)
history.replace(from.pathname)
} else {
Toast.info(res.msg, 2, null, false)
}
......
......@@ -11,13 +11,6 @@
margin-bottom: 21px;
}
.place {
width: 100%;
height: 39px;
margin-bottom: 33px;
background: #56abff;
}
.verification {
margin-bottom: 21px;
}
......
import React, { Component } from 'react';
import './binding-tel.scss'
import { withFormik, Field, Form } from "formik";
import { validateTel, http, api, getParam } from "@/utils";
import { compose } from "redux";
import { connect } from "react-redux";
import { setCurrentUser } from "@/store/userAction";
import Captcha from '@/common/Captcha'
import ClearableInput from '@common/ClearableInput'
import Button from '../common/Button'
import VeriCodeInput from '../common/veriCodeInput'
import { Toast } from "antd-mobile";
import { isEmpty } from "lodash";
class BindingTel extends Component {
constructor(props) {
super(props);
this.state = {
veriCode: ''
};
state = {
validate: null,
captchaInstance: null
}
handleChange = (val) => {
this.setState({veriCode: val});
getCaptchaInstance = instance => {
this.setState({
captchaInstance: instance
})
}
onVerify = (err, data) => {
if (err) {
console.log(err)
} else {
this.setState({
validate: data.validate
})
}
}
render() {
const {
values,
errors
} = this.props
return (
<div className={'binding-tel'}>
<p className={'title'}>为提高您的账号安全,请绑定手机号</p>
<Form>
<Field
name='tel'
render={({field, form}) => {
return (
<ClearableInput
type={'number'}
{...field}
setFieldValue={form.setFieldValue}
placeholder={'请输入需要绑定的手机号'}
wrapperClass={'tel'}
icon={<i className={'iconfont iconshouji'}
style={{fontSize: '22px', left: '11px'}}
/>}
/>
)
}}
/>
<Field
name='veriCode'
render={({field}) => {
return (
<VeriCodeInput
type={'number'}
{...field}
className={'verification'}
onChange={this.handleChange}
icon={<i className={'iconfont iconduanxin'}
style={{fontSize: '20px', left: '12px'}}
/>}
account={values.tel}
tel={values.tel}
challenge={this.state.validate}
instance={this.state.captchaInstance}
action={'auth'}
/>
<div className="place"/>
<Button className={'complete-btn'}>完成</Button>
)
}}
/>
<Captcha onVerify={this.onVerify} getInstance={this.getCaptchaInstance}/>
<Button className={'complete-btn'} active={values.tel && values.veriCode && isEmpty(errors)}>完成</Button>
</Form>
</div>
);
}
}
export default BindingTel;
\ No newline at end of file
const formikConfig = {
mapPropsToValues() {
return {
tel: '',
veriCode: ''
}
},
validateOnChange: true,
validate(values) {
let errors = {}
if (!validateTel(values.tel)) {
errors.tel = '请输入正确的手机号'
}
if (!values.veriCode) {
errors.veriCode = '请填写验证码'
}
return errors
},
handleSubmit(values, {props}) {
http.post(`${api['passport-api']}/bind_mobile`, {
phone_num: values.tel,
phone_code: values.veriCode,
mkey: getParam('mkey'),
plat: 5
}).then(res => {
const data = res.data
if (data.errno == 200) {
if (data.data['is_set_pwd']) {
props.setCurrentUser({
hasError: false,
data: {
uid: data.data.uid
},
msg: data.data.msg
})
props.history.replace(`/passport/set-password`)
}else {
location.assign(data.data['jump_url'])
}
} else {
Toast.info(data.msg, 2, null, false)
}
/*props.setCurrentUser({
hasError,
data
})*/
})
}
}
export default compose(
connect(
null,
{setCurrentUser}
),
withFormik(formikConfig),
)(BindingTel);
\ No newline at end of file
import React, { PureComponent } from 'react';
import { browser } from "@/utils";
import './loginWays.scss'
class LoginWays extends PureComponent {
handleClick = (index) => {
this.props.onClick(index)
state = {
ways: this.props.loginWays
}
componentDidMount() {
if (!browser.isWeixin) {
this.setState({
ways: this.state.ways.filter(item => item.text !== '微信')
})
}
}
handleClick = text => {
this.props.onClick(text)
}
render() {
......@@ -14,9 +28,9 @@ class LoginWays extends PureComponent {
<div className="bottom-title">其他登录方式</div>
<ul className='login-ways-container'>
{
this.props.loginWays.map((item, index) => {
this.state.ways.map((item, index) => {
return (
<li key={index} onClick={this.handleClick.bind(this, index)}>
<li key={index} onClick={this.handleClick.bind(this, item.text)}>
<img src={item.logo} alt=""/>
<p>{item.text}</p>
</li>
......
......@@ -36,6 +36,10 @@
li {
text-align: center;
a{
display: block;
}
img {
width: 34px;
}
......
......@@ -110,7 +110,7 @@ class VeriCodeInput extends Component {
if (!tel) {
content = '手机号码不能为空'
}
if (validateTel(tel)) {
if (!validateTel(tel)) {
content = '请输入正确格式的手机号码'
}
} else {
......
......@@ -128,7 +128,7 @@ const formikConfig = {
code: values.veriCode
}).then(res => {
if (res.data.errno == 0) {
props.history.push('/passport/set-password')
props.history.push('/passport/set-password', {from: props.location})
} else {
Toast.info(res.data.msg)
}
......
......@@ -8,6 +8,8 @@ import AccountLogin from './accountLogin'
import ForgotPassword from './forgotPassword'
import SetPassword from './setPassword'
import BindingTel from './bindingTel'
import {connect} from "react-redux";
import {compose} from "redux";
import account from './account.png'
import qq from './qq.png'
......@@ -17,6 +19,8 @@ import wechat from './wechat.png'
class Passport extends Component {
redirect_url = location.protocol + '//' + location.hostname
constructor(props) {
super(props);
......@@ -28,15 +32,18 @@ class Passport extends Component {
},
{
logo: wechat,
text: '微信'
text: '微信',
url: 'https://www.baidu.com'
},
{
logo: qq,
text: 'QQ'
text: 'QQ',
url: 'https://www.baidu.com'
},
{
logo: sina,
text: '新浪'
text: '新浪',
url: `http://passport-test.julyedu.com/mob/sinalogin?redirect_url=${this.redirect_url}`
},
]
}
......@@ -52,7 +59,9 @@ class Passport extends Component {
to={{...location, ...{pathname: '/passport/login'}}}
/>
<Route path={match.url + '/login'}
render={props => <Login {...props} loginWays={this.state.loginWays}/>}/>
render={props => {
return <Login {...props} loginWays={this.state.loginWays}/>
}}/>
<Route path={match.url + '/account-login'} component={AccountLogin}/>
<Route path={match.url + '/forgot-password'} component={ForgotPassword}/>
<Route path={match.url + '/set-password'} component={SetPassword}/>
......@@ -64,4 +73,10 @@ class Passport extends Component {
}
export default WithFullSize(Passport)
\ No newline at end of file
export default compose(
connect(
state => ({user: state.user}),
null
),
WithFullSize
)(Passport)
\ No newline at end of file
......@@ -12,21 +12,36 @@ import { connect } from 'react-redux';
import { compose } from 'redux';
import { isEmpty } from 'lodash'
import { Toast } from 'antd-mobile';
import {validateTel} from "@/utils";
import { validateTel } from "@/utils";
class WechatLogin extends Component {
class Login extends Component {
state = {
validate: null,
captchaInstance: null
}
loginWaysClick = index => {
switch (index) {
case 0:
this.props.history.push('/passport/account-login')
loginWaysClick = method => {
const {history, loginWays, location} = this.props
const item = loginWays.find(item => item.text === method)
switch (method) {
case '账号登录':
history.push('/passport/account-login', location.state)
break;
case '微信':
let {from} = location.state || {from: {pathname: '/'}}
const redirectURI = window.location.protocol + '//' + window.location.hostname + from.pathname
// alert(redirectURI)
window.location.assign(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=${encodeURIComponent(redirectURI)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`)
break;
default:
window.location.assign(item.url)
}
}
getCaptchaInstance = instance => {
......@@ -140,4 +155,4 @@ export default compose(
{quickLogin}
),
withFormik(FormikConfig),
)(WechatLogin)
\ No newline at end of file
)(Login)
\ No newline at end of file
......@@ -10,11 +10,15 @@ import { http, api } from "@/utils";
import { Toast } from "antd-mobile";
import { encrypt } from "@/components/passport/encryption";
import { Link } from "react-router-dom";
import { isEmpty } from "lodash";
import { connect } from "react-redux";
class SetPassword extends Component {
render() {
let {values, errors, location} = this.props
let {from} = location.state || {from: {pathname: '/'}}
return (
<>
<HeaderBar arrow={true} title={'设置密码'}/>
......@@ -34,7 +38,8 @@ class SetPassword extends Component {
)
}}
/>
<Button className={'btn-active'}>完成</Button>
<Button className={'btn-active'}
active={values.password && values.agreement && isEmpty(errors)}>完成</Button>
<label htmlFor="agreement" className='user-agreement'>
<Field type='checkbox'
name='agreement'
......@@ -46,7 +51,8 @@ class SetPassword extends Component {
同意<span>《七月在线用户使用协议》</span>
</label>
</Form>
<div className="skip">
<div className="skip"
style={{display: from && from.pathname.includes('forgot-password') ? 'none' : 'block'}}>
<Link replace to='/passport/account-login'>跳过</Link>
</div>
</div>
......@@ -64,28 +70,16 @@ const formikConfig = {
},
handleSubmit: (values, {props}) => {
let key = sessionStorage.getItem('r_type') === 'email' ? 'email' : 'tel'
let encrypted = encrypt(values.password)
http.post(`${api['passport-api']}/up_pass${key === 'tel' && '_by_phone' || ''}`, {
[key]: sessionStorage.getItem(key),
pass: encrypted,
re_pass: encrypted
})
.then(res => {
if (res.data.errno == 0) {
Toast.info('密码设置成功')
setTimeout(function () {
props.history.replace('/passport')
}, 1000)
let {from} = props.location.state || {from: {pathname: '/'}}
if (from.pathname.includes('forgot-password')) {
forgotPasswordReset(values, props);
} else {
Toast.info(res.data.msg, 2, null, false)
bindMobileSetPassword(values, props)
}
})
},
validateOnChange: false,
validate: (values) => {
validate: values => {
let errors = {}
const re = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$/;
if (!re.test(values.password)) {
......@@ -100,6 +94,49 @@ const formikConfig = {
}
}
function forgotPasswordReset(values, props) {
let key = sessionStorage.getItem('r_type') === 'email' ? 'email' : 'tel'
http.post(`${api['passport-api']}/account/up_pass_by_${key === 'email' ? 'email' : 'phone'}`, {
[key]: sessionStorage.getItem(key),
password: encrypt(values.password)
})
.then(res => {
if (res.data.errno == 200) {
Toast.info('密码设置成功')
setTimeout(function () {
props.history.replace('/passport/account-login')
}, 1000)
} else {
Toast.info(res.data.msg, 2, null, false)
}
})
}
function bindMobileSetPassword(values, props) {
http.post(`${api['passport-api']}/bind_mobile/set_pwd_new`, {
uid: props.user.data.uid,
password: encrypt(values.password)
})
.then(res => {
if (res.data.errno == 200) {
Toast.info('密码设置成功')
let {from} = location.state || {from: {pathname: '/'}}
setTimeout(function () {
props.history.replace(from.pathname)
}, 1000)
} else {
Toast.info(res.data.msg, 2, null, false)
}
})
}
export default compose(
connect(
state => ({user: state.user}),
null
),
withFormik(formikConfig)
)(SetPassword);
\ No newline at end of file
......@@ -4,6 +4,7 @@ import Tag from '@common/Tag/index.js'
import { http, api } from '@/utils'
import './index.scss';
import { Link } from 'react-router-dom'
import Loading from '@/common/Loading'
class Search extends PureComponent {
......@@ -12,14 +13,16 @@ class Search extends PureComponent {
searchHistory: JSON.parse(localStorage.getItem('searchHistory')) || [],
hot_words: [],
searchList: [],
value: ''
value: '',
isLoading: true
}
async componentDidMount() {
const res = await http.get(`${api['search-api']}/search_hot_word`)
if (res.data.errno === 0) {
this.setState({
hot_words: res.data.data.info.hot_words
hot_words: res.data.data.info.hot_words,
isLoading: false
})
}
}
......@@ -54,6 +57,7 @@ class Search extends PureComponent {
handleChange={this.handleChange}
handleSearch={this.handleSearch}
/>
<Loading isLoading={this.state.isLoading}>
<div className="search-main">
<div className="search-land">
<div className='search-history'>
......@@ -99,6 +103,7 @@ class Search extends PureComponent {
</div>
</div>
</div>
</Loading>
</div>
)
......
......@@ -2,6 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom'
import thunk from 'redux-thunk'
import logger from 'redux-logger'
import rootReducers from './store'
......@@ -20,6 +21,8 @@ const store = createStore(
ReactDOM.render(
<Provider store={store}>
<Router>
<App/>
</Router>
</Provider>,
document.getElementById('root'));
\ No newline at end of file
import React from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { Switch, Route } from 'react-router-dom'
import RouterConfig from './router-config'
import PrivateRoute from './privateRoute'
export default function () {
return (
<Router>
<Switch>
{RouterConfig.map((item, index) => {
let {isPrivate, ...rest} = item
......@@ -19,6 +18,5 @@ export default function () {
}
})}
</Switch>
</Router>
)
}
\ No newline at end of file
import React from 'react';
import { connect } from 'react-redux';
import React, { useEffect } from 'react';
import { withRouter } from "react-router-dom";
import { compose } from "redux";
import { connect } from "react-redux";
const Loading = ({user, history, state}) => {
if (Object.values(user.data).filter(item => !!item).length !== 0) {
history.replace(location.pathname)
function Loading(props) {
useEffect(() => {
let {user, location, history} = props
if (!user.isFetching) {
if (user.hasError) {
history.push('/passport', {from: location})
} else {
history.replace('/passport', {...state})
if (Object.values(user.data).every(item => !!item).length !== 0) {
history.push(location.pathname)
}
}
}
})
return (
<div className={'loading'}>
loading...
</div>
);
};
}
export default compose(
connect(
......@@ -23,4 +32,4 @@ export default compose(
null
),
withRouter
)(Loading);
\ No newline at end of file
)(Loading)
\ No newline at end of file
import React from 'react';
import { Route, Redirect } from "react-router-dom";
import { Route } from "react-router-dom";
import { connect } from "react-redux";
import jsCookie from 'js-cookie'
import Loading from './loading'
const PrivateRoute = ({component: Component, path, user, ...rest}) => {
let authenticated = jsCookie.get('token') && jsCookie.get('uid')
return (
<Route {...rest} render={props => {
return authenticated
......
......@@ -16,9 +16,10 @@ import Examination from '@/components/examination'
import ShopCart from '@/components/shopCart';
import BargainMiddlePage from '@/components/bargainMiddlePage';
import Passport from '@/components/passport';
import {Scholarship} from '@/components/scholarship/index';
import { Scholarship } from '@/components/scholarship/index';
import DrawDocument from '@/components/scholarship/DrawDocument/DrawDocument';
import PayOrder from '@/components/order/payOrder/PayOrder'
import Loading from '@/common/Loading'
const Coupons = loadable(() => import(/* webpackChunkName: 'coupons'*/ '@/components/coupons'))
const Study = loadable(() => import(/* webpackChunkName: 'study'*/'@/components/study'))
......@@ -137,5 +138,9 @@ export default [
component: ToGroup,
isPrivate: true
},
{
path: '/loading',
component: Loading,
},
]
\ No newline at end of file
......@@ -12,7 +12,7 @@ module.exports = function (app) {
pathRewrite: {
[`^${config[item]['development']}`]: ''
},
cookieDomainRewrite: 'localhost',
// cookieDomainRewrite: 'localhost',
...config[item]['proxy']
}
))
......
......@@ -45,7 +45,7 @@ const storeUser = (res, dispatch) => {
const SET_CURRENT_USER = 'SET_CURRENT_USER'
const setCurrentUser = payload => ({
type: SET_CURRENT_USER,
payload
payload: {...payload, isFetching: false}
})
......@@ -63,6 +63,12 @@ const updateUser = payload => ({
payload
})
const START_FETCH_USER = 'START_FETCH_USER'
const startFetchUser = () => ({
type: START_FETCH_USER
})
export {
accountLogin,
SET_CURRENT_USER,
......@@ -70,5 +76,7 @@ export {
quickLogin,
logout,
UPDATE_USER,
updateUser
updateUser,
START_FETCH_USER,
startFetchUser,
}
\ No newline at end of file
import { SET_CURRENT_USER, UPDATE_USER } from '@/store/userAction';
import { SET_CURRENT_USER, UPDATE_USER, START_FETCH_USER } from '@/store/userAction';
import { merge } from 'lodash'
......@@ -12,7 +12,8 @@ const initialState = {
token: '',
email: '',
uid: ''
}
},
isFetching: true
}
......@@ -22,6 +23,8 @@ export default function (state = initialState, action) {
return action.payload
case UPDATE_USER:
return merge({}, state, action.payload)
case START_FETCH_USER:
return {...state, isFetching: true}
default:
return state
}
......
export { default as http } from './http'
export { default as api } from './api'
export { html, initCaptcha, validateTel, validateEmail,browser }
import jsCookie from "js-cookie";
export const getParam = (key, str) => {
......@@ -88,3 +86,12 @@ const browser = (function () {
isIPad: /iPad/i.test(ua)
}
})()
const isLogin = (function () {
return jsCookie.get('uid') && jsCookie.get('token')
})()
export { default as http } from './http'
export { default as api } from './api'
export { html, initCaptcha, validateTel, validateEmail, browser, isLogin }
......@@ -25,11 +25,19 @@ const config = {
production: 'http://passport.julyedu.com',
proxy: {}
},
'pay-api':{
'pay-api': {
development: '/pay-api',
test: 'http://api-test.julyedu.com',
production: 'https://api.julyedu.com',
proxy: {}
},
'base-api': {
development: '/base-api',
test: 'http://api-test.julyedu.com',
production: 'https://api.julyedu.com',
proxy: {
secure: false
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment