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).
此为移动端项目在模板基础上添加屏幕适配,以 VW,VH 为单位实现页面适配,添加以下插件
......
......@@ -35,7 +35,7 @@ module.exports = function override(config, env) {
loader: require.resolve('less-loader'),
options: {
// 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) {
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
// ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-aspect-ratio-mini'),
require('postcss-px-to-viewport')({
viewportWidth: 750, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 5, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false, // (Boolean) Allow px to be converted in media queries.
}),
require('postcss-write-svg')({
utf8: false,
}),
require('postcss-cssnext'),
require('postcss-viewport-units'),
require('cssnano')({
preset: 'advanced',
autoprefixer: false,
'postcss-zindex': false,
}),
],
},
options: {},
},
],
});
......
......@@ -26,7 +26,12 @@
"autoprefixer": "^9.1.0",
"babel-plugin-import": "^1.8.0",
"cssnano-preset-advanced": "^4.0.0",
"less": "^3.8.1",
"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"
},
"browserslist": [
......
......@@ -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="theme-color" content="#000000">
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>成都师范后勤管理</title>
</head>
<body>
......@@ -29,18 +16,7 @@
You need to enable JavaScript to run this app.
</noscript>
<div id="root">
载入中。。。
</div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script> window.onload = function () { window.viewportUnitsBuggyfill.init({ hacks: window.viewportUnitsBuggyfillHacks }); } </script>
</body>
......
import axios from 'axios';
import config from '../config/index';
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实例
const service = axios.create({
baseURL: path, // api的base_url
timeout: 10000,
timeout: 20000,
});
// request拦截器
service.interceptors.request.use(
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,
reqSource: 'wx',
// token: token ? token : '',
};
return conf;
},
......@@ -28,17 +47,24 @@ service.interceptors.request.use(
);
// respone拦截器
service.interceptors.response.use(response => {
service.interceptors.response.use(
response => {
const res = response.data;
Toast.hide();
/**
* code为非'0'是抛错
* code为非1000是抛错
*/
if (res.code !== 1000) {
return Promise.reject(response.data);
} else {
return response.data;
}
}, errorHandle);
},
err => {
Toast.hide();
return errorHandle(err);
}
);
export default service;
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({
url: '/app/publicCarController/pageList',
url: '/login/sendMsg',
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 =>
fetch({
url: '/app/publicCarController/addPublicCar',
params: entity,
url: '/publicCar/app/teacherAppoCar4Add',
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 SERVER_URL = 'http://ex-dev-gx-manager.168cad.top';
// export const SERVER_URL = 'http://192.168.1.150:8080';
export const APP_ID = 'wx723baaa343083ca9';
// export const SERVER_URL = 'http://ex-dev-gx-manager.168cad.top';
export const SERVER_URL = 'http://ex-logistics-service-manager.168cad.top';
export default {
APP_ID,
......
import React, { Component } from 'react';
import '../styles/App.css';
import { is_weixn } from '../utils';
import { APP_ID } from '../config';
import UserInfoContext, { defaultUserinfo } from '../context/userinfo-context';
import { Route, Switch } from 'react-router-dom';
import Report from './report/report';
import { Route, Switch, Redirect } from 'react-router-dom';
import Home from './Home/Index';
import Login from './Login/Login';
import PublicCarList from './PublicCar/PublicCarList';
import PublicCarApply from './PublicCar/PublicCarApply';
import PublicCar from './PublicCar/index';
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 {
constructor(props) {
super(props);
this.updateUserInfo = data => {
this.setState({
userinfo: { ...this.state.userinfo, ...data },
});
};
this.state = {
isWx: is_weixn(),
userContext: {
userinfo: defaultUserinfo,
updateUserInfo: this.updateUserInfo,
},
loadingText: '加载中...',
};
}
renderLogin = props => {
// const { isWx } = this.state;
const isWx = true;
const { search } = props.location;
const paramsString = search.substring(1);
const searchParams = new URLSearchParams(paramsString);
const openid = searchParams.get('openid');
// const state = searchParams.get('state');
console.log(isWx, openid);
getParams = str => {
let obj = {};
const searchParams = new URLSearchParams(str);
let keys = searchParams.keys();
for (const key of keys) {
obj[key] = searchParams.get(key);
}
return obj;
};
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();
}
if (!isWx) {
return <div>请在微信客户端打开链接</div>;
} else if (!openid) {
let rUrl = encodeURIComponent('');
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>授权中。。。</div>;
return <div style={{ margin: '60px 24px 0' }}>授权中。。。</div>;
};
validateUserinfo = (props, code, Component) => {
const {
userContext: {
userinfo: { login, author },
},
} = this.state;
if (!login) {
console.log('no login');
// product 需要修改为去微信授权
// return <Component />;
return this.goWechatAuth();
}
let item = author.find(i => i.code === code);
if (item.type) {
return <Component {...props} />;
} else {
return <Login {...props} openid={openid} />;
Toast.fail('无权使用该功能');
const from = { pathname: '/home' };
return <Redirect to={from} />;
}
};
updateUserInfo = data => {
......@@ -64,10 +126,32 @@ class App extends Component {
<div className="App">
<UserInfoContext.Provider value={userContext}>
<Switch>
<Route exact path="/" render={this.renderLogin} />
<Route path="/report" component={Report} />
<Route path="/PublicCarList" component={PublicCarList} />
<Route path="/PublicCarApply" component={PublicCarApply} />
<Route exact path="/" render={this.renderIndex} />
<Route path="/home" component={Home} />
<Route path="/login" component={Login} />
<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>
</UserInfoContext.Provider>
</div>
......
import React from 'react';
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 {
static propTypes = {
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() {
return (
<div>
Home
<br />
<Link to="/report">report</Link>
<br />
<Link to="/report">report</Link>
<br />
<Link to="/report">report</Link>
<div style={HomePageStyle}>
<div className={styles.banner}>
<img src={bannerIcon} alt="" />
</div>
<div className={styles.iconBox}>
<div
className={styles.repairIcon}
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>
);
}
}
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 { Redirect } from 'react-router-dom';
import styles from './style.css';
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
},
};
import logoIcon from './images/school_logo@2x.png';
import logoWord from './images/school_word_logo@2x.png';
import { Button, Modal, Toast } from 'antd-mobile';
import { getVcode, sendMsg, login } from '../../api/index';
import CodeView from '../../components/CodeView/CodeView';
import UserInfoContext from '../../context/userinfo-context';
import store from '../../store';
class Login extends Component {
state = {
constructor(props) {
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);
});
};
resetDialog = () => {
this.setState({
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();
};
login = () => {
fakeAuth.authenticate(() => {
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() {
const { from } = this.props.location.state || { from: { pathname: '/' } };
const { redirectToReferrer } = this.state;
// const { from } = this.props.location.state || { from: { pathname: '/' } };
const {
redirectToReferrer,
sending,
count,
cellphone,
vcodeDialogVisible,
vcode,
svcode,
code,
} = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
return <Redirect to={{ pathname: '/home' }} />;
}
return (
<div className={styles.Login}>
<h2>Login</h2>
<button onClick={this.login}>Log in</button>
<div className={styles.loginBox}>
<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>
);
}
}
export default Login;
export default props => (
<UserInfoContext.Consumer>
{({ userinfo, updateUserInfo }) => (
<Login {...props} updateUserInfo={updateUserInfo} />
)}
</UserInfoContext.Consumer>
);
.Login {
height: 300px;
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 { 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 {
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() {
const {
name,
department,
createDate,
categoryList,
categoryId,
lineDescription,
showAllCategory,
} = this.state;
return (
<div>
PublicCarApply
<div className={styles.bg}>
<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>
);
}
}
export default PublicCarApply;
\ No newline at end of file
export default props => (
<UserInfoContext.Consumer>
{({ userinfo }) => <PublicCarApply userinfo={userinfo} {...props} />}
</UserInfoContext.Consumer>
);
import React, { Component } from 'react';
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 {
static state = {
list: [],
constructor(props) {
super(props);
const list = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.state = {
refreshing: false,
down: true,
height: document.documentElement.clientHeight,
list: list,
sourceList: [],
pagination: {
nowDate: '',
pageNum: 1,
pageSize: 50,
pageSize: 10,
},
};
componentWillMount() {
fetchPublicCarList()
}
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,
};
fetchPublicCarList(entity)
.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 => {
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() {
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 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';
export const defaultUserinfo = {
openid: '15',
login: false,
userId: '',
cellphone: '',
name: '',
phone: '',
department: '',
author: [],
};
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 {
margin: 0;
padding: 0;
font-family: sans-serif;
overflow: hidden;
}
figure,
p {
......@@ -14,6 +15,7 @@ p {
img {
display: block;
width: 100%;
height: 100%;
}
[aspectratio] {
position: relative;
......@@ -46,10 +48,10 @@ img {
[w-750-190] {
aspect-ratio: '750:190';
}
.bb1px {
:global(.bb1px) {
position: relative;
}
.bb1px::after {
:global(.bb1px::after) {
content: '';
display: block;
position: absolute;
......@@ -62,10 +64,10 @@ img {
background: #dcdcdc;
transform: scaleY(0.5);
}
.bt1px {
:global(.bt1px) {
position: relative;
}
.bt1px::after {
:global(.bt1px::after) {
content: '';
display: block;
position: absolute;
......@@ -78,23 +80,31 @@ img {
background: #dcdcdc;
transform: scaleY(0.5);
}
.ellipsis {
:global(.ellipsis) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis-2lines {
:global(.ellipsis-2lines) {
overflow: hidden;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
}
#root,
.App {
:global(#root),
:global(.App) {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
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 = () => {
var ua = navigator.userAgent.toLowerCase();
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