Commit 749f9c6c by 姜雷

Merge branch 'test' into 'master'

Test See merge request !15
parents e5457ee5 e0f76440
PORT=7001
\ No newline at end of file
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: {
'postcss-flexbugs-fixes': {},
'postcss-import': {},
'postcss-url': {},
'postcss-aspect-ratio-mini': {},
'postcss-write-svg': {
utf8: false,
},
'postcss-cssnext': {},
'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) Allow px to be converted in media queries.
},
'postcss-viewport-units': {},
cssnano: {
preset: 'advanced',
autoprefixer: false,
'postcss-zindex': false,
},
},
};
## 打包说明:
```bash
# 项目打包命令
npm run build
# 打包目标地址 ./build
# 线上需替换 ./src/config/index.js 文件中 SERVER_URL 为正式环境接口部署地址
```
#
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
此为移动端项目在模板基础上添加屏幕适配,以 VW,VH 为单位实现页面适配,添加以下插件 此为移动端项目在模板基础上添加屏幕适配,以 VW,VH 为单位实现页面适配,添加以下插件
......
...@@ -35,7 +35,7 @@ module.exports = function override(config, env) { ...@@ -35,7 +35,7 @@ module.exports = function override(config, env) {
loader: require.resolve('less-loader'), loader: require.resolve('less-loader'),
options: { options: {
// theme vars, also can use theme.js instead of this. // theme vars, also can use theme.js instead of this.
modifyVars: { '@brand-primary': '#1DA57A' }, modifyVars: { '@brand-primary': '#3695c0' },
}, },
}, },
], ],
...@@ -57,34 +57,7 @@ module.exports = function override(config, env) { ...@@ -57,34 +57,7 @@ module.exports = function override(config, env) {
}, },
{ {
loader: require.resolve('postcss-loader'), loader: require.resolve('postcss-loader'),
options: { 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,
}),
],
},
}, },
], ],
}); });
......
...@@ -26,7 +26,12 @@ ...@@ -26,7 +26,12 @@
"autoprefixer": "^9.1.0", "autoprefixer": "^9.1.0",
"babel-plugin-import": "^1.8.0", "babel-plugin-import": "^1.8.0",
"cssnano-preset-advanced": "^4.0.0", "cssnano-preset-advanced": "^4.0.0",
"less": "^3.8.1",
"less-loader": "^4.1.0", "less-loader": "^4.1.0",
"postcss-import": "^12.0.0",
"postcss-load-config": "^2.0.0",
"postcss-plugin": "^1.0.0",
"postcss-url": "^7.3.2",
"react-app-rewired": "^1.5.2" "react-app-rewired": "^1.5.2"
}, },
"browserslist": [ "browserslist": [
......
...@@ -6,22 +6,9 @@ ...@@ -6,22 +6,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no, viewport-fit=cover, shrink-to-fit=no"> <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"> <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> <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="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!-- <title>成都师范后勤管理</title>
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> </head>
<body> <body>
...@@ -29,18 +16,7 @@ ...@@ -29,18 +16,7 @@
You need to enable JavaScript to run this app. You need to enable JavaScript to run this app.
</noscript> </noscript>
<div id="root"> <div id="root">
载入中。。。
</div> </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> <script> window.onload = function () { window.viewportUnitsBuggyfill.init({ hacks: window.viewportUnitsBuggyfillHacks }); } </script>
</body> </body>
......
import axios from 'axios'; import axios from 'axios';
import config from '../config/index'; import config from '../config/index';
import errorHandle from './errorHandle'; import errorHandle from './errorHandle';
// import store from '../store/index'; import store from '../store/index';
import { Toast } from 'antd-mobile';
const path = config.SERVER_URL + '/dcxy/api/gx/construction'; const path = config.SERVER_URL;
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
baseURL: path, // api的base_url baseURL: path, // api的base_url
timeout: 10000, timeout: 20000,
}); });
// request拦截器 // request拦截器
service.interceptors.request.use( service.interceptors.request.use(
conf => { conf => {
// let token = store.getToken(); let userId = store.getUserId();
if (/post/.test(conf.method.toLocaleLowerCase())) {
let data = userId
? {
...conf.data,
userId,
}
: conf.data;
conf.data = data;
} else {
let data = userId
? {
...conf.params,
userId,
}
: conf.params;
conf.params = data;
}
Toast.loading('Loading...', 99);
conf.headers = { conf.headers = {
...conf.headers, ...conf.headers,
reqSource: 'wx', reqSource: 'wx',
// token: token ? token : '',
}; };
return conf; return conf;
}, },
...@@ -28,17 +47,24 @@ service.interceptors.request.use( ...@@ -28,17 +47,24 @@ service.interceptors.request.use(
); );
// respone拦截器 // respone拦截器
service.interceptors.response.use(response => { service.interceptors.response.use(
const res = response.data; response => {
const res = response.data;
/** Toast.hide();
* code为非'0'是抛错 /**
*/ * code为非1000是抛错
if (res.code !== 1000) { */
return Promise.reject(response.data); if (res.code !== 1000) {
} else { return Promise.reject(response.data);
return response.data; } else {
return response.data;
}
},
err => {
Toast.hide();
return errorHandle(err);
} }
}, errorHandle); );
export default service; export default service;
import fetch from './fetch'; import fetch from './fetch';
export const fetchPublicCarList = entity => // login
export const wxAuth = entity =>
fetch({
url: '/login/weChatAuthorization',
params: entity,
});
export const getVcode = entity =>
fetch({
url: '/login/getCode',
params: entity,
});
export const sendMsg = entity =>
fetch({ fetch({
url: '/app/publicCarController/pageList', url: '/login/sendMsg',
params: entity, params: entity,
}); });
export const login = entity =>
fetch({
url: '/login/app/login.do',
method: 'post',
data: entity,
});
// base
export const fetchRepairArea = () =>
fetch({
url: '/baseData/getBaseData',
params: { type: 1 },
});
export const fetchRepairTerm = () =>
fetch({
url: '/baseData/getBaseData',
params: { type: 2 },
});
export const fetchCarCategory = () =>
fetch({
url: '/baseData/getBaseData',
params: { type: 3 },
});
export const fetchCarPlateList = () =>
fetch({
url: '/baseData/getBaseData',
params: { type: 4 },
});
// publicCar
export const fetchPublicCarList = entity =>
fetch({
url: '/publicCar/app/teacherAppoCar4AppPage',
method: 'post',
data: entity,
});
export const applyPublicCar = entity => export const applyPublicCar = entity =>
fetch({ fetch({
url: '/app/publicCarController/addPublicCar', url: '/publicCar/app/teacherAppoCar4Add',
params: entity, method: 'post',
data: entity,
});
export const fetchApplyPublicCarList = entity =>
fetch({
url: '/publicCar/app/teacherAppoCar4AppPageAdmin',
method: 'post',
data: entity,
});
export const dealPublicCar = entity =>
fetch({
url: '/publicCar/app/teacherAppoCar4AppDeal',
method: 'post',
data: entity,
});
// repair
export const fetchRepairList = entity =>
fetch({
url: '/repair/app/pageList4Repair',
method: 'post',
data: entity,
});
export const reportRepair = entity =>
fetch({
url: '/repair/app/addRepair',
method: 'post',
data: entity,
});
export const fetchReportRepairList = entity =>
fetch({
url: '/repair/app/pageListAdmin4Repair',
method: 'post',
data: entity,
});
export const dealRepair = entity =>
fetch({
url: '/repair/app/deal4Repair',
method: 'post',
data: entity,
}); });
import config from '../config/index';
import axios from 'axios';
const path = config.SERVER_URL;
export const reportRepair = entity =>
axios({
url: path + '/repair/app/addRepair',
method: 'post',
data: entity,
});
import React, { Component } from 'react';
class CodeView extends Component {
constructor(props) {
super(props);
let canDom = document.createElement('canvas');
canDom.id = 'ksCanvas';
canDom.setAttribute('width', '160px');
canDom.setAttribute('height', '64px');
canDom.style = 'display: none';
document.body.appendChild(canDom);
this.canvas = canDom;
}
componentWillUnmount() {
document.body.removeChild(this.canvas);
}
renderImg = () => {
const { value } = this.props;
const ctx = this.canvas.getContext('2d');
ctx.fillStyle = '#F0F0F0';
ctx.fillRect(0, 0, 160, 64);
ctx.font = '26px arial';
// 创建渐变
var gradient = ctx.createLinearGradient(0, 0, 160, 0);
gradient.addColorStop('0', 'magenta');
gradient.addColorStop('0.5', 'blue');
gradient.addColorStop('1.0', 'red');
// 用渐变填色
ctx.strokeStyle = gradient;
ctx.strokeText(value, 50, 40); //画布上添加验证码
return this.canvas.toDataURL();
};
render() {
return <img src={this.renderImg()} alt="" />;
}
}
export default CodeView;
import React, { Component } from 'react';
import styles from './style.css';
import addimgIcon from './images/add_pic_bg@2x.png';
import delIcon from './images/delete_pic_btn@2x.png';
import { Toast } from 'antd-mobile';
class ImagePicker extends Component {
constructor(props) {
super(props);
let canDom = document.getElementById('ksCanvasBox');
if (!canDom) {
canDom = document.createElement('canvas');
canDom.id = 'ksCanvasBox';
canDom.setAttribute('width', '590px');
canDom.setAttribute('height', '334px');
canDom.style = 'display: none';
document.body.appendChild(canDom);
}
this.canvas = canDom;
this.state = {
aspectRatio: 590 / 334,
done: false,
hasValue:
Object.keys(props).find(i => i === 'value') === -1 ? false : true,
files: [],
};
}
shouldComponentUpdate(nextProps) {
const { value } = this.props;
if (!nextProps.value && value) {
this.deleteImage();
}
return true;
}
selectImage = () => {
const { done } = this.state;
const { showView } = this.props;
if (done) {
showView();
} else {
this.refs.input.dispatchEvent(new MouseEvent('click'));
}
};
deleteImage = e => {
e && e.stopPropagation();
const { onChange } = this.props;
console.log('del');
this.refs.input.value = '';
this.setState(() => ({
files: [],
done: false,
}));
if (onChange) {
onChange(null);
}
};
uploadImg = e => {
const { onChange } = this.props;
const filesList = e.target.files;
// const fileUrl = this.createObjectURL(filesList[0]);
this.resizeImg(filesList[0])
.then(({ file, url }) => {
this.setState(() => ({
files: [url],
done: true,
}));
if (onChange) {
onChange({
filesList: [file],
url: url,
});
}
})
.catch(err => {
Toast.fail(err);
});
};
createObjectURL = blob => {
if (window.URL) {
return window.URL.createObjectURL(blob);
} else if (window.webkitURL) {
return window.webkitURL.createObjectURL(blob);
}
return null;
};
revokeObjectURL = url => {
if (window.URL) {
return window.URL.revokeObjectURL(url);
} else if (window.webkitURL) {
return window.webkitURL.revokeObjectURL(url);
}
};
resizeImg = file =>
new Promise((resolve, reject) => {
const { aspectRatio } = this.state;
const ctx = this.canvas.getContext('2d');
const fileUrl = this.createObjectURL(file);
ctx.clearRect(0, 0, 590, 334);
const img = new Image();
img.onload = () => {
console.log(img.width, img.height);
let imgRatio = img.width / img.height;
let x, y;
if (imgRatio > aspectRatio) {
x = 590;
y = 590 / imgRatio;
} else {
x = 334 * imgRatio;
y = 334;
}
ctx.drawImage(img, (590 - x) / 2, (334 - y) / 2, x, y);
this.canvas.toBlob(blob => {
const dataURL = this.canvas.toDataURL('image/png');
console.log(file.size, dataURL.length);
resolve({ file: blob, url: dataURL });
});
};
img.onerror = () => {
reject('背景加载失败');
};
img.src = fileUrl;
});
render() {
const { className } = this.props;
const { files, done } = this.state;
return (
<div
className={
className ? `${className} ${styles.imageWrap}` : styles.imageWrap
}
onClick={this.selectImage}
>
{done ? (
<img className={styles.innerImg} src={files[0]} alt="" />
) : (
<img src={addimgIcon} alt="" />
)}
{done && (
<img
src={delIcon}
className={styles.delBtn}
alt=""
onClick={this.deleteImage}
/>
)}
<input
ref="input"
type="file"
accept="image/*"
onChange={this.uploadImg}
style={{ display: 'none' }}
/>
</div>
);
}
}
export default ImagePicker;
.imageWrap {
width: 170px;
height: 96px;
/* background-image: url('./images/add_pic_bg@2x.png'); */
position: relative;
}
.innerImg {
width: 100%;
height: 100%;
}
.delBtn {
position: absolute;
width: 38px;
height: 38px;
left: 50%;
top: 76px;
transform: translate(-50%, 0);
}
import React, { Component } from 'react';
import styles from './style.css';
class Radio extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<label className={styles.radioBox}>
<span className={styles.radio}>
<span className={styles.radioInner} />
<input type="radio" className={styles.radioInput} />
</span>
{this.props.children}
</label>
);
}
}
export default Radio;
.radioBox {
}
.radio {
position: relative;
display: inline-block;
vertical-align: middle;
width: 30px;
height: 30px;
border: 1px solid #ccc;
border-radius: 50%;
margin-right: 10px;
}
.radioInner {
position: absolute;
right: 0;
width: 15px;
height: 15px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
.radioInput {
position: absolute;
top: 0;
left: 0;
opacity: 0;
width: 100%;
height: 100%;
z-index: 2;
border: 0 none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import styles from './style.css';
import arrIcon from './images/select_icon@2x.png';
import Popup from '../../Popup/Popup';
class Select extends Component {
constructor(props) {
super(props);
this.state = {
hasValue:
Object.keys(props).find(i => i === 'value') === -1 ? false : true,
value: [],
};
}
showPickerView = () => {
console.log('showPickerView');
console.log(this.props);
const { placeholder, onChange, ...props } = this.props;
let box = document.getElementById('ks-picker-box');
if (!box) {
box = document.createElement('div');
box.id = 'ks-picker-box';
document.body.appendChild(box);
}
this.picker = ReactDom.render(
<Popup
{...props}
onChange={this.changeHandle}
cancelHandle={this.cancelHandle}
okHandle={this.okHandle}
/>,
box
);
};
changeHandle = val => {
console.log(val);
const { onChange } = this.props;
onChange && onChange(val);
};
okHandle = () => {
const { onChange } = this.props;
const { value } = this.picker.state;
const { hasValue } = this.state;
if (hasValue) {
onChange && onChange(value);
} else {
this.setState({
value: value,
});
}
this.closePickerHandle();
};
cancelHandle = () => {
this.closePickerHandle();
};
closePickerHandle = () => {
let box = document.getElementById('ks-picker-box');
document.body.removeChild(box);
};
render() {
const { placeholder, data } = this.props;
const { value, hasValue } = this.state;
let text = '';
if (hasValue) {
let val = this.props.value;
text = val.length
? data.find(item => item.value === val[0]).label
: placeholder
? placeholder
: '';
} else {
text = value.length
? data.find(item => item.value === value[0]).label
: placeholder
? placeholder
: '';
}
return (
<div className={styles.selectWrap} onClick={this.showPickerView}>
{text}
<span className={styles.arrow}>
<img src={arrIcon} alt="" />
</span>
</div>
);
}
}
export default Select;
.selectWrap {
position: relative;
color: #666;
}
.arrow {
position: absolute;
right: 0;
bottom: 0;
width: 24px;
height: 24px;
}
import React, { Component } from 'react';
// import { createPortal } from 'react-dom';
import styles from './style.css';
import { Carousel } from 'antd-mobile';
import pageIcon from './images/next_pic_btn@2x.png';
class Perview extends Component {
constructor(props) {
super(props);
this.state = {
index: props.index,
list: props.data.filter(i => i),
};
}
onChangeHandle = index => {
this.setState({
index: index,
});
};
goPer = () => {
const { index } = this.state;
if (index === 0) return;
this.setState({
index: index - 1,
});
};
goNext = () => {
const { index, list } = this.state;
if (index + 1 >= list.length) return;
this.setState({
index: index + 1,
});
};
render() {
const { index, list } = this.state;
return (
<div>
<div className={styles.content}>
<div className={styles.imgBox}>
<Carousel
selectedIndex={index}
dots={false}
afterChange={this.onChangeHandle}
>
{list.length === 1 ? (
<div>
<img
className={styles.carouselImg}
src={list[0].url || list[0]}
alt=""
/>
</div>
) : (
list.map(
(item, index) =>
item ? (
<img
className={styles.carouselImg}
key={index}
src={item.url || item}
alt=""
/>
) : null
)
)}
</Carousel>
</div>
<div className={styles.footer}>
<div className={styles.per} onClick={this.goPer}>
<div className={styles.leftIcon}>
<img src={pageIcon} alt="" />
</div>
<div>上一张</div>
</div>
<div className={styles.pagination}>
{index + 1}/{list.length}
</div>
<div className={styles.next} onClick={this.goNext}>
<div>下一张</div>
<div className={styles.rightIcon}>
<img src={pageIcon} alt="" />
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Perview;
.content {
}
.imgBox {
margin: 0 auto;
width: 590px;
height: 334px;
}
.carouselImg {
width: 590px;
height: 334px;
}
.footer {
display: flex;
justify-content: center;
height: 76px;
line-height: 76px;
}
.footerItem {
}
.per,
.next {
display: flex;
font-size: 24px;
color: #3695c0;
align-items: center;
}
.footerIcon {
width: 30px;
height: 30px;
margin: 0 18px;
}
.leftIcon {
composes: footerIcon;
}
.rightIcon {
composes: footerIcon;
transform: rotate(180deg);
}
.pagination {
width: 160px;
}
import React, { Component } from 'react';
import styles from './style.css';
import { PickerView } from 'antd-mobile';
class Popup extends Component {
constructor(props) {
super(props);
let val = props.data[0] ? [props.data[0]] : [];
this.state = {
value: val,
};
}
changeHandle = val => {
const { onChange } = this.props;
this.setState({
value: val,
});
onChange(val);
};
render() {
const { cancelHandle, okHandle, onChange, ...props } = this.props;
const { value } = this.state;
return (
<div>
<div className={styles.mask} />
<div className={styles.ksPopupWrap}>
<div role="document" className={styles.ksPopupContent}>
<div className={styles.ksPopupHeader}>
<div className={styles.headerLeft} onClick={cancelHandle}>
取消
</div>
<div className={styles.headerTitle} />
<div className={styles.headerRight} onClick={okHandle}>
确定
</div>
</div>
<PickerView {...props} value={value} onChange={this.changeHandle} />
</div>
</div>
</div>
);
}
}
export default Popup;
.mask {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
height: 100%;
z-index: 1000;
transform: translateZ(1px);
}
.ksPopupWrap {
position: fixed;
overflow: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
-webkit-overflow-scrolling: touch;
outline: 0;
transform: translateZ(1px);
}
.ksPopupHeader {
display: flex;
align-items: center;
position: relative;
}
.headerItem {
display: flex;
font-size: 26px;
padding: 23px 15px;
height: 40px;
line-height: 40px;
}
.headerLeft {
composes: headerItem;
padding-left: 40px;
color: #333;
}
.headerTitle {
composes: headerItem;
flex: 1;
text-align: center;
color: #000;
}
.headerRight {
composes: headerItem;
padding-right: 40px;
color: #4986cb;
}
.ksPopupContent {
left: 0;
bottom: 0;
position: fixed;
width: 100%;
background-color: #fff;
padding-bottom: env(safe-area-inset-bottom);
}
export const APP_ID = 'wxed39740c39fa1c0a'; export const APP_ID = 'wx723baaa343083ca9';
export const SERVER_URL = 'http://ex-dev-gx-manager.168cad.top'; // export const SERVER_URL = 'http://ex-dev-gx-manager.168cad.top';
// export const SERVER_URL = 'http://192.168.1.150:8080'; export const SERVER_URL = 'http://ex-logistics-service-manager.168cad.top';
export default { export default {
APP_ID, APP_ID,
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import '../styles/App.css';
import { is_weixn } from '../utils'; import { is_weixn } from '../utils';
import { APP_ID } from '../config'; import { APP_ID } from '../config';
import UserInfoContext, { defaultUserinfo } from '../context/userinfo-context'; import UserInfoContext, { defaultUserinfo } from '../context/userinfo-context';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch, Redirect } from 'react-router-dom';
import Report from './report/report'; import Home from './Home/Index';
import Login from './Login/Login'; import Login from './Login/Login';
import PublicCarList from './PublicCar/PublicCarList'; import PublicCar from './PublicCar/index';
import PublicCarApply from './PublicCar/PublicCarApply'; import PublicCarDeal from './PublicCarDeal/index';
import Repair from './Repair/index';
import RepairDeal from './RepairDeal/index';
import NoMatch from './NoMatch';
import { wxAuth } from '../api/index';
import { Toast } from 'antd-mobile';
import store from '../store/index';
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.updateUserInfo = data => {
this.setState({
userinfo: { ...this.state.userinfo, ...data },
});
};
this.state = { this.state = {
isWx: is_weixn(), isWx: is_weixn(),
userContext: { userContext: {
userinfo: defaultUserinfo, userinfo: defaultUserinfo,
updateUserInfo: this.updateUserInfo, updateUserInfo: this.updateUserInfo,
}, },
loadingText: '加载中...',
}; };
} }
renderLogin = props => { getParams = str => {
// const { isWx } = this.state; let obj = {};
const isWx = true; const searchParams = new URLSearchParams(str);
const { search } = props.location; let keys = searchParams.keys();
const paramsString = search.substring(1); for (const key of keys) {
const searchParams = new URLSearchParams(paramsString); obj[key] = searchParams.get(key);
const openid = searchParams.get('openid'); }
// const state = searchParams.get('state'); return obj;
console.log(isWx, openid); };
renderIndex = props => {
console.log(props);
const {
userContext: {
userinfo: { login },
},
loadingText,
} = this.state;
const { history, location } = props;
let params = this.getParams(location.search);
if (login) {
return <div style={{ margin: '60px 24px 0' }}>{loadingText}</div>;
}
if (params.code) {
console.log('fetch UserInfo');
console.log('redirect', params);
wxAuth({
code: params.code,
})
.then(res => {
console.log(res);
const { firstLogin, user, author } = res.data;
store.updateUserId(user.userId);
this.updateUserInfo({ ...user, login: true, author });
if (firstLogin) {
history.replace('/login');
} else {
history.replace('/home');
}
})
.catch(err => {
console.log(err);
Toast.fail(err.msg || '获取授权失败!');
this.updateUserInfo({ login: true });
this.setState({
loadingText: '请求授权失败!请重新进入或刷新页面!',
});
});
} else {
console.log('go in wxAuth');
this.goWechatAuth();
}
return <div style={{ margin: '60px 24px 0' }}>{loadingText}</div>;
};
goWechatAuth = () => {
let rUrl = encodeURIComponent(window.location.origin);
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 style={{ margin: '60px 24px 0' }}>授权中。。。</div>;
};
validateUserinfo = (props, code, Component) => {
const {
userContext: {
userinfo: { login, author },
},
} = this.state;
if (!isWx) { if (!login) {
return <div>请在微信客户端打开链接</div>; console.log('no login');
} else if (!openid) { // product 需要修改为去微信授权
let rUrl = encodeURIComponent(''); // return <Component />;
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 this.goWechatAuth();
return <div>授权中。。。</div>; }
let item = author.find(i => i.code === code);
if (item.type) {
return <Component {...props} />;
} else { } else {
return <Login {...props} openid={openid} />; Toast.fail('无权使用该功能');
const from = { pathname: '/home' };
return <Redirect to={from} />;
} }
}; };
updateUserInfo = data => { updateUserInfo = data => {
...@@ -64,10 +126,32 @@ class App extends Component { ...@@ -64,10 +126,32 @@ class App extends Component {
<div className="App"> <div className="App">
<UserInfoContext.Provider value={userContext}> <UserInfoContext.Provider value={userContext}>
<Switch> <Switch>
<Route exact path="/" render={this.renderLogin} /> <Route exact path="/" render={this.renderIndex} />
<Route path="/report" component={Report} /> <Route path="/home" component={Home} />
<Route path="/PublicCarList" component={PublicCarList} /> <Route path="/login" component={Login} />
<Route path="/PublicCarApply" component={PublicCarApply} /> <Route
path="/publicCar"
render={props =>
this.validateUserinfo(props, 'CSHQ0003', PublicCar)
}
/>
<Route
path="/publicCarDeal"
render={props =>
this.validateUserinfo(props, 'CSHQ0004', PublicCarDeal)
}
/>
<Route
path="/Repair"
render={props => this.validateUserinfo(props, 'CSHQ0001', Repair)}
/>
<Route
path="/RepairDeal"
render={props =>
this.validateUserinfo(props, 'CSHQ0002', RepairDeal)
}
/>
<Route component={NoMatch} />
</Switch> </Switch>
</UserInfoContext.Provider> </UserInfoContext.Provider>
</div> </div>
......
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Link } from 'react-router-dom'; import styles from './style.css';
import { Toast } from 'antd-mobile';
import UserInfoContext from '../../context/userinfo-context';
import bgImg from './images/bg@2x.png';
import bannerIcon from './images/banner@2x.png';
import repairIcon from './images/repairs-new@2x.png';
import repairDealIcon from './images/repair_process@2x.png';
import publicCarIcon from './images/official_car_order@2x.png';
import publicCarDealIcon from './images/official_car_order_process@2x.png';
const HomePageStyle = {
width: '100%',
height: '100%',
background: `url(${bgImg})`,
overflowY: 'auto',
};
export class Index extends React.Component { export class Index extends React.Component {
static propTypes = { static propTypes = {
name: PropTypes.string, name: PropTypes.string,
}; };
validate = (code, path) => {
const {
userinfo: { author },
history,
} = this.props;
let item = author.find(i => i.code === code);
if (item && item.type) {
history.push(path);
return;
} else {
Toast.fail('无权使用该功能');
return;
}
};
render() { render() {
return ( return (
<div> <div style={HomePageStyle}>
Home <div className={styles.banner}>
<br /> <img src={bannerIcon} alt="" />
<Link to="/report">report</Link> </div>
<br /> <div className={styles.iconBox}>
<Link to="/report">report</Link> <div
<br /> className={styles.repairIcon}
<Link to="/report">report</Link> onClick={() => this.validate('CSHQ0001', '/Repair')}
>
<img src={repairIcon} alt="" />
</div>
<div
className={styles.publicCarIcon}
onClick={() => this.validate('CSHQ0003', '/publicCar')}
>
<img src={publicCarIcon} alt="" />
</div>
<div
className={styles.repairDealIcon}
onClick={() => this.validate('CSHQ0002', '/RepairDeal')}
>
<img src={repairDealIcon} alt="" />
</div>
<div
className={styles.publicCarDealIcon}
onClick={() => this.validate('CSHQ0004', '/publicCarDeal')}
>
<img src={publicCarDealIcon} alt="" />
</div>
</div>
</div> </div>
); );
} }
} }
export default Index; export default props => (
<UserInfoContext.Consumer>
{({ userinfo }) => <Index userinfo={userinfo} {...props} />}
</UserInfoContext.Consumer>
);
.banner {
height: 384px;
margin: 17px 20.5px;
}
.iconBox {
/* display: flex;
flex-wrap: wrap;
justify-content: space-between; */
position: relative;
height: 600px;
margin: 0 20.5px 0;
}
.iconItem {
position: absolute;
width: 290px;
/* height: 293.5px; */
/* margin-bottom: 18.7px; */
}
.repairIcon {
composes: iconItem;
height: 409px;
}
.publicCarIcon {
composes: iconItem;
height: 317.44px;
right: 0;
}
.repairDealIcon {
composes: iconItem;
height: 256px;
bottom: 0;
}
.publicCarDealIcon {
composes: iconItem;
height: 328.53px;
right: 0;
bottom: 0;
}
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Redirect } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import styles from './style.css'; import styles from './style.css';
import logoIcon from './images/school_logo@2x.png';
const fakeAuth = { import logoWord from './images/school_word_logo@2x.png';
isAuthenticated: false, import { Button, Modal, Toast } from 'antd-mobile';
authenticate(cb) { import { getVcode, sendMsg, login } from '../../api/index';
this.isAuthenticated = true; import CodeView from '../../components/CodeView/CodeView';
setTimeout(cb, 100); // fake async import UserInfoContext from '../../context/userinfo-context';
}, import store from '../../store';
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
},
};
class Login extends Component { class Login extends Component {
state = { constructor(props) {
redirectToReferrer: false, super(props);
this.state = {
redirectToReferrer: false,
sending: false,
count: 60,
cellphone: '',
vcodeDialogVisible: false,
vcode: '',
svcode: '',
code: '',
};
}
componentWillUnmount() {
const { sending } = this.state;
if (sending) clearTimeout(this.timer);
}
updateCellphone = e => {
this.setState({
cellphone: e.target.value.replace(/\D/g, ''),
});
};
getVcodeHandle = () => {
const { sending, cellphone } = this.state;
if (sending) return;
if (!cellphone) {
Toast.fail('请输入手机号');
return;
}
if (cellphone.length !== 11) {
Toast.fail('请输入正确手机号');
return;
}
getVcode({ cellphone })
.then(res => {
console.log(res);
this.setState({
svcode: res.data,
vcodeDialogVisible: true,
vcode: '',
});
})
.catch(err => {
Toast.fail(err.msg || '获取验证码失败!');
console.log(err);
});
}; };
login = () => { resetDialog = () => {
fakeAuth.authenticate(() => { this.setState({
this.setState({ redirectToReferrer: true }); vcodeDialogVisible: false,
}); });
}; };
checkVcode = () => {
const { vcode, svcode } = this.state;
if (vcode === svcode) {
this.sendMsgHandle();
this.resetDialog();
} else {
Toast.fail('请输入正确的验证码!');
}
};
sendMsgHandle = () => {
const { sending, cellphone, vcode } = this.state;
if (sending) return;
sendMsg({
cellphone,
valiCode: vcode,
})
.then(res => {
console.log(res);
Toast.success(res.msg || '发送验证码成功!');
})
.catch(err => {
Toast.fail(err.msg || '发送验证码失败!');
console.log(err);
});
this.setState({
sending: true,
});
this.startCountDown();
};
startCountDown = () => {
const { count } = this.state;
if (count === 0) {
clearTimeout(this.timer);
this.setState(({ count }) => ({
sending: false,
count: 60,
}));
} else {
this.setState(({ count }) => ({
count: --count,
}));
this.timer = setTimeout(() => {
this.startCountDown();
}, 1000);
}
};
loginHandle = () => {
const { updateUserInfo } = this.props;
const { code, cellphone } = this.state;
if (!code) {
Toast.fail('请输入短信验证码');
return;
}
const entity = {
cellphone,
code,
};
login(entity)
.then(res => {
const { user, author } = res.data;
store.updateUserId(user.userId);
updateUserInfo({
...user,
author,
});
this.setState({ redirectToReferrer: true });
console.log(res);
})
.catch(err => {
console.log(err);
Toast.fail(err.msg || '登陆失败!');
});
};
render() { render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }; // const { from } = this.props.location.state || { from: { pathname: '/' } };
const { redirectToReferrer } = this.state; const {
redirectToReferrer,
sending,
count,
cellphone,
vcodeDialogVisible,
vcode,
svcode,
code,
} = this.state;
if (redirectToReferrer) { if (redirectToReferrer) {
return <Redirect to={from} />; return <Redirect to={{ pathname: '/home' }} />;
} }
return ( return (
<div className={styles.Login}> <div className={styles.Login}>
<h2>Login</h2> <div className={styles.loginBox}>
<button onClick={this.login}>Log in</button> <div className={styles.logo}>
<img src={logoIcon} alt="" />
</div>
<div className={styles.logoWord}>
<img src={logoWord} alt="" />
</div>
<div className={styles.inputWrap}>
<div className={styles.inputBox}>
<input
className={styles.input}
type="tel"
placeholder="请输入手机号"
value={cellphone}
onChange={this.updateCellphone}
maxLength="11"
/>
</div>
<div className={styles.vcodeBox}>
<div className={styles.vInputBox}>
<input
className={styles.input}
type="tel"
placeholder="请输入短信验证码"
value={code}
maxLength="6"
onChange={e =>
this.setState({
code: e.target.value,
})
}
/>
</div>
<div className={styles.vcode} onClick={this.getVcodeHandle}>
{sending ? `${count}s后重新获取` : '获取验证码'}
</div>
</div>
</div>
<div className={styles.btnBox}>
<Button className={styles.okbtn} onClick={this.loginHandle}>
登录
</Button>
<Button className={styles.canclbtn}>取消</Button>
</div>
<Modal
className={styles.vcodeDialog}
visible={vcodeDialogVisible}
transparent
maskClosable={false}
onClose={this.resetDialog}
title=""
>
<div className={styles.vcodeinputBox}>
<div className={styles.vcodeItem}>
<input
className={styles.input}
type="tel"
value={vcode}
onChange={e => this.setState({ vcode: e.target.value })}
maxLength="6"
/>
</div>
<div className={styles.vcodeItem}>
<CodeView value={svcode} alt="" />
</div>
</div>
<Button className={styles.vcodeOkbtn} onClick={this.checkVcode}>
确认
</Button>
</Modal>
</div>
</div> </div>
); );
} }
} }
export default Login; export default props => (
<UserInfoContext.Consumer>
{({ userinfo, updateUserInfo }) => (
<Login {...props} updateUserInfo={updateUserInfo} />
)}
</UserInfoContext.Consumer>
);
.Login { .Login {
height: 300px;
text-align: center; text-align: center;
background-color: #f0f0f0;
width: 100%;
height: 100%;
overflow: hidden;
}
.loginBox {
width: 560px;
height: 442px;
margin: 158px auto 0;
position: relative;
background-color: #fff;
border-radius: 8px;
}
.logo {
position: absolute;
width: 140px;
height: 140px;
top: -62px;
left: 50%;
transform: translate(-50%, 0);
}
.logoWord {
width: 140px;
height: 40px;
margin: 0 auto 40px;
padding-top: 78px;
}
.inputWrap {
width: 460px;
margin: 0 auto;
line-height: 64px;
padding-bottom: 30px;
}
.inputBox {
height: 64px;
background-color: #f0f0f0;
}
.input {
composes: input from '../Repair/style.css';
box-sizing: border-box;
background-color: #f0f0f0;
text-align: left;
padding-left: 20px;
border-radius: 8px;
}
.vcodeBox {
display: flex;
width: 460px;
margin: 20px auto 0;
}
.vInputBox {
composes: inputBox;
width: 280px;
margin-right: 10px;
}
.vcode {
flex: 1;
font-size: 22px;
color: #3695c0;
}
.btnBox {
display: flex;
height: 64px;
width: 460px;
margin: 0 auto;
justify-content: space-between;
}
.btn {
width: 200px;
height: 64px;
line-height: 64px;
font-size: 30px;
}
.okbtn {
composes: btn;
background-color: #3695c0;
color: #fff;
}
.canclbtn {
composes: btn;
color: #3695c0;
}
.canclbtn::before {
border-color: #3695c0 !important;
}
.vcodeDialog {
}
.vcodeinputBox {
width: 350px;
margin: 15px auto 0;
display: flex;
justify-content: space-between;
}
.vcodeItem {
width: 160px;
height: 64px;
background-color: #f0f0f0;
border-radius: 8px;
}
.vcodeOkbtn {
composes: okbtn;
width: 350px;
margin: 30px auto 0;
} }
import React from 'react';
class Index extends React.Component {
render() {
return <div>404 NoMatch</div>;
}
}
export default Index;
import React, { Component } from 'react'; import React, { Component } from 'react';
import { TextareaItem, DatePicker, Button, Toast } from 'antd-mobile';
import { applyPublicCar, fetchCarCategory } from '../../api/index';
import styles from './style.css';
import { formatDate } from '../../utils/index';
import arrIcon from '../../components/Input/Select/images/select_icon@2x.png';
import offCheckIcon from '../../images/Check/radio_off_btn@2x.png';
import onCheckIcon from '../../images/Check/radio_on_btn@2x.png';
import UserInfoContext from '../../context/userinfo-context';
const CustomChildren = ({ extra, onClick, children }) => (
<div onClick={onClick} className={styles.selectWrap}>
<div className={extra === '请选择' ? '' : styles.selectValue}>
{extra === '请选择' ? '年-月-日 时-分' : extra}
</div>
</div>
);
class PublicCarApply extends Component { class PublicCarApply extends Component {
constructor(props) {
super(props);
this.state = {
department: props.userinfo.department,
createDate: formatDate(new Date(), 'yyyy-MM-dd'),
name: props.userinfo.name,
categoryId: '',
userCount: '',
beginDate: '',
endDate: '',
lineDescription: '',
showAllCategory: false,
categoryList: [],
};
}
componentDidMount() {
fetchCarCategory().then(res => {
this.setState({
categoryList: res.data,
});
});
}
selectCategory = id => {
const { showAllCategory } = this.state;
if (!showAllCategory) {
this.toggleCategoryOptions();
} else {
this.setState({
categoryId: id,
showAllCategory: false,
});
}
};
toggleCategoryOptions = () => {
this.setState(({ showAllCategory }) => ({
showAllCategory: !showAllCategory,
}));
};
onChangeBeginDate = date => {
const { endDate } = this.state;
if (endDate && endDate.getTime() < date.getTime()) {
this.setState({ beginDate: date, endDate: date });
} else {
this.setState({ beginDate: date });
}
};
validateForm = () => {
if (!this.state.categoryId) {
Toast.fail('请选择用车类型');
return false;
}
if (!this.state.userCount) {
Toast.fail('请填写用车人数');
return false;
}
if (!this.state.beginDate) {
Toast.fail('请选择用车开始时间');
return false;
}
if (!this.state.endDate) {
Toast.fail('请选择用车结束时间');
return false;
}
return true;
};
resetForm = () => {
this.setState({
createDate: formatDate(new Date(), 'yyyy-MM-dd'),
categoryId: '',
userCount: '',
beginDate: '',
endDate: '',
lineDescription: '',
showAllCategory: false,
});
};
onSubmit = () => {
if (!this.validateForm()) {
return;
}
let categoryName = JSON.stringify(
this.state.categoryList.map(i => ({
id: i.id,
name: i.name,
checked: i.id === this.state.categoryId,
}))
);
const entity = {
department: this.state.department,
name: this.state.name,
categoryName: categoryName,
beginDate: formatDate(this.state.beginDate, 'yyyy-MM-dd hh:mm:ss'),
endDate: formatDate(this.state.endDate, 'yyyy-MM-dd hh:mm:ss'),
lineDescription: this.state.lineDescription,
userCount: this.state.userCount,
};
applyPublicCar(entity)
.then(res => {
console.log('res: ' + res);
const { history } = this.props;
this.resetForm();
Toast.success(res.msg);
history.replace('/publicCar/PublicCarList');
})
.catch(err => {
console.log('err: ' + err);
Toast.fail(err.msg || '提交失败!');
});
};
render() { render() {
const {
name,
department,
createDate,
categoryList,
categoryId,
lineDescription,
showAllCategory,
} = this.state;
return ( return (
<div> <div className={styles.bg}>
PublicCarApply <div className={styles.content}>
<div className={styles.listItem}>
<div className={styles.itemLabel}>用车部门</div>
<div className={styles.itemValue}>{department}</div>
</div>
<div className={styles.listItem}>
<div className={styles.itemLabel}>申请时间</div>
<div className={styles.itemValue}>{createDate}</div>
</div>
<div className={styles.listItem}>
<div className={styles.itemLabel}>申请人</div>
<div className={styles.itemValue}>{name}</div>
</div>
<div className={styles.listItemAutoHeight}>
<div className={styles.itemLabel}>用车类型</div>
<div className={styles.itemValue}>
{categoryId ? null : (
<div
className={styles.selectInput}
onClick={this.toggleCategoryOptions}
>
请选择
<span className={styles.arrow}>
<img src={arrIcon} alt="" />
</span>
</div>
)}
{showAllCategory
? categoryList.map(i => (
<div
key={i.id}
className={styles.optionItem}
onClick={() => this.selectCategory(i.id)}
>
<div className={styles.radioLabel}>
{i.id === categoryId ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</div>
<div className={styles.radioValue}>{i.name}</div>
</div>
))
: categoryList.map(
i =>
categoryId === i.id ? (
<div
key={i.id}
className={styles.optionItem}
onClick={() => this.selectCategory(i.id)}
>
<div className={styles.radioLabel}>
{i.id === categoryId ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</div>
<div className={styles.radioValue}>{i.name}</div>
</div>
) : null
)}
</div>
</div>
<div className={styles.listItem}>
<div className={styles.itemLabel}>用车人数</div>
<div className={styles.itemValue}>
<input
className={styles.itemInput}
type="tel"
maxLength="5"
value={this.state.userCount}
onChange={e =>
this.setState({
userCount: e.target.value.replace(/\D/g, ''),
})
}
/>
</div>
</div>
<div className={styles.listItem}>
<div className={styles.itemLabel}>用车开始时间</div>
<div className={styles.itemSelect}>
<DatePicker
value={this.state.beginDate}
onChange={this.onChangeBeginDate}
minDate={new Date()}
>
<CustomChildren className={styles.DateInnerWrap} />
</DatePicker>
</div>
</div>
<div className={styles.listItem}>
<div className={styles.itemLabel}>用车结束时间</div>
<div className={styles.itemSelect}>
<DatePicker
value={this.state.endDate}
onChange={date => this.setState({ endDate: date })}
minDate={this.state.beginDate}
>
<CustomChildren className={styles.DateInnerWrap} />
</DatePicker>
</div>
</div>
<div className={styles.listItemAutoHeight}>
<div className={styles.itemLabel}>用车详细线路</div>
<div className={styles.itemValue}>
<TextareaItem
className={styles.textarea}
placeholder="例:温江校区-富顺县-川大望江校区"
rows={4}
count={50}
value={lineDescription}
onChange={val =>
this.setState({ lineDescription: val.replace(/\n/, '') })
}
/>
</div>
</div>
</div>
<Button className={styles.submitBtn} onClick={this.onSubmit}>
提交
</Button>
</div> </div>
); );
} }
} }
export default PublicCarApply; export default props => (
\ No newline at end of file <UserInfoContext.Consumer>
{({ userinfo }) => <PublicCarApply userinfo={userinfo} {...props} />}
</UserInfoContext.Consumer>
);
import React, { Component } from 'react'; import React, { Component } from 'react';
import { fetchPublicCarList } from '../../api/index'; import { fetchPublicCarList } from '../../api/index';
import { ListView, PullToRefresh, Toast } from 'antd-mobile';
import styles from './style.css';
import PublicCarItem from './components/PublicCarItem';
class PublicCarList extends Component { class PublicCarList extends Component {
static state = { constructor(props) {
list: [], super(props);
pagination: { const list = new ListView.DataSource({
pageNum: 1, rowHasChanged: (row1, row2) => row1 !== row2,
pageSize: 50, });
}, this.state = {
refreshing: false,
down: true,
height: document.documentElement.clientHeight,
list: list,
sourceList: [],
pagination: {
nowDate: '',
pageNum: 1,
pageSize: 10,
},
};
}
componentDidMount() {
let wrap = document.querySelector('.RepairContent');
const hei = wrap.clientHeight;
setTimeout(
() =>
this.setState({
height: hei,
}),
0
);
this.fetchList();
}
updateList = () => {
const { sourceList } = this.state;
this.setState(({ list }) => ({
list: list.cloneWithRows(sourceList),
}));
}; };
componentWillMount() { fetchList = fetchMore => {
fetchPublicCarList() const { pagination } = this.state;
const nowDate = new Date().toISOString();
const entity = fetchMore
? {
...pagination,
pageNum: pagination.pageNum + 1,
}
: {
...pagination,
pageNum: 1,
nowDate,
};
fetchPublicCarList(entity)
.then(res => { .then(res => {
console.log(res); this.setState(
({ sourceList, pagination }) =>
fetchMore
? res.data.length
? {
sourceList: [...sourceList, ...res.data],
pagination: {
...pagination,
pageNum: pagination.pageNum + 1,
},
refreshing: false,
}
: {
refreshing: false,
}
: {
sourceList: res.data,
pagination: {
...pagination,
pageNum: 1,
nowDate,
},
refreshing: false,
}
);
this.updateList();
}) })
.catch(err => { .catch(err => {
console.log(err); Toast.fail(err.msg || '请求失败!');
this.setState({
refreshing: false,
});
}); });
} };
refreshHandle = () => {
this.fetchList(true);
};
MyBody = props => {
return (
<div className={styles.repairList}>
<div className={styles.listInfo}>显示最近半年的报修记录</div>
{props.children}
</div>
);
};
render() { render() {
return <div>PublicCarList</div>; const { list, pagination } = this.state;
return (
<ListView
style={{
height: this.state.height,
}}
className={styles.scrollWrap}
dataSource={list}
pageSize={pagination.pageSize}
renderBodyComponent={() => <this.MyBody />}
renderRow={rowData => (
<PublicCarItem
key={rowData.id}
data={rowData}
showView={this.showViewHandle}
/>
)}
onEndReached={this.refreshHandle}
pullToRefresh={
<PullToRefresh
refreshing={this.state.refreshing}
onRefresh={this.fetchList}
/>
}
// renderFooter={() => (
// <div style={{ textAlign: 'center' }}>
// {this.state.refreshing ? 'Loading...' : 'Loaded'}
// </div>
// )}
/>
);
} }
} }
......
import React, { Component } from 'react';
import styles from './style.css';
import titleIcon from '../../Repair/images/dropdown_icon@2x.png';
class PublicCarItem extends Component {
constructor(props) {
super(props);
this.state = {
showDetail: false,
};
}
toggleDetail = () => {
this.setState(({ showDetail }) => ({
showDetail: !showDetail,
}));
};
getCategoryValue = str => {
let list = str ? JSON.parse(str) : [];
let item = list.find(item => item.checked);
return item ? item.name : '';
};
dealHandle = e => {
const { dealHandle, data } = this.props;
if (dealHandle && data.dealResult === 1) {
e.stopPropagation();
e.preventDefault();
dealHandle(data.id);
}
};
render() {
const { showDetail } = this.state;
const { data } = this.props;
return (
<div className={styles.wrap}>
<div
className={data.dealResult === 1 ? styles.titleDealing : styles.title}
>
<i
className={
showDetail ? styles.titleIconShowDetail : styles.titleIcon
}
>
<img src={titleIcon} alt="" />
</i>
</div>
<div className={styles.content} onClick={this.toggleDetail}>
{data.dealResult === 1 ? (
<div className={styles.dealing} onClick={this.dealHandle}>
处理中
</div>
) : data.dealResult === 2 ? (
<div className={styles.dealedY}>已同意</div>
) : (
<div className={styles.dealedN}>不同意</div>
)}
{showDetail
? [
<div key="time" className={styles.listLine}>
<div className={styles.listLabel}>用车时间:</div>
<div className={styles.listValue}>{data.createDate}</div>
</div>,
<div key="type" className={styles.listLine}>
<div className={styles.listLabel}>用车类型:</div>
<div className={styles.listValueMutLine}>
{this.getCategoryValue(data.categoryName)}
</div>
</div>,
<div key="line" className={styles.listLine}>
<div className={styles.listLabel}>用车路线:</div>
<div className={styles.listValueMutLine}>
{data.lineDescription}
</div>
</div>,
<div key="department" className={styles.listLine}>
<div className={styles.mLineItem}>
<div className={styles.listLabel}>用车部门:</div>
<div className={styles.listValue}>{data.department}</div>
</div>
<div className={styles.mLineItem}>
<div className={styles.listLabel}>申请人:</div>
<div className={styles.listValue}>{data.name}</div>
</div>
</div>,
<div key="userCount" className={styles.listLine}>
<div className={styles.listLabel}>用车人数:</div>
<div className={styles.listValue}>{data.userCount}</div>
</div>,
<div key="beginDate" className={styles.listLine}>
<div className={styles.listLabel}>开始时间:</div>
<div className={styles.listValue}>{data.beginDate}</div>
</div>,
<div key="endDate" className={styles.listLine}>
<div className={styles.listLabel}>结束时间:</div>
<div className={styles.listValue}>{data.endDate}</div>
</div>,
<div
key="dealName"
className={`${styles.listLine} ${
data.dealResult === 1
? styles.dealingColor
: styles.dealedColor
}`}
>
<div className={styles.listLabel}>处理人:</div>
<div className={styles.listValue}>
{data.dealResult === 1 ? '处理中' : data.dealName}
</div>
</div>,
<div
key="dealDate"
className={`${styles.listLine} ${
data.dealResult === 1
? styles.dealingColor
: styles.dealedColor
}`}
>
<div className={styles.listLabel}>处理时间:</div>
<div className={styles.listValue}>
{data.dealResult === 1 ? '待处理后显示' : data.dealDate}
</div>
</div>,
<div
key="carPlate"
className={`${styles.listLine} ${
data.dealResult === 1
? styles.dealingColor
: styles.dealedColor
}`}
>
<div className={styles.listLabel}>派车车牌:</div>
<div className={styles.listValueMutLine}>
{data.dealResult === 1
? '待处理后显示'
: data.carPlate
? data.carPlate
: '无'}
</div>
</div>,
data.dealResult === 3 ? (
<div
key="dealOpinion"
className={`${styles.listLine} ${styles.dealedColor}`}
>
<div className={styles.listLabel}>处理意见:</div>
<div className={styles.listValueMutLine}>
{data.dealOpinion}
</div>
</div>
) : null,
]
: [
<div key="time" className={styles.listLine}>
<div className={styles.listLabel}>用车时间:</div>
<div className={styles.listValue}>{data.createDate}</div>
</div>,
<div key="department" className={styles.listLine}>
<div className={styles.mLineItem}>
<div className={styles.listLabel}>用车部门:</div>
<div className={styles.listValue}>{data.department}</div>
</div>
<div className={styles.mLineItem}>
<div className={styles.listLabel}>申请人:</div>
<div className={styles.listValue}>{data.name}</div>
</div>
</div>,
<div key="type" className={styles.listLine}>
<div className={styles.listLabel}>用车类型:</div>
<div className={styles.listValue}>
{this.getCategoryValue(data.categoryName)}
</div>
</div>,
<div key="line" className={styles.listLine}>
<div className={styles.listLabel}>用车路线:</div>
<div className={styles.listValue}>{data.lineDescription}</div>
</div>,
]}
</div>
</div>
);
}
}
export default PublicCarItem;
.wrap {
display: flex;
width: 600px;
margin: 0 auto 20px;
border-radius: 8px;
overflow: hidden;
background-color: #fff;
}
.title {
composes: title from '../../Repair/components/style.css';
}
.titleDealing {
composes: titleDealing from '../../Repair/components/style.css';
}
.titleIcon {
composes: titleIcon from '../../Repair/components/style.css';
}
.titleIconShowDetail {
composes: titleIconShowDetail from '../../Repair/components/style.css';
}
.content {
composes: content from '../../Repair/components/style.css';
}
.dealing {
composes: dealing from '../../Repair/components/style.css';
}
.dealedN {
composes: dealed from '../../Repair/components/style.css';
}
.dealedY {
composes: dealed from '../../Repair/components/style.css';
color: #3695c0;
}
.listLine {
composes: listLine from '../../Repair/components/style.css';
}
.listLabel {
composes: listLabel from '../../Repair/components/style.css';
}
.listValue {
composes: listValue from '../../Repair/components/style.css';
}
.listValueMutLine {
composes: listValueMutLine from '../../Repair/components/style.css';
}
.mLineItem {
width: 50%;
display: flex;
}
.dealingColor {
color: #ff3131;
}
.dealedColor {
color: #3695c0;
}
import React, { Component } from 'react';
import { Switch, Route, Redirect, NavLink } from 'react-router-dom';
import PublicCarList from './PublicCarList';
import PublicCarApply from './PublicCarApply';
import styles from './style.css';
class PublicCar extends Component {
constructor(props) {
super(props);
this.state = {
showFooter: true,
};
}
componentDidMount() {
let screenHeight = document.body.offsetHeight;
window.onresize = () => {
let nowHeight = document.body.offsetHeight;
if (nowHeight < screenHeight) {
this.setState({
showFooter: false,
});
} else {
this.setState({
showFooter: true,
});
}
};
}
render() {
const { showFooter } = this.state;
return (
<div className="Content-Wrap">
<div className="RepairContent">
<Switch>
<Route
path="/publicCar/PublicCarApply"
component={PublicCarApply}
/>
<Route path="/publicCar/PublicCarList" component={PublicCarList} />
<Route render={() => <Redirect to="/publicCar/PublicCarApply" />} />
</Switch>
</div>
{showFooter && (
<div className={styles.footer}>
<NavLink
className={styles['footer-item']}
to="/publicCar/PublicCarApply"
replace
>
约车服务
</NavLink>
<span className={styles.line} />
<NavLink
className={styles['footer-item']}
to="/publicCar/PublicCarList"
replace
>
约车记录
</NavLink>
</div>
)}
</div>
);
}
}
export default PublicCar;
.footer {
composes: footer from '../Repair/style.css';
}
.footer-item {
composes: footer-item from '../Repair/style.css';
}
.publicCarList {
composes: repairList from '../Repair/style.css';
}
.listInfo {
composes: listInfo from '../Repair/style.css';
}
.noData {
composes: noData from '../Repair/style.css';
}
/* apply */
.scrollWrap {
background-color: #f2f2f2;
}
.content {
padding: 30px 40px 20px;
}
.listItem {
composes: listItem from '../Repair/style.css';
padding: 0;
min-height: 53px;
line-height: 53px;
align-items: center;
}
.listItemAutoHeight {
composes: listItem;
height: auto;
align-items: flex-start;
}
.optionItem {
display: flex;
}
.radioLabel {
composes: checkLabel from '../RepairDeal/style.css';
margin-top: 10px;
}
.inner {
composes: checkInner from '../RepairDeal/style.css';
}
.radioValue {
text-align: left;
flex: 1;
}
.itemValue {
composes: inputBox from '../Repair/style.css';
flex: 1;
}
.itemValue::after {
display: none;
}
.inputBox {
background-color: #f0f0f0;
border-radius: 8px;
}
.itemLabel {
composes: itemLabel from '../Repair/style.css';
margin: 0;
margin-right: 14px;
width: 160px;
text-align: right;
text-align-last: right;
}
.itemInput {
composes: input from '../Repair/style.css';
background-color: #f0f0f0;
border-radius: 8px;
line-height: 48px;
height: 48px;
}
.selectInput {
composes: inputBox from '../Repair/style.css';
min-height: 50px;
}
.arrow {
composes: arrow from '../../components/Input/Select/style.css';
}
.itemSelect {
flex: 1;
}
.DateWrap {
width: 100%;
}
.DateInnerWrap {
width: 100%;
}
.selectWrap {
background-color: #f0f0f0;
border-radius: 8px;
height: 48px;
line-height: 48px;
text-align: center;
color: #999;
}
.selectValue {
text-align: center;
color: #333;
}
.textarea {
background-color: #f0f0f0;
border-radius: 8px;
font-size: 22px;
}
.submitBtn {
width: 560px;
height: 80px;
line-height: 80px;
background: #3695c0;
color: #fff;
margin: 40px auto 0;
}
import React, { Component } from 'react';
import styles from './style.css';
import SelectIcon from './images/more_item_btn@2x.png';
class Select extends Component {
constructor(props) {
super(props);
this.state = {
showOptions: false,
};
}
componentWillMount() {
console.log(this.props.children);
if (this.props.children.length) {
this.props.onChange([this.props.children[0].props.value]);
}
}
toggleOptions = () => {
console.log('in click');
this.setState(({ showOptions }) => ({
showOptions: !showOptions,
}));
};
selectHandle = (e, val) => {
e.preventDefault();
e.stopPropagation();
console.log(val);
// this.setState({
// showOptions: false,
// });
const { value } = this.props;
if (value.indexOf(val) !== -1) {
this.props.onChange(value.filter(i => i !== val));
} else {
this.props.onChange([...value, val]);
}
};
render() {
const { showOptions } = this.state;
const { value } = this.props;
return (
<div className={styles.selectWrap} onClick={this.toggleOptions}>
<div className={styles.selectValue}>
<div className={styles.selectValueOption}>{value.join(',')}</div>
{showOptions && (
<div className={styles.selectOptionsBox}>
{this.props.children.map(
(i, idx) =>
value.indexOf(i.props.value) !== -1 ? (
<div
className={styles.selectOptionChecked}
key={idx}
onClick={e => this.selectHandle(e, i.props.value)}
>
{i}
<div className={styles.selected}>
<div className={styles.selectedInner} />
</div>
</div>
) : (
<div
className={styles.selectOption}
key={idx}
onClick={e => this.selectHandle(e, i.props.value)}
>
{i}
</div>
)
)}
</div>
)}
</div>
<div className={styles.selectIcon}>
<img src={SelectIcon} alt="" />
</div>
</div>
);
}
}
export default Select;
import React, { Component } from 'react';
import {
ListView,
PullToRefresh,
Modal,
Toast,
TextareaItem,
} from 'antd-mobile';
import styles from './style.css';
import PublicCarItem from '../PublicCar/components/PublicCarItem';
import {
fetchApplyPublicCarList,
dealPublicCar,
fetchCarPlateList,
} from '../../api/index';
// import Select from '../../components/Input/Select/Select';
import offCheckIcon from '../../images/Check/radio_off_btn@2x.png';
import onCheckIcon from '../../images/Check/radio_on_btn@2x.png';
import UserInfoContext from '../../context/userinfo-context';
import Select from './Select';
class PublicCarDeal extends Component {
constructor(props) {
super(props);
const list = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
refreshing: false,
down: true,
height: document.documentElement.clientHeight,
pagination: {
pageNum: 1,
pageSize: 10,
},
list: list,
sourceList: [],
dealDialogVisible: false,
selected: {
id: '',
value: '',
carPlate: [],
dealOpinion: '',
},
carPlateList: [],
};
}
componentDidMount() {
this.fetchList();
}
updateList = () => {
const { sourceList } = this.state;
this.setState(({ list }) => ({
list: list.cloneWithRows(sourceList),
}));
};
fetchList = fetchMore => {
this.setState({ refreshing: true });
const { pagination } = this.state;
const nowDate = new Date().toISOString();
const entity = fetchMore
? {
...pagination,
pageNum: pagination.pageNum + 1,
}
: {
...pagination,
pageNum: 1,
nowDate,
};
fetchApplyPublicCarList(entity)
.then(res => {
this.setState(
({ sourceList }) =>
fetchMore
? res.data.length
? {
sourceList: [...sourceList, ...res.data],
pagination: entity,
refreshing: false,
}
: {
refreshing: false,
}
: {
sourceList: res.data,
pagination: entity,
refreshing: false,
}
);
this.updateList();
})
.catch(err => {
Toast.fail(err.msg || '请求失败!');
this.setState({
refreshing: false,
});
});
};
refreshHandle = () => {
this.fetchList(true);
};
dealHandle = id => {
if (this.state.carPlateList.length === 0) {
fetchCarPlateList()
.then(res => {
this.setState(({ carPlateList }) => ({
carPlateList: res.data.map(i => ({
value: i.id,
lable: i.name,
})),
}));
})
.catch(err => {
console.log(err);
});
}
this.setState(({ selected }) => ({
dealDialogVisible: true,
selected: {
...selected,
id,
},
}));
};
resetDialog = () => {
this.setState(({ selected }) => ({
dealDialogVisible: false,
selected: {
id: '',
value: '',
carPlate: [],
dealOpinion: '',
},
}));
};
toggleSelectedState = val => {
this.setState(({ selected }) => ({
selected: {
...selected,
value: val,
},
}));
};
validateSelect = () => {
const {
selected: { value, carPlate },
} = this.state;
if (!value) {
Toast.fail('请选择处理结果!');
return false;
}
if (value === 2) {
if (carPlate.length === 0) {
Toast.fail('请选择派车!');
return false;
}
}
return true;
};
submitHandle = () => {
if (!this.validateSelect()) {
return;
}
const {
selected: { id, value, carPlate, dealOpinion },
} = this.state;
const { userinfo } = this.props;
const entity =
value === 2
? {
id: id,
dealName: userinfo.name,
dealResult: value,
carPlate: carPlate.join(','),
}
: {
id: id,
dealName: userinfo.name,
dealResult: value,
dealOpinion: dealOpinion
? `不同意 - ${dealOpinion}`
: '不同意 - 无车可派送',
};
dealPublicCar(entity)
.then(res => {
this.resetDialog();
this.fetchList();
Toast.success(res.msg || '处理成功!');
})
.catch(err => {
Toast.fail(err.msg || '处理失败!');
});
};
onSelectHandle = val => {
console.log(val);
this.setState(({ selected }) => ({
selected: {
...selected,
carPlate: val,
},
}));
};
onInputHandle = val => {
this.setState(({ selected }) => ({
selected: {
...selected,
dealOpinion: val,
},
}));
};
MyBody = props => {
const {
selected: { value, dealOpinion, carPlate },
dealDialogVisible,
carPlateList,
} = this.state;
return (
<div className={styles.repairList}>
<div className={styles.listInfo}>显示最近半年的报修记录</div>
{props.children}
<Modal
className={styles.dealDialog}
visible={dealDialogVisible}
transparent
maskClosable={false}
onClose={this.resetDialog}
title=""
footer={[
{ text: '放弃', onPress: this.resetDialog },
{
text: '提交',
onPress: this.submitHandle,
},
]}
>
<div className={styles.dialogBody}>
<div key="radio" className={styles.alertInfo}>
<div
className={styles.radioBox}
onClick={() => this.toggleSelectedState(2)}
>
<span className={styles.checkLabel}>
{value === 2 ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</span>
<span>同意</span>
</div>
<div
className={styles.radioBox}
onClick={() => this.toggleSelectedState(3)}
>
<span className={styles.checkLabel}>
{value === 3 ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</span>
<span>不同意</span>
</div>
</div>
{value === 2 ? (
<div key="agree" className={styles.agree}>
<span className={styles.dialogAgreeLabel}>派车车牌</span>
<div className={styles.selectWrapBox}>
<Select
className={styles.select}
onChange={this.onSelectHandle}
value={carPlate}
>
{carPlateList.map(i => (
<div key={i.value} value={i.lable}>
{i.lable}
</div>
))}
</Select>
</div>
</div>
) : value === 3 ? (
<div key="notAgree" className={styles.notAgree}>
<span className={styles.dialogLabel}>处理结果</span>
<TextareaItem
className={styles.textarea}
placeholder="输入处理结果"
rows={3}
count={50}
value={dealOpinion}
onChange={this.onInputHandle}
/>
</div>
) : null}
</div>
</Modal>
</div>
);
};
render() {
const { list, pagination } = this.state;
return (
<ListView
style={{
height: this.state.height,
}}
className={styles.scrollWrap}
dataSource={list}
pageSize={pagination.pageSize}
renderBodyComponent={() => <this.MyBody />}
renderRow={rowData => (
<PublicCarItem
key={rowData.id}
data={rowData}
dealHandle={this.dealHandle}
/>
)}
onEndReached={this.refreshHandle}
pullToRefresh={
<PullToRefresh
refreshing={this.state.refreshing}
onRefresh={this.fetchList}
/>
}
// renderFooter={() => (
// <div style={{ textAlign: 'center' }}>
// {this.state.refreshing ? 'Loading...' : 'Loaded'}
// </div>
// )}
/>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ userinfo }) => <PublicCarDeal userinfo={userinfo} {...props} />}
</UserInfoContext.Consumer>
);
.listInfo {
composes: listInfo from '../Repair/style.css';
}
.noData {
composes: noData from '../Repair/style.css';
}
.dialogBody {
/* height: 200px; */
}
.dealDialog {
width: 520px;
}
.alertInfo {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
.radioBox {
display: flex;
align-items: center;
margin-right: 84px;
}
.radioBox:last-child {
margin-right: 0;
}
.checkLabel {
composes: checkLabel from '../RepairDeal/style.css';
}
.checkInner {
composes: checkInner from '../RepairDeal/style.css';
}
.dialogLabel {
margin-top: 20px;
margin-left: 30px;
margin-right: 14px;
}
.dialogAgreeLabel {
line-height: 60px;
margin-right: 14px;
}
.agree,
.notAgree {
display: flex;
font-size: 26px;
color: #333;
}
.selectWrapBox {
width: 310px;
/* height: 60px; */
height: auto;
}
.selectWrap {
box-sizing: border-box;
position: relative;
width: 100%;
height: 100%;
line-height: 60px;
border-radius: 8px;
}
.selectValue {
padding-left: 26px;
background-color: #f2f2f2;
margin-right: 46px;
}
.selectIcon {
position: absolute;
right: 0;
top: 0;
width: 46px;
height: 60px;
}
.selectOptionsBox {
z-index: 999;
width: 100%;
text-align: left;
}
.selectOption {
text-align: left;
height: 60px;
border-bottom: 1px solid #fff;
}
.selectValueOption {
composes: selectOption;
white-space: nowrap;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
}
.selectOptionChecked {
composes: selectOption;
position: relative;
}
.selected {
position: absolute;
right: 10px;
top: 20px;
width: 20px;
height: 10px;
border-left: 1px solid #666;
border-bottom: 1px solid #666;
transform: rotate(-45deg);
}
.select {
border: none;
appearance: none;
outline: none;
width: 100%;
height: 100%;
background-color: transparent;
position: relative;
z-index: 2;
}
.textarea {
background-color: #f2f2f2;
width: 310px;
height: 180px;
box-sizing: border-box;
line-height: 30px;
}
import React, { Component } from 'react';
import { ListView, PullToRefresh, Modal, Toast } from 'antd-mobile';
import styles from './style.css';
import RepairItem from './components/RepairItem';
import { fetchRepairList } from '../../api/index';
import Perview from '../../components/Perview/Perview';
class RepairList extends Component {
constructor(props) {
super(props);
const list = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
refreshing: false,
down: true,
height: document.documentElement.clientHeight,
pagination: {
nowDate: '',
pageNum: 1,
pageSize: 10,
},
list: list,
sourceList: [],
perviewVisibility: false,
selectedImgList: [],
perviewIndex: 0,
};
// this.sourceList = [];
}
componentDidMount() {
let wrap = document.querySelector('.RepairContent');
const hei = wrap.clientHeight;
setTimeout(
() =>
this.setState({
height: hei,
}),
0
);
this.fetchList();
}
updateList = () => {
const { sourceList } = this.state;
this.setState(({ list }) => ({
list: list.cloneWithRows(sourceList),
}));
};
fetchList = fetchMore => {
const { pagination } = this.state;
const nowDate = new Date().toISOString();
const entity = fetchMore
? {
...pagination,
pageNum: pagination.pageNum + 1,
}
: {
...pagination,
pageNum: 1,
nowDate,
};
fetchRepairList(entity)
.then(res => {
this.setState(
({ sourceList, pagination }) =>
fetchMore
? res.data.length
? {
sourceList: [...sourceList, ...res.data],
pagination: {
...pagination,
pageNum: pagination.pageNum + 1,
},
refreshing: false,
}
: {
refreshing: false,
}
: {
sourceList: res.data,
pagination: {
...pagination,
pageNum: 1,
nowDate,
},
refreshing: false,
}
);
this.updateList();
})
.catch(err => {
Toast.fail(err.msg || '请求失败!');
this.setState({
refreshing: false,
});
});
};
refreshHandle = () => {
this.fetchList(true);
};
showViewHandle = (list, index) => {
this.setState({
perviewVisibility: true,
perviewIndex: index,
selectedImgList: list,
});
};
resetDialog = () => {
this.setState({
perviewVisibility: false,
});
};
MyBody = props => {
const { perviewVisibility, selectedImgList, perviewIndex } = this.state;
return (
<div className={styles.repairList}>
<div className={styles.listInfo}>显示最近半年的报修记录</div>
{props.children}
<Modal
className={styles.perViewDialog}
visible={perviewVisibility}
transparent
maskClosable={true}
onClose={this.resetDialog}
title=""
>
<Perview data={selectedImgList} index={perviewIndex} />
</Modal>
</div>
);
};
render() {
const { list, pagination } = this.state;
return (
<ListView
style={{
height: this.state.height,
}}
className={styles.scrollWrap}
dataSource={list}
pageSize={pagination.pageSize}
renderBodyComponent={() => <this.MyBody />}
renderRow={rowData => (
<RepairItem
key={rowData.id}
data={rowData}
showView={this.showViewHandle}
/>
)}
onEndReached={this.refreshHandle}
pullToRefresh={
<PullToRefresh
refreshing={this.state.refreshing}
onRefresh={this.fetchList}
/>
}
// renderFooter={() => (
// <div style={{ textAlign: 'center' }}>
// {this.state.refreshing ? 'Loading...' : 'Loaded'}
// </div>
// )}
/>
);
}
}
export default RepairList;
import React, { Component } from 'react';
import styles from './style.css';
import { Toast, Button, TextareaItem, Modal } from 'antd-mobile';
import peopleIcon from './images/repair_people_icon@2x.png';
import phoneIcon from './images/phone_tel_icon@2x.png';
import roleIcon from './images/user_role_icon@2x.png';
import areaIcon from './images/repair_area_icon@2x.png';
import typeIcon from './images/repair_item_icon@2x.png';
import addrIcon from './images/repair_addr_icon@2x.png';
import imgIcon from './images/upload_pic_icon@2x.png';
import ImagePicker from '../../components/Input/ImagePicker/ImagePicker';
import Select from '../../components/Input/Select/Select';
import { fetchRepairArea, fetchRepairTerm } from '../../api/index';
import { reportRepair } from '../../api/repairAdd';
import Perview from '../../components/Perview/Perview';
import store from '../../store/index';
import UserInfoContext from '../../context/userinfo-context';
import offCheckIcon from '../../images/Check/radio_off_btn@2x.png';
import onCheckIcon from '../../images/Check/radio_on_btn@2x.png';
import { emojiFilter } from '../../utils/index';
class ReprtRepair extends Component {
constructor(props) {
super(props);
this.state = {
areaList: [],
termList: [],
applyForm: {
name: props.userinfo.name,
repairCellphone: props.userinfo.cellphone,
applyIdentity: '学生',
area: '',
areaId: [],
term: '',
termId: [],
address: '',
uploadImg: [null, null, null],
},
perviewVisibility: false,
perviewIndex: 0,
};
}
componentDidMount() {
fetchRepairArea()
.then(res => {
let list = res.data.length
? res.data.map(i => ({
value: i.id,
label: i.name,
}))
: [];
this.setState(() => ({
areaList: list,
}));
})
.catch(err => {
console.log(err);
});
fetchRepairTerm()
.then(res => {
let list = res.data.length
? res.data.map(i => ({
value: i.id,
label: i.name,
}))
: [];
this.setState(() => ({
termList: list,
}));
})
.catch(err => {
console.log(err);
});
}
resetApplyForm = () => {
const { userinfo } = this.props;
this.setState({
applyForm: {
name: userinfo.name,
repairCellphone: userinfo.cellphone,
applyIdentity: '学生',
area: '',
areaId: [],
term: '',
termId: [],
address: '',
uploadImg: [null, null, null],
},
});
};
onChangeHandle = e => {
const { name, value } = e.target;
this.setState(({ applyForm }) => ({
applyForm: {
...applyForm,
[name]: emojiFilter(value),
},
}));
};
onSelectHandle = (key, val) => {
console.log(key, val);
this.setState(({ applyForm }) => ({
applyForm: {
...applyForm,
[key]: val,
},
}));
};
onImagePicker = (index, val) => {
console.log(index, val);
const { uploadImg } = this.state.applyForm;
uploadImg[index] = val;
this.setState(({ applyForm }) => ({
applyForm: {
...applyForm,
uploadImg: uploadImg,
},
}));
};
showViewHandle = index => {
console.log(index);
this.setState({
perviewVisibility: true,
perviewIndex: index,
});
};
validateForm = () => {
const { applyForm } = this.state;
if (!applyForm.name) {
Toast.fail('请输入报修人!');
return;
}
if (!applyForm.repairCellphone) {
Toast.fail('请输入报修人电话!');
return;
}
if (applyForm.repairCellphone.length !== 11) {
Toast.fail('请输入正确电话!');
return;
}
if (!applyForm.applyIdentity) {
Toast.fail('请选择报修人身份!');
return;
}
if (!applyForm.areaId.length) {
Toast.fail('请选择报修区域!');
return;
}
if (!applyForm.termId.length) {
Toast.fail('请选择报修项目!');
return;
}
if (!applyForm.address) {
Toast.fail('请输入报修地址!');
return;
}
let imgList = applyForm.uploadImg.filter(i => i !== null);
if (imgList.length) {
let size = imgList.reduce((totle, item) => {
let fileSize = item.filesList[0].size;
return totle + fileSize;
}, 0);
if (size > 10485760) {
Toast.fail('上传图片过大!');
return;
}
}
return true;
};
startApply = () => {
if (!this.validateForm()) {
return;
}
Toast.loading('', 99);
let formData = new FormData();
this.state.applyForm.uploadImg.map(i => {
if (i) {
console.log(i);
formData.append('file', i.filesList[0]);
}
return i;
});
let area = this.state.areaList.find(
i => i.value === this.state.applyForm.areaId[0]
).label;
let term = this.state.termList.find(
i => i.value === this.state.applyForm.termId[0]
).label;
formData.append('userId', store.getUserId());
// product 需要登陆后的用户ID
formData.append('applyIdentity', this.state.applyForm.applyIdentity);
formData.append('area', area);
formData.append('term', term);
formData.append('address', this.state.applyForm.address);
formData.append('repairCellphone', this.state.applyForm.repairCellphone);
formData.append('termId', this.state.applyForm.termId);
formData.append('areaId', this.state.applyForm.areaId);
formData.append('name', this.state.applyForm.name);
reportRepair(formData)
.then(res => {
console.log(res);
const { data } = res;
if (data.code && data.code === 1000) {
const { data } = res;
const { history } = this.props;
console.log('resetForm');
this.resetApplyForm();
// Toast.hide();
Toast.success(data.msg);
history.replace('/repair/RepairList');
} else {
throw { msg: data.msg };
}
})
.catch(err => {
console.log(err);
// Toast.hide();
Toast.fail(err.msg || '提交失败!');
});
};
resetDialog = () => {
this.setState({
perviewVisibility: false,
});
};
render() {
const {
applyForm,
areaList,
termList,
perviewVisibility,
perviewIndex,
} = this.state;
return (
<div className={styles.wrap}>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={peopleIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修人</span>
<div className={styles.inputBox}>
<input
className={styles.input}
type="text"
maxLength="20"
name="name"
value={applyForm.name}
onChange={this.onChangeHandle}
/>
</div>
</div>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={phoneIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修人tel</span>
<div className={styles.inputBox}>
<input
className={styles.input}
type="tel"
maxLength="11"
name="repairCellphone"
value={applyForm.repairCellphone}
onChange={this.onChangeHandle}
/>
</div>
</div>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={roleIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修身份</span>
<div className={styles.inputBoxNoBB}>
{/* <input className={styles.input} type="text" /> */}
<div
className={styles.checkBox}
onClick={() =>
this.onChangeHandle({
target: { name: 'applyIdentity', value: '老师' },
})
}
>
<div className={styles.checkLabel}>
{applyForm.applyIdentity === '老师' ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</div>
<div>老师</div>
</div>
<div
className={styles.checkBox}
onClick={() =>
this.onChangeHandle({
target: { name: 'applyIdentity', value: '学生' },
})
}
>
<div className={styles.checkLabel}>
{applyForm.applyIdentity === '学生' ? (
<img src={onCheckIcon} alt="" />
) : (
<img src={offCheckIcon} alt="" />
)}
</div>
<div>学生</div>
</div>
{/* <Radio
className="my-radio"
name="applyIdentity"
value="老师"
checked={applyForm.applyIdentity === '老师'}
onChange={this.onChangeHandle}
/>
<Radio
className="my-radio"
name="applyIdentity"
value="学生"
checked={applyForm.applyIdentity === '学生'}
onChange={this.onChangeHandle}
>
学生
</Radio> */}
</div>
</div>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={areaIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修区域</span>
<div className={styles.inputBox}>
<Select
placeholder="请选择"
data={areaList}
cols={1}
value={applyForm.areaId}
onChange={val => this.onSelectHandle('areaId', val)}
/>
</div>
</div>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={typeIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修项目</span>
<div className={styles.inputBox}>
{/* <input className={styles.input} type="text" /> */}
<Select
placeholder="请选择"
data={termList}
cols={1}
value={applyForm.termId}
onChange={val => this.onSelectHandle('termId', val)}
/>
</div>
</div>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={addrIcon} alt="" />
</i>
<span className={styles.itemLabel}>报修地址</span>
</div>
<TextareaItem
className="textarea"
placeholder="输入报修地址"
rows={3}
count={50}
value={this.state.applyForm.address}
onChange={val =>
this.setState(({ applyForm }) => ({
applyForm: {
...applyForm,
address: emojiFilter(val.replace(/\n/, '')),
},
}))
}
/>
<div className={styles.listItem}>
<i className={styles.itemIcon}>
<img src={imgIcon} alt="" />
</i>
<span className={styles.itemLabel}>上传图片</span>
</div>
<div className={styles.imageArea}>
<ImagePicker
value={applyForm.uploadImg[0]}
showView={() => this.showViewHandle(0)}
onChange={fileList => this.onImagePicker(0, fileList)}
/>
<ImagePicker
value={applyForm.uploadImg[1]}
showView={() => this.showViewHandle(1)}
onChange={fileList => this.onImagePicker(1, fileList)}
/>
<ImagePicker
value={applyForm.uploadImg[2]}
showView={() => this.showViewHandle(2)}
onChange={fileList => this.onImagePicker(2, fileList)}
/>
</div>
<Button className={styles.applyBtn} onClick={this.startApply}>
提交
</Button>
<Modal
className={styles.perViewDialog}
visible={perviewVisibility}
transparent
maskClosable={true}
onClose={this.resetDialog}
title=""
>
<Perview data={applyForm.uploadImg} index={perviewIndex} />
</Modal>
</div>
);
}
}
export default props => (
<UserInfoContext.Consumer>
{({ userinfo }) => <ReprtRepair userinfo={userinfo} {...props} />}
</UserInfoContext.Consumer>
);
import React, { Component } from 'react';
import styles from './style.css';
import titleIcon from '../images/dropdown_icon@2x.png';
class RepairItem extends Component {
constructor(props) {
super(props);
this.state = {
showDetail: false,
};
}
toggleDetail = () => {
this.setState(({ showDetail }) => ({
showDetail: !showDetail,
}));
};
dealHandle = e => {
const { dealHandle, data } = this.props;
if (dealHandle && data.dealState === 1) {
e.stopPropagation();
e.preventDefault();
dealHandle(data.id);
}
};
showViewHandle = (e, imgList, index) => {
e.stopPropagation();
const { showView } = this.props;
showView && showView(imgList, index);
};
render() {
const { showDetail } = this.state;
const { data } = this.props;
let imgList = data.uploadImg ? data.uploadImg.split(',') : [];
return (
<div className={styles.wrap}>
<div
className={data.dealState === 1 ? styles.titleDealing : styles.title}
>
<i
className={
showDetail ? styles.titleIconShowDetail : styles.titleIcon
}
>
<img src={titleIcon} alt="" />
</i>
</div>
<div className={styles.content} onClick={this.toggleDetail}>
{data.dealState === 1 ? (
<div className={styles.dealing} onClick={this.dealHandle}>
处理中
</div>
) : (
<div className={styles.dealed} onClick={this.dealHandle}>
已处理
</div>
)}
{showDetail
? [
<div key="time" className={styles.listLine}>
<div className={styles.listLabel}>报修时间:</div>
<div className={styles.listValue}>{data.createDate}</div>
</div>,
<div key="area" className={styles.listLine}>
<div className={styles.listLine2ItemLeft}>
<div className={styles.listLabel}>报修区域:</div>
<div className={styles.listValue}>{data.area}</div>
</div>
<div className={styles.listLine2ItemRight}>
<div className={styles.listLabel}>报修项目:</div>
<div className={styles.listValue}>{data.term}</div>
</div>
</div>,
<div key="address" className={styles.listLineArea}>
<div className={styles.listLabel}>报修地址:</div>
<div className={styles.listValueMutLine}>{data.address}</div>
</div>,
<div key="name" className={styles.listLine}>
<div className={styles.listLine2ItemLeft}>
<div className={styles.listLabel}>报修人:</div>
<div className={styles.listValue}>{data.name}</div>
</div>
<div className={styles.listLine2ItemRight}>
<div className={styles.listLabel}>报修人tel</div>
<div className={styles.listValue}>
{data.repairCellphone}
</div>
</div>
</div>,
<div key="uploadImg" className={`${styles.listLine} ${styles.listLineImg}`}>
{imgList[0] &&
imgList.map((item, idx) => (
<div
key={idx}
className={styles.imgItem}
onClick={e => this.showViewHandle(e, imgList, idx)}
>
<img src={item} alt="" />
</div>
))}
</div>,
]
: [
<div key="time" className={styles.listLine}>
<div className={styles.listLabel}>报修时间:</div>
<div className={styles.listValue}>{data.createDate}</div>
</div>,
<div key="name" className={styles.listLine}>
<div className={styles.listLine2ItemLeft}>
<div className={styles.listLabel}>报修人:</div>
<div className={styles.listValue}>{data.name}</div>
</div>
<div className={styles.listLine2ItemRight}>
<div className={styles.listLabel}>报修人tel</div>
<div className={styles.listValue}>
{data.repairCellphone}
</div>
</div>
</div>,
<div key="address" className={styles.listLineArea}>
<div className={styles.listLabel}>报修事项:</div>
<div className={styles.listValue}>{data.affair}</div>
</div>,
]}
</div>
</div>
);
}
}
export default RepairItem;
.wrap {
display: flex;
width: 600px;
margin: 0 auto 20px;
border-radius: 8px;
overflow: hidden;
background-color: #fff;
}
.title {
width: 40px;
display: flex;
justify-content: center;
align-items: center;
background-color: #3695c0;
}
.titleDealing {
composes: title;
background-color: #ff3131;
}
.titleIcon {
width: 24px;
height: 24px;
transform: rotate(90deg);
transition: all 500ms ease-in;
}
.titleIconShowDetail {
composes: titleIcon;
transform: rotate(0);
}
.content {
position: relative;
flex: 1;
padding: 30px 12px;
overflow: hidden;
}
.dealing {
position: absolute;
top: 24px;
right: 20px;
font-size: 26px;
color: #ff0000;
}
.dealed {
composes: dealing;
color: #999;
}
.listLine {
display: flex;
line-height: 30px;
margin-bottom: 30px;
}
.listLineImg {
/* justify-content: space-between; */
}
.listLine:last-child {
margin-bottom: 0;
}
.listLineArea {
composes: listLine;
line-height: 30px;
}
.listLine2Item {
display: flex;
}
.listLine2ItemLeft {
composes: listLine2Item;
width: 250px;
}
.listLine2ItemRight {
composes: listLine2Item;
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.listValueMutLine {
white-space: wrap;
word-break: break-all;
}
.listLabel {
white-space: nowrap;
}
.listValue {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.imgItem {
width: 170px;
height: 96px;
margin-right: 13px;
}
.imgItem:last-child {
margin-right: 0px;
}
import React, { Component } from 'react';
import { Switch, Route, Redirect, NavLink } from 'react-router-dom';
import styles from './style.css';
import ReprtRepair from './ReprtRepair';
import RepairList from './RepairList';
class index extends Component {
constructor(props) {
super(props);
this.state = {
showFooter: true,
};
}
componentDidMount() {
let screenHeight = document.body.offsetHeight;
window.onresize = () => {
let nowHeight = document.body.offsetHeight;
if (nowHeight < screenHeight) {
this.setState({
showFooter: false,
});
} else {
this.setState({
showFooter: true,
});
}
};
}
render() {
const { showFooter } = this.state;
return (
<div className="Content-Wrap">
<div className="RepairContent">
<Switch>
<Route path="/repair/ReprtRepair" component={ReprtRepair} />
<Route path="/repair/RepairList" component={RepairList} />
<Route render={() => <Redirect to="/repair/ReprtRepair" />} />
</Switch>
</div>
{showFooter && (
<div className={styles.footer}>
<NavLink
className={styles['footer-item']}
to="/repair/ReprtRepair"
replace
>
报修服务
</NavLink>
<span className={styles.line} />
<NavLink
className={styles['footer-item']}
to="/repair/RepairList"
replace
>
报修记录
</NavLink>
</div>
)}
</div>
);
}
}
export default index;
/* index */
:global(.RepairContent) {
flex: auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.scrollWrap {
background-color: #f2f2f2;
}
.footerTopLine {
position: absolute;
top: -5px;
width: 100%;
height: 6px;
background: linear-gradient(0deg, #626466, transparent);
opacity: 0.3;
}
.footer {
width: 100%;
height: 80px;
line-height: 80px;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
border-top: 1px solid #dee0de;
overflow: hidden;
}
.line {
width: 2px;
height: 50px;
background-color: #f0f0f0;
}
.footer-item {
flex: 1;
color: #999;
text-align: center;
background-color: #fff;
}
:global(.active) {
background-color: #3695c0;
color: #fff;
}
/* Report */
.wrap {
padding-bottom: 20px;
}
.listItem {
height: 48px;
line-height: 48px;
display: flex;
align-items: center;
padding: 32px 40px 0;
}
.itemIcon {
width: 30px;
height: 30px;
overflow: hidden;
}
.itemLabel {
width: 120px;
margin: 0 12px 0 10px;
font-size: 26px;
color: #333;
text-align: justify;
text-justify: distribute-all-lines;
text-align-last: justify;
}
.inputBox {
position: relative;
flex: auto;
text-align: center;
}
.input {
border: none;
width: 100%;
height: 100%;
padding: 0;
text-align: center;
font-size: 26px;
color: #666;
}
.inputBox::after {
content: '';
position: absolute;
background-color: #f0f0f0;
display: block;
z-index: 1;
top: auto;
right: auto;
bottom: 0;
left: 0;
width: 100%;
height: 1px;
transform-origin: 50% 100%;
transform: scaleY(0.5);
}
.inputBoxNoBB {
composes: inputBox;
}
.inputBoxNoBB::after {
display: none;
}
.checkBox {
display: inline-flex;
align-items: center;
margin-right: 60px;
}
.checkBox:last-child {
margin-right: 0;
}
.checkLabel {
width: 30px;
height: 30px;
margin-right: 10px;
}
.radio {
position: relative;
display: inline-block;
vertical-align: middle;
width: 30px;
height: 30px;
border: 1px solid #ccc;
border-radius: 50%;
margin-right: 10px;
}
.radioInner {
position: absolute;
right: 0;
width: 15px;
height: 15px;
box-sizing: border-box;
transform: rotate(0deg);
}
.radioInput {
position: absolute;
top: 0;
left: 0;
opacity: 0;
width: 100%;
height: 100%;
z-index: 2;
border: 0 none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
:global(.textarea) {
background-color: #f0f0f0;
border-radius: 8px;
width: 560px;
height: 150px;
line-height: 30px;
margin: 0px auto 0;
color: #666;
padding: 16px 20px 0;
}
:global(.textarea .am-textarea-control) {
padding: 0;
}
:global(.am-textarea-item .am-textarea-control textarea) {
font-size: 26px;
color: #666;
}
.imageArea {
display: flex;
justify-content: space-between;
width: 560px;
margin: 0 auto;
padding-bottom: 18px;
}
.applyBtn {
height: 80px;
line-height: 80px;
width: 560px;
margin: 20px auto 0;
color: #fff;
font-size: 30px;
background-color: #3695c0;
}
/* reportList */
.repairList {
background-color: #f0f0f0;
}
.listInfo {
height: 60px;
line-height: 60px;
color: #ff3131;
font-size: 22px;
text-align: center;
}
.noData {
height: 300px;
line-height: 80px;
text-align: center;
padding: 50px 0;
}
.perViewDialog {
width: 100%;
}
import React, { Component } from 'react';
import { ListView, PullToRefresh, Modal, Toast } from 'antd-mobile';
import styles from './style.css';
import RepairItem from '../Repair/components/RepairItem';
import { fetchReportRepairList, dealRepair } from '../../api/index';
import Perview from '../../components/Perview/Perview';
import onCheckIcon from '../../images/Check/radio_on_btn@2x.png';
class RepairDeal extends Component {
constructor(props) {
super(props);
const list = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
refreshing: false,
down: true,
height: document.documentElement.clientHeight,
pagination: {
nowDate: '',
pageNum: 1,
pageSize: 10,
},
list: list,
sourceList: [],
};
}
componentDidMount() {
this.fetchList();
}
updateList = () => {
const { sourceList } = this.state;
this.setState(({ list }) => ({
list: list.cloneWithRows(sourceList),
}));
};
fetchList = fetchMore => {
this.setState({ refreshing: true });
const { pagination } = this.state;
const nowDate = new Date().toISOString();
const entity = fetchMore
? {
...pagination,
pageNum: pagination.pageNum + 1,
}
: {
...pagination,
pageNum: 1,
nowDate,
};
fetchReportRepairList(entity)
.then(res => {
this.setState(
({ sourceList }) =>
fetchMore
? res.data.length
? {
sourceList: [...sourceList, ...res.data],
pagination: entity,
refreshing: false,
}
: {
refreshing: false,
}
: {
sourceList: res.data,
pagination: entity,
refreshing: false,
}
);
this.updateList();
})
.catch(err => {
Toast.fail(err.msg || '请求失败!');
this.setState({
refreshing: false,
});
});
};
refreshHandle = () => {
this.fetchList(true);
};
dealHandle = id => {
console.log('in date');
Modal.alert(
'',
<div className={styles.alertInfo}>
<span className={styles.checkLabel}>
<img src={onCheckIcon} alt="" />
</span>
<span>已处理</span>
</div>,
[
{ text: '放弃', onPress: () => console.log('cancel') },
{
text: '提交',
onPress: () =>
dealRepair({
id: id,
})
.then(res => {
this.fetchList();
Toast.fail(res.msg || '处理成功!');
})
.catch(err => {
Toast.fail(err.msg || '处理失败!');
}),
},
]
);
};
showViewHandle = (list, index) => {
this.setState({
perviewVisibility: true,
perviewIndex: index,
selectedImgList: list,
});
};
resetDialog = () => {
this.setState({
perviewVisibility: false,
});
};
MyBody = props => {
const { perviewVisibility, selectedImgList, perviewIndex } = this.state;
return (
<div className={styles.repairList}>
<div className={styles.listInfo}>显示最近半年的报修记录</div>
{props.children}
<Modal
key="repairImgPerview"
className={styles.perViewDialog}
visible={perviewVisibility}
transparent
maskClosable={true}
onClose={this.resetDialog}
title=""
>
<Perview data={selectedImgList} index={perviewIndex} />
</Modal>
</div>
);
};
render() {
const { list, pagination } = this.state;
return (
<ListView
style={{
height: this.state.height,
}}
className={styles.scrollWrap}
dataSource={list}
pageSize={pagination.pageSize}
renderBodyComponent={() => <this.MyBody />}
renderRow={rowData => (
<RepairItem
key={rowData.id}
data={rowData}
dealHandle={this.dealHandle}
showView={this.showViewHandle}
/>
)}
onEndReached={this.refreshHandle}
pullToRefresh={
<PullToRefresh
refreshing={this.state.refreshing}
onRefresh={this.fetchList}
/>
}
/>
);
}
}
export default RepairDeal;
.listInfo {
composes: listInfo from '../Repair/style.css';
}
.noData {
composes: noData from '../Repair/style.css';
}
.alertInfo {
display: flex;
justify-content: center;
align-items: center;
color: #000;
height: 86px;
}
.checkLabel {
margin-right: 14px;
width: 30px;
height: 30px;
position: relative;
}
.checkInner {
position: absolute;
width: 15px;
height: 15px;
background-color: #3695c0;
border-radius: 50%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.perViewDialog {
composes: perViewDialog from '../Repair/style.css';
}
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'; import React from 'react';
export const defaultUserinfo = { export const defaultUserinfo = {
openid: '15', login: false,
userId: '',
cellphone: '',
name: '', name: '',
phone: '', department: '',
author: [],
}; };
export const UserContext = React.createContext({ export const UserContext = React.createContext({
......
class Store {
constructor() {
this.userId = '';
}
getUserId = () => {
return this.userId;
};
updateUserId = userId => {
this.userId = userId;
};
}
export default new Store();
.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
...@@ -5,6 +5,7 @@ html { ...@@ -5,6 +5,7 @@ html {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: sans-serif; font-family: sans-serif;
overflow: hidden;
} }
figure, figure,
p { p {
...@@ -14,6 +15,7 @@ p { ...@@ -14,6 +15,7 @@ p {
img { img {
display: block; display: block;
width: 100%; width: 100%;
height: 100%;
} }
[aspectratio] { [aspectratio] {
position: relative; position: relative;
...@@ -46,10 +48,10 @@ img { ...@@ -46,10 +48,10 @@ img {
[w-750-190] { [w-750-190] {
aspect-ratio: '750:190'; aspect-ratio: '750:190';
} }
.bb1px { :global(.bb1px) {
position: relative; position: relative;
} }
.bb1px::after { :global(.bb1px::after) {
content: ''; content: '';
display: block; display: block;
position: absolute; position: absolute;
...@@ -62,10 +64,10 @@ img { ...@@ -62,10 +64,10 @@ img {
background: #dcdcdc; background: #dcdcdc;
transform: scaleY(0.5); transform: scaleY(0.5);
} }
.bt1px { :global(.bt1px) {
position: relative; position: relative;
} }
.bt1px::after { :global(.bt1px::after) {
content: ''; content: '';
display: block; display: block;
position: absolute; position: absolute;
...@@ -78,23 +80,31 @@ img { ...@@ -78,23 +80,31 @@ img {
background: #dcdcdc; background: #dcdcdc;
transform: scaleY(0.5); transform: scaleY(0.5);
} }
.ellipsis { :global(.ellipsis) {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.ellipsis-2lines { :global(.ellipsis-2lines) {
overflow: hidden; overflow: hidden;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
display: -webkit-box; display: -webkit-box;
} }
#root, :global(#root),
.App { :global(.App) {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
overflow-y: scroll; overflow-y: scroll;
} }
:global(.Content-Wrap) {
background-color: #fff;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
...@@ -2,3 +2,35 @@ export const is_weixn = () => { ...@@ -2,3 +2,35 @@ export const is_weixn = () => {
var ua = navigator.userAgent.toLowerCase(); var ua = navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) === 'micromessenger'; return ua.match(/MicroMessenger/i) === 'micromessenger';
}; };
export function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
);
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? str : padLeftZero(str)
);
}
}
return fmt;
}
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}
export const emojiFilter = str => {
return str.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '');
};
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