提交 f64275c3 authored 作者: zhenglide's avatar zhenglide

初始化项目

上级 020e4070
流水线 #144 已失败 于阶段
{
"presets": [
[
"@babel/env",
{
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}
],
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"helpers": false,
"regenerator": true
}
],
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-syntax-dynamic-import",
"@babel/transform-async-to-generator",
"@babel/plugin-proposal-do-expressions",
"@babel/plugin-proposal-class-properties"
]
}
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = LF
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
node_modules
src/public/js/*.js
build
{
"root": true,
"globals": {
"HWH5": true
},
"env": {
"es6": true,
"browser": true,
"node": true,
"commonjs": true,
"worker": true,
"amd": true,
"mocha": true,
"jest": true,
"jquery": true,
"serviceworker": true
},
"extends": "airbnb",
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"experimentalDecorators": true,
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"settings": {
"import/ignore": ["node_modules"]
},
"rules": {
"arrow-parens": 0,
"arrow-spacing": 0,
"block-scoped-var": 0,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [1, { "properties": "always" }],
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
"consistent-return": 2,
"consistent-this": 0,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 0,
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"func-names": 0,
"func-style": 0,
"generator-star-spacing": [2, "both"],
"guard-for-in": 0,
"handle-callback-err": [2, "^(err|error|anySpecificError)$"],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"linebreak-style": 0,
"max-depth": 0,
"id-length": 0,
"max-nested-callbacks": 0,
"max-params": 0,
"max-statements": 0,
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"no-alert": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 1,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 0,
"no-empty": 0,
"no-empty-character-class": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 0,
"no-extra-strict": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 0,
"no-lonely-if": 0,
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": [2, false],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-throw-literal": 2,
"no-trailing-spaces": 0,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-expressions": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-use-before-define": 2,
"no-var": 0,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"no-param-reassign": 0,
"one-var": 0,
"operator-assignment": 0,
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [
2,
{ "anonymous": "always", "named": "never" }
],
"space-in-brackets": 0,
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always"],
"strict": 0,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 2,
"wrap-iife": [2, "any"],
"wrap-regex": 0,
"yoda": [2, "never"],
"react/no-multi-comp": 0,
"react/forbid-prop-types": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/jsx-indent-props": 0,
"react/jsx-indent": 0,
"react/jsx-no-literals": 0,
"react/no-set-state": 0,
"react/jsx-one-expression-per-line": 0,
"react/jsx-no-bind": 0,
"react/no-string-refs": 0,
"react/jsx-filename-extension": 0,
"react/no-array-index-key": 0,
"react/no-typos": 0,
"react/require-default-props": 0,
"react/no-render-return-value": 0,
"react/destructuring-assignment": [2, "always"],
"import/default": 0,
"import/no-duplicates": 0,
"import/named": 0,
"import/namespace": 0,
"import/no-unresolved": 0,
"import/no-named-as-default": 0,
"import/no-named-as-default-member": 0,
"import/newline-after-import": 0,
"import/no-mutable-exports": 0,
"import/no-absolute-path": 0,
"import/no-dynamic-require": 0,
"import/no-extraneous-dependencies": 0,
"import/no-amd": 0,
"import/first": 0,
"import/extensions": 0,
"import/prefer-default-export": 0,
"import/no-webpack-loader-syntax": 0,
"import/no-named-default": 0,
"jsx-a11y/click-events-have-key-events": 0,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/anchor-is-valid": 0,
"jsx-a11y/no-noninteractive-element-interactions": 0,
"global-require": 0,
"class-methods-use-this": 0,
"prefer-arrow-callback": 0,
"prefer-template": 0
},
"plugins": ["import", "react"]
}
此差异已折叠。
// Workspace settings
{
// The following will hide the js and map files in the editor
"files.exclude": {
"node_modules": true,
"jsconfig.json": true,
"package-lock.json": true,
".*": true,
"yarn.lock": true
}
}
此差异已折叠。
function sum(a, b) {
return a + b;
}
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
\ No newline at end of file
{
"name": "wlk-react-template",
"engines": {
"node": ">=8.11.0"
},
"config": {
"network": "internet"
},
"version": "1.0.0",
"description": "Template written in ReactJS for WeCode development.",
"author": "wecode development team",
"homepage": "https://cloudlinkworkplace.huaweicloud.com/wecode/",
"keywords": [
"wlk-react-template",
"wecode",
"cloudlink workspace"
],
"private": false,
"scripts": {
"start": "wlk-cli start",
"lint": "eslint -c .eslintrc src --fix",
"build": "wlk-cli build",
"add": "wlk-react add",
"test": "jest",
"lint-css": "stylelint src/**/*.less --fix"
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-decorators": "^7.0.0",
"@babel/plugin-proposal-do-expressions": "^7.0.0",
"@babel/plugin-proposal-export-default-from": "^7.0.0",
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"@babel/plugin-proposal-function-bind": "^7.0.0",
"@babel/plugin-proposal-function-sent": "^7.0.0",
"@babel/plugin-proposal-json-strings": "^7.0.0",
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@wecode/wlk-cli": "1.0.3",
"@wecode/wlk-jsapi": "^1.0.0",
"autoprefixer": "9.1.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "8.2.6",
"babel-loader": "^8.0.0",
"babel-plugin-react-transform": "3.0.0",
"babel-plugin-transform-decorators-legacy": "1.3.5",
"babel-preset-react-hmre": "1.1.1",
"css-loader": "1.0.0",
"eslint": "5.3.0",
"eslint-config-airbnb": "17.1.0",
"eslint-find-rules": "3.1.1",
"eslint-loader": "2.1.0",
"eslint-plugin-import": "2.14.0",
"eslint-plugin-jsx-a11y": "6.1.1",
"eslint-plugin-react": "7.11.1",
"expect": "1.12.0",
"file-loader": "1.1.11",
"hard-source-webpack-plugin": "^0.12.0",
"jest": "23.4.2",
"jest-runtime": "23.4.2",
"less": "3.8.1",
"less-loader": "4.1.0",
"mini-css-extract-plugin": "^0.4.2",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-loader": "3.0.0",
"prettier-eslint": "8.8.2",
"react-addons-test-utils": "15.6.2",
"react-dev-utils": "6.0.0-next.3e165448",
"react-test-renderer": "16.4.2",
"react-transition-group": "2.4.0",
"redux-promise": "0.6.0",
"style-loader": "0.22.1",
"stylelint": "9.4.0",
"stylelint-order": "0.8.1",
"stylelint-webpack-plugin": "0.10.5",
"uglifyjs-webpack-plugin": "^2.0.0",
"url-loader": "1.0.1",
"webpack": "4.16.5"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"@wecode/react-weui": "^1.0.1",
"@wecode/wlk-react-template-util": "1.0.0",
"core-js": "2.5.7",
"history": "4.7.2",
"html-webpack-plugin": "3.2.0",
"i18next": "11.6.0",
"i18next-resource-store-loader": "0.1.2",
"prop-types": "15.6.2",
"react": "16.4.2",
"react-dom": "16.4.2",
"react-fastclick-alt": "2.0.1",
"react-hot-loader": "4.3.4",
"react-redux": "5.0.7",
"react-router-dom": "4.3.1",
"react-router-redux": "4.0.8",
"react-weui": "^1.1.3",
"redux": "4.0.0",
"redux-devtools-extension": "2.13.5",
"redux-thunk": "2.3.0",
"weui": "^1.1.0"
}
}
module.exports = {
plugins: [
require('autoprefixer')
]
};
/* ! iNoBounce - v0.1.0
* https://github.com/lazd/iNoBounce/
* Copyright (c) 2013 Larry Davis <lazdnet@gmail.com>; Licensed BSD */
(function (global) {
// Stores the Y position where the touch started
var startY = 0;
// Store enabled status
var enabled = false;
var handleTouchmove = function (evt) {
// Get the element that was scrolled upon
var el = evt.target;
// Check all parent elements for scrollability
while (el !== document.body && el !== document) {
// Get some style properties
var style = window.getComputedStyle(el);
if (!style) {
// If we've encountered an element we can't compute the style for, get out
break;
}
// Ignore range input element
if (el.nodeName === 'INPUT' && el.getAttribute('type') === 'range') {
return;
}
var scrolling = style.getPropertyValue('-webkit-overflow-scrolling');
var overflowY = style.getPropertyValue('overflow-y');
var height = parseInt(style.getPropertyValue('height'), 10);
// Determine if the element should scroll
var isScrollable = scrolling === 'touch' && (overflowY === 'auto' || overflowY === 'scroll');
var canScroll = el.scrollHeight > el.offsetHeight;
if (isScrollable && canScroll) {
// Get the current Y position of the touch
var curY = evt.touches ? evt.touches[0].screenY : evt.screenY;
// Determine if the user is trying to scroll past the top or bottom
// In this case, the window will bounce, so we have to prevent scrolling completely
var isAtTop = (startY <= curY && el.scrollTop === 0);
var isAtBottom = (startY >= curY && el.scrollHeight - el.scrollTop === height);
// Stop a bounce bug when at the bottom or top of the scrollable element
if (isAtTop || isAtBottom) {
evt.preventDefault();
}
// No need to continue up the DOM, we've done our job
return;
}
// Test the next parent
el = el.parentNode;
}
// Stop the bouncing -- no parents are scrollable
evt.preventDefault();
};
var handleTouchstart = function (evt) {
// Store the first Y position of the touch
startY = evt.touches ? evt.touches[0].screenY : evt.screenY;
};
var enable = function () {
// Listen to a couple key touch events
window.addEventListener('touchstart', handleTouchstart, false);
window.addEventListener('touchmove', handleTouchmove, false);
enabled = true;
};
var disable = function () {
// Stop listening
window.removeEventListener('touchstart', handleTouchstart, false);
window.removeEventListener('touchmove', handleTouchmove, false);
enabled = false;
};
var isEnabled = function () {
return enabled;
};
// Enable by default if the browser supports -webkit-overflow-scrolling
// Test this by setting the property with JavaScript on an element that exists in the DOM
// Then, see if the property is reflected in the computed style
var testDiv = document.createElement('div');
document.documentElement.appendChild(testDiv);
testDiv.style.WebkitOverflowScrolling = 'touch';
var scrollSupport = 'getComputedStyle' in window && window.getComputedStyle(testDiv)['-webkit-overflow-scrolling'] === 'touch';
document.documentElement.removeChild(testDiv);
if (scrollSupport) {
enable();
}
// A module to support enabling/disabling iNoBounce
var iNoBounce = {
enable,
disable,
isEnabled
};
if (typeof module !== 'undefined' && module.exports) {
// Node.js Support
module.exports = iNoBounce;
}
if (typeof global.define === 'function') {
// AMD Support
(function (define) {
define('iNoBounce', [], () => iNoBounce);
}(global.define));
} else {
// Browser support
global.iNoBounce = iNoBounce;
}
}(this));
/**
* 服务器端口的配置、应用名称、版本号
*/
module.exports = {
host: '127.0.0.1',
port: 3000,
sessionTTL: 24 * 60 * 60 * 1000
};
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Route from 'react-router-dom/Route';
import Router from 'react-router-dom/Router';
import createHistory from 'history/createHashHistory';
import { Tab, TabBarItem, TabBar } from 'react-weui';
const history = createHistory();
/*
全局导入less
*/
import './app.less';
import * as global from 'actions/global';
import routes from './routes';
@connect(
state => ({ ...state.global }),
dispatch => bindActionCreators(global, dispatch)
)
export default class App extends React.Component {
componentDidMount() {
window.addEventListener('hashchange', () => {
});
window.HWH5.addEventListener({
type: 'back',
func: () => {
// 监听页面返回事件,return true直接返回,return false,拒绝返回。只支持安卓
const flag = true;
return flag;
}
}).catch((error) => {
console.log('监听事件发生异常', error);
});
}
render() {
return (
<div className="appContainer">
<Router history={history}>
<Route render={({ location }) => {
{
return routes.map(route => (
<Route
key={route.path}
location={location}
path={route.path}
exact={route.exact}
component={route.component}
/>
));
}
}
}
/>
</Router>
<Tab type="tabbar">
<TabBar>
<TabBarItem label="会议">
{i18n.t('home:meeting')}
</TabBarItem>
<TabBarItem label="预定">
预定
</TabBarItem>
<TabBarItem label="我的">
我的
</TabBarItem>
</TabBar>
</Tab>
</div>
);
}
};
export const receiveCurrentUser = (response) => ({
type: 'CURRENT_USER',
userInfo: response
});
export const getUserInfo = () => async (dispatch, getState) => {
try {
const response = await window.HWH5.userInfo().then((data)=> data);
await dispatch(receiveCurrentUser(response));
return response;
} catch (error) {
console.log('error: ', error);
return error;
}
};
import { urls } from '../config/web.config';
const receiveHome = response => ({
type: 'RECEIVE_HOME',
homeInfo: response
});
export const getHomeInfo = () => async (dispatch, getState) => {
try {
const response = await new Promise((resolve, reject) => {
/* 模拟异步操作成功,这样可以通过fetch调接口获取数据 */
setTimeout(() => {
resolve({ title: 'React App' });
}, 1000);
});
await dispatch(receiveHome(response));
return response;
} catch (error) {
console.log('error: ', error);
return error;
}
};
const receiveFetchDemo = response => ({
type: 'RECEIVE_FETCHDEMO',
dataList: response
});
export const getFetchDemo = () => async (dispatch, getState) => {
try {
console.log('Request url:', urls.getData);
// const url = `${urls.getData}`;
// const r = await HWH5.fetch(url).then(res => res
// .json()
// .then(d => d)
// .catch(e => e));
await dispatch(receiveFetchDemo({ data: [] }));
return null;
} catch (error) {
// console.log('error: ', error);
return error;
}
};
/*全局css样式*/
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
outline: 0;
margin: 0;
-webkit-tap-highlight-color: transparent;
#root {
height: 100vh;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td {
padding: 0;
margin: 0;
vertical-align: baseline;
}
a {
text-decoration: none;
}
ul {
list-style: none;
}
img {
border: 0 none;
vertical-align: top;
}
.clearfix {
display: block;
*zoom: 1;
}
.clearfix::after,
.clearfix::before {
display: table;
line-height: 0;
content: "";
}
.pt20 {
padding-top: 20px;
}
.pl10 {
padding-left: 10px;
}
.pr10 {
padding-right: 10px;
}
.fr {
float: right;
}
\ No newline at end of file
import React from 'react';
import PropTypes from 'prop-types';
import './index.less';
import backImg from './images/back.png';
export default function Header(props) {
const { title, backHandle } = props;
return (
<header>
<img id="back" src={backImg} alt={title} onClick={() => backHandle()} />
<div className="tit">
{title}
</div>
</header>
);
};
Header.propTypes = {
title: PropTypes.string,
backHandle: PropTypes.func
};
header {
position: relative;
width: 100%;
height: 44px;
background: #343745 100%;
z-index: 999;
}
header #back {
position: absolute;
top: 6px;
left: 6px;
width: 32px;
height: 32px;
}
header #share {
position: absolute;
top: 6px;
right: 6px;
width: 32px;
height: 32px;
}
header .tit {
top: 90px;
width: 100%;
height: 44px;
color: #fff;
font-size: 17px;
font-family: PingFangSC-Regular;
line-height: 44px;
text-align: center;
}
\ No newline at end of file
{
"name": "index",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
import React from 'react';
import './index.less';
import { Tab, TabBarItem, TabBar } from 'react-weui';
import { Link } from 'react-router-dom';
import i18n from 'i18n';
// import { meetingIcon } from '../../../build/apps/20190226111443945/1.0.0/images';
export default class MyTab extends React.Component {
componentWillMount() {
}
componentDidMount() {
}
render() {
return (
<Tab type="tabbar">
<TabBar>
<TabBarItem label="会议">
<Link to="/meeting">{i18n.t('home:meeting')}</Link>
</TabBarItem>
<TabBarItem label="预定">
<Link to="/meeting">预定</Link>
</TabBarItem>
<TabBarItem label="我的">
<Link to="/meeting">我的</Link>
</TabBarItem>
</TabBar>
</Tab>
);
}
};
{
"name": "index",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
import React from 'react';
import PropTypes from 'prop-types';
import './index.less';
export default function Page(props) {
const {
title, subTitle, spacing, className, children, footer
} = props;
return (
<section className={`page ${className}`}>
<div className="page__hd">
<h1 className="page__title">{title}</h1>
<p className="page__desc">{subTitle}</p>
</div>
<div className={`page__bd ${spacing ? 'page__bd_spacing' : ''}`}>
{children}
</div>
{footer ? <div className="page__ft">{footer}</div> : false}
</section>
);
}
Page.propTypes = {
title: PropTypes.string,
subTitle: PropTypes.string,
spacing: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.any,
footer: PropTypes.object
};
.page {
padding: 10px;
}
\ No newline at end of file
{
"name": "index",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
let configs = {};
/**
* 前端基本配置文件,可配置对应环境的接口地址
*/
if (process.env.APP_ENV === 'production') {
configs = {
prefix: {
mcloud: 'https://xx.xx.com'
}
};
} else if (process.env.APP_ENV === 'uat') {
configs = {
prefix: {
mcloud: 'https://xx.xx.com'
}
};
} else {
// 本地开发
configs = {
prefix: {
mcloud: 'https://xx.xx.com'
}
};
}
const {
prefix
} = configs;
module.exports = {
urls: { // 接口地址配置
getData: `${prefix.mcloud}/getData`
}
};
import 'core-js/es6/string';
import 'core-js/es6/array';
import 'core-js/es6/object';
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import { routerReducer, routerMiddleware } from 'react-router-redux';
import { AppContainer } from 'react-hot-loader';
import FastClick from 'react-fastclick-alt';
import createHistory from 'history/createBrowserHistory';
import App from './App';
import rootReducer from './reducers/index';
import i18n from './i18n.js';
import getLang from './utils/getLang';
const history = createHistory();
const middleware = routerMiddleware(history);
const middlewares = [thunk, middleware];
const store = createStore(
combineReducers({ routing: routerReducer, ...rootReducer }),
composeWithDevTools(applyMiddleware(...middlewares))
);
const render = Component => ReactDOM.render(
<FastClick>
<AppContainer>
<Provider store={store}>
<Component />
</Provider>
</AppContainer>
</FastClick>,
document.getElementById('root')
);
// 获取当前app语言参数,并初始化国际化和渲染页面。开发时,mock数据默认返回中文。
getLang().then((language)=>{
i18n.initByLang(language === 'zh' ? 'zh_CN' : 'en_US');
render(App);
if (module.hot) {
module.hot.accept('./App', () => {
const NextRootContainer = require('./App').default;
render(NextRootContainer);
});
};
});
// locales文件必须是严格的json数据,最后一个不能有逗号!!
import i18n from 'i18next';
import resBundle from 'i18next-resource-store-loader?include=\\.json$!../src/locales/index.js';
function i18nInit(lang) {
if (typeof window !== 'undefined') {
const backendOpts = {
loadPath: './src/locales/{{lng}}/{{ns}}.json',
addPath: './src/locales/{{lng}}/{{ns}}.missing.json',
jsonIndent: 2
};
const i18nOpts = {
lng: lang,
resources: resBundle,
ns: ['common'],
preload: ['zh_CN', 'en_US'],
defaultNS: 'common',
debug: false,
backend: backendOpts,
interpolation: {
escapeValue: false // not needed for react!!
}
};
i18n.init(i18nOpts);
}
}
i18n.initByLang = i18nInit;
export default i18n;
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
<link href="../../../../common/css/hwh5.css?v=<%= htmlWebpackPlugin.options.version %>" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
<%= htmlWebpackPlugin.options.vconsole === true ? '<script type="text/javascript" src="../../../../common/js/vconsole.js"></script>' : '' %>
<script type="text/javascript" src="../../../../common/js/hwh5.js?v=<%= htmlWebpackPlugin.options.version %>"></script>
</body>
<script>
// 解决iOS webview 滚动反弹会出现整个页面可以跟着动的问题
if (/iP(hone|od|ad)/.test(navigator.userAgent)) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "%PUBLIC_URL%/js/inobounce.js";
document.body.appendChild(script);
}
document.body.addEventListener('touchstart', function () { });
</script>
</html>
\ No newline at end of file
{
"appName": "React App",
"welcome": "Hello World!"
}
\ No newline at end of file
{
"getStart" : "To get started, ",
"edit": "edit",
"saveReload": "and save to reload.",
"meeting":"meeting"
}
{
"appName" : "React App",
"welcome": "Hello World!"
}
{
"getStart" : "在开始前,",
"edit": "编辑",
"saveReload": "并保存将重新加载修改结果。",
"meeting":"会议"
}
\ No newline at end of file
const initState = {
userInfo: {}
};
export const global = (state = initState, action) => {
switch (action.type) {
case 'CURRENT_USER':
return {
...state,
userInfo: action.userInfo
};
default:
return state;
}
};
// 初始化状态
const initState = {
homeInfo: {},
dataList: []
};
export function home(state = initState, action) {
switch (action.type) {
case 'RECEIVE_HOME':
return {
...state,
homeInfo: action.homeInfo
};
case 'RECEIVE_FETCHDEMO':
return {
...state,
dataList: action.dataList
};
default:
return { ...state };
}
};
import { home } from './home';
import { global } from './global';
const rootReducer = {
home,
global
};
export default rootReducer;
import React from 'react';
import { Article } from '@wecode/react-weui';
import './index.less';
export default class Desc extends React.Component {
componentWillMount() {
window.HWH5.navTitle({ title: '框架说明' });
}
componentDidMount() {}
render() {
return (
<div>
<Article>
<h1>框架说明</h1>
<h2>一套 weui 设计语言和 React 实现:</h2>
<ul className="advantage">
<li> 符合官方交互语言和视觉风格。</li>
<li> 开箱即用的高质量 React 组件。</li>
<li> 本地开发自动检测JSAPI新版本并集成到开发环境。</li>
<li> 使用 eslint,按照规则给出报告的代码检测工具,避免低级错误和统一代码的风格。</li>
<li>
基于 npm + webpack + react + redux + less + jsapi + weuimit license),
的快速开发本地化的框架。
</li>
</ul>
</Article>
</div>
);
}
}
.advantage {
list-style: circle;
padding-left: 20px;
li {
margin-bottom: 10px;
}
}
{
"name": "index.js",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import i18n from 'i18n';
import { Link } from 'react-router-dom';
import * as home from '../../actions/home';
import * as global from '../../actions/global';
import { Tab, TabBarItem, TabBar } from 'react-weui';
import './index.less';
import MyTab from '../../components/MyTab';
// import { MyTab } from '../../components/MyTab';
@connect(
state => ({ ...state.home }),
dispatch => bindActionCreators({ ...home, ...global }, dispatch)
)
export default class Home extends React.Component {
async componentWillMount() {
window.HWH5.navTitle({ title: 'Hello World' });
const {
homeInfo, dataList, getHomeInfo, getFetchDemo
} = this.props;
if (!homeInfo) {
getHomeInfo().then(data => {
document.title = data.title;
});
}
if (!dataList || dataList.length === 0) {
getFetchDemo();
}
}
componentDidMount() { }
componentWillUnmount() { }
// 打开新的窗口
openWebview(url) {
window.HWH5.openWebview({ uri: url });
}
render() {
return (
<div className="App">
helloworld
</div>
);
}
}
Home.propTypes = {
homeInfo: PropTypes.object,
dataList: PropTypes.array,
getHomeInfo: PropTypes.func,
getFetchDemo: PropTypes.func
};
.App {
padding: 10px;
.App-logo {
margin: 20px 0;
text-align: center;
}
.App-header {
height: 90px;
text-align: center;
}
.App-title {
margin-bottom: 30px;
font-size: 1.5em;
text-align: center;
}
.App-intro {
margin-bottom: 10px;
font-size: large;
}
.desc-link {
display: block;
width: 100px;
height: 50px;
margin: 0 auto;
margin-top: 30px;
color: #2b88e6;
font-size: 14px;
text-align: center;
}
.develop-doc {
h3 {
margin-bottom: 5px;
}
ul {
list-style: circle;
padding-left: 30px;
}
li {
margin-bottom: 5px;
color: #2b88e6;
cursor: pointer;
}
}
}
{
"name": "Home",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
import React from 'react';
import './index.less';
export default class Meeting extends React.Component {
componentWillMount() {
}
componentDidMount() {
}
render() {
return (
<div>Meeting</div>
);
}
};
{
"name": "index.js",
"version": "1.0.0",
"private": true,
"main": "./index.js"
}
/**
* 路由配置文件
* 如果需要按需加载,可使用asyncComponent加载,用法请参考README.md文件
*/
import { AsyncComponent } from '@wecode/wlk-react-template-util';
import Home from './Home';
import Meeting from './Meeting';
// webpackChunkName 可指定模块名称,访问路由按需加载
const routes = [
{ path: '/', component: Home, exact: true },
{ path: '/meeting', component: Meeting, exact: true },
{ path: '/desc', component: AsyncComponent(() => import('./Desc')) }
];
export default routes;
var Cookie = exports;
var decode = decodeURIComponent;
var encode = encodeURIComponent;
// Helpers
function isString(o) {
return typeof o === 'string';
}
function isNonEmptyString(s) {
return isString(s) && s !== '';
}
function validateCookieName(name) {
if (!isNonEmptyString(name)) {
throw new TypeError('Cookie name must be a non-empty string');
}
}
function same(s) {
return s;
}
function parseCookieString(text, shouldDecode) {
var cookies = {};
if (isString(text) && text.length > 0) {
const decodeValue = shouldDecode ? decode : same;
const cookieParts = text.split(/;\s/g);
let cookieName;
let cookieValue;
let cookieNameValue;
for (let i = 0, len = cookieParts.length; i < len; i++) {
// Check for normally-formatted cookie (name-value)
cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
if (cookieNameValue instanceof Array) {
try {
cookieName = decode(cookieNameValue[1]);
cookieValue = decodeValue(cookieParts[i]
.substring(cookieNameValue[1].length + 1));
} catch (ex) {
// Intentionally ignore the cookie -
// the encoding is wrong
}
} else {
// Means the cookie does not have an "=", so treat it as
// a boolean flag
cookieName = decode(cookieParts[i]);
cookieValue = '';
}
if (cookieName) {
cookies[cookieName] = cookieValue;
}
}
}
return cookies;
}
/**
* Returns the cookie value for the given name.
*
* @param {String} name The name of the cookie to retrieve.
*
* @param {Function|Object} options (Optional) An object containing one or
* more cookie options: raw (true/false) and converter (a function).
* The converter function is run on the value before returning it. The
* function is not used if the cookie doesn't exist. The function can be
* passed instead of the options object for conveniently. When raw is
* set to true, the cookie value is not URI decoded.
*
* @return {*} If no converter is specified, returns a string or undefined
* if the cookie doesn't exist. If the converter is specified, returns
* the value returned from the converter.
*/
Cookie.get = function (name, options) {
validateCookieName(name);
if (typeof options === 'function') {
options = { converter: options };
} else {
options = options || {};
}
const cookies = parseCookieString(document.cookie, !options['raw']);
return (options.converter || same)(cookies[name]);
};
/**
* Sets a cookie with a given name and value.
*
* @param {string} name The name of the cookie to set.
*
* @param {*} value The value to set for the cookie.
*
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string),
* expires (number or a Date object), secure (true/false),
* and raw (true/false). Setting raw to true indicates that the cookie
* should not be URI encoded before being set.
*
* @return {string} The created cookie string.
*/
Cookie.set = function (name, value, options) {
validateCookieName(name);
options = options || {};
const { expires, domain, path } = options;
if (!options['raw']) {
value = encode(String(value));
}
let text = `${name}=${value}`;
// expires
let date = expires;
if (typeof date === 'number') {
date = new Date();
date.setDate(date.getDate() + expires);
}
if (date instanceof Date) {
text += `; expires=${date.toUTCString()}`;
}
// domain
if (isNonEmptyString(domain)) {
text += `; domain=${domain}`;
}
// path
if (isNonEmptyString(path)) {
text += `; path=${path}`;
}
// secure
if (options['secure']) {
text += '; secure';
}
document.cookie = text;
return text;
};
/**
* Removes a cookie from the machine by setting its expiration date to
* sometime in the past.
*
* @param {string} name The name of the cookie to remove.
*
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string),
* and secure (true/false). The expires option will be overwritten
* by the method.
*
* @return {string} The created cookie string.
*/
Cookie.remove = function (name, options) {
options = options || {};
options['expires'] = new Date(0);
return this.set(name, '', options);
};
function getLang() {
return new Promise((resolve) => {
window.HWH5.appInfo().then((data) => {
const { language } = data;
// 本地可直接模拟,中英文环境,默认是中文环境,模拟英文只需把 language 传成 en
// resolve('en');
resolve(language);
});
});
}
export default getLang;
/**
*
*/
const isProd = ()=>window.location.hash.indexOf('env=isProd');
export default isProd;
/**
* 是否是安卓
*/
const isIOS = /iP(hone|od|ad)/.test(navigator.userAgent);
export default isIOS;
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const requireJSON = function (filePath) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};
const babelrc = requireJSON('./.babelrc');
const babelOptions = Object.assign({}, babelrc, { cacheDirectory: true });
// 判断当前wepback运行环境是否为Debug模式
const DEBUG = process.argv.includes('start')
|| (!process.argv.includes('prod') && !process.argv.includes('uat'));
// 判断当前运行环境是UAT模式还是生产模式
const { appName, versionCode } = require('@wecode/wlk-cli/lib/tools/lib/plugin');
const VERBOSE = process.argv.includes('verbose');
/*
Node环境变量,只有开发环境和生产环境,涉及react、react-hot-loader等模块的打包
如果在package.json中启动指定环境,则优先使用该环境,win和mac设置node环境变量语法不同,比如开发环境指定生产环境:
mac "start": "export NODE_ENV=production&&babel-node tools/run start"
win "start": "set NODE_ENV=production&&babel-node tools/run start",
*/
let { NODE_ENV } = process.env;
if (NODE_ENV === undefined) {
NODE_ENV = DEBUG ? 'development' : 'production';
}
// 应用环境变量,包含开发环境,测试环境和生产环境。
const APP_ENV = DEBUG ? 'development' : process.argv.includes('uat') ? 'uat' : 'production';
// public文件夹目录路径,文件将统一复制至项目根目录,`..` 代表往 `html/index.html` 上级目录,即根目录
const publicUrl = '..';
const plugins = [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
new HtmlWebpackPlugin({
filename: 'html/index.html',
title: appName,
version: Date.now(),
vconsole: APP_ENV !== 'production',
inject: true,
template: '../src/index.html'
}),
new InterpolateHtmlPlugin({ NODE_ENV, PUBLIC_URL: publicUrl, APP_ENV }),
new webpack.DefinePlugin({
'process.env.APP_ENV': JSON.stringify(
DEBUG ? 'development' : process.argv.includes('uat') ? 'uat' : 'production'
)
})
];
/*
need to review, HardSourceWebpackPlugin has bugs in build uat/prod
use it in devServer currently
*/
if (!DEBUG) {
plugins.push(
new MiniCssExtractPlugin({
filename: 'css/[name]-[hash].css'
})
);
} else {
plugins.push(new HardSourceWebpackPlugin());
}
module.exports = {
mode: DEBUG ? 'development' : process.argv.includes('uat') ? 'none' : 'production',
context: path.resolve(__dirname, 'src'),
entry: {
app: ['./entry']
},
stats: {
colors: true,
reasons: DEBUG,
hash: VERBOSE,
version: VERBOSE,
timings: true,
chunks: VERBOSE,
chunkModules: VERBOSE,
cached: VERBOSE,
cachedAssets: VERBOSE
},
output: {
filename: 'js/[name]-[hash].js',
path: path.join(__dirname, `build/apps/${appName}/${versionCode}/`),
publicPath: !DEBUG ? '../' : `/apps/${appName}/${versionCode}/`,
chunkFilename: 'js/[name]-[hash].js'
},
// BASE_URL是全局的api接口访问地址
plugins,
// alias是配置全局的路径入口名称,只要涉及到下面配置的文件路径,可以直接用定义的单个字母表示整个路径
resolve: {
extensions: ['.js', '.jsx', '.less', '.scss', '.css'],
modules: [path.resolve(__dirname, 'node_modules'), path.join(__dirname, './src')],
alias: {
actions: path.resolve(__dirname, 'src/actions'),
components: path.resolve(__dirname, 'src/components'),
routes: path.resolve(__dirname, 'routes'),
reducers: path.resolve(__dirname, 'src/reducers'),
utils: path.resolve(__dirname, 'src/utils'),
i18n: path.resolve(__dirname, 'src/i18n')
}
},
externals: {},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: babelOptions
}
},
{
test: /\.(less|css)$/,
use: [
!DEBUG ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
use: ['file-loader?limit=1000&name=assets/images/[md5:hash:base64:10].[ext]']
}
// {
// test: /(\.jsx|\.js)$/,
// loader: 'eslint-loader',
// exclude: /node_modules/
// }
]
},
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 5,
automaticNameDelimiter: '~',
cacheGroups: {
styles: {
name: 'styles',
test: /\.(less|css)$/,
chunks: 'all',
enforce: true
},
vendor: {
// 基础类库
chunks: 'all',
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
priority: 10,
enforce: true
}
}
},
minimize: APP_ENV === 'production',
minimizer: [
new OptimizeCSSAssetsPlugin({}),
new UglifyJsPlugin({
cache: true,
parallel: true
})
]
}
};
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论