Commit e5457ee5 by 姜雷

init page

parents
# Auto detect text files and perform LF normalization
* text=auto
# See https://help.github.com/ignore-files/ for more about ignoring files.
package-lock.json
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
此为移动端项目在模板基础上添加屏幕适配,以 VW,VH 为单位实现页面适配,添加以下插件
## postcss-cssnext
postcss-cssnext 其实就是 cssnext。该插件可以让我们使用 CSS 未来的特性,其会对这些特性做相关的兼容性处理。其包含的特性主要有:
![cssnext-img](https://www.w3cplus.com/sites/default/files/blogs/2018/1801/vw-layout-4.png)
## cssnano
cssnano 主要用来压缩和清理 CSS 代码。在 Webpack 中,cssnano 和 css-loader 捆绑在一起,所以不需要自己加载它。不过你也可以使用 postcss-loader 显式的使用 cssnano。有关于 cssnano 的详细文档,可以点击这里获取。
在 cssnano 的配置中,使用了 preset: "advanced",所以我们需要另外安装:
```
npm i cssnano-preset-advanced --save-dev
```
cssnano 集成了一些其他的 PostCSS 插件,如果你想禁用 cssnano 中的某个插件的时候,可以像下面这样操作:
```js
"cssnano": {
autoprefixer: false,
"postcss-zindex": false
}
```
## postcss-aspect-ratio-mini
`postcss-aspect-ratio-mini`主要用来处理元素容器宽高比。在实际使用的时候,具有一个默认的结构,使用方法:
```css
/* in css */
[aspectratio] {
position: relative;
}
[aspectratio]::before {
content: '';
display: block;
width: 1px;
margin-left: -1px;
height: 0;
}
[aspectratio-content] {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
[w-750-260] {
aspect-ratio: '750:260';
}
```
在实际使用的时候,你可以把自定义属性`aspectratio``aspectratio-content`换成相应的类名
```html
<!-- in html -->
<div aspectratio="" w-750-260="">
<div aspectratio-content=""></div>
</div>
```
此时内层盒子的大小就是设置的`750:260`的纵横比
## postcss-px-to-viewport
`postcss-px-to-viewport`插件主要用来把`px`单位转换为`vw``vh``vmin`或者`vmax`这样的视窗单位,也是 vw 适配方案的核心插件之一。
## postcss-write-svg
postcss-write-svg 插件主要用来处理移动端 1px 的解决方案。该插件主要使用的是 border-image 和 background 来做 1px 的相关处理。比如
```css
@svg 1px-border {
height: 2px;
@rect {
fill: var(--color, black);
width: 100%;
height: 50%;
}
}
.example {
border: 1px solid transparent;
border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}
```
## postcss-viewport-units
`postcss-viewport-units`插件主要是给 CSS 的属性添加`content`的属性,配合`viewport-units-buggyfill`库给`vw``vh``vmin``vmax`做适配的操作。
### 引入 JavaScript 文件
`viewport-units-buggyfill`主要有两个 JavaScript 文件:viewport-units-buggyfill.js 和 viewport-units-buggyfill.hacks.js。你只需要在你的 HTML 文件中引入这两个文件。比如在 Vue 项目中的 `index.html` 引入它们:
```html
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
```
第二步,在 HTML 文件中调用 viewport-units-buggyfill,比如:
```html
<script> window.onload = function () { window.viewportUnitsBuggyfill.init({ hacks: window.viewportUnitsBuggyfillHacks }); } </script>
```
const { injectBabelPlugin, getLoader } = require('react-app-rewired');
const autoprefixer = require('autoprefixer');
const fileLoaderMatcher = function(rule) {
return rule.loader && rule.loader.indexOf(`file-loader`) != -1;
};
module.exports = function override(config, env) {
config = injectBabelPlugin(
['import', { libraryName: 'antd-mobile', style: 'css' }],
config
);
// customize theme
config.module.rules[1].oneOf.unshift({
test: /\.less$/,
use: [
require.resolve('style-loader'),
require.resolve('css-loader'),
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
},
},
{
loader: require.resolve('less-loader'),
options: {
// theme vars, also can use theme.js instead of this.
modifyVars: { '@brand-primary': '#1DA57A' },
},
},
],
});
// css-modules
config.module.rules[1].oneOf.unshift({
test: /\.css$/,
exclude: /node_modules|antd-mobile\.css/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]___[hash:base64:5]',
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
// ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-aspect-ratio-mini'),
require('postcss-px-to-viewport')({
viewportWidth: 750, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 5, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false, // (Boolean) Allow px to be converted in media queries.
}),
require('postcss-write-svg')({
utf8: false,
}),
require('postcss-cssnext'),
require('postcss-viewport-units'),
require('cssnano')({
preset: 'advanced',
autoprefixer: false,
'postcss-zindex': false,
}),
],
},
},
],
});
// file-loader exclude
let l = getLoader(config.module.rules, fileLoaderMatcher);
l.exclude.push(/\.less$/);
return config;
};
{
"name": "wx-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd-mobile": "^2.2.5",
"axios": "^0.18.0",
"cssnano": "^4.0.5",
"postcss-aspect-ratio-mini": "0.0.2",
"postcss-cssnext": "^3.1.0",
"postcss-px-to-viewport": "0.0.3",
"postcss-viewport-units": "^0.1.4",
"postcss-write-svg": "^3.0.1",
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"autoprefixer": "^9.1.0",
"babel-plugin-import": "^1.8.0",
"cssnano-preset-advanced": "^4.0.0",
"less-loader": "^4.1.0",
"react-app-rewired": "^1.5.2"
},
"browserslist": [
"> 1%",
"last 2 versions",
"iOS >= 7",
"Android > 4.1",
"last 2 versions"
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no, viewport-fit=cover, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root">
载入中。。。
</div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script> window.onload = function () { window.viewportUnitsBuggyfill.init({ hacks: window.viewportUnitsBuggyfillHacks }); } </script>
</body>
</html>
\ No newline at end of file
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
export const errorHandle = error => {
console.log('err' + error); // for debug
if (error.message && /timeout/.test(error.message)) {
return Promise.reject({ error, msg: '请求超时!' });
}
return Promise.reject(error);
};
export default errorHandle;
import axios from 'axios';
import config from '../config/index';
import errorHandle from './errorHandle';
// import store from '../store/index';
const path = config.SERVER_URL + '/dcxy/api/gx/construction';
// 创建axios实例
const service = axios.create({
baseURL: path, // api的base_url
timeout: 10000,
});
// request拦截器
service.interceptors.request.use(
conf => {
// let token = store.getToken();
conf.headers = {
...conf.headers,
reqSource: 'wx',
// token: token ? token : '',
};
return conf;
},
error => {
console.log(error); // for debug
Promise.reject(error);
}
);
// respone拦截器
service.interceptors.response.use(response => {
const res = response.data;
/**
* code为非'0'是抛错
*/
if (res.code !== 1000) {
return Promise.reject(response.data);
} else {
return response.data;
}
}, errorHandle);
export default service;
import fetch from './fetch';
export const fetchPublicCarList = entity =>
fetch({
url: '/app/publicCarController/pageList',
params: entity,
});
export const applyPublicCar = entity =>
fetch({
url: '/app/publicCarController/addPublicCar',
params: entity,
});
export const APP_ID = 'wxed39740c39fa1c0a';
export const SERVER_URL = 'http://ex-dev-gx-manager.168cad.top';
// export const SERVER_URL = 'http://192.168.1.150:8080';
export default {
APP_ID,
SERVER_URL,
};
import React, { Component } from 'react';
import '../styles/App.css';
import { is_weixn } from '../utils';
import { APP_ID } from '../config';
import UserInfoContext, { defaultUserinfo } from '../context/userinfo-context';
import { Route, Switch } from 'react-router-dom';
import Report from './report/report';
import Login from './Login/Login';
import PublicCarList from './PublicCar/PublicCarList';
import PublicCarApply from './PublicCar/PublicCarApply';
class App extends Component {
constructor(props) {
super(props);
this.updateUserInfo = data => {
this.setState({
userinfo: { ...this.state.userinfo, ...data },
});
};
this.state = {
isWx: is_weixn(),
userContext: {
userinfo: defaultUserinfo,
updateUserInfo: this.updateUserInfo,
},
};
}
renderLogin = props => {
// const { isWx } = this.state;
const isWx = true;
const { search } = props.location;
const paramsString = search.substring(1);
const searchParams = new URLSearchParams(paramsString);
const openid = searchParams.get('openid');
// const state = searchParams.get('state');
console.log(isWx, openid);
if (!isWx) {
return <div>请在微信客户端打开链接</div>;
} else if (!openid) {
let rUrl = encodeURIComponent('');
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${rUrl}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
return <div>授权中。。。</div>;
} else {
return <Login {...props} openid={openid} />;
}
};
updateUserInfo = data => {
this.setState({
userContext: {
...this.state.userContext,
userinfo: { ...this.state.userContext.userinfo, ...data },
},
});
};
render() {
const { userContext } = this.state;
return (
<div className="App">
<UserInfoContext.Provider value={userContext}>
<Switch>
<Route exact path="/" render={this.renderLogin} />
<Route path="/report" component={Report} />
<Route path="/PublicCarList" component={PublicCarList} />
<Route path="/PublicCarApply" component={PublicCarApply} />
</Switch>
</UserInfoContext.Provider>
</div>
);
}
}
export default App;
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
export class Index extends React.Component {
static propTypes = {
name: PropTypes.string,
};
render() {
return (
<div>
Home
<br />
<Link to="/report">report</Link>
<br />
<Link to="/report">report</Link>
<br />
<Link to="/report">report</Link>
</div>
);
}
}
export default Index;
import merge from 'lodash/merge';
const base = (state = '', action) => {
switch (action.type) {
case 'FILTER':
return merge(state, {
filter: action.filter,
});
default:
return state;
}
};
export default base;
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import styles from './style.css';
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
},
};
class Login extends Component {
state = {
redirectToReferrer: false,
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div className={styles.Login}>
<h2>Login</h2>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
export default Login;
.Login {
height: 300px;
text-align: center;
}
import React, { Component } from 'react';
class PublicCarApply extends Component {
render() {
return (
<div>
PublicCarApply
</div>
);
}
}
export default PublicCarApply;
\ No newline at end of file
import React, { Component } from 'react';
import { fetchPublicCarList } from '../../api/index';
class PublicCarList extends Component {
static state = {
list: [],
pagination: {
pageNum: 1,
pageSize: 50,
},
};
componentWillMount() {
fetchPublicCarList()
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
}
render() {
return <div>PublicCarList</div>;
}
}
export default PublicCarList;
import React from 'react';
import { List, InputItem } from 'antd-mobile';
export default () => (
<div>
<List>
<InputItem clear placeholder="报修人">
报修人
</InputItem>
<InputItem clear placeholder="报修电话">
报修电话
</InputItem>
<InputItem clear placeholder="报修人">
报修人
</InputItem>
</List>
</div>
);
import React from 'react';
export const defaultUserinfo = {
openid: '15',
name: '',
phone: '',
};
export const UserContext = React.createContext({
userinfo: defaultUserinfo,
updateUserInfo: () => {},
});
export default UserContext;
import React from 'react';
import ReactDOM from 'react-dom';
import './styles/index.css';
import App from './containers/App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
registerServiceWorker();
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.box {
display: flex;
justify-content: center;
}
\ No newline at end of file
body,
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif;
}
figure,
p {
margin: 0;
padding: 0;
}
img {
display: block;
width: 100%;
}
[aspectratio] {
position: relative;
}
[aspectratio]::before {
content: '';
display: block;
width: 1px;
margin-left: -1px;
height: 0;
}
[aspectratio-content] {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
[aspectratio-content] img {
width: 100%;
height: 100%;
vertical-align: top;
}
[w-750-260] {
aspect-ratio: '750:260';
}
[w-750-190] {
aspect-ratio: '750:190';
}
.bb1px {
position: relative;
}
.bb1px::after {
content: '';
display: block;
position: absolute;
width: 100%;
height: 1px;
margin-top: -1px;
left: 0;
right: 0;
bottom: 0;
background: #dcdcdc;
transform: scaleY(0.5);
}
.bt1px {
position: relative;
}
.bt1px::after {
content: '';
display: block;
position: absolute;
width: 100%;
height: 1px;
margin-top: -1px;
left: 0;
right: 0;
top: 0;
background: #dcdcdc;
transform: scaleY(0.5);
}
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis-2lines {
overflow: hidden;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
}
#root,
.App {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
overflow-y: scroll;
}
export const is_weixn = () => {
var ua = navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) === 'micromessenger';
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment