├── .vscode └── settings.json ├── src ├── redux │ ├── actions │ │ ├── counter.js │ │ └── userInfo.js │ ├── middleware │ │ ├── promiseMiddleware.js │ │ └── clientMiddleware.js │ ├── reducers.js │ ├── store.js │ └── reducers │ │ ├── counter.js │ │ ├── userInfo.js │ │ └── reduxCommon.js ├── containers │ ├── Es6 │ │ ├── scss │ │ │ └── Es6.scss │ │ └── Es6.js │ ├── Default │ │ ├── scss │ │ │ └── Default.scss │ │ └── Default.js │ ├── img │ │ ├── 1.jpg │ │ └── 2.jpg │ ├── UserInfo │ │ ├── img │ │ │ └── smiley_0.png │ │ ├── ui.scss │ │ ├── UserInfo.css │ │ └── UserInfo.js │ ├── Home │ │ ├── scss │ │ │ └── Home.scss │ │ └── Home.js │ ├── page2 │ │ └── page2.js │ ├── index.js │ ├── page3 │ │ └── page3.js │ ├── page1 │ │ ├── page.scss │ │ └── page1.js │ ├── page4 │ │ ├── page.scss │ │ └── page4.js │ ├── Counter │ │ └── Counter.js │ └── App │ │ ├── scss │ │ └── App.scss │ │ └── App.js ├── components │ ├── Navbar │ │ ├── img │ │ │ └── icon-arrow-left-white.png │ │ ├── scss │ │ │ └── Navbar.scss │ │ └── Navbar.js │ ├── ListItem │ │ ├── scss │ │ │ └── ListItem.scss │ │ └── ListItem.js │ ├── index.js │ └── DefHref │ │ ├── scss │ │ └── DefHref.scss │ │ └── DefHref.js ├── styles │ └── Common.scss ├── config.js ├── index.js ├── router │ ├── Bundle.js │ ├── router.js │ └── router.scss ├── helpers │ └── ApiClient.js ├── index.html └── Common │ └── Utility.js ├── .gitignore ├── dist └── api │ └── user.json ├── public └── image │ └── home.png ├── tsconfig.json ├── .babelrc ├── postcss.config.js ├── .github └── workflows │ └── main.yml ├── webpack ├── dev.config.js ├── prod.config.js └── common.config.js ├── readme.md ├── .eslintrc └── package.json /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/redux/actions/counter.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/containers/Es6/scss/Es6.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/containers/Default/scss/Default.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history/ 2 | dist/www/ 3 | dist/react/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /src/redux/middleware/promiseMiddleware.js: -------------------------------------------------------------------------------- 1 | // export default function clientMi -------------------------------------------------------------------------------- /dist/api/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": " 纺三啦纺三啦", 3 | "intro": " 哈哈!@#哈哈!@#哈哈!@#哈哈!@#" 4 | } -------------------------------------------------------------------------------- /public/image/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotuni/react-webpack-demo/HEAD/public/image/home.png -------------------------------------------------------------------------------- /src/containers/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotuni/react-webpack-demo/HEAD/src/containers/img/1.jpg -------------------------------------------------------------------------------- /src/containers/img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotuni/react-webpack-demo/HEAD/src/containers/img/2.jpg -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowJs": true 5 | } 6 | } -------------------------------------------------------------------------------- /src/containers/UserInfo/img/smiley_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotuni/react-webpack-demo/HEAD/src/containers/UserInfo/img/smiley_0.png -------------------------------------------------------------------------------- /src/components/Navbar/img/icon-arrow-left-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaotuni/react-webpack-demo/HEAD/src/components/Navbar/img/icon-arrow-left-white.png -------------------------------------------------------------------------------- /src/components/ListItem/scss/ListItem.scss: -------------------------------------------------------------------------------- 1 | .listItemCss { 2 | padding: 5px; 3 | 4 | .row { 5 | display: flex; 6 | 7 | .name{ 8 | flex: 1; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "react-hot-loader/babel", 9 | "transform-decorators-legacy", 10 | "transform-runtime" 11 | ] 12 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer'), 4 | require('postcss-pxtorem')({ 5 | rootValue: 37.4, 6 | replace: true, 7 | minPixelValue: 3, 8 | propList: [ 9 | '*' 10 | ], 11 | }) 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import ApiClient from '../helpers/ApiClient'; 2 | export const ApiInfo = new ApiClient().API; 3 | 4 | export Utility from 'common/Utility'; 5 | export Navbar from './Navbar/Navbar'; 6 | export DefHref from './DefHref/DefHref'; 7 | export ListItem from './ListItem/ListItem'; 8 | 9 | -------------------------------------------------------------------------------- /src/containers/UserInfo/ui.scss: -------------------------------------------------------------------------------- 1 | .a { 2 | width: 100%; 3 | text-align: center; 4 | font-size: 18px; 5 | padding: 10px; 6 | margin: 10px; 7 | display: flex; 8 | color: #222; 9 | justify-content: center; 10 | 11 | .b { 12 | width: 100px; 13 | height: 200px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/styles/Common.scss: -------------------------------------------------------------------------------- 1 | .btns { 2 | display: flex; 3 | 4 | .btn, 5 | button { 6 | padding: 5px 15px; 7 | text-align: center; 8 | margin: 5px; 9 | } 10 | 11 | .btn { 12 | border: 1px solid #f0c0f0; 13 | border-radius: 5px; 14 | } 15 | } 16 | 17 | .navbar { 18 | margin-top: 40px; 19 | } 20 | -------------------------------------------------------------------------------- /src/containers/UserInfo/UserInfo.css: -------------------------------------------------------------------------------- 1 | .userInfoCss { 2 | border: 1px solid yellow; 3 | padding: 10px 20px; 4 | } 5 | 6 | .userInfoCss .img01{ 7 | width: 100%; 8 | height: 48px; 9 | background-image: url('./img/smiley_0.png'); 10 | background-position: center; 11 | background-size: contain; 12 | background-repeat: no-repeat; 13 | } -------------------------------------------------------------------------------- /src/redux/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import { routerReducer } from 'react-router-redux'; 4 | import counter from 'reducers/counter'; 5 | import userInfo from 'reducers/userInfo'; 6 | import Common from 'reducers/reduxCommon'; 7 | 8 | export default combineReducers({ 9 | routing: routerReducer, counter, userInfo, Common 10 | }); 11 | -------------------------------------------------------------------------------- /src/containers/Home/scss/Home.scss: -------------------------------------------------------------------------------- 1 | .homeCss { 2 | position: relative; 3 | width: 100%; 4 | // margin-top: 45px; 5 | 6 | .btns { 7 | margin: 10px; 8 | display: flex; 9 | 10 | > div { 11 | padding: 5px 25px; 12 | text-align: center; 13 | border: 1px solid #eee; 14 | border-radius: 5px; 15 | background: #1438d4; 16 | color: #fff; 17 | margin: 5px; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/redux/actions/userInfo.js: -------------------------------------------------------------------------------- 1 | export const GET_USER_INFO_LOADING = 'userinfo/loading'; 2 | export const GET_USER_INFO_SUCCESS = 'userinfo/success'; 3 | export const GET_USER_INFO_FAIL = 'userinfo/fail'; 4 | 5 | export function getUserInfo() { 6 | return { 7 | types: [GET_USER_INFO_LOADING, GET_USER_INFO_SUCCESS, GET_USER_INFO_FAIL], 8 | promise: (client) => client.get(client.API.UserInfo, { params: { id: 1 }, data: { data: 1234 } }) 9 | }; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const env = { 2 | development: { 3 | // serverApi: 'https://127.0.0.1:30081/webapi', 4 | serverApi: 'https://127.0.0.1:4011/webapi', 5 | isProduction: false 6 | }, 7 | production: { 8 | serverApi: 'https://127.0.0.1:30081/webapi', 9 | isProduction: true 10 | } 11 | }[process.env.NODE_ENV || 'development']; 12 | 13 | module.exports = Object.assign({ 14 | app: { 15 | BaseName: '/react/', 16 | BuildPath: '/dist/react' 17 | } 18 | }, env); 19 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Build and Deploy Demo 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | 13 | - name: Build and Deploy 14 | uses: JamesIves/github-pages-deploy-action@releases/v3 15 | env: 16 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 17 | BRANCH: master 18 | FOLDER: build 19 | BUILD_SCRIPT: npm install && npm run build 20 | -------------------------------------------------------------------------------- /src/components/DefHref/scss/DefHref.scss: -------------------------------------------------------------------------------- 1 | .defHrefCss { 2 | font-size: 14px; 3 | .groups { 4 | display: flex; 5 | flex-wrap: wrap; 6 | text-align: center; 7 | .urlInfo { 8 | width: 33.33vw; 9 | .url { 10 | padding: 5px; 11 | >div { 12 | padding: 5px 10px; // margin: 5px; 13 | border: 1px solid #a0a0a0a0; 14 | border-radius: 5px; 15 | background: rgba(219, 186, 186, 0.88); 16 | } 17 | } 18 | } 19 | & :nth-of-type(3n) { 20 | margin-right: 0px; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/containers/Default/Default.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { DefHref } from 'components'; 3 | const comStyles = require('styles/Common.scss'); 4 | 5 | const styles = require('./scss/Default.scss'); 6 | 7 | export default class Default extends Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = {}; 11 | } 12 | 13 | componentWillMount() { 14 | // const a = 123; 15 | } 16 | render() { 17 | return ( 18 |
19 | 20 | 哈哈123 21 |
22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Navbar/scss/Navbar.scss: -------------------------------------------------------------------------------- 1 | .navbarCss { 2 | width: 100.1vw; 3 | height: 40px; 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | background: #e6e6e6; 8 | z-index: 99999; 9 | display: flex; 10 | align-items: center; 11 | .left { 12 | padding: 5px; 13 | >div { 14 | height: 25px; 15 | width: 30px; 16 | background-image: url('../img/icon-arrow-left-white.png'); 17 | background-position: center; 18 | background-repeat: no-repeat; 19 | background-size: contain; 20 | } 21 | } 22 | .center { 23 | text-align: center; 24 | flex: 1; 25 | font-size: 20px; 26 | color: #222; 27 | } 28 | .right { 29 | padding: 5px; 30 | } 31 | } -------------------------------------------------------------------------------- /src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | // import thunkMiddleware from 'redux-thunk'; 3 | import reducers from './reducers.js'; 4 | import clientMiddleware from './middleware/clientMiddleware.js'; 5 | 6 | // let store = createStore(reducers, applyMiddleware(thunkMiddleware)); 7 | // export default store; 8 | 9 | import { routerMiddleware } from 'react-router-redux'; 10 | 11 | export default function BuildStore(client, history) { 12 | const reduxRouterMiddleware = routerMiddleware(history); 13 | const middleware = [clientMiddleware(client), reduxRouterMiddleware]; 14 | const finalCreateStore = applyMiddleware(...middleware)(createStore); 15 | return finalCreateStore(reducers); 16 | } 17 | -------------------------------------------------------------------------------- /src/redux/reducers/counter.js: -------------------------------------------------------------------------------- 1 | export const INCREMENT = 'counter/INCREMENT'; 2 | export const DECREMENT = 'counter/DECREMENT'; 3 | export const RESET = 'counter/RESET'; 4 | 5 | const initState = { count: 0 }; 6 | 7 | export default function reducer(state = initState, action) { 8 | switch (action.type) { 9 | case INCREMENT: 10 | return { ...state, count: state.count + 1 }; 11 | case DECREMENT: 12 | return { ...state, count: state.count - 1 }; 13 | case RESET: 14 | return { ...state, count: 0 }; 15 | default: 16 | return state; 17 | } 18 | } 19 | 20 | 21 | export function increment() { 22 | return { type: INCREMENT }; 23 | } 24 | export function decrement() { 25 | return { type: DECREMENT }; 26 | } 27 | export function reset() { 28 | return { type: RESET }; 29 | } 30 | -------------------------------------------------------------------------------- /src/redux/reducers/userInfo.js: -------------------------------------------------------------------------------- 1 | import { GET_USER_INFO_LOADING, GET_USER_INFO_SUCCESS, GET_USER_INFO_FAIL } from 'actions/userInfo'; 2 | 3 | const initState = { 4 | isLoading: false, 5 | userInfo: {}, 6 | errorMsg: '', 7 | }; 8 | 9 | export default function reducer(state = initState, action) { 10 | switch (action.type) { 11 | case GET_USER_INFO_FAIL: 12 | return { 13 | ...state, isLoading: false, userInfo: null, errorMsg: '请求错误啦', 14 | }; 15 | case GET_USER_INFO_LOADING: 16 | return { 17 | ...state, isLoading: true, userInfo: null, errorMsg: '请求中...', 18 | }; 19 | case GET_USER_INFO_SUCCESS: 20 | return { 21 | ...state, isLoading: false, userInfo: action.result, errorMsg: '成功了', 22 | }; 23 | default: 24 | return state; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/containers/page2/page2.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Utility } from 'components'; 3 | const comStyles = require('styles/Common.scss'); 4 | 5 | export default class Page2 extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { count: 0 }; 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 |
15 | 16 | 17 |
18 | 哈哈这是Page2 19 |
20 | { 21 | this.state.count 22 | } 23 |
24 | 25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/containers/index.js: -------------------------------------------------------------------------------- 1 | import cfg from '../config'; 2 | const { isProduction } = cfg; 3 | 4 | import App from './App/App'; 5 | import Default from './Default/Default'; 6 | import page1 from './page1/page1'; 7 | import page2 from './page2/page2'; 8 | import page3 from './page3/page3'; 9 | import page4 from './page4/page4'; 10 | import Home from './Home/Home'; 11 | import Counter from './Counter/Counter'; 12 | import UserInfo from './UserInfo/UserInfo'; 13 | import Es6 from './Es6/Es6'; 14 | 15 | const obj = { 16 | Default, page1, page2, page3, page4, Home, UserInfo, Es6, Counter 17 | }; 18 | if (!!isProduction) { 19 | // 生产环境下使用懒加载方法 20 | Object.keys(obj).forEach((key) => { 21 | try { 22 | obj[key] = require('bundle-loader?lazy&name=[name]!containers/' + key + '/' + key); 23 | } catch (ex) { 24 | console.log(ex); 25 | } 26 | }); 27 | } 28 | export default Object.assign(obj, { App }); 29 | -------------------------------------------------------------------------------- /src/containers/page3/page3.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Utility } from 'components'; 3 | const comStyles = require('styles/Common.scss'); 4 | 5 | export default class Page3 extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = {}; 10 | console.log('----page3---', new Date().getTime()); 11 | } 12 | 13 | render() { 14 | console.log('----page3--111-', new Date().getTime()); 15 | return ( 16 |
17 |
18 | 19 | 20 | 21 |
{ 22 | setTimeout(() => { }, 1000); 23 | }}> aaaaaaaa
24 |
25 | 哈哈这是Page3 26 |
27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/redux/middleware/clientMiddleware.js: -------------------------------------------------------------------------------- 1 | export default function clientMiddleware(client) { 2 | return ({ dispatch, getState }) => { 3 | return (next) => (action) => { 4 | if (typeof action === 'function') { 5 | return action(dispatch, getState); 6 | } 7 | 8 | const { promise, types, ...rest } = action; // eslint-disable-line no-redeclare 9 | if (!promise) { 10 | return next(action); 11 | } 12 | 13 | const [REQUEST, SUCCESS, FAILURE] = types; 14 | next({ ...rest, type: REQUEST }); 15 | 16 | const actionPromise = promise(client); 17 | actionPromise.then( 18 | (result) => next({ ...rest, result, type: SUCCESS }), 19 | (error) => next({ ...rest, error, type: FAILURE }) 20 | ).catch((error) => { 21 | console.error('MIDDLEWARE ERROR:', error); 22 | next({ ...rest, error, type: FAILURE }); 23 | }); 24 | 25 | return actionPromise; 26 | }; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Navbar/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | // import { connect } from 'react-redux'; 4 | import { Utility } from 'components'; 5 | const styles = require('./scss/Navbar.scss'); 6 | 7 | export default class Navbar extends Component { 8 | static propTypes = { 9 | Title: PropTypes.string, 10 | } 11 | 12 | constructor(props) { 13 | super(props); 14 | this.state = {}; 15 | this.__HandlerOnClickLeft = this.__HandlerOnClickLeft.bind(this); 16 | } 17 | 18 | __HandlerOnClickLeft() { 19 | Utility.$goBack(); 20 | } 21 | 22 | render() { 23 | const { Title } = this.props; 24 | return ( 25 |
26 |
27 |
28 |
29 |
{Title || '这是标题'}
30 |
31 |
32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/containers/page1/page.scss: -------------------------------------------------------------------------------- 1 | .page1Css { 2 | padding: 0; 3 | .info { 4 | line-height: 2; // background-image: url('./img/0001.jpg'); 5 | background-position: center; 6 | background-size: 100% 100%; 7 | background-repeat: no-repeat; 8 | height: 600px; 9 | display: -webkit-box; 10 | -webkit-box-align: center; 11 | -webkit-box-pack: center; 12 | >div { 13 | text-align: center; 14 | color: #e8b801; 15 | margin-top: -68%; 16 | font-weight: bolder; 17 | .a1 { 18 | font-size: 28px; 19 | } 20 | .a2 { 21 | font-size: 16px; 22 | >span { 23 | margin: 0 5px; 24 | } 25 | } 26 | } 27 | } 28 | .row { 29 | display: flex; 30 | margin-bottom: 5px; 31 | border: 1px solid #f0f0f0; 32 | padding: 5px 10px; 33 | } 34 | } 35 | 36 | .page4Css { 37 | position: relative; // .abc { 38 | // .btn { 39 | // padding: 10px; 40 | // border: 1px solid; 41 | // border-radius: 5px; 42 | // } 43 | // } 44 | } -------------------------------------------------------------------------------- /src/containers/page4/page.scss: -------------------------------------------------------------------------------- 1 | .page1Css { 2 | padding: 0; 3 | 4 | .info { 5 | line-height: 2; 6 | // background-image: url('./img/0001.jpg'); 7 | background-position: center; 8 | background-size: 100% 100%; 9 | background-repeat: no-repeat; 10 | height: 600px; 11 | display: -webkit-box; 12 | -webkit-box-align: center; 13 | -webkit-box-pack: center; 14 | 15 | > div { 16 | text-align: center; 17 | color: #e8b801; 18 | margin-top: -68%; 19 | font-weight: bolder; 20 | 21 | .a1 { 22 | font-size: 28px; 23 | } 24 | 25 | .a2 { 26 | font-size: 16px; 27 | 28 | > span { 29 | margin: 0 5px; 30 | } 31 | } 32 | } 33 | } 34 | 35 | .row { 36 | display: flex; 37 | margin-bottom: 5px; 38 | border: 1px solid #f0f0f0; 39 | padding: 5px 10px; 40 | } 41 | } 42 | 43 | .page4Css { 44 | position: relative; 45 | // .abc { 46 | // .btn { 47 | // padding: 10px; 48 | // border: 1px solid; 49 | // border-radius: 5px; 50 | // } 51 | // } 52 | } 53 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { AppContainer } from 'react-hot-loader'; 4 | import { Provider } from 'react-redux'; 5 | import BuildStore from './redux/store'; 6 | import ApiClient from './helpers/ApiClient'; 7 | import createHistory from 'history/createBrowserHistory'; 8 | const history = createHistory(); 9 | const ApiClientStore = BuildStore(new ApiClient(), history); 10 | import getRouter from './router/router'; 11 | import { Router } from 'react-router'; 12 | 13 | function renderWithHotReload(RootElement) { 14 | ReactDOM.render( 15 | 16 | 17 | 18 | {RootElement} 19 | 20 | 21 | , 22 | document.getElementById('app') 23 | ); 24 | } 25 | 26 | /** 27 | * 初始化 28 | */ 29 | renderWithHotReload(getRouter()); 30 | 31 | /** 32 | * 热更新 33 | */ 34 | if (module.hot) { 35 | module.hot.accept('./router/router', () => { 36 | const getRouter1 = require('./router/router').default; 37 | renderWithHotReload(getRouter1()); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /src/components/DefHref/DefHref.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | // import { connect } from 'react-redux'; 4 | import { Utility } from 'components'; 5 | const styles = require('./scss/DefHref.scss'); 6 | 7 | export default class DefHref extends Component { 8 | static propTypes = { 9 | Title: PropTypes.string, 10 | } 11 | 12 | constructor(props) { 13 | super(props); 14 | this.state = {}; 15 | } 16 | 17 | __HandlerToJump(url, key) { 18 | const a = key.substring(1, key.length); 19 | if (a) { 20 | Utility.toPage(a); 21 | } 22 | } 23 | 24 | __BuildHrefHtml() { 25 | return Object.keys(Utility.constItem.UrlTitle).map((key, index) => { 26 | const url = Utility.constItem.UrlTitle[key]; 27 | return ( 28 |
29 |
30 |
31 | {`${index + 1}${url.Title}`} 32 |
33 |
34 |
35 | ); 36 | }); 37 | } 38 | 39 | render() { 40 | return ( 41 |
42 |
43 | { 44 | this.__BuildHrefHtml() 45 | } 46 |
47 |
48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/containers/Counter/Counter.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import * as cActions from 'reducers/counter'; 5 | import { Utility } from 'components'; 6 | const comStyles = require('styles/Common.scss'); 7 | 8 | @connect((state) => ({ counter: state.counter }), { ...cActions }) 9 | export default class Counter extends Component { 10 | static propTypes = { 11 | increment: PropTypes.func, 12 | decrement: PropTypes.func, 13 | reset: PropTypes.func, 14 | } 15 | 16 | constructor(props) { 17 | super(props); 18 | this.state = {}; 19 | } 20 | 21 | render() { 22 | const { increment, decrement, reset } = this.props; 23 | return ( 24 |
25 |
26 | 27 | 28 |
29 |
当前计数为(显示redux计数):
30 |
31 | 35 | ___ 36 | 37 | 41 | ___ 42 | 46 | 47 |
48 |
49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/router/Bundle.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | // import { connect } from 'react-redux'; 4 | import { Utility, Navbar } from 'components'; 5 | 6 | class Bundle extends Component { 7 | static propTypes = { 8 | load: PropTypes.any, 9 | isProduction: PropTypes.bool, 10 | children: PropTypes.any, 11 | location: PropTypes.any, 12 | } 13 | constructor(props) { 14 | super(props); 15 | this.state = { 16 | mod: null // short for "module" but that's keyword in js,so "mod" 17 | }; 18 | } 19 | 20 | componentWillMount() { 21 | this.load(this.props); 22 | } 23 | 24 | componentWillReceiveProps(nextProps) { 25 | if (nextProps.load !== this.props.load) { 26 | this.load(nextProps); 27 | } 28 | } 29 | 30 | getTitle() { 31 | const title = Utility.getContent('__URL_TITLE_INFO_'); 32 | if (title) { 33 | return title.Title; 34 | } 35 | return '默认标题'; 36 | } 37 | 38 | load(props) { 39 | const { load, isProduction } = props; 40 | if (!!isProduction) { 41 | this.setState({ mod: null }); 42 | props.load((mod) => { 43 | // handle both es import and cjs 44 | this.setState({ mod: mod.default ? mod.default : mod }); 45 | }); 46 | } else { 47 | this.setState({ mod: load }); 48 | } 49 | } 50 | 51 | render() { 52 | return ( 53 |
54 | 55 | { 56 | this.props.children(this.state.mod) 57 | } 58 |
59 | ); 60 | } 61 | } 62 | 63 | export default Bundle; 64 | -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const merge = require('webpack-merge'); 3 | const CommCfg = require('./common.config.js'); 4 | const APP_PATH = path.join(__dirname, '..'); 5 | const AppCfg = require('../src/config'); 6 | 7 | const devConfig = { 8 | devtool: 'inline-source-map', 9 | entry: { 10 | app: [ 11 | 'react-hot-loader/patch', 12 | path.join(APP_PATH, 'src/index.js') 13 | ] 14 | }, 15 | output: { 16 | filename: '[name].[hash].js', 17 | }, 18 | module: { 19 | rules: [ 20 | { test: /\.css$/, use: ['style-loader', 'css-loader'] }, 21 | { 22 | test: /\.scss$/, 23 | use: [ 24 | { loader: 'style-loader' }, 25 | { 26 | loader: 'css-loader', options: { 27 | sourceMap: true, modules: true, 28 | localIdentName: '[local]_[hash:base64:5]' 29 | } 30 | }, 31 | { 32 | loader: 'postcss-loader', 33 | options: { 34 | sourceMap: true, 35 | config: { 36 | path: 'postcss.config.js' 37 | } 38 | } 39 | }, 40 | { 41 | loader: 'sass-loader', options: { sourceMap: true } 42 | } 43 | ] 44 | } 45 | ] 46 | }, 47 | devServer: { 48 | port: 11111, 49 | // contentBase: path.join(__dirname, '..', './react'), 50 | historyApiFallback: { index: AppCfg.app.BaseName }, // 解决进行非默认页面,刷新报404问题。 51 | host: '0.0.0.0' 52 | }, 53 | }; 54 | 55 | const mergeCfg = merge({ 56 | customizeArray(a, b, key) { 57 | /** 58 | * entry.app不合并,全替换 59 | */ 60 | if (key === 'entry.app') { 61 | return b; 62 | } 63 | return undefined; 64 | } 65 | })(CommCfg, devConfig); 66 | 67 | module.exports = mergeCfg; 68 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 3 | // import { connect } from 'react-redux'; 4 | import pageComponent from 'containers'; 5 | const { 6 | App, Default, UserInfo, Counter, Home, page1, page2, page3, page4, Es6, 7 | } = pageComponent; 8 | const AppCfg = require('../config'); 9 | const { isProduction } = AppCfg; 10 | import Bundle from './Bundle'; 11 | 12 | const Loading = () => { 13 | return
加载中...
; 14 | }; 15 | // args = {history, location, match} 三个参数 16 | const CreateComponent = (component) => (args) => ( 17 | 18 | { 19 | (Component) => Component ? : 20 | } 21 | 22 | ); 23 | 24 | const getRouters = () => ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | 41 | export default getRouters; 42 | 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | // 51 | // 52 | -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | // 清空打包目录 4 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | // 代码丑化 6 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 7 | // 抽取css 8 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 9 | const merge = require('webpack-merge'); 10 | const CommCfg = require('./common.config.js'); 11 | 12 | const AppCfg = require('../src/config'); 13 | const APP_PATH = path.join(__dirname, '..'); 14 | // const AppCfg = CommCfg.AppCfg; 15 | // console.log('-----CommCfg---------'); 16 | // console.log(AppCfg); 17 | // console.log('-----CommCfg---------'); 18 | 19 | const proCfg = { 20 | // devtool: 'cheap-module-source-map', 21 | devtool: 'source-map', 22 | module: { 23 | rules: [ 24 | { test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) }, 25 | { 26 | test: /\.scss$/, 27 | use: ExtractTextPlugin.extract({ 28 | fallback: 'style-loader', 29 | use: [ 30 | { 31 | loader: 'css-loader', options: { 32 | sourceMap: true, minimize: true, modules: true, 33 | localIdentName: '[local]_[hash:base64:5]' 34 | } 35 | }, 36 | { 37 | loader: 'postcss-loader', 38 | options: { sourceMap: true, config: { path: 'postcss.config.js' } } 39 | }, 40 | { 41 | loader: 'sass-loader', options: { sourceMap: true } 42 | }] 43 | }) 44 | } 45 | ] 46 | }, 47 | plugins: [ 48 | // new CleanWebpackPlugin([path.join(APP_PATH, AppCfg.app.BuildPath)]), 49 | new CleanWebpackPlugin([path.join(APP_PATH, AppCfg.app.BuildPath)], { root: APP_PATH }), 50 | new UglifyJSPlugin(), 51 | new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }), 52 | new ExtractTextPlugin({ filename: '[name].[contenthash:5].css', allChunks: true }), 53 | ], 54 | }; 55 | 56 | module.exports = merge(CommCfg, proCfg); 57 | -------------------------------------------------------------------------------- /src/containers/Home/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Utility } from 'components'; 4 | 5 | const styles = require('./scss/Home.scss'); 6 | 7 | export default class Home extends Component { 8 | static propTypes = { 9 | children: PropTypes.object, // 子项 10 | location: PropTypes.object, // location信息 11 | }; 12 | static contextTypes = { 13 | router: PropTypes.object.isRequired, 14 | // history: PropTypes.object, 15 | } 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = {}; 20 | } 21 | 22 | componentWillMount() { 23 | const __key = Utility.constItem.KeyHistory; 24 | if (!Utility.getContent(__key)) { 25 | Utility.setContent(__key, this.context.router.history); 26 | const self = this; 27 | const { UrlTitle } = Utility.constItem; 28 | const __IsGoBackKey = Utility.constItem.KeyGoBack; 29 | this.context.router.history.listen((location, action) => { 30 | Utility.setContent(__IsGoBackKey, action === 'POP'); 31 | const { pathname } = location; 32 | if (UrlTitle && UrlTitle[pathname]) { 33 | self.state.UrlTitle = UrlTitle[pathname]; 34 | Utility.setContent('__URL_TITLE_INFO_', UrlTitle[pathname]); 35 | } 36 | }); 37 | } 38 | } 39 | 40 | componentDidMount() { 41 | this.state.IsMount = true; 42 | } 43 | 44 | componentWillUnmount() { 45 | delete this.state.IsMount; 46 | console.log('-------home-----component Will Umount'); 47 | } 48 | 49 | __UpdateRender() { 50 | if (!!this.state.IsMount) { 51 | this.setState({ __CURRENT_TIME_: new Date() }); 52 | } 53 | } 54 | 55 | __HandlerJudgPage() { 56 | Utility.toPage('userinfo'); 57 | } 58 | render() { 59 | // const { UrlTitle } = this.state; 60 | // const { Title } = UrlTitle || {}; 61 | // const __Flag = 0; 62 | 63 | return ( 64 |
65 | {} 66 |
67 | ); 68 | } 69 | } 70 | // { 71 | // __Flag === 2 && 72 | // } 73 | -------------------------------------------------------------------------------- /webpack/common.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const webpack = require('webpack'); 4 | const APP_PATH = path.join(__dirname, '..'); 5 | const AppCfg = require('../src/config'); 6 | 7 | const Config = { 8 | entry: { 9 | app: [path.join(APP_PATH, 'src/index.js')], 10 | vendor: ['react', 'react-router-dom', 'redux', 'react-dom', 'react-redux'] 11 | }, 12 | output: { 13 | path: path.join(APP_PATH, AppCfg.app.BuildPath),// './dist/react'), 14 | filename: '[name].[chunkhash].js', 15 | chunkFilename: '[name].[chunkhash].js', 16 | publicPath: AppCfg.app.BaseName // '/react/' // 这里必须和router里面写的那个basename要一样。要不能会出问题。 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.js$/, 22 | use: [ 23 | 'babel-loader?cacheDirectory=true', 24 | { 25 | loader: 'eslint-loader', options: {} 26 | } 27 | ], 28 | include: path.join(APP_PATH, 'src') 29 | }, 30 | { 31 | test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, 32 | use: [ 33 | { 34 | loader: 'url-loader', options: { limit: 8192 } 35 | } 36 | ] 37 | }, 38 | ] 39 | }, 40 | resolve: { 41 | alias: { 42 | pages: path.join(APP_PATH, 'src/pages'), 43 | component: path.join(APP_PATH, 'src/component'), 44 | router: path.join(APP_PATH, 'src/router'), 45 | actions: path.join(APP_PATH, 'src/redux/actions'), 46 | reducers: path.join(APP_PATH, 'src/redux/reducers'), 47 | containers: path.join(APP_PATH, 'src/containers'), 48 | components: path.join(APP_PATH, 'src/components'), 49 | common: path.join(APP_PATH, 'src/common'), 50 | styles: path.join(APP_PATH, 'src/styles'), 51 | } 52 | }, 53 | plugins: [ 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', 56 | template: path.join(APP_PATH, 'src/index.html') 57 | }), 58 | new webpack.HashedModuleIdsPlugin(), 59 | new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', }), 60 | new webpack.optimize.CommonsChunkPlugin({ name: 'runtime' }), // runtime以及vendor的顺序关系很重要要。 61 | ], 62 | }; 63 | 64 | module.exports = Config; 65 | -------------------------------------------------------------------------------- /src/containers/UserInfo/UserInfo.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import { getUserInfo } from 'actions/userInfo'; 5 | import { Utility } from 'components'; 6 | const comStyles = require('styles/Common.scss'); 7 | 8 | require('./UserInfo.css'); 9 | 10 | @connect((state) => ({ userInfo: state.userInfo }), { getUserInfo }) 11 | export default class UserInfo extends Component { 12 | static propTypes = { 13 | userInfo: PropTypes.object, 14 | getUserInfo: PropTypes.func, 15 | } 16 | static contextTypes = { 17 | router: PropTypes.object.isRequired, 18 | // history: PropTypes.object, 19 | } 20 | 21 | constructor(props) { 22 | super(props); 23 | this.state = {}; 24 | this.__HandlerGoBack = this.__HandlerGoBack.bind(this); 25 | } 26 | 27 | __HandlerGoBack() { 28 | Utility.$goBack(); 29 | } 30 | 31 | render() { 32 | const styles = require('./ui.scss'); 33 | const demoimg = require('./img/smiley_0.png'); 34 | const { userInfo, isLoading, errorMsg } = this.props.userInfo; 35 | return ( 36 |
37 |
38 | 39 | 40 |
41 |
42 |
43 | 哈哈!!看看了 44 |
45 |
46 |
47 | 54 |
55 |
56 | {errorMsg} 57 |
58 | 59 | { 60 | !isLoading && 61 |
62 |

用户信息

63 |

用户名称:{userInfo && userInfo.name}

64 |

介绍:{userInfo && userInfo.intro}

65 |
66 | } 67 | 68 |
69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/helpers/ApiClient.js: -------------------------------------------------------------------------------- 1 | import superagent from 'superagent'; 2 | 3 | const methods = ['get', 'post', 'put', 'patch', 'del']; 4 | function formatUrl(path) { 5 | const adjustedPath = path[0] !== '/' ? '/' + path : path; 6 | // const _ApiUrl = 'http://127.0.0.1:11111/react/www' + adjustedPath; 7 | const _ApiUrl = 'https://127.0.0.1:30081/webapi' + adjustedPath; 8 | return _ApiUrl; 9 | } 10 | 11 | export default class ApiClient { 12 | API = { 13 | /** 14 | * 通用的方法。 15 | */ 16 | Common: { 17 | 18 | }, 19 | UserInfo: 'api/user.json', 20 | Users: 'userinfo/users', 21 | MapPlacelist: 'map/placelist', 22 | Demo: 'demo', 23 | } 24 | 25 | constructor() { 26 | const self = this; 27 | methods.forEach((method) => 28 | self[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => { 29 | const request = superagent[method](formatUrl(path)); 30 | if (params) { 31 | request.query(params); 32 | } 33 | if (data) { 34 | request.send(data); 35 | } 36 | request.header.token = 'xtn_21232f297a57a5a743894a0e4a801fc3_c3284d0f94606de1fd2af172aba15bf3'; 37 | 38 | /** 39 | * 错误处理及提示 40 | * 41 | * @param {any} err 42 | */ 43 | function __ProcessError(err) { 44 | try { 45 | if (err.status) { 46 | console.log(err.status); 47 | } else if (!!err.crossDomain) { 48 | console.log('与服务器连接中断...'); 49 | } else if (err.message && err.message !== '') { 50 | console.log(err.message); 51 | } 52 | } catch (ex) { 53 | console.log(ex); 54 | } 55 | } 56 | 57 | function __SendRequest(_request) { 58 | _request.end((err, response) => { 59 | const { body, headers } = response || {}; 60 | const { date } = headers || {}; 61 | console.log(date); 62 | if (err) { 63 | __ProcessError(err, body, response); 64 | reject(body || err); // reject-->拒绝; resolve-->解决 65 | } else { 66 | if (!body) { 67 | console.log({ status: response.status, msg: '处理成功' }); 68 | } 69 | setTimeout(() => { 70 | resolve(body); 71 | }, 1000); 72 | } 73 | }); 74 | } 75 | 76 | try { 77 | __SendRequest(request); 78 | } catch (ex) { 79 | console.log(ex); 80 | } 81 | })); 82 | } 83 | empty() { 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # react-webpack-demo 2 | 3 | 4 | ## 关于 5 | 当前项目模版中使用到的技术: 6 | 7 | * [React 16](https://github.com/facebook/react) 8 | * [React Router4](https://github.com/rackt/react-router) 9 | * [React Hot Loader](https://github.com/gaearon/react-hot-loader) 模块热加载 10 | * [Babel](http://babeljs.io) ES6 、ES7 语法转换 11 | * [Webpack3](http://webpack.github.io) 打包工具 12 | * [webpack Dev Server](http://github.com/webpack/webpack-dev-serverl) 13 | * [Redux](https://github.com/rackt/redux) 14 | * [React Router Redux](https://github.com/reactjs/react-router-redux) Redux/React 路由绑定. 15 | * [ESLint](http://eslint.org) 保持一致的代码风格 16 | * [Superagent](https://github.com/visionmedia/superagent) 接口调用 17 | * [bundle-loader](https://github.com/webpack-contrib/bundle-loader) 按需要加载相应页面的JS 18 | * [Postcss Loader](https://github.com/postcss/postcss-loader) 兼容不同浏览器样式加前缀 19 | 20 | 21 | ## 安装 22 | ```bash 23 | npm install 24 | 或者 25 | yarn install 26 | ``` 27 | 28 | ## 运行开发环境 29 | ```base 30 | npm run dev 31 | ``` 32 | 起来在浏览器上输入:http://127.0.0.1:11111/react; /react这个是可以在config.js里进行配置,这个就是route basename。 33 | 34 | ![ick Alias](public/image/home.png) 35 | 36 | ## 项目打包 37 | ```base 38 | npm run build 39 | ``` 40 | 项目打包后,会在项目根目录里生成 dist/www目录,拿到这个就可以进行部署了。 41 | 42 | 如果部署到Nginx里面,如果出现在404的时候,可以参考:[Nginx配置ReactJs项目,Url后面直接输入路由路径时老报404问题。](http://blog.csdn.net/xiaotuni/article/details/77745189) 43 | 44 | ## image 引入 45 | ``` 46 | render(){ 47 | const image = require('../img/demo.png); 48 | return( 49 |
50 | ); 51 | } 52 | ``` 53 | ## 样式引入 54 | ``` 55 | render(){ 56 | const styles = require('./scss/style.scss'); 57 | return ( 58 |
class name
59 | ); 60 | } 61 | ``` 62 | 63 | ---- 64 | 65 | ## 引入scss 66 | 67 | webpack.dev.config 68 | ```code 69 | const styles = require('./demo.scss'); 70 | 71 | render(){ 72 | return( 73 |
content
74 | ); 75 | } 76 | ``` 77 | ### 配置 url:https://github.com/webpack-contrib/css-loader 78 | ``` 79 | 80 | { 81 | test: /\.scss$/, 82 | use: [ 83 | { loader: "style-loader" }, 84 | { 85 | loader: "css-loader", options: { 86 | sourceMap: true, 87 | modules: true, minimize: true, 88 | localIdentName: '[local]_[hash:base64:5]' 89 | } 90 | }, 91 | {loader: "sass-loader", options:{sourceMap:true }} 92 | ] 93 | } 94 | ``` 95 | 96 | 97 | ## 参与文献 98 | * [从零搭建React全家桶框架教程](https://github.com/brickspert/blog/issues/1) 99 | * [React Redux Universal Hot Example](https://github.com/erikras/react-redux-universal-hot-example) 100 | -------------------------------------------------------------------------------- /src/components/ListItem/ListItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Utility } from 'components'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const styles = require('./scss/ListItem.scss'); 6 | 7 | export default class ListItem extends Component { 8 | static propTypes = { 9 | DataSource: PropTypes.array, 10 | onDelete: PropTypes.func, 11 | } 12 | 13 | constructor(props) { 14 | super(props); 15 | this.state = {}; 16 | } 17 | componentWillMount() { 18 | console.log('-listitem-1----will mount------'); 19 | } 20 | componentDidMount() { 21 | console.log('-listitem-2----did mount------'); 22 | } 23 | componentWillReceiveProps(nextProps, nextState) { 24 | const { a } = nextState; 25 | if (a) { 26 | console.log(a); 27 | } 28 | console.log('-listitem-3----will receive props------------'); 29 | } 30 | shouldComponentUpdate(nextProps, nextState, nextContext) { 31 | const { a } = nextContext; 32 | if (a) { 33 | console.log(a); 34 | } 35 | 36 | console.log('-listitem-4----should update-----------------'); 37 | return true; 38 | } 39 | componentWillUpdate(nextProps, nextState, nextContext) { 40 | const { a } = nextContext; 41 | if (a) { 42 | console.log(a); 43 | } 44 | console.log('-listitem-5----will update-------------'); 45 | } 46 | componentDidUpdate(prevProps, prevState) { 47 | const { a } = prevState; 48 | if (a) { 49 | console.log(a); 50 | } 51 | console.log('-listitem-6----did update-----------'); 52 | } 53 | componentWillUnmount() { 54 | console.log('-listitem-7----will unmount-----------'); 55 | } 56 | 57 | onClickSelectItem(item, index) { 58 | console.log(item); 59 | const { onDelete } = this.props; 60 | if (onDelete) { 61 | onDelete(item, index); 62 | } 63 | } 64 | 65 | componentDidCatch() { 66 | console.log('-listitem-----------did catch-------------'); 67 | } 68 | 69 | BuildHtml() { 70 | const { DataSource } = this.props; 71 | if (!Utility.isArray(DataSource)) { 72 | return null; 73 | } 74 | return DataSource.map((item, index) => { 75 | const { CurrentDate } = item; 76 | return (
77 |
{item.id}
78 |
{item.Name}
79 |
{CurrentDate.toLocaleDateString() + ' ' + CurrentDate.toLocaleTimeString()}
80 |
); 81 | }); 82 | } 83 | 84 | render() { 85 | return ( 86 |
87 | { 88 | this.BuildHtml() 89 | } 90 |
91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/router/router.scss: -------------------------------------------------------------------------------- 1 | $top-hight:0;// 40px; 2 | 3 | .com { 4 | background: #fff; 5 | position: fixed; 6 | top: $top-hight; //bottom:50px; 去掉是因为界面最下面老是会跳的效果,改为下面这种 7 | // padding-bottom: 50px; 8 | bottom: 0; 9 | left: 0; 10 | right: 0; 11 | } 12 | 13 | .__spEnter { 14 | @extend .com; 15 | transform: translate3d(100%, 0, 0); 16 | } 17 | 18 | .__spEnterActive { 19 | @extend .com; 20 | transform: translate3d(0, 0, 0); 21 | transition: all ease-out 500ms; 22 | } 23 | 24 | .__spLeave { 25 | @extend .com; 26 | transform: translate3d(0, 0, 0); 27 | } 28 | 29 | .__spLeaveActive { 30 | @extend .com; 31 | transform: translate3d(-100%, 0, 0); 32 | transition: all ease-out 500ms; 33 | } 34 | 35 | .__spAppear { 36 | opacity: .01; 37 | } 38 | 39 | .__spAppearActive { 40 | opacity: 1; 41 | transition: opacity 1.5s ease-in; 42 | } 43 | 44 | /**************************返回***************************/ 45 | 46 | .__spEnterReturn { 47 | @extend .com; 48 | transform: translate3d(-100%, 0, 0); 49 | } 50 | 51 | .__spEnterActiveReturn { 52 | @extend .com; 53 | transform: translate3d(0, 0, 0); 54 | transition: all ease-out 500ms; 55 | } 56 | 57 | .__spLeaveReturn { 58 | @extend .com; 59 | transform: translate3d(0, 0, 0); 60 | } 61 | 62 | .__spLeaveActiveReturn { 63 | @extend .com; 64 | transform: translate3d(100%, 0, 0); 65 | transition: all ease-out 500ms; 66 | } 67 | 68 | .__spAppearReturn { 69 | opacity: .01; 70 | transition: opacity 1.5s ease-in; 71 | } 72 | 73 | .__spAppearActiveReturn { 74 | opacity: 1; 75 | transition: opacity 1.5s ease-in; 76 | } 77 | 78 | .appContent { 79 | width: 100%; 80 | background: #fff; 81 | position: absolute; 82 | top: $top-hight; 83 | 84 | .spEnter { 85 | @extend .__spEnter; 86 | } 87 | 88 | .spEnterActive { 89 | @extend .__spEnterActive; 90 | } 91 | 92 | .spLeave { 93 | @extend .__spLeave; 94 | } 95 | 96 | .spLeaveActive { 97 | @extend .__spLeaveActive; 98 | } 99 | 100 | .spAppear { 101 | @extend .__spAppear; 102 | } 103 | 104 | .spAppearActive { 105 | @extend .__spAppearActive; 106 | } 107 | /**************************返回***************************/ 108 | .spEnterReturn { 109 | @extend .__spEnterReturn; 110 | } 111 | 112 | .spEnterActiveReturn { 113 | @extend .__spEnterActiveReturn; 114 | } 115 | 116 | .spLeaveReturn { 117 | @extend .__spLeaveReturn; 118 | } 119 | 120 | .spLeaveActiveReturn { 121 | @extend .__spLeaveActiveReturn; 122 | } 123 | 124 | .spAppearReturn { 125 | @extend .__spAppearReturn; 126 | } 127 | 128 | .spAppearActiveReturn { 129 | @extend .__spAppearActiveReturn; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/containers/App/scss/App.scss: -------------------------------------------------------------------------------- 1 | $top-hight:0;// 40px; 2 | 3 | .com { 4 | background: #fff; 5 | position: fixed; 6 | top: $top-hight; //bottom:50px; 去掉是因为界面最下面老是会跳的效果,改为下面这种 7 | // padding-bottom: 50px; 8 | bottom: 0; 9 | left: 0; 10 | right: 0; 11 | } 12 | 13 | .__spEnter { 14 | @extend .com; 15 | transform: translate3d(100%, 0, 0); 16 | } 17 | 18 | .__spEnterActive { 19 | @extend .com; 20 | transform: translate3d(0, 0, 0); 21 | transition: all ease-out 500ms; 22 | } 23 | 24 | .__spLeave { 25 | @extend .com; 26 | transform: translate3d(0, 0, 0); 27 | } 28 | 29 | .__spLeaveActive { 30 | @extend .com; 31 | transform: translate3d(-100%, 0, 0); 32 | transition: all ease-out 500ms; 33 | } 34 | 35 | .__spAppear { 36 | opacity: .01; 37 | } 38 | 39 | .__spAppearActive { 40 | opacity: 1; 41 | transition: opacity 1.5s ease-in; 42 | } 43 | 44 | /**************************返回***************************/ 45 | 46 | .__spEnterReturn { 47 | @extend .com; 48 | transform: translate3d(-100%, 0, 0); 49 | } 50 | 51 | .__spEnterActiveReturn { 52 | @extend .com; 53 | transform: translate3d(0, 0, 0); 54 | transition: all ease-out 500ms; 55 | } 56 | 57 | .__spLeaveReturn { 58 | @extend .com; 59 | transform: translate3d(0, 0, 0); 60 | } 61 | 62 | .__spLeaveActiveReturn { 63 | @extend .com; 64 | transform: translate3d(100%, 0, 0); 65 | transition: all ease-out 500ms; 66 | } 67 | 68 | .__spAppearReturn { 69 | opacity: .01; 70 | transition: opacity 1.5s ease-in; 71 | } 72 | 73 | .__spAppearActiveReturn { 74 | opacity: 1; 75 | transition: opacity 1.5s ease-in; 76 | } 77 | 78 | .appContent { 79 | width: 100%; 80 | background: #fff; 81 | position: absolute; 82 | top: $top-hight; 83 | 84 | .spEnter { 85 | @extend .__spEnter; 86 | } 87 | 88 | .spEnterActive { 89 | @extend .__spEnterActive; 90 | } 91 | 92 | .spLeave { 93 | @extend .__spLeave; 94 | } 95 | 96 | .spLeaveActive { 97 | @extend .__spLeaveActive; 98 | } 99 | 100 | .spAppear { 101 | @extend .__spAppear; 102 | } 103 | 104 | .spAppearActive { 105 | @extend .__spAppearActive; 106 | } 107 | /**************************返回***************************/ 108 | .spEnterReturn { 109 | @extend .__spEnterReturn; 110 | } 111 | 112 | .spEnterActiveReturn { 113 | @extend .__spEnterActiveReturn; 114 | } 115 | 116 | .spLeaveReturn { 117 | @extend .__spLeaveReturn; 118 | } 119 | 120 | .spLeaveActiveReturn { 121 | @extend .__spLeaveActiveReturn; 122 | } 123 | 124 | .spAppearReturn { 125 | @extend .__spAppearReturn; 126 | } 127 | 128 | .spAppearActiveReturn { 129 | @extend .__spAppearActiveReturn; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/containers/page1/page1.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import { Utility, ApiInfo } from 'components'; 5 | import * as CommonActions from 'reducers/reduxCommon'; 6 | const comStyles = require('styles/Common.scss'); 7 | const styles = require('./page.scss'); 8 | 9 | @connect((state) => ({ 10 | UserList: state.Common.UserList, 11 | Demo: state.Common.Demo, 12 | MapPlacelist: state.Common.MapPlacelist, 13 | }), { ...CommonActions }) 14 | export default class Page1 extends Component { 15 | static propTypes = { 16 | UserList: PropTypes.any, 17 | Demo: PropTypes.any, 18 | MapPlacelist: PropTypes.any, 19 | onApiGet: PropTypes.func, 20 | } 21 | 22 | constructor(props) { 23 | super(props); 24 | this.state = {}; 25 | } 26 | 27 | onCallApi() { 28 | const { onApiGet } = this.props; 29 | if (!onApiGet) { 30 | return; 31 | } 32 | // onApiGet('UserList', ApiInfo.Users, {}).then((data) => { 33 | // console.log(data); 34 | // }).catch((er) => { 35 | // console.log(er); 36 | // }); 37 | 38 | function* testa() { 39 | console.log('---------1----------'); 40 | yield onApiGet('UserList', ApiInfo.Users, {}); 41 | console.log('---------2----------'); 42 | yield onApiGet('MapPlacelist', ApiInfo.MapPlacelist, {}); 43 | console.log('---------3----------'); 44 | yield onApiGet('Demo', ApiInfo.Demo, {}); 45 | console.log('---------4----------'); 46 | yield 'ok'; 47 | } 48 | const gen = testa(); 49 | 50 | let result = gen.next(); 51 | console.log(result); 52 | result = gen.next(); 53 | console.log(result); 54 | result = gen.next(); 55 | console.log(result); 56 | result = gen.next(); 57 | console.log(result); 58 | result = gen.next(); 59 | console.log(result); 60 | } 61 | 62 | 63 | render() { 64 | console.log('-----------------', new Date().getTime()); 65 | const { UserList } = this.props; 66 | return ( 67 |
68 |
69 | 70 | 71 | 72 |
73 | acdef 74 | { 75 | Utility.isArray(UserList) && UserList.map((item, index) => { 76 | const { 77 | address, age, cityname, username 78 | } = item; 79 | return ( 80 |
81 |
{username}
82 |
{cityname}
83 |
{age}
84 |
{address}
85 |
86 | ); 87 | }) 88 | } 89 | 90 |
91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "settings": { 9 | "import/parser": "babel-eslint", 10 | "import/resolve": { 11 | "moduleDirectory": ["node_modules", "src"] 12 | }, 13 | "propWrapperFunctions": [ "forbidExtraProps" ] // The names of any functions used to wrap the propTypes object, such as `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. 14 | }, 15 | "parser": "babel-eslint", 16 | "plugins": [ 17 | "react", "import", "jsx-a11y" 18 | ], 19 | "rules": { 20 | "jsx-a11y/href-no-hash": "off", 21 | "jsx-a11y/anchor-is-valid": ["warn", { "aspects": ["invalidHref"] }], 22 | "import/newline-after-import": 0, 23 | "import/no-extraneous-dependencies": 0, 24 | "import/extensions":0, 25 | "import/first":0, 26 | "import/no-dynamic-require":0, 27 | "react/require-default-props":0, 28 | "react/no-unused-prop-types":0, 29 | "class-methods-use-this":0, 30 | "react/no-array-index-key":0, 31 | "prefer-template":0, 32 | "jsx-a11y/no-static-element-interactions":0, 33 | "no-underscore-dangle": 0, 34 | "react/jsx-uses-react": 2, 35 | "react/forbid-prop-types":0, 36 | "jsx-a11y/click-events-have-key-events":0, 37 | "react/jsx-uses-vars": 2, 38 | "prefer-destructuring": 0, 39 | "react/no-unused-state": 0, 40 | "react/jsx-closing-tag-location": 0, 41 | "react/no-multi-comp": 0, 42 | "react/jsx-curly-brace-presence": 0, 43 | "import/default": 0, 44 | "import/no-duplicates": 0, 45 | "import/named": 0, 46 | "no-proto": 0, 47 | "no-prototype-builtins": 0, 48 | "import/namespace": 0, 49 | "import/no-unresolved": 0, 50 | "import/no-named-as-default": 2, 51 | "comma-dangle": 0, // not sure why airbnb turned this on. gross! 52 | "indent": [2, 2, {"SwitchCase": 1}], 53 | "no-console": 0, 54 | "no-alert": 0, 55 | "id-length": 0, 56 | "no-trailing-spaces": 0, 57 | "no-nested-ternary": 0, 58 | "camelcase": [0], 59 | "semi": 2, 60 | "eqeqeq": "off", 61 | "curly": "error", 62 | "no-cond-assign": 2, 63 | "linebreak-style": 0, 64 | "arrow-body-style": 0, 65 | "arrow-parens": ["error", "always"], 66 | "max-len": 0, 67 | "global-require": 0, 68 | "no-return-assign": 0, 69 | "no-extra-boolean-cast": 0, 70 | "no-multi-spaces": 0, 71 | "no-restricted-syntax": 0, 72 | "no-param-reassign": 0, 73 | "prefer-rest-params": 0, 74 | "react/jsx-no-bind": 0, 75 | "object-property-newline": 0, 76 | "import/no-webpack-loader-syntax":0, 77 | "react/jsx-closing-bracket-location":0, 78 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 79 | "no-confusing-arrow": 0, // ["error", {"allowParens": false}], 80 | "prefer-arrow-callback": 2, // 箭头回调函数优先 81 | "no-var": "error" // 禁用var变量 82 | // "quotes": ["error", "double"] 83 | }, 84 | "parserOptions": { 85 | "ecmaFeatures": { 86 | "jsx": true 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-webpack-demo", 3 | "version": "1.0.0", 4 | "description": "Example of webapp using react redux and hot reloading", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start-prod": "better-npm-run start-prod", 9 | "build": "better-npm-run start-prod", 10 | "start-dev": "better-npm-run start-dev", 11 | "dev": "concurrent --kill-others \"npm run start-dev\"" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/xiaotuni/react-webpack-demo" 16 | }, 17 | "homepage": "https://github.com/xiaotuni/react-webpack-demo", 18 | "keywords": [ 19 | "babel", 20 | "bundle-loader", 21 | "eslint", 22 | "hot reloading", 23 | "react", 24 | "react-router-dom", 25 | "react-hot-reloader", 26 | "redux", 27 | "webpack" 28 | ], 29 | "author": "xiaotuni (http://github.com/xiaotuni)", 30 | "license": "ISC", 31 | "betterScripts": { 32 | "start-dev": { 33 | "command": "webpack-dev-server --config webpack/dev.config.js --color --progress --hot", 34 | "env": { 35 | "NODE_ENV": "development" 36 | } 37 | }, 38 | "start-prod": { 39 | "command": "webpack --config webpack/prod.config.js", 40 | "env": { 41 | "NODE_ENV": "production" 42 | } 43 | } 44 | }, 45 | "dependencies": { 46 | "autoprefixer": "^7.1.5", 47 | "babel-core": "^6.26.0", 48 | "babel-eslint": "^8.0.1", 49 | "babel-loader": "^7.1.2", 50 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 51 | "babel-plugin-transform-runtime": "^6.23.0", 52 | "babel-polyfill": "^6.26.0", 53 | "babel-preset-es2015": "^6.24.1", 54 | "babel-preset-react": "^6.24.1", 55 | "babel-preset-stage-0": "^6.24.1", 56 | "better-npm-run": "^0.1.0", 57 | "bundle-loader": "^0.5.5", 58 | "clean-webpack-plugin": "^0.1.16", 59 | "concurrently": "^3.5.0", 60 | "css-loader": "^0.28.7", 61 | "eslint": "^4.8.0", 62 | "eslint-config-airbnb": "^16.0.0", 63 | "eslint-loader": "^1.9.0", 64 | "eslint-plugin-import": "^2.7.0", 65 | "eslint-plugin-jsx-a11y": "^6.0.2", 66 | "eslint-plugin-react": "^7.3.0", 67 | "eslint-plugin-redux-saga": "^0.5.0", 68 | "extract-text-webpack-plugin": "^3.0.0", 69 | "file-loader": "^0.11.2", 70 | "html-webpack-plugin": "^2.30.1", 71 | "node-sass": "^4.5.3", 72 | "postcss-loader": "^2.0.6", 73 | "postcss-pxtorem": "^4.0.1", 74 | "react": "^16.0.0", 75 | "react-dom": "^16.0.0", 76 | "react-hot-loader": "next", 77 | "react-redux": "^5.0.6", 78 | "react-router-dom": "^4.2.2", 79 | "react-router-redux": "^4.0.8", 80 | "react-transition-group": "1.x", 81 | "redux": "^3.7.2", 82 | "redux-thunk": "^2.2.0", 83 | "sass-loader": "^6.0.6", 84 | "style-loader": "^0.18.2", 85 | "superagent": "^3.6.0", 86 | "uglifyjs-webpack-plugin": "^0.4.6", 87 | "url-loader": "^0.5.9", 88 | "webpack": "^3.7.1", 89 | "webpack-dev-server": "^2.9.1", 90 | "webpack-merge": "^4.1.0" 91 | }, 92 | "browserslist": [ 93 | "defaults", 94 | "not ie < 11", 95 | "last 2 versions", 96 | "> 1%", 97 | "iOS 7", 98 | "last 3 iOS versions" 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /src/containers/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { CSSTransitionGroup } from 'react-transition-group'; 4 | import { Switch } from 'react-router-dom'; 5 | import { Utility } from 'components'; 6 | const styles = require('./scss/App.scss'); 7 | 8 | export default class App extends Component { 9 | static propTypes = { 10 | children: PropTypes.any, // 子项 11 | location: PropTypes.object, 12 | Title: PropTypes.string, 13 | } 14 | static contextTypes = { 15 | router: PropTypes.object.isRequired, 16 | history: PropTypes.object, 17 | } 18 | 19 | constructor(props) { 20 | super(props); 21 | this.state = {}; 22 | } 23 | 24 | componentWillMount() { 25 | const __key = Utility.constItem.KeyHistory; 26 | if (!Utility.getContent(__key)) { 27 | Utility.setContent(__key, this.context.router.history); 28 | const self = this; 29 | const { UrlTitle } = Utility.constItem; 30 | const __IsGoBackKey = Utility.constItem.KeyGoBack; 31 | this.context.router.history.listen((location, action) => { 32 | Utility.setContent(__IsGoBackKey, action === 'POP'); 33 | const { pathname } = location; 34 | if (UrlTitle && UrlTitle[pathname]) { 35 | self.state.UrlTitle = UrlTitle[pathname]; 36 | Utility.setContent('__URL_TITLE_INFO_', UrlTitle[pathname]); 37 | } 38 | }); 39 | } 40 | } 41 | componentDidMount() { 42 | console.log('app did mount'); 43 | } 44 | 45 | 46 | getTransitionsName() { 47 | const __IsGoback = Utility.getContent(Utility.constItem.KeyGoBack); 48 | const __tranName = {}; 49 | if (!!__IsGoback) { 50 | __tranName.enter = styles.spEnterReturn; 51 | __tranName.enterActive = styles.spEnterActiveReturn; 52 | __tranName.leave = styles.spLeaveReturn; 53 | __tranName.leaveActive = styles.spLeaveActiveReturn; 54 | __tranName.appear = styles.spAppearReturn; 55 | __tranName.appearActive = styles.spAppearActiveReturn; 56 | } else { 57 | __tranName.enter = styles.spEnter; 58 | __tranName.enterActive = styles.spEnterActive; 59 | __tranName.leave = styles.spLeave; 60 | __tranName.leaveActive = styles.spLeaveActive; 61 | __tranName.appear = styles.spAppear; 62 | __tranName.appearActive = styles.spAppearActive; 63 | } 64 | return __tranName; 65 | } 66 | 67 | getTitle() { 68 | const title = Utility.getContent('__URL_TITLE_INFO_'); 69 | if (title) { 70 | return title.Title; 71 | } 72 | return '默认标题'; 73 | } 74 | 75 | render() { 76 | const __timeout = 500; 77 | const { context } = this; 78 | const { router } = context || {}; 79 | const { route } = router || {}; 80 | const { location } = route || {}; 81 | const { key } = location || {}; 82 | 83 | return ( 84 |
85 | 92 | 93 | {this.props.children} 94 | 95 | 96 |
97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/containers/page4/page4.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Utility, ListItem } from 'components'; 3 | const comStyles = require('styles/Common.scss'); 4 | const styles = require('./page.scss'); 5 | 6 | export default class Page4 extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { DataSource: [] }; 10 | } 11 | 12 | componentWillMount() { 13 | console.log('--1----will mount------'); 14 | } 15 | componentDidMount() { 16 | console.log('--2----did mount------'); 17 | } 18 | componentWillReceiveProps(nextProps, nextState) { 19 | const { a } = nextState; 20 | if (a) { 21 | console.log(a); 22 | } 23 | console.log('--3----will receive props------------'); 24 | } 25 | shouldComponentUpdate(nextProps, nextState, nextContext) { 26 | const { a } = nextContext; 27 | if (a) { 28 | console.log(a); 29 | } 30 | 31 | console.log('--4----should update-----------------'); 32 | return true; 33 | } 34 | componentWillUpdate(nextProps, nextState, nextContext) { 35 | const { a } = nextContext; 36 | if (a) { 37 | console.log(a); 38 | } 39 | console.log('--5----will update-------------'); 40 | } 41 | componentDidUpdate(prevProps, prevState) { 42 | const { a } = prevState; 43 | if (a) { 44 | console.log(a); 45 | } 46 | console.log('--6----did update--12---------'); 47 | } 48 | componentWillUnmount() { 49 | console.log('--7----will unmount-----------'); 50 | } 51 | 52 | 53 | onListItemDelete(item, index) { 54 | this.state.DataSource.splice(index, 1); 55 | this.setState({ CurrentDate: new Date() }); 56 | } 57 | 58 | componentDidCatch() { 59 | console.log('------------did catch-------------'); 60 | } 61 | 62 | btnUpdateDate() { 63 | // new Date().toLocaleTimeString(); 64 | const { DataSource } = this.state; 65 | DataSource.push({ id: DataSource.length + 1, CurrentDate: new Date(), Name: '哈哈__' }); 66 | 67 | this.setState({ CurrentDate: new Date() }); 68 | } 69 | btnArrayObjectSort() { 70 | const ArrayObj = [ 71 | { Count: 7, name: 1 }, 72 | { Count: 4, name: 2 }, 73 | { Count: 245, name: 3 }, 74 | { Count: 1, name: 4 }, 75 | { Count: 2, name: '张三5' }, 76 | { Count: 21, name: '张6三' }, 77 | { Count: 80, name: '张8三' }, 78 | ]; 79 | 80 | const sortResult = ArrayObj.sort((a, b) => a.Count > b.Count); 81 | console.log(JSON.stringify(sortResult)); 82 | console.log('-----------排序--------------'); 83 | const aaaList = []; 84 | for (let i = 0; i < 10000; i += 1) { 85 | aaaList.push(Math.round(Math.random() * 100)); 86 | } 87 | const _BeginDate = new Date().getTime(); 88 | console.log('----------开始--------', _BeginDate); 89 | // console.log(JSON.stringify(aaaList)); 90 | const aaaCountInfo = {}; 91 | aaaList.forEach((key) => { 92 | if (!aaaCountInfo[key]) { 93 | aaaCountInfo[key] = { Count: 0, Key: key, name: '张——' }; 94 | } 95 | aaaCountInfo[key].Count += 1; 96 | }); 97 | // console.log(JSON.stringify(aaaCountInfo)); 98 | console.log('------------------------'); 99 | const aaaValue = Object.values(aaaCountInfo); 100 | // console.log(JSON.stringify(aaaValue)); 101 | console.log('------------------------'); 102 | const CountResult = aaaValue.sort((a, b) => a.Count - b.Count); 103 | console.log('------------------------'); 104 | console.log(JSON.stringify(CountResult)); 105 | console.log('----------开始--------', new Date().getTime() - _BeginDate); 106 | 107 | this.state.index = 0; 108 | this.setState({ index: this.state.index += 1 }); 109 | } 110 | 111 | render() { 112 | const { CurrentDate, DataSource, index } = this.state; 113 | return ( 114 |
115 |
116 | 117 | 118 |
119 | 120 |
121 |
单击事件
122 |
数组排序
123 | 124 |
125 |
{index}{CurrentDate && CurrentDate.toLocaleTimeString()}
126 | 127 |
128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | webpack react redux router es6 9 | 127 | 128 | 129 | 130 |
131 | 132 | 133 | -------------------------------------------------------------------------------- /src/redux/reducers/reduxCommon.js: -------------------------------------------------------------------------------- 1 | // import Utility from '../../Common/Utility'; 2 | const __Base = 'XIAOTUNI/REDUX/Common/'; 3 | const Load = __Base + 'Load'; 4 | const LoadSuccess = __Base + 'LoadSuccess'; 5 | const LoadFail = __Base + 'LoadFail'; 6 | 7 | const _BaseNBEditTitle = __Base + 'NavBar/'; 8 | const NbTitleEdit = _BaseNBEditTitle + 'Title_Edit'; 9 | const NbTitleInfo = _BaseNBEditTitle + 'Title_Info'; 10 | 11 | const __UpdateTime = __Base + '/__UpdateTime'; 12 | const __DeleteStateByFields = __Base + '/DeleteStateByFields'; 13 | // 清空信息 14 | const ClearContentSuccess = __Base + '/ClearContentSuccess'; 15 | 16 | /** 17 | * 设置变量操作 18 | * @type {string} 19 | */ 20 | const OnSetContent = __Base + 'OnSetContent'; 21 | const UrlParamsEdit = __Base + 'UrlParamsEdit'; 22 | 23 | const __API = __Base + 'API/'; 24 | const __POST = __API + '/POST'; // POST方法处理 25 | const __PUT = __API + '/PUT'; // PUT方法处理 26 | const __DELETE = __API + '/DELETE'; // DELETE方法处理 27 | const __GET = __API + '/GET'; // GET方法处理 28 | 29 | let __TEMP_SERVICES_SYSTEM_TIEM; 30 | const initialState = { loaded: false, __Times: 0, }; 31 | 32 | 33 | export default function reducer(state = initialState, action = {}) { 34 | const __Result = { ...state }; 35 | if (action.result) { // 这里就是请求完成了 36 | Object.assign(__Result, { loading: false, loaded: true }); 37 | __Result.Result = action.result; 38 | } 39 | if (action.error) { // 请求完了之后出错信息 40 | Object.assign(__Result, { loading: false, loaded: false }); 41 | __Result.Error = action.error; 42 | } 43 | 44 | const { StateName, result } = action; 45 | const { __CurrentSystemTimes } = result || {}; 46 | if (__CurrentSystemTimes) { 47 | __TEMP_SERVICES_SYSTEM_TIEM = __CurrentSystemTimes; 48 | } 49 | __Result.CurrentSystemTimes = __TEMP_SERVICES_SYSTEM_TIEM; 50 | __Result.SystemTimes = __TEMP_SERVICES_SYSTEM_TIEM; 51 | switch (action.type) { 52 | case OnSetContent: 53 | __Result[action.Key] = action.Value; 54 | break; 55 | case Load: // 加载 56 | __Result.loading = true; 57 | break; 58 | case LoadSuccess: // 加载成功 59 | __Result.data = action.result; 60 | break; 61 | case NbTitleInfo: // 修改标题,是否显示返回按键 62 | __Result.Title = action.Title || '默认标题'; 63 | __Result.IsShowBackArrow = action.IsShowBackArrow ? action.IsShowBackArrow : false; 64 | break; 65 | case NbTitleEdit: // 修改标题 66 | __Result.Title = action.Title || '默认标题'; 67 | break; 68 | case UrlParamsEdit: // url 参数 69 | __Result.UrlParams = action.query; 70 | break; 71 | case LoadFail: // 加载失败 72 | break; 73 | case __DELETE: 74 | break; 75 | case __POST: 76 | case __PUT: 77 | if (StateName) { 78 | __Result[StateName] = result; 79 | } 80 | break; 81 | case __GET: 82 | __Result[StateName || 'GetResult'] = result; 83 | // __Result = __ProcessRequestByGet(state, action); 84 | break; 85 | case __UpdateTime: 86 | __Result.__Times += 1; 87 | break; 88 | case ClearContentSuccess: 89 | __Result[action.Key] = null; // 清空信息 90 | break; 91 | default: 92 | } 93 | return __Result; 94 | } 95 | 96 | function __RequestProcess(method, StateName, api, args) { 97 | const __Method = { 98 | get: __GET, post: __POST, put: __PUT, del: __DELETE 99 | }; 100 | return { 101 | types: [Load, __Method[method] || __GET, LoadFail], 102 | promise: (client) => client[method || 'get'](api, args), 103 | Condition: args, 104 | StateName 105 | }; 106 | } 107 | 108 | /** 109 | * post保存接口调用方法。 110 | * 111 | * @export 112 | * @param {any} StateName 113 | * @param {any} Api 114 | * @param {any} Args {params:{}:data:{}} 115 | * @returns 116 | */ 117 | export function onApiPost(StateName, Api, Args) { 118 | return __RequestProcess('post', StateName, Api, Args); 119 | } 120 | 121 | /** 122 | * put方法,修改方法操作。 123 | * @export 124 | * @param {any} StateName 125 | * @param {any} Api 126 | * @param {any} Args 127 | * @returns 128 | */ 129 | export function onApiPut(StateName, Api, Args) { 130 | return __RequestProcess('put', StateName, Api, Args); 131 | } 132 | 133 | /** 134 | * 删除 135 | * 136 | * @export 137 | * @param {any} Api 138 | * @param {any} Args 139 | * @returns 140 | */ 141 | export function onApiDelete(Api, Args) { 142 | return __RequestProcess('del', null, Api, Args); 143 | } 144 | 145 | /** 146 | * 获取 147 | * 148 | * @export 149 | * @param {any} StateName 150 | * @param {any} Api 151 | * @param {any} Args 152 | * @returns 153 | */ 154 | export function onApiGet(StateName, Api, Args) { 155 | return __RequestProcess('get', StateName, Api, Args); 156 | } 157 | 158 | /** 159 | * 删除List的数据. 160 | * 161 | * @export 162 | * @param {any} StateName 163 | * @param {any} Fields [{Key:'SourceId',Value:123},...] or {Key:'SourceId',Value:123} 164 | * @returns 165 | */ 166 | export function onDeleteByFields(StateName, Fields) { 167 | return { type: __DeleteStateByFields, StateName, Fields }; 168 | } 169 | 170 | /** 171 | * 清空信息 172 | * @param {key} string 173 | * @param value 174 | * @returns 175 | */ 176 | export function onClearContent(key) { 177 | return { type: ClearContentSuccess, Key: key }; 178 | } 179 | 180 | export function onUpdateRedux() { 181 | return { type: __UpdateTime }; 182 | } 183 | 184 | /** 185 | * 保存信息到 store里面去。 186 | * @param key 187 | * @param value 188 | * @returns {{type: string, Key: *, Value: *}} 189 | */ 190 | export function onSetContent(key, value) { 191 | return { type: OnSetContent, Key: key, Value: value }; 192 | } 193 | -------------------------------------------------------------------------------- /src/containers/Es6/Es6.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const comStyles = require('styles/Common.scss'); 4 | const styles = require('./scss/Es6.scss'); 5 | 6 | export default class Es6 extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {}; 10 | } 11 | 12 | componentWillMount() { 13 | // this.testDemo(); 14 | // this.testClassDemo(); 15 | try { 16 | this.testObjectDefineProperty(); 17 | this.testGenerator(); 18 | } catch (ex) { 19 | console.log(ex); 20 | } 21 | } 22 | printLine(args) { 23 | console.log('-------------------' + (args || '') + '-------------------'); 24 | } 25 | testGenerator() { 26 | function* tempOther() { 27 | yield 11; 28 | yield 12; 29 | yield 13; 30 | } 31 | 32 | function* temp() { 33 | yield 1; 34 | yield 2; 35 | yield* tempOther(); 36 | yield 4; 37 | return '最后啦'; 38 | } 39 | const list = temp(); 40 | for (const a of list) { 41 | console.log(a); 42 | } 43 | 44 | class Tree { 45 | constructor(value, children) { 46 | this.value = value; 47 | this.children = children; 48 | } 49 | 50 | * print() { 51 | yield this.value; 52 | for (const child of this.children) { 53 | yield* child.print(); 54 | } 55 | } 56 | } 57 | this.printLine(); 58 | const __tree = new Tree(0, [ 59 | new Tree(11, [new Tree(111, [])]), 60 | new Tree(22, [new Tree(222, [])]), 61 | new Tree(33, [new Tree(333, [])]), 62 | ]); 63 | 64 | for (const tree of __tree.print()) { 65 | console.log(tree); 66 | } 67 | this.printLine(); 68 | const isEves = (x) => x % 2 === 0; 69 | console.log(isEves(10)); 70 | const notEves = (fn) => (...args) => { 71 | console.log('--------------b----------'); 72 | return !fn(args); 73 | }; 74 | 75 | console.log(notEves(isEves)(10)); 76 | 77 | this.printLine(); 78 | } 79 | 80 | testObjectDefineProperty() { 81 | const obj = {}; 82 | Object.defineProperty(obj, 'field', { 83 | value: 1234, 84 | writable: true, 85 | enumerable: true, // 可以枚举。 86 | }); 87 | Object.defineProperty(obj, 'field2', { 88 | enumerable: true, // 可以枚举。 89 | get() { 90 | return this._field2; 91 | }, 92 | set(newValue) { 93 | if (this._field2 !== newValue) { 94 | this._field2 = newValue; 95 | } 96 | } 97 | }); 98 | obj.field = 111; 99 | obj.field2 = '哈哈'; 100 | obj._field2 = 'private field'; 101 | console.log(Object.keys(obj)); 102 | console.log(obj); 103 | this.printLine(); 104 | 105 | const [a, b, c, d, ...e] = [[1, 2], [3, 4], [5, 6], [7, 8], 11, 22, 33, 44, 55]; 106 | // const [a, b, c, d, e] = [1, 2, 3, 4, 5]; 107 | console.log(a, b, c, d, e); 108 | const { field: xxField, field } = obj; 109 | console.log(xxField); 110 | console.log(field); 111 | const [aa, bb, ...cc] = '哈啦是不是的呀'; 112 | console.log(aa, bb, cc); 113 | } 114 | 115 | testClassDemo() { 116 | class Point { 117 | constructor(x, y) { 118 | this.x = x; 119 | this.y = y; 120 | } 121 | toString() { 122 | const value = 'x:' + this.x + ' y:' + this.y; 123 | return value; 124 | } 125 | } 126 | Object.assign(Point.prototype, { 127 | sayHello() { 128 | console.log('say hello'); 129 | } 130 | }); 131 | const p = new Point(1, 2, 'bbc'); 132 | this.printLine(p.toString()); 133 | p.sayHello(); 134 | const a = p.__proto__.hasOwnProperty('toString'); 135 | console.log(a); 136 | 137 | class GetSetClass { 138 | get(name) { 139 | return this[name]; 140 | } 141 | set(name, value = '默认值') { 142 | this[name] = value; 143 | } 144 | } 145 | 146 | const gs = new GetSetClass(); 147 | gs.set('name01', '张三'); 148 | gs.set('name00'); 149 | console.log('--------', gs.get('name00')); 150 | console.log('--------', gs.get('name01')); 151 | } 152 | 153 | testDemo() { 154 | const arr = [1, 2, 3]; 155 | const [a, b, c] = arr; 156 | console.log(a, b, c); 157 | const [foo, [[bar], baz]] = [1, [[2], 3]]; 158 | console.log(foo, bar, baz); 159 | const [, , third] = ['foo', 'bar', 'baz']; 160 | console.log(third); 161 | const [head, ...tail] = [1, 2, 3, 4]; 162 | console.log(head, tail); 163 | console.log('-----------------------es6 1-------------'); 164 | // const [first, second, third1, fourth, fifth, sixth] = fibs(); 165 | // console.log(first, second, third1, fourth, fifth, sixth); 166 | 167 | function* helloWorldGenerator() { 168 | yield 'hello'; 169 | yield 'world'; 170 | return 'ending'; 171 | } 172 | 173 | const hw = helloWorldGenerator(); 174 | console.log(hw); 175 | 176 | this.testIterator(); 177 | } 178 | testIterator() { 179 | console.log('------------testIterator-------------'); 180 | const makeIterator = (arr) => { 181 | let nextIndex = -1; 182 | return { 183 | next: () => { 184 | return nextIndex < arr.length ? 185 | { value: arr[nextIndex += 1], done: false } : { value: undefined, done: true }; 186 | } 187 | }; 188 | }; 189 | const it = makeIterator(['a', 'b', 'c']); 190 | console.log(it.next()); 191 | console.log(it.next()); 192 | console.log(it.next()); 193 | console.log(it.next()); 194 | 195 | this.printLine('Iterator'); 196 | 197 | const arr = [1, 2, 3, 4, 5, 6]; 198 | const iter = arr[Symbol.iterator](); 199 | console.log(iter.next()); 200 | console.log(iter.next()); 201 | console.log(iter.next()); 202 | console.log(iter.next()); 203 | console.log(iter.next()); 204 | console.log(iter.next()); 205 | console.log(iter.next()); 206 | this.printLine('||||||||'); 207 | 208 | const obj = (value) => { 209 | this.value = value; 210 | this.next = null; 211 | }; 212 | obj.prototype[Symbol.iterator] = () => { 213 | let current = this; 214 | const nexta = () => { 215 | if (current) { 216 | const value = current.value; 217 | current = value; 218 | return { value, done: false }; 219 | } 220 | return { done: true }; 221 | }; 222 | const iterator = { next: nexta }; 223 | return iterator; 224 | }; 225 | const place = 55.03; 226 | const a = (place * 24000 * 0.92) - 50000; 227 | const b = a / 2; 228 | 229 | this.printLine(a + ' ' + b); 230 | } 231 | 232 | render() { 233 | return ( 234 |
235 |
aecdqqaa
236 |
237 | ); 238 | } 239 | } 240 | 241 | -------------------------------------------------------------------------------- /src/Common/Utility.js: -------------------------------------------------------------------------------- 1 | export default class Utility { 2 | static __Instance; 3 | 4 | constructor() { 5 | this._TempSaveContent = {}; 6 | this.__ConstPrefix = 'WeiXinXTN'; 7 | } 8 | 9 | /** 10 | * 实例 11 | * @returns {*} 12 | */ 13 | static instance() { 14 | if (this.__Instance === null || typeof this.__Instance === 'undefined') { 15 | this.__Instance = new this(); 16 | } 17 | return this.__Instance; 18 | } 19 | 20 | /** 21 | * 常量 22 | * @type {{SaveUrlPath: string}} 23 | */ 24 | static constItem = { 25 | PageSize: 15, // 每页大小数据 26 | CaptchaTimeout: 60, 27 | /** 28 | * 当前的上下文 29 | */ 30 | Context: 'XTNContext', // 当前页面的Context 31 | ReduxKey: { 32 | ManualInputModify: 'ManualInputModify', 33 | }, 34 | /** 35 | * 事件 36 | */ 37 | Event: 'onXTNEvent', // 事件。 38 | Events: { 39 | HttpStatus: { 40 | 1: 'onHttpStatus_XTN_1', 41 | 200: 'onHttpStatus_XTN_200', // 处理成功 42 | 400: 'onHttpStatus_XTN_400', // 请求无效 43 | 401: 'onHttpStatus_XTN_401', // 未授权访问 44 | 402: 'onHttpStatus_XTN_402', 45 | 403: 'onHttpStatus_XTN_403', // 禁止访问 46 | 404: 'onHttpStatus_XTN_404', // 资源未找到 47 | 405: 'onHttpStatus_XTN_405', 48 | 406: 'onHttpStatus_XTN_406', 49 | 407: 'onHttpStatus_XTN_407', 50 | 408: 'onHttpStatus_XTN_408', 51 | 409: 'onHttpStatus_XTN_409', 52 | 411: 'onHttpStatus_XTN_411', // 登陆超时 53 | 500: 'onHttpStatus_XTN_500', // 服务器错误 54 | 501: 'onHttpStatus_XTN_501', 55 | 502: 'onHttpStatus_XTN_502', 56 | 503: 'onHttpStatus_XTN_503', 57 | }, 58 | ShowModel: { 59 | OnActionSheet: 'onXTN_ShowModel_ActionSheet', // 60 | OnLoading: 'onXTN_ShowModel_Loading', // 加载 61 | OnAlert: 'onXTN_ShowModel_Alert', // 弹出信息 62 | OnConfirm: 'onXTN_ShowModel_Confirm', // 确定--取消 63 | OnShowDialog: 'onXTN_ShowModel_ShowDialog', // 打开对话框 64 | OnShowDialogHide: 'onXTN_ShowModel_ShowDialogHide', // 隐藏对话框 65 | OnShowDialogClose: 'onXTN_ShowModel_ShowDialogClose', // 关闭对话框 66 | OnActionSheetHide: 'onXTN_ShowModel_ActionSheetHide', // 关闭 67 | OnLoadingHide: 'onXTN_ShowModel_LoadingHide', 68 | OnConfirmHide: 'onXTN_ShowModel_ConfirmHide', 69 | }, 70 | OnGoBack: 'onXTNEvent_GoBack', // 页面退回事件 71 | OnEditNavBarTitle: 'onXTNEvent_EditNavBarTitle', // 修改导航条标题 72 | OnEditNavBarRight: 'onXTNEvent_EditNavBarRight', // 修改导航条右边 73 | OnEditPageSliderInfo: 'onXTNEvent_EditPageSliderInfo', // 页面切换 74 | OnOpenDatePicker: 'onXTNEvent_OnOpenDatePicker', // 打开日期控件 75 | OnKeyboard: 'onXTNEvent_Keyboard', // 获取焦点键盘弹起;失去焦点键盘消失 76 | OnSetTitle: 'onXTNEvent_OnSetTitle', // 修改导航条的标题 77 | }, 78 | /** 79 | * url 列表 80 | */ 81 | UrlItem: { 82 | GoBack: 'goBack', // 回退操作 83 | Login: 'login', // 登录 84 | Page1: 'page1', // 首页-->商品列表 85 | Page2: 'page2', // 首页-->商品列表 86 | Page3: 'page3', // 首页-->商品列表 87 | Page4: 'page4', // 首页-->商品列表 88 | Counter: 'counter', // 首页-->商品列表 89 | UserInfo: 'userinfo', // 首页-->商品列表 90 | Es6: 'es6', // 首页-->商品列表 91 | }, 92 | UrlTitle: { 93 | '/': { Title: '默认页面', Index: 0 }, 94 | '/page1': { Title: 'page1', Index: 0 }, 95 | '/page2': { Title: 'page2', Index: 0 }, 96 | '/page3': { Title: 'page3', Index: 0 }, 97 | '/page4': { Title: 'page4', Index: 0 }, 98 | '/counter': { Title: '计数', Index: 0 }, 99 | '/userinfo': { Title: '用户信息', Index: 0 }, 100 | '/es6': { Title: 'Es6', Index: 0 }, 101 | }, 102 | /** 103 | * 显示模式 104 | */ 105 | ShowModel: { 106 | ActionSheet: 'XTNShowModelActionSheet', // 107 | Loading: 'XTNShowModelLoading', // 加载 108 | Alert: 'XTNShowModelAlert', // 弹出信息 109 | Confirm: 'XTNShowModelConfirm', // 确定--取消 110 | }, 111 | KeyHistory: 'XTN_KEY_HISTORY', 112 | KeyGoBack: 'XTN_KEY_GOBACK' 113 | } 114 | 115 | /** 116 | * 是否是数组 117 | * @param obj 118 | * @returns {boolean} 119 | */ 120 | static isArray(obj) { 121 | if (!obj || !obj.length || obj.length === 0) { 122 | return false; 123 | } 124 | return Array.isArray(obj); 125 | } 126 | 127 | /** 128 | * 判断是否为空 129 | * true-为空;false-不为空 130 | * @param obj 131 | * @returns {boolean} 132 | */ 133 | static isNull(obj) { 134 | return obj === null; 135 | } 136 | 137 | /** 138 | * 判断是否是微信打开的 139 | * @returns {boolean} 140 | */ 141 | static isWeiXin() { 142 | try { 143 | const ua = window.navigator.userAgent.toLowerCase(); 144 | const isWeiXin = ua.match(/micromessenger/i).indexOf('micromessenger'); 145 | console.log(isWeiXin); 146 | return isWeiXin >= 0; 147 | } catch (ex) { 148 | return false; 149 | } 150 | } 151 | 152 | /** 153 | * 浏览器信息 154 | * @returns {Browser} 155 | */ 156 | static browserInfo() { 157 | const _Browser = { 158 | versions: () => { 159 | const uu = navigator.userAgent; 160 | // const app = navigator.appVersion; 161 | return { 162 | trident: uu.indexOf('Trident') > -1, // IE内核 163 | presto: uu.indexOf('Presto') > -1, // opera内核 164 | webKit: uu.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核 165 | gecko: uu.indexOf('Gecko') > -1 && uu.indexOf('KHTML') === -1, // 火狐内核 166 | mobile: !!uu.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端 167 | ios: !!uu.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端 168 | android: uu.indexOf('Android') > -1 || uu.indexOf('Adr') > -1, // android终端 169 | iPhone: uu.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器 170 | iPad: uu.indexOf('iPad') > -1, // 是否iPad 171 | webApp: uu.indexOf('Safari') === -1, // 是否web应该程序,没有头部与底部 172 | weixin: uu.indexOf('MicroMessenger') > -1, // 是否微信 (2015-01-22新增) 173 | qq: uu.match(/\sQQ/i) === ' qq' // 是否QQ 174 | }; 175 | }, 176 | language: (navigator.browserLanguage || navigator.language).toLowerCase() 177 | }; 178 | return _Browser; 179 | } 180 | 181 | /** 182 | * 是否IOS系统 183 | * 184 | * @static 185 | * @returns 186 | * 187 | * @memberOf Utility 188 | */ 189 | static $isIOS() { 190 | try { 191 | const u = navigator.userAgent; 192 | const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); 193 | return isIOS; 194 | } catch (ex) { 195 | console.log(ex); 196 | return false; 197 | } 198 | } 199 | 200 | /** 201 | * 获取android版本 202 | * 203 | * @static 204 | * @returns 205 | * 206 | * @memberOf Utility 207 | */ 208 | static $androidVersion() { 209 | const { userAgent } = navigator; 210 | if (userAgent.indexOf('Android') > -1 || userAgent.indexOf('Linux') > -1) { 211 | const num = userAgent.substr(userAgent.indexOf('Android') + 8, 3); 212 | return { type: 'Android', version: num }; 213 | } 214 | return null; 215 | } 216 | 217 | /** 218 | * 对Date的扩展,将 Date 转化为指定格式的String 219 | * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 220 | * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 221 | * @method __FormatDate 222 | * @param fmt 223 | * @param date 224 | * @return {*} 225 | * @example 226 | * Utility.FormatDate('yyyy-MM-dd hh:mm:ss.S',new Date()); 227 | * @constructor 228 | */ 229 | static formatDate(fmt, date) { 230 | if (!date) { 231 | return ''; 232 | } 233 | let __this = new Date(); 234 | let _fmt = fmt || 'yyyy-MM-dd HH:mm:ss.S'; 235 | if (date !== null) { 236 | if (Date.parse(date)) { 237 | __this = date; 238 | } else { 239 | try { 240 | __this = new Date(date); 241 | } catch (ex) { 242 | __this = new Date(); 243 | } 244 | } 245 | } 246 | const oo = { 247 | 'M+': __this.getMonth() + 1, // 月份 248 | 'd+': __this.getDate(), // 日 249 | 'D+': __this.getDate(), // 日 250 | 'H+': __this.getHours(), // 小时 251 | 'h+': __this.getHours(), // 小时 252 | 'm+': __this.getMinutes(), // 分 253 | 's+': __this.getSeconds(), // 秒 254 | 'q+': Math.floor((__this.getMonth() + 3) / 3), // 季度 255 | S: __this.getMilliseconds() // 毫秒 256 | }; 257 | if (/(y+)/.test(_fmt)) { 258 | /(y+)/.exec(_fmt); 259 | // const aa = /(y+)/.test(_fmt); 260 | // if (aa) { 261 | const fmt1 = _fmt.replace(RegExp.$1, (__this.getFullYear() + '').substr(4 - RegExp.$1.length)); 262 | _fmt = fmt1; 263 | // } 264 | } 265 | for (const kk in oo) { 266 | if (new RegExp('(' + kk + ')').test(fmt)) { 267 | _fmt = _fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (oo[kk]) : (('00' + oo[kk]).substr(('' + oo[kk]).length))); 268 | } 269 | } 270 | return _fmt; 271 | } 272 | 273 | /** 274 | * 打印输出日志 275 | * @method __PrintLog 276 | * @param {object} args 内容 277 | * @private 278 | */ 279 | static printLog(args) { 280 | try { 281 | let __callmethod = ''; 282 | try { 283 | throw new Error(); 284 | } catch (ex) { 285 | // console.log(e.stack); 286 | __callmethod = ex.stack.replace(/Error\n/).split(/\n/)[1].replace(/^\s+|\s+$/, ''); 287 | } 288 | 289 | const _curDate = new Date(); 290 | const _aa = _curDate.toLocaleDateString() + ' ' + _curDate.toLocaleTimeString() + '.' + _curDate.getMilliseconds(); 291 | console.log('--begin->', _aa, ' call method :', __callmethod); 292 | const __content = JSON.stringify(args); 293 | console.log(__content); 294 | } catch (ex) { 295 | console.log('---------输出日志,传入的内容传为JSON出现在异常--------------'); 296 | console.log(ex); 297 | console.log('---------输出日志,内容为下--------------'); 298 | console.log(args); 299 | } 300 | } 301 | 302 | /** 303 | * 判断输入的是否是手机号 304 | * @method __PhonePattern 305 | * @param {number} phone 手机号 306 | * @return {boolean} true 成功;false 失败 307 | * @example 308 | * Utility.PhonePattern('13000100100'); 309 | * @private 310 | */ 311 | static PhonePattern(phone) { 312 | const ex = /^(13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9])\d{8}$/; 313 | return ex.test(phone); 314 | } 315 | 316 | /** 317 | * 密码验证 318 | * @method __PasswordPattern 319 | * @param {string} password 密码 320 | * @return {boolean} true 成功;false 失败 321 | * @private 322 | */ 323 | static PasswordPattern(password) { 324 | // test('/^[_0-9a-z]{6,16}$/i'); 325 | const ex = /^[_0-9a-zA-Z]{6,25}$/; 326 | return ex.test(password); 327 | } 328 | 329 | /** 330 | * 是否含有中文(也包含日文和韩文) 331 | * @method __IsChineseChar 332 | * @param {string} str 要判断的内容 333 | * @return {boolean} true:成功;false:失败. 334 | * @private 335 | */ 336 | static IsChineseChar(str) { 337 | // const reg = !/^[\u4E00-\u9FA5]/g; // \uF900-\uFA2D 338 | // return !/^[\u4e00-\u9fa5]+$/gi.test(str);// .test(str); 339 | 340 | const regu = '^[\u4e00-\u9fa5]+$'; 341 | const re = new RegExp(regu); 342 | return re.test(str); 343 | } 344 | 345 | /** 346 | * 设置内容,这里主要是用来存放临时数据的。 347 | * @method _SetContent 348 | * @param key 键值,用于下次的时候获取内容用的。其实就是 _TempSaveContent的属性名称。 349 | * @param content 要存储的内容 350 | * @param isSaveLocalStorage 是否保存到本地存储里面 351 | * @param IsUser 根据用户uid 来获取缓存里的数据。 352 | * @private 353 | */ 354 | static setContent(key, content, isSaveLocalStorage, IsUser) { 355 | try { 356 | const self = this.instance(); 357 | if (isSaveLocalStorage) { 358 | let __Content = content; 359 | if (IsUser) { 360 | const __UserInfo = self._TempSaveContent[this.constItem.API.UserInfo]; 361 | if (typeof __UserInfo !== 'undefined' && __UserInfo !== null) { 362 | __Content = {}; 363 | __Content[__UserInfo.member_id] = content; 364 | } 365 | } 366 | __Content = JSON.stringify(__Content); 367 | // __content = CryptoJS.AES.encrypt(__Content, __key); 368 | 369 | if (typeof window !== 'undefined') { 370 | window.localStorage.setItem(key, __Content); 371 | } 372 | } 373 | self._TempSaveContent[key] = content; 374 | } catch (ex) { 375 | console.log(ex); 376 | } 377 | } 378 | 379 | /** 380 | * 删除指定字段值。 381 | * @method __RemoveContent 382 | * @param key 383 | * @return {null} 384 | * @private 385 | */ 386 | static removeContent(key, IsRemoveLocalStorage) { 387 | try { 388 | const __self = this.instance(); 389 | if (key === null || typeof key === 'undefined') { 390 | return; 391 | } 392 | if (__self._TempSaveContent[key]) { 393 | delete __self._TempSaveContent[key]; 394 | } 395 | 396 | if (IsRemoveLocalStorage && typeof window !== 'undefined') { 397 | window.localStorage.removeItem(key); 398 | } 399 | } catch (ex) { 400 | this.printLog(ex.toString()); 401 | } 402 | } 403 | 404 | /** 405 | * 获取内容, 406 | * @method _GetContent 407 | * @param key 健名称。其实就是 _TempSaveContent的属性名称。 408 | * @return {*} 返回内容 409 | * @private 410 | */ 411 | static getContent(key, IsUser) { 412 | try { 413 | let __Content = null; 414 | const __self = this.instance(); 415 | if (__self._TempSaveContent[key]) { 416 | __Content = __self._TempSaveContent[key]; 417 | return __Content; 418 | } 419 | if (typeof window === 'undefined') { 420 | return null; 421 | } 422 | if (__Content === null || typeof __Content === 'undefined') { 423 | const _value = window.localStorage.getItem(key); 424 | if (_value !== null && _value !== '' && typeof _value !== 'undefined') { 425 | const __JSONValue = JSON.parse(_value); 426 | __self._TempSaveContent[key] = __JSONValue; 427 | if (IsUser) { 428 | // 429 | } 430 | __Content = __self._TempSaveContent[key]; 431 | } 432 | } 433 | 434 | return __Content; 435 | } catch (ex) { 436 | console.log(ex); 437 | return null; 438 | } 439 | } 440 | 441 | /** 442 | * 判断是否是函数 443 | * @param func 判断函数对象 444 | * @returns {boolean} true:成功,false:失败。 445 | */ 446 | static isFunction(func) { 447 | if (func !== null && typeof func !== 'undefined' && func.constructor.name === 'Function') { 448 | return true; 449 | } 450 | return false; 451 | } 452 | 453 | /** 454 | * 判断是否未定义 455 | * @param obj 判断对象 456 | * @returns {boolean} true:成功,false:失败。 457 | */ 458 | static isUndefined(obj) { 459 | if (typeof obj === 'undefined') { 460 | return true; 461 | } 462 | return false; 463 | } 464 | 465 | /** 466 | * 判断是否未定义 467 | * @param obj 判断对象 468 | * @returns {boolean} true:成功,false:失败。 469 | */ 470 | static isInvalid(obj) { 471 | if (typeof obj === 'undefined' || obj === null || obj === '' || obj === {}) { 472 | return true; 473 | } 474 | return false; 475 | } 476 | 477 | /** 478 | * 判断是否定义。 479 | * @param obj 判断对象 480 | * @return {boolean} true:成功,false:失败。 481 | */ 482 | static isDefined(obj) { 483 | if (typeof obj !== 'undefined') { 484 | return true; 485 | } 486 | return false; 487 | } 488 | 489 | /** 490 | * 判断是否是日期类型 491 | * 492 | * @static * @param {any} obj 判断对象 493 | * @returns {boolean} true: 是日期,false:不是日期。 494 | * @example 495 | * Utility.isDate('abcadfa') ---> false 496 | * Utility.isDate(new Date()) ---> true 497 | * Utility.isDate('2013年10月10日') ---> true 498 | * @memberOf Utility 499 | */ 500 | static isDate(obj) { 501 | if (typeof obj === 'undefined' || obj === null || obj === '') { // 判断是不是 undefined,或 null 502 | return false; 503 | } 504 | const __isDate = obj.constructor.name === 'Date'; // 如果传入的就是日期 505 | if (__isDate) { 506 | return true; 507 | } 508 | try { 509 | return (new Date(obj.replace('年', '-').replace('月', '-').replace('日', ''))).constructor.name === 'Date'; 510 | } catch (ex) { 511 | return false; 512 | } 513 | } 514 | 515 | /** 516 | * 将一个 对象转成url参数与&分开 517 | * 518 | * @param params 参数对象 519 | * @param split 分割符 520 | * @returns {*} 521 | * @example {a:a,b:b,c:c,e:e} 522 | * a=a&b=b&c=c&e=e 523 | */ 524 | static convertToUrlParams(params, options) { 525 | const { split, notFields } = options || {}; 526 | if (this.isUndefined(params) || params === null) { 527 | return ''; 528 | } 529 | const __KeyValue = []; 530 | const self = this; 531 | const __JSONValue = (value) => { 532 | try { 533 | let __JValue; 534 | if (value === null) { 535 | return ''; 536 | } 537 | const { constructor } = value; 538 | if (typeof constructor === 'undefined' || constructor === null) { 539 | return ''; 540 | } 541 | switch (value.constructor.name) { 542 | case 'Object': 543 | __JValue = '{' + this.convertToUrlParams(value) + '}'; 544 | break; 545 | case 'Array': 546 | __JValue = JSON.stringify(value); 547 | break; 548 | default: 549 | __JValue = value; 550 | } 551 | return __JValue; 552 | } catch (ex) { 553 | console.log(ex.message); 554 | return value || ''; 555 | } 556 | }; 557 | Object.keys(params).forEach((key) => { 558 | const __value = params[key]; 559 | if (self.isDefined(__value) && __value !== '') { 560 | if (key.toLowerCase() !== 'IsExistsNextData'.toLowerCase()) { 561 | // const __JsonValue = (self.isArray(__value) ? JSON.stringify(__value) : __value); 562 | if (notFields) { 563 | if (notFields.indexOf(key) === -1) { 564 | __KeyValue.push(key + '=' + __JSONValue(__value)); 565 | } 566 | } else { 567 | __KeyValue.push(key + '=' + __JSONValue(__value)); 568 | } 569 | } 570 | } 571 | }); 572 | return __KeyValue.join(split || '&'); 573 | } 574 | 575 | /** 576 | * 将 map对象 转成 key-value 数组对象 577 | * @param row 578 | * @returns {Array} 579 | */ 580 | static convertMapToObject(row) { 581 | if (this.isUndefined(row) || this.isNull(row) || row === '') { 582 | return []; 583 | } 584 | const __Array = []; 585 | Object.keys(row).forEach((key) => { 586 | const __obj = {}; 587 | __obj.key = key; 588 | __obj.value = row[key]; 589 | __Array.push(__obj); 590 | }); 591 | return __Array; 592 | } 593 | 594 | /** 595 | * 解析状态数据 596 | * @param state 状态 597 | * @param fieldName 字段名称 598 | * @param action 行为 599 | * @returns {*} 600 | */ 601 | static parseStateMoreData(state, fieldName, action) { 602 | if (typeof action.result === 'undefined') { 603 | return state; 604 | } 605 | if (Utility.isUndefined(state[fieldName]) || state[fieldName] === null) { 606 | state[fieldName] = {}; 607 | } 608 | const __Condition = action.Condition || {}; 609 | const __pgIndex = __Condition.pgIndex || 0; 610 | const __pgCount = __Condition.pgCount || this.constItem.PageSize; 611 | const __Result = action.result; 612 | __Condition.IsExistsNextData = action.result.length === __pgCount; 613 | if (__pgIndex !== 0 && Utility.isArray(state[fieldName].List)) { 614 | state[fieldName].List = state[fieldName].List.concat(__Result); 615 | } else { 616 | state[fieldName].List = __Result; 617 | } 618 | __Condition.pgIndex = __Condition.IsExistsNextData ? (__pgIndex + 1) : __pgIndex; 619 | __Condition.pgCount = __pgCount; 620 | state[fieldName].Condition = __Condition; 621 | return state; 622 | } 623 | 624 | /** 625 | * 页面跳转 626 | * @param url 要跳转的页面。 627 | * @param params 参数 628 | */ 629 | static toPage(url, params) { 630 | try { 631 | const __history = this.getContent(this.constItem.KeyHistory); 632 | if (this.isUndefined(url) || url === '' || this.isUndefined(__history)) { 633 | return; 634 | } 635 | this.$loadingHide(); 636 | 637 | if (url === this.constItem.UrlItem.GoBack) { 638 | this.setContent(this.constItem.KeyGoBack, true); 639 | __history.goBack(); 640 | return; 641 | } 642 | const __pathname = '/' + url; 643 | __history.push(__pathname, Object.assign(params || {}, { _timestamp: new Date().getTime() })); 644 | } catch (ex) { 645 | console.log(ex.toString()); 646 | } 647 | } 648 | 649 | /** 650 | * 格式化 651 | * @example 652 | * sprintf('Latitude: %s, Longitude: %s, Count: %d', 41.847, -87.661, 'two') 653 | * Expected output: Latitude: 41.847, Longitude: -87.661, Count: 0 654 | * @returns {*} 655 | */ 656 | static sprintf() { 657 | const args = arguments; 658 | const string = args[0]; 659 | let __index = 1; 660 | return string.replace(/%((%)|s|d)/g, (mm) => { 661 | // m is the matched format, e.g. %s, %d 662 | let val = null; 663 | if (mm[2]) { 664 | val = mm[2]; 665 | } else { 666 | val = args[__index]; 667 | // A switch statement so that the formatter can be extended. Default is %s 668 | switch (mm) { 669 | case '%d': 670 | val = parseFloat(val); 671 | if (!val) { 672 | val = 0; 673 | } 674 | break; 675 | default: 676 | break; 677 | } 678 | __index += 1; 679 | } 680 | return val; 681 | }); 682 | } 683 | 684 | /** 685 | * 格式化 686 | * @example 687 | * format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET'); 688 | * ASP is dead, but ASP.NET is alive! ASP {2} 689 | * @param format 690 | * @returns {*} 691 | */ 692 | static format(format) { 693 | const args = Array.prototype.slice.call(arguments, 1); 694 | return format.replace(/{(\d+)}/g, (match, number) => { 695 | return typeof args[number] !== 'undefined' 696 | ? args[number] : match; 697 | }); 698 | } 699 | 700 | /** 701 | * 解析URL地址 702 | * @method __ParseURL 703 | * @param {string} url 完整的URL地址 704 | * @return {object} 自定义的对象 705 | * @example 706 | * 用法示例:var myURL = parseURL('http://abc.com:8080/dir/index.html?id=255&m=hello#top'); 707 | * myURL.file='index.html' 708 | * myURL.hash= 'top' 709 | * myURL.host= 'abc.com' 710 | * myURL.query= '?id=255&m=hello' 711 | * myURL.params= Object = { id: 255, m: hello } 712 | * myURL.path= '/dir/index.html' 713 | * myURL.segments= Array = ['dir', 'index.html'] 714 | * myURL.port= '8080' 715 | * yURL.protocol= 'http' 716 | * myURL.source= 'http://abc.com:8080/dir/index.html?id=255&m=hello#top' 717 | */ 718 | static parseURL(url) { 719 | const ae = document.createElement('a'); 720 | ae.href = url; 721 | return { 722 | source: url, 723 | protocol: ae.protocol.replace(':', ''), 724 | host: ae.hostname, 725 | port: ae.port, 726 | query: ae.search, 727 | params: (() => { 728 | const ret = {}; 729 | const seg = ae.search.replace(/^\?/, '').split('&'); 730 | const len = seg.length; 731 | let ii = 0; 732 | let ss; 733 | for (; ii < len; ii += 1) { 734 | if (seg[ii]) { 735 | ss = seg[ii].split('='); 736 | ret[ss[0]] = ss[1]; 737 | } 738 | } 739 | return ret; 740 | })(), 741 | file: (ae.pathname.match(/\/([^/?#]+)$/i) || [''])[1], 742 | hash: ae.hash.replace('#', ''), 743 | path: ae.pathname.replace(/^([^/])/, '/$1'), 744 | relative: (ae.href.match(/tps?:\/\/[^/]+(.+)/) || [''])[1], 745 | segments: ae.pathname.replace(/^\//, '').split('/') 746 | }; 747 | } 748 | 749 | /** 750 | * 事件处理 751 | * @param eventName 事件名称 752 | * @param param1 参数名称1 753 | * @param param2 参数名称2 754 | * @param param3 参数名称3 755 | * @param param4 参数名称4 756 | * @param param5 参数名称5 757 | * @param param6 参数名称6 758 | * @param param7 参数名称7 759 | * @param param8 参数名称8 760 | * @param param9 参数名称9 761 | */ 762 | static $emit(eventName, param1, param2, param3, param4, param5, param6, param7, param8, param9) { 763 | if (this.isUndefined(eventName)) { 764 | return; 765 | } 766 | const event = this.getContent(this.constItem.Event); 767 | if (this.isUndefined(event) || event === null) { 768 | return; 769 | } 770 | if (!this.isFunction(event.emit)) { 771 | return; 772 | } 773 | event.emit(eventName, param1, param2, param3, param4, param5, param6, param7, param8, param9); 774 | } 775 | 776 | /** 777 | * 添加事件 778 | * @param eventName {string} 事件名称 779 | * @param callBack {function} 回调的方法名称 780 | */ 781 | static $on(eventName, callBack) { 782 | if (this.isUndefined(eventName)) { 783 | return; 784 | } 785 | const event = this.getContent(this.constItem.Event); 786 | if (this.isUndefined(event) || event === null) { 787 | return; 788 | } 789 | if (!this.isFunction(event.on)) { 790 | return; 791 | } 792 | event.on(eventName, callBack); 793 | } 794 | 795 | /** 796 | * 弹出提示信息 797 | * @param Content 弹出显示内容 798 | * @param Title 弹出显示的标题,可以不填写,默认为当前导航条里的标题 799 | * @param ToPage 弹出来,页面跳转到下一个页面 {Url: Utility.constItem.UrlItem.Login, Options: {}} 800 | * @constructor 801 | */ 802 | static $actionSheet(Content, Title, ToPage) { 803 | this.$emit(this.constItem.Events.ShowModel.OnActionSheet, { 804 | Title, ContentInfo: { Content }, ToPage 805 | }); 806 | } 807 | 808 | /** 809 | * 确定,取消窗体 810 | * @param Message 信息内容 811 | * @param okButton 点击确定事件 812 | * @param Title 弹出窗体上的标题 (可空) 813 | * @param onCancel 点击取消事件 (可空) 814 | */ 815 | static $confirm(Message, okButton, Title, onCancel, options) { 816 | this.$emit( 817 | this.constItem.Events.ShowModel.OnConfirm, 818 | { 819 | Title, Content: Message, okButton, onCancel, Options: options 820 | } 821 | ); 822 | } 823 | 824 | static $showDialog(Html, Title, okButton, onCancel, Options) { 825 | this.$emit( 826 | this.constItem.Events.ShowModel.OnShowDialog, 827 | { 828 | Title, Html, okButton, onCancel, isShowAction: true, 829 | Options: Object.assign(Options || {}, { IsHideCancel: true, IsHideOk: true }) 830 | } 831 | ); 832 | } 833 | 834 | static $showDialogHide(args) { 835 | this.$emit(this.constItem.Events.ShowModel.OnShowDialogHide, args); 836 | } 837 | 838 | static $showDialogConfirm(msg, Title, okButton, onCancel) { 839 | let _title = Title; 840 | let _okButton = okButton; 841 | if (this.isFunction(Title)) { 842 | _title = '提示信息'; 843 | _okButton = Title; 844 | } 845 | this.$emit( 846 | this.constItem.Events.ShowModel.OnShowDialog, 847 | { 848 | Content: msg, Title: _title, okButton: _okButton, onCancel 849 | } 850 | ); 851 | } 852 | 853 | static $alert(msg, title) { 854 | let _title = title; 855 | let _okButton; 856 | if (this.isFunction(title)) { 857 | _title = '提示信息'; 858 | _okButton = title; 859 | } 860 | this.$emit( 861 | this.constItem.Events.ShowModel.OnShowDialog, 862 | { 863 | Content: msg, Title: _title, okButton: _okButton, Options: { IsHideCancel: true } 864 | } 865 | ); 866 | } 867 | 868 | /** 869 | * 打开加载动画 870 | */ 871 | static $loading() { 872 | this.$emit(this.constItem.Events.ShowModel.OnLoading); 873 | } 874 | 875 | /** 876 | * 关闭加载动画 877 | */ 878 | static $loadingHide() { 879 | this.$emit(this.constItem.Events.ShowModel.OnLoadingHide); 880 | } 881 | 882 | /** 883 | * 去空格 884 | * @param value 885 | * @returns {*} 886 | */ 887 | static $trim(value) { 888 | if (typeof value !== 'undefined') { 889 | return value.replace(/(^\s*)|(\s*$)/g, ''); 890 | } 891 | return ''; 892 | } 893 | 894 | /** 895 | * 去右边空格 896 | * @param value 897 | * @returns {*} 898 | */ 899 | static $trimRight(value) { 900 | if (typeof value !== 'undefined') { 901 | return value.replace(/(\s*$)/g, ''); 902 | } 903 | return ''; 904 | } 905 | 906 | /** 907 | * 去左边空格 908 | * @param s 909 | * @returns {*} 910 | */ 911 | static $trimLeft(value) { 912 | if (typeof value !== 'undefined') { 913 | return value.replace(/(^\s*)/g, ''); 914 | } 915 | return ''; 916 | } 917 | 918 | /** 919 | * 菜单-->添加按键 920 | * @param Text 921 | * @param onClick 922 | * @param Color 923 | * @param BgColor 924 | */ 925 | static $navBarRightAddButton(Text, onClick, Color, BgColor) { 926 | this.$emit( 927 | this.constItem.Events.OnEditNavBarRight, 928 | this.constItem.NavBarRightType.NBButton, 929 | { 930 | Text, onClick, Color, BgColor 931 | } 932 | ); 933 | } 934 | 935 | /** 936 | * 菜单-->添加图标 937 | * @param Icon 938 | * @param onClick 939 | */ 940 | static $navBarRightAddIcon(Icon, onClick) { 941 | this.$emit( 942 | this.constItem.Events.OnEditNavBarRight, 943 | this.constItem.NavBarRightType.NBIcon, 944 | { Icon, onClick } 945 | ); 946 | } 947 | 948 | /** 949 | * 菜单-->添加菜单 950 | * [{Text:'菜单1',onClick:onClick},{Text:'菜单2',onClick:onClick},...] 951 | * @param MenuItem 952 | */ 953 | static $navBarRightAddMenuItem(MenuItem) { 954 | this.$emit(this.constItem.Events.OnEditNavBarRight, this.constItem.NavBarRightType.NBMenu, MenuItem); 955 | } 956 | 957 | /** 958 | * 打开日期控件 959 | * 960 | * @static * @param {datetime} currentDate 当前时间 961 | * @param {boolean} isShowTime 是否显示时间 962 | * @param {Function} ok 点击确定按钮事件-->这里可以获取到返回的日期 963 | * @param {Function} cancel 取消按钮事件 964 | * 965 | * @example 966 | * // 打开日期 967 | * Utility.$openDatePicker(new Date(),false,(date)=>{console.log(date);},()=>{ // cancel todo }); 968 | * // 打开日期和时间 969 | * Utility.$openDatePicker(new Date(),true,(date)=>{console.log(date);},()=>{ // cancel todo }); 970 | * // 传入日期 971 | * Utility.$openDatePicker('2013-10-10',false,(date)=>{console.log(date);},()=>{// cancel todo }); 972 | * // 传入日期和时间 973 | * Utility.$openDatePicker('2010-10-10 12:20,true,(date)=>{console.log(date);},()=>{ // cancel todo }); 974 | * // 传入值,如果是null 或 '' 默认为当前时间 975 | * Utility.$openDatePicker('',false,(date)=>{console.log(date);},()=>{// cancel todo }); 976 | * 977 | * @memberOf Utility 978 | */ 979 | static $openDatePicker(currentDate, isShowTime, ok, cancel) { 980 | this.$emit(this.constItem.Events.OnOpenDatePicker, currentDate, isShowTime, ok, cancel); 981 | } 982 | 983 | /** 984 | * 将日期转为时间戳 985 | * 986 | * @static * @param {any} date 987 | * @returns 988 | * 989 | * @memberOf Utility 990 | */ 991 | static $convertToTimestamp(date) { 992 | if (typeof date === 'undefined' || date === null || date === '') { 993 | return 0; 994 | } 995 | if (this.isDate(date)) { 996 | // replace(/(\s*$)/g, '') r.replace(/-/g, "/") 997 | return date.constructor.name === 'Date' ? date.getTime() : new Date(date.replace('年', '-').replace('月', '-').replace('日', '').replace(/-/g, '/')).getTime(); 998 | } 999 | return 0; 1000 | } 1001 | 1002 | /** 1003 | * 将时间戳转为日期类型 1004 | * 1005 | * @static * @param {number} value 1006 | * @returns 1007 | * @example 1008 | * Utility.$convertToDateByTimestamp('1478131200000') -> 2016-11-03 1009 | * Utility.$convertToDateByTimestamp('1478131200000','yyyy年MM月dd日') -> 2016年11月03日 1010 | * @memberOf Utility 1011 | */ 1012 | static $convertToDateByTimestamp(value, format) { 1013 | if (this.$isNumber(value)) { 1014 | const __date = new Date(parseInt(value, 0)); 1015 | return this.formatDate(format || 'yyyy-MM-dd', __date); 1016 | } 1017 | return ''; 1018 | } 1019 | 1020 | /** 1021 | * 字符串转为日期类型 1022 | * 1023 | * @static * @param {string} value 日期 1024 | * @returns Date 或为 null 1025 | * @example 1026 | * Utility.$convertToDateByString('2013-10-10'); 1027 | * @memberOf Utility 1028 | */ 1029 | static $convertToDateByString(value) { 1030 | if (this.isDate(value)) { 1031 | return value.constructor.name === 'Date' ? value : new Date(value.replace('年', '-').replace('月', '-').replace('日', '')); 1032 | } 1033 | return null; 1034 | } 1035 | 1036 | /** 1037 | * 状态转换,将状态转为对应显示的名称 1038 | * 1039 | * @static * @param {object} obj 对象 1040 | * @param {string} status 状态 1041 | * @returns 返回状态对应的名称 1042 | * 1043 | * @memberOf Utility 1044 | */ 1045 | static $statusConvert(obj, status) { 1046 | if (this.isUndefined(obj) || obj === null || obj === '') { 1047 | return this.isUndefined(status) ? '' : status; 1048 | } 1049 | if (this.isUndefined(status)) { 1050 | return ''; 1051 | } 1052 | const __Value = obj[status]; 1053 | return __Value || status; 1054 | } 1055 | 1056 | /** 1057 | * @param value 1058 | * @returns {*} 1059 | */ 1060 | static $isNumber(value) { 1061 | if (typeof value === 'undefined' || value === null || value === '') { 1062 | return false; 1063 | } 1064 | return /^\+?[0-9]\d*$/.test(value); 1065 | } 1066 | 1067 | /** 1068 | * 格式化数字 1069 | * 1070 | * @static 1071 | * @param {any} number 1072 | * @returns 1073 | * 1074 | * @example Utility.$formatNumber(10000) ==> 10,000 1075 | * @memberOf Utility 1076 | */ 1077 | static $formatNumber(number) { 1078 | if (!this.$isNumber(number)) { 1079 | return number; 1080 | } 1081 | const __value = this.$trim(number); 1082 | return String(__value).split('').reverse().join('') 1083 | .replace(/(\d{3})(?=[^$])/g, '$1,') 1084 | .split('') 1085 | .reverse() 1086 | .join(''); 1087 | } 1088 | 1089 | /** 1090 | * 判断是否为空 1091 | * 1092 | * @static 1093 | * @param {string} value 要判断的值 1094 | * @returns true:是 ; false:否 1095 | * 1096 | * @memberOf Utility 1097 | */ 1098 | static $isEmpty(value) { 1099 | if (!value) { 1100 | return true; 1101 | } 1102 | const __value = this.$trim(value); 1103 | if (__value === '') { 1104 | return true; 1105 | } 1106 | return false; 1107 | } 1108 | 1109 | static $clone(obj) { 1110 | if (!obj) { 1111 | return null; 1112 | } 1113 | const __temp = {}; 1114 | Object.keys(obj).forEach((key) => { 1115 | if (key !== 'IsExistsNextData' && key !== '_timestamp') { 1116 | __temp[key] = obj[key] ? obj[key].toString() : ''; 1117 | } 1118 | }); 1119 | return __temp; 1120 | } 1121 | 1122 | /** 1123 | * 后退操作 1124 | * 1125 | * @static 1126 | * 1127 | * @memberOf Utility 1128 | */ 1129 | static $goBack(times) { 1130 | this.toPage(this.constItem.UrlItem.GoBack, { times }); 1131 | } 1132 | } 1133 | --------------------------------------------------------------------------------