Commit 5366f0d2 by 姜雷

改造页面

parent e64d39db
const {
override,
fixBabelImports,
addLessLoader,
addPostcssPlugins,
} = require('customize-cra');
module.exports = override(
fixBabelImports('babel-plugin-import', {
libraryName: 'antd-mobile',
style: true,
}),
addLessLoader({
javascriptEnabled: true,
modifyVars: {
'@brand-primary': '#ff900d',
'@brand-primary-tap': '#fac17d',
},
}),
addPostcssPlugins([
require('postcss-aspect-ratio-mini'),
require('postcss-write-svg')({
utf8: false,
}),
require('postcss-px-to-viewport')({
viewportWidth: 640, // (Number) The width of the viewport.
viewportHeight: 1136, // (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) Allo
}),
require('postcss-viewport-units'),
]),
);
......@@ -4,16 +4,17 @@
"private": true,
"homepage": "/schoolHome",
"dependencies": {
"postcss-px-to-viewport": "^1.1.0",
"antd-mobile": "^2.2.8",
"axios": "^0.18.0",
"react": "^16.8.2",
"react-dom": "^16.8.2",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
"eslintConfig": {
......@@ -24,5 +25,19 @@
"not dead",
"not ie <= 11",
"not op_mini all"
]
],
"devDependencies": {
"babel-plugin-import": "^1.11.0",
"css-loader": "^2.1.0",
"customize-cra": "^0.2.11",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"node-sass": "^4.11.0",
"postcss-aspect-ratio-mini": "^1.0.1",
"postcss-px-to-viewport": "^1.1.0",
"postcss-viewport-units": "^0.1.6",
"postcss-write-svg": "^3.0.1",
"react-app-rewired": "^2.1.0",
"style-loader": "^0.23.1"
}
}
......@@ -5,7 +5,7 @@
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta name="theme-color" content="#000000" />
<!--
......@@ -15,16 +15,28 @@
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<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>
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<!--
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>
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if ('addEventListener' in document) {
document.addEventListener(
'DOMContentLoaded',
function() {
FastClick.attach(document.body);
},
false,
);
}
if (!window.Promise) {
document.writeln(
'<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"' +
'>' +
'<' +
'/' +
'script>',
);
}
</script>
<title>多彩运维</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
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 => {
// Do something with request 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';
import store from '../store/index';
export const wxLogin = entity =>
fetch({
url: '/weixinLogin',
method: 'post',
data: entity,
});
export const login = entity =>
fetch({
url: '/login',
method: 'post',
data: entity,
}).then(res => {
// store.next('123');
console.log(res);
const {
data: { token },
} = res;
store.updateToken(token);
return res;
});
export const testWater = entity =>
fetch({
url: `/devices/${entity.deviceCode}/testWater`,
method: 'post',
data: entity,
});
export const checkWater = entity =>
fetch({
url: `/devices/${entity.deviceCode}/waterVerification`,
method: 'post',
data: entity,
});
export const fetchRate = deviceCode =>
fetch({
url: `/devices/${deviceCode}/rate`,
// data: entity,
});
export const activateRate = entity =>
fetch({
url: `/devices/${entity.deviceCode}/rate`,
method: 'post',
data: entity,
});
export const getPositions = entity =>
fetch({
url: `/devices/${entity.deviceCode}/positions`,
});
export const getPositionsList = entity =>
fetch({
url: '/positions/unusing',
params: entity,
});
export const bindEquipment = entity =>
fetch({
url: `/devices/${entity.deviceCode}/binding`,
method: 'post',
data: entity,
});
export const unbindEquipment = entity =>
fetch({
url: `/devices/${entity.deviceCode}/unbinding`,
method: 'post',
data: entity,
});
export const getWxConfig = entity =>
fetch({
url: '/richScan',
params: entity,
});
import React from 'react';
import './style.css';
import backIcon from '../../images/NavBar/back_pre_btn@2x.png';
function NavBar(props) {
const { title, history } = props;
return (
<div className='nav'>
<div className='backIcon' onClick={() => history.goBack()}>
<div className='backIcon_Inner'>
<img src={backIcon} alt='' />
</div>
</div>
<div className='title'>{title}</div>
</div>
);
}
export default NavBar;
.nav {
height: 80px;
line-height: 80px;
text-align: center;
font-size: 30px;
color: #444;
background-color: #fff;
border-bottom: 2px solid #ebebeb;
position: relative;
}
.backIcon {
float: left;
margin-left: 30px;
width: 30px;
height: 100%;
}
.backIcon_Inner {
margin-top: 25px;
margin-left: 10px;
width: 18px;
height: 30px;
}
.title {
margin: 0 auto;
width: 300px;
}
import React, { useState } from 'react';
import { Switch, Route } from 'react-router-dom';
import UserInfoContext, { initUserinfo } from '../context/userinfo-context';
import NavBar from '../components/NavBar';
import Index from './Index/Index';
import Login from './Login/index';
import Equipment from './EquipmentContent/index';
import EquipmentTest from './Equipment/EquipmentTest';
import BindEquipment from './BindEquipment';
import NoMatch from './NoMatch';
function App() {
const [userinfo, setUserinfo] = useState(initUserinfo);
const [title, setTitle] = useState('多彩运维');
return (
<UserInfoContext.Provider
value={{
userinfo: userinfo,
updateUserInfo: setUserinfo,
}}>
<Switch>
<Route exact path='/' component={Index} />
</Switch>
</UserInfoContext.Provider>
<div className='App'>
<UserInfoContext.Provider
value={{
userinfo: userinfo,
updateUserInfo: setUserinfo,
}}>
<Route render={props => <NavBar {...props} title={title} />} />
<Switch>
<Route
exact
path='/'
render={props => <Index {...props} setTitle={setTitle} />}
/>
<Route
path='/login'
render={props => <Login {...props} setTitle={setTitle} />}
/>
<Route
path='/BindEquipment'
render={props => <BindEquipment {...props} setTitle={setTitle} />}
/>
<Route
path='/Equipment/:deviceCode'
render={props => <Equipment {...props} setTitle={setTitle} />}
/>
<Route
path='/EquipmentTest'
render={props => <EquipmentTest {...props} setTitle={setTitle} />}
/>
<Route component={NoMatch} />
</Switch>
</UserInfoContext.Provider>
</div>
);
}
......
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Toast } from 'antd-mobile';
import './style.scss';
import { getPositions } from '../../api/index';
import UserInfoContext from '../../context/userinfo-context';
// import WxWrap from '../../HOC/wx';
import scanImg from '../../images/Equipment/scan.png';
import { filterEmoji } from '../../utils/index';
export class Bindequipment extends React.Component {
static propTypes = {
updateUserInfo: PropTypes.func,
userinfo: PropTypes.object,
};
constructor(props) {
super(props);
this.state = {
deviceCode: '',
};
}
componentWillMount() {
const { setTitle } = this.props;
setTitle('输入设备编号');
console.log(this.props);
}
changeHandle = e => {
let value = e.target.value.trim();
value = filterEmoji(value);
this.setState({
deviceCode: value,
});
};
validate = () => {
const { deviceCode } = this.state;
if (deviceCode) {
return true;
} else {
Toast.fail('请输入设备编号');
}
};
bindEquipment = () => {
if (this.validate()) {
Toast.loading('', 20);
console.log(this.props);
const { history, userinfo, updateUserInfo } = this.props;
const { deviceCode } = this.state;
getPositions({
deviceCode,
token: userinfo.token,
})
.then(res => {
Toast.hide();
console.log(res);
const { barcode, ...data } = res.data;
if (data.id) {
updateUserInfo({ barcode, position: { ...data, isBind: true } });
} else {
updateUserInfo({ barcode, position: { ...data, isBind: false } });
}
history.push({
pathname: '/Equipment/' + deviceCode,
});
})
.catch(err => {
Toast.fail(err.msg || '请求失败');
});
}
};
scanEquipment = () => {
const { wxDone, wxConfigHandle } = this.props;
console.log('wxDone', wxDone);
if (wxDone) {
this.scanHandle();
} else {
wxConfigHandle()
.then(() => {
console.log('in res');
this.scanHandle();
})
.catch(() => {
console.log('in error');
Toast.fail('启用微信失败!');
});
}
};
scanHandle = () => {
const { wxConfigHandle } = this.props;
const wx = window.wx;
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: res => {
const result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
this.setState({
deviceCode: result,
});
},
fail: err => {
console.log(err);
Toast.info(err.errMsg || '调用扫一扫失败!');
wxConfigHandle();
},
});
};
render() {
const { deviceCode } = this.state;
return (
<div className='appContent BindEquipment'>
<div className='inputBox'>
<div className='inputWrap'>
<input
className='br8 input'
onChange={this.changeHandle}
value={deviceCode}
type='text'
placeholder='请输入设备编号'
maxLength='8'
/>
<Button
type='primary'
className='scan'
onClick={this.scanEquipment}>
<div className='scanicon'>
<img src={scanImg} alt='' />
</div>
</Button>
</div>
<Button type='primary' className='btn' onClick={this.bindEquipment}>
确定
</Button>
</div>
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ updateUserInfo, userinfo }) => (
<Bindequipment
{...props}
updateUserInfo={updateUserInfo}
userinfo={userinfo}
/>
)}
</UserInfoContext.Consumer>
);
.BindEquipment {
flex: 1;
background: #ebebeb url('../../images/Login/login_bg@2x.png') no-repeat;
background-size: contain;
background-position-y: -624px;
background-color: #fff;
.inputBox {
width: 500px;
height: 200px;
margin: 127px auto 0;
}
.inputWrap {
display: flex;
}
.input {
box-sizing: border-box;
flex: 1;
height: 80px;
line-height: 80px;
text-indent: 20px;
border: 2px solid #ff900d;
word-break: break-all;
}
.scan {
display: flex;
justify-content: center;
align-items: center;
height: 80px;
line-height: 80px;
margin-left: 10px;
font-size: 24px;
padding: 0 24px;
}
.scanicon {
width: 45px;
height: 45px;
}
.btn {
margin-top: 30px;
}
}
import React, { Component } from 'react';
import { Toast, Button } from 'antd-mobile';
import './style.scss';
import eqIcon from '../../images/Equipment/equip_number_icon@2x.png';
import testIcon from '../../images/Equipment/test_water_icon@2x.png';
import checkIcon from '../../images/Equipment/check_water_icon@2x.png';
import scanImg from '../../images/Equipment/scan.png';
// import WxWrap from '../../HOC/wx';
let DeviceCode = '';
class EquipmentTest extends Component {
state = {
deviceCode: DeviceCode,
};
componentWillMount() {
const { setTitle } = this.props;
setTitle('功能页');
console.log(this.props);
}
changeHandle = e => {
this.setState({
deviceCode: e.target.value.trim(),
});
DeviceCode = e.target.value.trim();
};
validateDeviceCode = () => {
const { deviceCode } = this.state;
return deviceCode ? true : false;
};
goPage = path => {
if (this.validateDeviceCode()) {
const { history } = this.props;
const { deviceCode } = this.state;
history.push(`/Equipment/${deviceCode}` + path);
} else {
Toast.fail('请输入设备编号');
}
};
scanEquipment = () => {
const { wxDone, wxConfigHandle } = this.props;
console.log('wxDone', wxDone);
if (wxDone) {
this.scanHandle();
} else {
wxConfigHandle()
.then(() => {
console.log('in res');
this.scanHandle();
})
.catch(() => {
console.log('in error');
Toast.fail('启用微信失败!');
});
}
};
scanHandle = () => {
const { wxConfigHandle } = this.props;
const wx = window.wx;
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: res => {
const result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
this.setState({
deviceCode: result,
});
},
fail: err => {
console.log(err);
Toast.info(err.errMsg || '调用扫一扫失败!');
wxConfigHandle();
},
});
};
render() {
const { deviceCode } = this.state;
return (
<div className='appContent Equipment'>
<div className='inputBox'>
<div className='listItem'>
<i className='inputIcon'>
<img src={eqIcon} alt='' />
</i>
<span className='ellipsis inputText'>
设备编号:
<input
className='br8 listInput'
onChange={this.changeHandle}
type='text'
placeholder='请输入设备编号'
maxLength='8'
value={deviceCode}
/>
</span>
</div>
<Button
type='primary'
className='scan'
onClick={this.scanEquipment}>
<div className='scanicon'>
<img src={scanImg} alt='' />
</div>
</Button>
</div>
<div className='br8 btnBox testBtnBox'>
<div className='boxBs testBoxBs' />
<div
className='linkItem'
onClick={() => this.goPage('/TestWater')}>
<i className='linkIcon'>
<img src={testIcon} alt='' />
</i>
<span>测试放水</span>
</div>
<div
className='linkItem'
onClick={() => this.goPage('/CheckWater')}>
<i className='linkIcon'>
<img src={checkIcon} alt='' />
</i>
<span>校准流量</span>
</div>
</div>
</div>
);
}
}
export default EquipmentTest;
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import './style.scss';
import eqIcon from '../../images/Equipment/equip_number_icon@2x.png';
import bindIcon from '../../images/Equipment/link_position_icon@2x.png';
import testIcon from '../../images/Equipment/test_water_icon@2x.png';
import checkIcon from '../../images/Equipment/check_water_icon@2x.png';
import rateIcon from '../../images/Equipment/rate_active_icon@2x.png';
import UserInfoContext from '../../context/userinfo-context';
export class Index extends React.Component {
static propTypes = {
name: PropTypes.string,
};
componentWillMount() {
const { setTitle } = this.props;
setTitle('功能页');
console.log(this.props);
}
render() {
const { deviceCode } = this.props.match.params;
return (
<div className='appContent Equipment'>
<div className='inputBox'>
<i className='inputIcon'>
<img src={eqIcon} alt='' />
</i>
<span className='ellipsis inputText'>
设备编号:
{deviceCode}
</span>
</div>
<div className='br8 btnBox'>
<div className='boxB boxBs' />
<div className='boxBv' />
<Link
className='linkItem'
to={'/Equipment/' + deviceCode + '/SelectEquipment'}>
<i className='linkIcon'>
<img src={bindIcon} alt='' />
</i>
<span>绑定解绑位置</span>
</Link>
<Link
className='linkItem'
to={'/Equipment/' + deviceCode + '/TestWater'}>
<i className='linkIcon'>
<img src={testIcon} alt='' />
</i>
<span>测试放水</span>
</Link>
<Link
className='linkItem'
to={'/Equipment/' + deviceCode + '/CheckWater'}>
<i className='linkIcon'>
<img src={checkIcon} alt='' />
</i>
<span>校准流量</span>
</Link>
<Link
className='linkItem'
to={'/Equipment/' + deviceCode + '/RateActivity'}>
<i className='linkIcon'>
<img src={rateIcon} alt='' />
</i>
<span>费率激活</span>
</Link>
</div>
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ updateUserInfo, userinfo }) => (
<Index {...props} updateUserInfo={updateUserInfo} userinfo={userinfo} />
)}
</UserInfoContext.Consumer>
);
.Equipment {
flex: 1;
background: #ebebeb url('../../images/Login/login_bg@2x.png') no-repeat;
background-size: contain;
background-position-y: -624px;
.inputBox {
display: flex;
width: 500px;
margin: 84px auto 0;
}
/* .inputInner {
composes: listItem from '../Login/style.css';
} */
/* .input {
composes: listInput from '../Login/style.css';
} */
.inputIcon {
width: 24px;
height: 28px;
margin: 26px 10px 0 20px;
}
.inputText {
font-size: 26px;
color: #444;
flex: 1;
line-height: 80px;
}
.btnBox {
width: 500px;
height: 500px;
margin: 86px auto 0;
background-color: #fff;
box-shadow: 0 0 5px rgba(43, 43, 43, 0.3);
display: flex;
flex-wrap: wrap;
position: relative;
}
.testBtnBox {
height: 250px;
}
.boxB,
.boxBv,
.testBoxBs {
position: absolute;
background-color: #ebebeb;
}
.boxBs,
.testBoxBs {
width: 2px;
height: 440px;
left: 249px;
top: 30px;
}
.testBoxBs {
height: 190px;
}
.boxBv {
width: 440px;
height: 2px;
left: 30px;
top: 249px;
}
.linkItem {
width: 250px;
height: 250px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #444;
font-size: 26px;
position: relative;
}
.linkIcon {
width: 60px;
height: 60px;
margin-bottom: 20px;
}
.scan {
display: flex;
justify-content: center;
align-items: center;
height: 80px;
line-height: 80px;
margin-left: 10px;
font-size: 24px;
padding: 0 24px;
}
.scanicon {
width: 45px;
height: 45px;
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Switch, Route } from 'react-router-dom';
import Equipment from '../Equipment/index';
import SelectEquipment from '../SelectEquipment';
// import TestWater from '../TestWater';
// import CheckWater from '../CheckWater';
import RateActivity from '../RateActivity';
import Location from '../Location';
class EquipmentContetn extends React.Component {
static propTypes = {
name: PropTypes.string,
};
constructor(props) {
super(props);
}
render() {
const { setTitle } = this.props;
// const { position, campusId } = this.state;
return (
<Switch>
<Route
path="/Equipment/:deviceCode"
exact
render={props => <Equipment {...props} setTitle={setTitle} />}
/>
<Route
path="/Equipment/:deviceCode/SelectEquipment"
render={props => (
<SelectEquipment
{...props}
setTitle={setTitle}
/>
)}
/>
{/* <Route
path="/Equipment/:deviceCode/TestWater"
render={props => <TestWater {...props} setTitle={setTitle} />}
/> */}
{/* <Route
path="/Equipment/:deviceCode/CheckWater"
render={props => <CheckWater {...props} setTitle={setTitle} />}
/> */}
<Route
path="/Equipment/:deviceCode/RateActivity"
render={props => <RateActivity {...props} setTitle={setTitle} />}
/>
<Route
path="/Equipment/:deviceCode/Location"
render={props => <Location {...props} setTitle={setTitle} />}
/>
</Switch>
);
}
}
export default EquipmentContetn;
import React, { useState, useEffect } from 'react';
import './style.css';
function Index() {
function Index(props) {
const [loginText, setLoginText] = useState('载入中...');
useEffect(() => {
const { search } = window.location;
......@@ -11,6 +11,8 @@ function Index() {
console.log(code);
if (code) {
setLoginText('微信授权中...');
const { history } = props;
history.replace('/login');
} else {
console.log('跳转微信授权接口');
// const { APP_ID } = config;
......
import React from 'react';
import PropTypes from 'prop-types';
import styles from './style.css';
import { Toast } from 'antd-mobile';
import searchIcon from '../../images/Search/default_search_icon@2x.png';
import { getPositionsList } from '../../api/index';
import UserInfoContext from '../../context/userinfo-context';
import { filterEmoji } from '../../utils/index';
export class Location extends React.Component {
static propTypes = {
campusId: PropTypes.number,
};
constructor(props) {
super(props);
this.state = {
showToast: false,
localText: '',
localList: [],
pagination: {},
};
}
componentWillMount() {
const { setTitle, userinfo } = this.props;
setTitle('检索位置');
getPositionsList({
campusId: userinfo.campusId,
})
.then(res => {
const { list, ...pagination } = res.data;
this.setState({
localList: list,
pagination,
});
})
.catch(err => {
Toast.fail(err.msg || '请求失败');
});
}
showToast = text => {
this.setState({
showToast: true,
localText: text,
});
let elem = document.getElementById(text);
elem.scrollIntoView();
setTimeout(() => {
this.setState({
showToast: false,
});
}, 1000);
};
changeHandle = e => {
let value = e.target.value.trim();
value = filterEmoji(value);
this.setState({
localText: value,
});
};
searchList = () => {
Toast.loading('', 20);
const { userinfo } = this.props;
const { localText } = this.state;
getPositionsList({
campusId: userinfo.campusId,
name: localText,
})
.then(res => {
const { list, ...pagination } = res.data;
this.setState({
localList: list,
pagination,
});
Toast.hide();
})
.catch(err => {
Toast.fail(err.msg || '请求失败');
});
};
bindEquipment = (positionId, position) => {
const { history, updateUserInfo, userinfo } = this.props;
updateUserInfo({
position: {
...userinfo.position,
id: positionId,
position,
},
});
history.goBack();
};
render() {
const { showToast, localText, localList } = this.state;
return (
<div className={'appContent ' + styles.Location}>
<div className={styles.searchBox}>
<input
className={'br8 ' + styles.searchInput}
type='text'
onChange={this.changeHandle}
placeholder='请输入位置关键词'
/>
<span className={styles.searchBtn} onClick={this.searchList}>
<i className={styles.searchIcon}>
<img src={searchIcon} alt='' />
</i>
搜索
</span>
</div>
<div className={styles.infoBar}>
{localList.map(item => (
<div
key={item.firstLetter}
className={styles.infoItem}
onClick={() => this.showToast(item.firstLetter)}
>
{item.firstLetter}
</div>
))}
</div>
<div
className={
showToast
? styles.show + ' br8 ' + styles.infoToast
: 'br8 ' + styles.infoToast
}
>
{localText}
</div>
<div className={styles.locationList}>
{localList.map(area => (
<div key={area.firstLetter} className={styles.locationArea}>
<div id={area.firstLetter} className={styles.locationTitle}>
{area.firstLetter}
</div>
{area.positions.map((location, index) => (
<div
key={index}
className={styles.locationItem}
onClick={() =>
this.bindEquipment(location.id, location.position)
}
>
{location.position}
</div>
))}
</div>
))}
</div>
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ userinfo, updateUserInfo }) => (
<Location
{...props}
userinfo={userinfo}
updateUserInfo={updateUserInfo}
/>
)}
</UserInfoContext.Consumer>
);
.Location {
background-color: #fff;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
.searchBox {
height: 90px;
}
.searchInput {
/* composes: input from '../BindEquipment/style.css'; */
margin-top: 18px;
margin-left: 40px;
width: 440px;
height: 56px;
border-color: #ff900d;
}
.searchBtn {
display: inline-block;
height: 90px;
line-height: 90px;
margin-left: 24px;
font-size: 30px;
color: #ff900d;
}
.searchIcon {
display: inline-block;
width: 30px;
height: 30px;
margin-right: 10px;
vertical-align: middle;
}
.infoBar {
position: fixed;
top: 262px;
right: 30px;
width: 40px;
text-align: center;
font-size: 26px;
color: #666;
}
.infoItem {
height: 50px;
line-height: 50px;
}
.infoToast {
position: fixed;
width: 180px;
height: 180px;
line-height: 180px;
background-color: rgba(254, 148, 10, 0.4);
left: 9999px;
top: 480px;
margin-left: -90px;
text-align: center;
font-size: 116px;
color: #fff;
opacity: 0;
transition: opacity 500ms;
}
.show {
opacity: 1;
left: 50%;
}
.locationTitle {
padding: 0 40px;
height: 70px;
line-height: 70px;
background-color: #ebebeb;
font-size: 26px;
color: #999;
}
.locationItem {
width: 530px;
line-height: 80px;
margin: 0 auto;
font-size: 30px;
color: #444;
border-bottom: 2px solid #ebebeb;
word-break: break-all;
}
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Toast } from 'antd-mobile';
import './style.scss';
import acIcon from '../../images/Login/account_icon@2x.png';
import psIcon from '../../images/Login/password_icon@2x.png';
import { wxLogin, login } from '../../api/index';
import UserInfoContext from '../../context/userinfo-context';
export class Index extends React.Component {
static propTypes = {
code: PropTypes.string,
updateUserInfo: PropTypes.func,
};
constructor(props, context) {
super(props, context);
this.state = {
account: '',
password: '',
};
}
componentWillMount() {
console.log(this.props);
const { setTitle, updateUserInfo } = this.props;
setTitle('登陆');
const { code } = this.props;
if (code) {
Toast.loading('', 20);
wxLogin({
code,
})
.then(res => {
Toast.hide();
console.log(res);
const data = res.data;
const { history } = this.props;
updateUserInfo(data);
const location = {
pathname: '/BindEquipment',
state: data,
};
history.push(location);
})
.catch(err => {
console.log(err);
Toast.fail(err.msg || '自动登陆失败!');
});
}
}
changeHandle = (key, e) => {
this.setState({
[key]: e.target.value.trim(),
});
};
validate = () => {
const { account, password } = this.state;
if (account === '') {
Toast.fail('请输入账号');
return false;
}
if (password === '') {
Toast.fail('请输入密码');
return false;
}
return true;
};
login = () => {
if (this.validate()) {
Toast.loading('', 20);
const { account, password } = this.state;
const { updateUserInfo, code } = this.props;
let entity = code
? {
account,
password,
weixinCode: code,
}
: {
account,
password,
};
login(entity)
.then(res => {
Toast.hide();
console.log(res);
const data = res.data;
const { accountCategory } = data;
const { history } = this.props;
updateUserInfo({
...data,
position: {
id: null,
position: null,
mode: null,
isBind: false,
deviceType: null,
},
});
const location =
accountCategory === 1
? {
pathname: '/BindEquipment',
state: data,
}
: {
pathname: '/EquipmentTest',
state: data,
};
history.push(location);
})
.catch(err => {
console.log(err);
Toast.fail(err.msg || '登陆失败!');
});
}
};
render() {
return (
<div className='appContent Login'>
<div className='br8 loginBox'>
<label className='listItem accountItem'>
<i className='inputIcon'>
<img src={acIcon} alt='' />
</i>
<input
className='listInput'
onChange={e => this.changeHandle('account', e)}
type='text'
placeholder='请输入运维账号'
maxLength='10'
/>
</label>
<label className='listItem pwdItem'>
<i className='inputIcon'>
<img src={psIcon} alt='' />
</i>
<input
className='listInput'
onChange={e => this.changeHandle('password', e)}
type='password'
placeholder='请输入登陆密码'
maxLength='10'
/>
</label>
<Button type='primary' className='login-btn' onClick={this.login}>
登录
</Button>
</div>
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ updateUserInfo }) => (
<Index {...props} updateUserInfo={updateUserInfo} />
)}
</UserInfoContext.Consumer>
);
.Login {
box-sizing: border-box;
background-image: url('../../images/Login/login_bg@2x.png');
background-size: contain;
.loginBox {
box-sizing: border-box;
width: 560px;
height: 342px;
margin: 126px auto 0;
background-color: rgba(255, 255, 255, 0.5);
padding: 30px;
}
.accountItem {
margin-bottom: 12px;
}
.pwdItem {
margin-bottom: 30px;
}
.inputIcon {
width: 20px;
height: 26px;
margin: 27px 10px 0 20px;
}
.login-btn {
height: 80px;
line-height: 80px;
font-size: 30px;
}
}
import React from 'react';
class Index extends React.Component {
render() {
return <div>404 NoMatch</div>;
}
}
export default Index;
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Toast } from 'antd-mobile';
import styles from './style.scss';
import eqIcon from '../../images/Equipment/equip_number_icon@2x.png';
import rateIcon from '../../images/Rate/rate_detail_icon@2x.png';
import { fetchRate, activateRate } from '../../api/index';
export class Index extends React.Component {
static propTypes = {
name: PropTypes.string,
};
constructor(props) {
super(props);
this.state = {
mode: null,
type: 1,
coldMeasure: '0',
coldMoney: '0.00',
hotMeasure: '0',
hotMoney: '0.00',
warmMeasure: '0',
warmMoney: '0.00',
};
}
componentWillMount() {
const { setTitle } = this.props;
const { deviceCode } = this.props.match.params;
setTitle('费率激活');
fetchRate(deviceCode)
.then(res => {
console.log(res);
const {
coldMeasure,
coldMoney,
hotMeasure,
hotMoney,
mode,
warmMeasure,
warmMoney,
type,
} = res.data;
this.setState({
coldMeasure,
coldMoney: (coldMoney / 100).toFixed(2),
hotMeasure,
hotMoney: (hotMoney / 100).toFixed(2),
warmMeasure,
warmMoney: (warmMoney / 100).toFixed(2),
mode,
type,
});
})
.catch(err => {
Toast.fail(err.msg || '费率请求失败');
});
}
clickHandle = () => {
Toast.loading('', 20);
const { deviceCode } = this.props.match.params;
activateRate({ deviceCode })
.then(res => {
console.log(res);
Toast.success(res.msg);
})
.catch(err => {
Toast.fail(err.msg || '费率请求失败');
});
};
render() {
const { deviceCode } = this.props.match.params;
const {
coldMeasure,
coldMoney,
hotMeasure,
hotMoney,
warmMeasure,
warmMoney,
mode,
type,
} = this.state;
let rateTypeText = 'ML|S';
if (mode === 1) {
rateTypeText = 'ML';
}
if (mode === 2) {
rateTypeText = 'S';
}
return (
<div className={'appContent ' + styles.RateActivity}>
<div className={styles.inputBox}>
<i className={styles.inputIcon}>
<img src={eqIcon} alt='' />
</i>
<span className={'ellipsis ' + styles.inputText}>
设备编号:
{deviceCode}
</span>
</div>
<div className={styles.inputBox}>
<i className={styles.inputIcon}>
<img src={rateIcon} alt='' />
</i>
<span className={'ellipsis ' + styles.inputText}>费率详情:</span>
</div>
<div className={styles.rateBox}>
{type === 2
? [
<div className={styles.rateArea}>
<div className={styles.rateTitle}>
<span className={styles.rateItem}>一路费率价格()</span>
<span className={styles.rateItem}>
费率单位({rateTypeText})
</span>
</div>
<div className={styles.line} />
<div className={styles.ratePrice}>
<span className={styles.rateItem}>{coldMoney}</span>
<span className={styles.rateItem}>{coldMeasure}</span>
</div>
</div>,
<div className={styles.rateArea}>
<div className={styles.rateTitle}>
<span className={styles.rateItem}>二路费率价格()</span>
<span className={styles.rateItem}>
费率单位({rateTypeText})
</span>
</div>
<div className={styles.line} />
<div className={styles.ratePrice}>
<span className={styles.rateItem}>{hotMoney}</span>
<span className={styles.rateItem}>{hotMeasure}</span>
</div>
</div>,
<div className={styles.rateArea}>
<div className={styles.rateTitle}>
<span className={styles.rateItem}>三路费率价格()</span>
<span className={styles.rateItem}>
费率单位({rateTypeText})
</span>
</div>
<div className={styles.line} />
<div className={styles.ratePrice}>
<span className={styles.rateItem}>{warmMoney}</span>
<span className={styles.rateItem}>{warmMeasure}</span>
</div>
</div>,
]
: [
<div className={styles.rateArea}>
<div className={styles.rateTitle}>
<span className={styles.rateItem}>冷水费率价格()</span>
<span className={styles.rateItem}>
冷水费率单位({rateTypeText})
</span>
</div>
<div className={styles.line} />
<div className={styles.ratePrice}>
<span className={styles.rateItem}>{coldMoney}</span>
<span className={styles.rateItem}>{coldMeasure}</span>
</div>
</div>,
<div className={styles.rateArea}>
<div className={styles.rateTitle}>
<span className={styles.rateItem}>热水费率价格()</span>
<span className={styles.rateItem}>
热水费率单位({rateTypeText})
</span>
</div>
<div className={styles.line} />
<div className={styles.ratePrice}>
<span className={styles.rateItem}>{hotMoney}</span>
<span className={styles.rateItem}>{hotMeasure}</span>
</div>
</div>,
]}
</div>
<Button
type='primary'
className={styles.btn}
onClick={this.clickHandle}
>
激活费率
</Button>
</div>
);
}
}
export default Index;
.RateActivity {
background-color: #fff;
.inputBox {
/* composes: inputBox from '../Equipment/style.css'; */
margin-top: 62px;
margin-bottom: 62px;
}
.inputBox:nth-child(2) {
margin-bottom: 4px;
}
.inputIcon {
/* composes: inputIcon from '../Equipment/style.css'; */
margin-left: 0;
}
.inputText {
/* composes: inputText from '../Equipment/style.css'; */
}
.rateBox {
margin: 0 auto;
width: 460px;
}
.rateTitle {
display: flex;
justify-content: space-between;
font-size: 22px;
color: #444;
}
.line {
width: 100%;
height: 2px;
background-color: #ff900d;
margin: 10px 0;
}
.ratePrice {
display: flex;
justify-content: space-between;
font-size: 26px;
}
.ratePrice .rateItem {
width: 188px;
height: 41px;
line-height: 41px;
background-color: #f0f0f0;
text-indent: 10px;
}
.rateArea {
margin-bottom: 60px;
}
.rateArea:last-child {
margin-bottom: 0;
}
.btn {
/* composes: btn from '../BindEquipment/style.css'; */
width: 500px;
margin: 90px auto;
font-size: 30px;
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Toast } from 'antd-mobile';
import './style.scss';
import eqIcon from '../../images/Equipment/equip_number_icon@2x.png';
import locationIcon from '../../images/Equipment/install_position_icon@2x.png';
import UserInfoContext from '../../context/userinfo-context';
import { bindEquipment, unbindEquipment } from '../../api/index';
export class SelectEquipment extends React.Component {
static propTypes = {
name: PropTypes.string,
userinfo: PropTypes.object,
};
componentWillMount() {
const { setTitle } = this.props;
setTitle('绑定解绑设备');
console.log(this.props);
}
goLocation = () => {
const { history } = this.props;
const { deviceCode } = this.props.match.params;
history.push('/Equipment/' + deviceCode + '/Location');
};
bindEquipment = () => {
console.log('bind');
const { deviceCode } = this.props.match.params;
const {
userinfo: { position },
updateUserInfo,
} = this.props;
if (position.id) {
bindEquipment({
deviceCode,
positionId: position.id,
})
.then(res => {
Toast.success('绑定成功' || res.msg);
updateUserInfo({
position: {
...position,
isBind: true,
},
});
})
.catch(err => {
Toast.fail(err.msg || '绑定位置失败');
});
} else {
Toast.fail('请选择安装位置');
}
};
unbindEquipment = () => {
console.log('unbind');
const { deviceCode } = this.props.match.params;
const {
userinfo: { position },
updateUserInfo,
} = this.props;
unbindEquipment({
deviceCode,
positionId: position.id,
})
.then(res => {
Toast.success('解绑成功' || res.msg);
updateUserInfo({
position: {
...position,
id: null,
position: null,
isBind: false,
},
});
})
.catch(err => {
Toast.fail(err.msg || '请选择安装位置');
});
};
render() {
const { deviceCode } = this.props.match.params;
const { position } = this.props.userinfo;
return (
<div className='appContent SelectEquipment'>
<div className='List'>
<label className='listItem'>
<div className='inputBox'>
<i className='inputIcon'>
<img src={eqIcon} alt='' />
</i>
<span className='ellipsis inputText'>设备编号:</span>
</div>
<div className='br8 input nobor'>{deviceCode}</div>
</label>
<label className='listItem'>
<div className='inputBox'>
<i className='inputIcon'>
<img src={locationIcon} alt='' />
</i>
<span className='ellipsis inputText'>选择安装位置:</span>
</div>
{position.isBind ? (
<div className={'br8 input'}>{position.position}</div>
) : (
<div className='br8 input autoH' onClick={this.goLocation}>
{position.id ? position.position : '点击选择设备安装位置'}
</div>
)}
</label>
</div>
{position.isBind ? (
<div className='btnBox'>
<Button className='btn'>绑定设备</Button>
<Button
className='btn'
type='primary'
onClick={this.unbindEquipment}>
解绑设备
</Button>
</div>
) : (
<div className='btnBox'>
<Button className='btn' type='primary' onClick={this.bindEquipment}>
绑定设备
</Button>
<Button className='btn'>解绑设备</Button>
</div>
)}
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ userinfo, updateUserInfo }) => (
<SelectEquipment
{...props}
userinfo={userinfo}
updateUserInfo={updateUserInfo}
/>
)}
</UserInfoContext.Consumer>
);
.SelectEquipment {
background-color: #fff;
.List {
width: 500px;
margin: 126px auto 0;
}
.listItem {
display: block;
}
.listItem:nth-child(1) {
margin-bottom: 38px;
}
.inputBox {
display: flex;
font-size: 26px;
height: 28px;
margin-bottom: 20px;
}
.inputIcon {
width: 24px;
height: 28px;
margin-right: 10px;
}
.input {
/* composes: input from '../BindEquipment/style.css'; */
text-indent: 20px;
}
.nobor {
border: none;
}
.autoH {
height: auto;
padding: 0 20px;
text-indent: 0;
}
.btnBox {
margin: 60px auto;
display: flex;
width: 500px;
justify-content: space-between;
}
.btn {
width: 220px;
font-size: 30px;
}
}
class Store {
constructor() {
this.token = '';
}
static getToken = () => {
return this.token;
};
static updateToken = token => {
this.token = token;
};
}
export default Store;
......@@ -98,3 +98,26 @@ img {
overflow: hidden;
overflow-y: scroll;
}
.App {
display: flex;
flex-direction: column;
}
.appContent {
flex: 1;
}
.br8 {
border-radius: 8px;
}
.listItem {
display: flex;
width: 100%;
height: 80px;
line-height: 80px;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
}
.listInput {
flex: 1;
border: none;
}
\ No newline at end of file
export const filterEmoji = str => {
let regRule = /\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g;
if (str.match(regRule)) {
str = str.replace(regRule, '');
}
return str;
};
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