|
|
@@ -0,0 +1,515 @@
|
|
|
+/**
|
|
|
+ * Feedback 页面
|
|
|
+ * @邠心vbe on 2021/04/28
|
|
|
+ */
|
|
|
+import React from 'react';
|
|
|
+import { View, Text, StyleSheet, ScrollView, TextInput, Image, Pressable, FlatList } from 'react-native';
|
|
|
+import Button from '../../components/Button';
|
|
|
+import apiUpload from '../../api/apiUpload';
|
|
|
+import apiUser from '../../api/apiUser';
|
|
|
+import Dialog from '../../components/Dialog';
|
|
|
+import ImagePicker from 'react-native-image-crop-picker';
|
|
|
+import Dropdown from '../../components/Dropdown';
|
|
|
+import Modal from 'react-native-modal';
|
|
|
+import { UploadThemes } from '../../components/ThemesConfig';
|
|
|
+import apiBase from '../../api/apiBase.js';
|
|
|
+import utils from '../../utils/utils';
|
|
|
+import MyModal from '../../components/MyModal';
|
|
|
+import VbeSkeleton from '../../components/VbeSkeleton';
|
|
|
+
|
|
|
+const options = {
|
|
|
+ cropping: false,
|
|
|
+ multiple: false,
|
|
|
+ minFiles: 1,
|
|
|
+ maxFiles: 3,
|
|
|
+ mediaType: 'photo',
|
|
|
+ writeTempFile: false,
|
|
|
+ compressImageQuality: 0.8,
|
|
|
+ compressImageMaxWidth: 720,
|
|
|
+ compressImageMaxHeight: 1280,
|
|
|
+ ...UploadThemes
|
|
|
+}
|
|
|
+
|
|
|
+export default class Feedback extends React.Component {
|
|
|
+
|
|
|
+ constructor(props) {
|
|
|
+ super(props);
|
|
|
+ this.state= {
|
|
|
+ typeList: [],
|
|
|
+ feedback: '',
|
|
|
+ searchId: '',
|
|
|
+ connectorId: '',
|
|
|
+ chargeBoxId: '',
|
|
|
+ typeOfFeedback: '',
|
|
|
+ imageUrl: ['', '', ''],
|
|
|
+ chargeBoxList: [],
|
|
|
+ connectorList: [],
|
|
|
+ showDialog: false,
|
|
|
+ searching: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ componentDidMount() {
|
|
|
+ this.pageShow = true;
|
|
|
+ this.props.navigation.addListener('blur', () => {
|
|
|
+ this.pageShow = false;
|
|
|
+ });
|
|
|
+
|
|
|
+ apiUser.getTypeOfFeedback().then(res => {
|
|
|
+ if (res.success && res.data.length > 0) {
|
|
|
+ this.setState({
|
|
|
+ typeList: res.data
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ if (this.pageShow) {
|
|
|
+ this.noTypeDialog();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ if (this.pageShow) {
|
|
|
+ this.noTypeDialog();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.getChargeBox("");
|
|
|
+ }
|
|
|
+
|
|
|
+ noTypeDialog() {
|
|
|
+ setTimeout(() => {
|
|
|
+ if (this.pageShow) {
|
|
|
+ Dialog.showResultDialog($t('feedback.errFetchType'), $t('nav.ok'), back => {
|
|
|
+ goBack();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+
|
|
|
+ changeType(type, index) {
|
|
|
+ this.setState({
|
|
|
+ typeOfFeedback: type
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ listChargeBox(searchId) {
|
|
|
+ this.setState({
|
|
|
+ searching: true,
|
|
|
+ searchId: searchId
|
|
|
+ }, () => {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.getChargeBox(searchId)
|
|
|
+ }, 300);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ getChargeBox(searchId) {
|
|
|
+ if (searchId != this.state.searchId) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ apiBase.listChargeBox(this.state.searchId).then(res => {
|
|
|
+ if (res.data) {
|
|
|
+ this.setState({
|
|
|
+ chargeBoxList: res.data,
|
|
|
+ searching: false
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.setState({
|
|
|
+ chargeBoxList: [],
|
|
|
+ searching: false,
|
|
|
+ connectorId: ""
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ this.setState({
|
|
|
+ chargeBoxList: [],
|
|
|
+ searching: false,
|
|
|
+ connectorId: ""
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ listConnector() {
|
|
|
+ //Dialog.showProgressDialog()
|
|
|
+ apiBase.listConnector(this.state.chargeBoxId).then(res => {
|
|
|
+ if (res.data) {
|
|
|
+ this.setState({
|
|
|
+ connectorList: res.data
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.setState({
|
|
|
+ connectorList: []
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ this.setState({
|
|
|
+ connectorList: []
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ uploadImage(index) {
|
|
|
+ ImagePicker.openPicker(options).then(image => {
|
|
|
+ if (image.path) {
|
|
|
+ apiUpload.uploadImage(image.path, image.mime, 'FEEDBACK').then(res => {
|
|
|
+ if (res.success && res.data.picturePath) {
|
|
|
+ let imageUrl = this.state.imageUrl;
|
|
|
+ imageUrl[index] = res.data.picturePath;
|
|
|
+ this.setState({
|
|
|
+ imageUrl: imageUrl
|
|
|
+ });
|
|
|
+ toastShort($t('common.uploadSuccess'));
|
|
|
+ } else {
|
|
|
+ toastShort($t('common.uploadFailed'));
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ toastShort(err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ //console.log(err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ submitFeedback() {
|
|
|
+ if (this.state.typeOfFeedback == '') {
|
|
|
+ toastShort($t('feedback.errFeedbackType'));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.state.feedback == '') {
|
|
|
+ toastShort($t('feedback.errFeednackContent'));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const params = {
|
|
|
+ "typeOfFeedback": this.state.typeOfFeedback,
|
|
|
+ "feedback": this.state.feedback,
|
|
|
+ "feedbackImgOne": this.state.imageUrl[0],
|
|
|
+ "feedbackImgTwo": this.state.imageUrl[1],
|
|
|
+ "feedbackImgThree": this.state.imageUrl[2],
|
|
|
+ "chargeBoxId": this.state.chargeBoxId,
|
|
|
+ "connectorId": this.state.connectorId
|
|
|
+ }
|
|
|
+ Dialog.showProgressDialog();
|
|
|
+ apiUser.feedback(params).then(res => {
|
|
|
+ Dialog.dismissLoading();
|
|
|
+ Dialog.showResultDialog($t('feedback.sendSuccess'), $t('nav.ok'), back => {
|
|
|
+ goBack();
|
|
|
+ });
|
|
|
+ }).catch(err => {
|
|
|
+ Dialog.dismissLoading();
|
|
|
+ toastShort(err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ changeChargeBox(id) {
|
|
|
+ if (id) {
|
|
|
+ this.setState({
|
|
|
+ showDialog: false,
|
|
|
+ chargeBoxId: id
|
|
|
+ }, () => this.listConnector())
|
|
|
+ } else {
|
|
|
+ this.setState({
|
|
|
+ showDialog: false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ changeConnector(id) {
|
|
|
+ this.setState({
|
|
|
+ connectorId: id
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <ScrollView
|
|
|
+ style={styles.container}
|
|
|
+ keyboardShouldPersistTaps={isIOS ? 'never' : 'handled'}
|
|
|
+ contentInsetAdjustmentBehavior='automatic'>
|
|
|
+ <View style={styles.headerView}>
|
|
|
+ <View style={ui.flex1}>
|
|
|
+ <Text style={styles.title}>{$t('feedback.tipsSomething')}</Text>
|
|
|
+ <Text style={styles.content}>{$t('feedback.tipsLetKnow')}</Text>
|
|
|
+ </View>
|
|
|
+ <Image
|
|
|
+ style={styles.headerImage}
|
|
|
+ resizeMode="contain"
|
|
|
+ source={require('../../images/top-feedback.png')}/>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <View style={styles.contentView}>
|
|
|
+ <Text style={styles.typeTitle}>{$t('feedback.typeOfFeedback')}</Text>
|
|
|
+ <View style={styles.pickerView}>
|
|
|
+ <Dropdown
|
|
|
+ style={styles.pickerViewInfo}
|
|
|
+ title={$t('feedback.typeOfFeedback')}
|
|
|
+ list={this.state.typeList}
|
|
|
+ value={this.state.typeOfFeedback}
|
|
|
+ nameKey={'value'}
|
|
|
+ valueKey={'key'}
|
|
|
+ placeholder={$t('common.select')}
|
|
|
+ onChange={(value, index) => this.changeType(value, index)}/>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <Text style={styles.typeTitle}>{$t('feedback.labelContent')}</Text>
|
|
|
+ <TextInput
|
|
|
+ style={styles.feedbackInput}
|
|
|
+ multiline={true}
|
|
|
+ numberOfLines={8}
|
|
|
+ maxLength={1000}
|
|
|
+ textAlignVertical='top'
|
|
|
+ onChangeText={text => {
|
|
|
+ this.setState({
|
|
|
+ feedback: text
|
|
|
+ });
|
|
|
+ }}/>
|
|
|
+
|
|
|
+ <Text style={styles.typeTitle}>{$t('feedback.labelUpload')}</Text>
|
|
|
+ <View
|
|
|
+ style={styles.uploadGroup}>
|
|
|
+ { this.state.imageUrl.map((item, index) => {
|
|
|
+ return (
|
|
|
+ <Pressable
|
|
|
+ key={index}
|
|
|
+ style={styles.uploadView}
|
|
|
+ onPress={() => {
|
|
|
+ this.uploadImage(index)
|
|
|
+ }}>
|
|
|
+ { item == ''
|
|
|
+ ? <Image
|
|
|
+ style={styles.uploadIcon}
|
|
|
+ source={require('../../images/icon/ic-add-photo.png')}/>
|
|
|
+ : <Image
|
|
|
+ style={styles.uploadIcon}
|
|
|
+ defaultSource={require('../../images/icon/icon-upload-default.png')}
|
|
|
+ source={{uri: utils.getImageUrl(item)}}/>
|
|
|
+ }
|
|
|
+ </Pressable>
|
|
|
+ );
|
|
|
+ })
|
|
|
+ }
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {this.state.typeOfFeedback == "csf" && <>
|
|
|
+ <Text style={styles.typeTitle}>{$t('feedback.labelStation')}</Text>
|
|
|
+ <Button
|
|
|
+ style={styles.pickerView}
|
|
|
+ viewStyle={[styles.stationView, ui.flex1]}
|
|
|
+ onClick={() => this.setState({showDialog: true})}>
|
|
|
+ <MaterialIcons
|
|
|
+ name="search"
|
|
|
+ size={24}
|
|
|
+ color="#00638C"
|
|
|
+ />
|
|
|
+ <Text style={styles.textStation}>{this.state.chargeBoxId}</Text>
|
|
|
+ </Button>
|
|
|
+ <Text style={styles.typeTitle}>{$t('feedback.labelConnector')}</Text>
|
|
|
+ <View style={styles.pickerView}>
|
|
|
+ <Dropdown
|
|
|
+ style={styles.pickerViewInfo}
|
|
|
+ title={$t('feedback.selectConnector')}
|
|
|
+ list={this.state.connectorList}
|
|
|
+ value={this.state.connectorId}
|
|
|
+ placeholder={$t('common.select')}
|
|
|
+ onChange={value => this.changeConnector(value)}/>
|
|
|
+ </View>
|
|
|
+ </>}
|
|
|
+ <Button
|
|
|
+ style={styles.button}
|
|
|
+ text={$t('feedback.submitFeedback')}
|
|
|
+ elevation={1.5}
|
|
|
+ onClick={() => {
|
|
|
+ this.submitFeedback();
|
|
|
+ }}/>
|
|
|
+ </View>
|
|
|
+ <MyModal
|
|
|
+ visible={this.state.showDialog}
|
|
|
+ onLayerPress={() => this.changeChargeBox()}>
|
|
|
+ <View style={$padding(8, 0)}>
|
|
|
+ <View style={styles.searchView}>
|
|
|
+ <MaterialIcons
|
|
|
+ name="search"
|
|
|
+ size={24}
|
|
|
+ color="#00638C"
|
|
|
+ />
|
|
|
+ <TextInput
|
|
|
+ style={styles.inputView}
|
|
|
+ maxLength={50}
|
|
|
+ placeholder={$t('feedback.searchingChargeBox')}
|
|
|
+ onChangeText={text => this.listChargeBox(text)}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ { this.state.searching
|
|
|
+ ? <VbeSkeleton
|
|
|
+ style={styles.stationList}
|
|
|
+ layout={[
|
|
|
+ {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
|
|
|
+ {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
|
|
|
+ {width: "80%", height: 20, ...$margin(14, 0, 14, 16)}
|
|
|
+ ]}/>
|
|
|
+ : <FlatList
|
|
|
+ style={styles.stationList}
|
|
|
+ data={this.state.chargeBoxList}
|
|
|
+ keyExtractor={(item,index) => index}
|
|
|
+ renderItem={this.listItem}
|
|
|
+ ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
|
|
|
+ />
|
|
|
+ }
|
|
|
+ </View>
|
|
|
+ </MyModal>
|
|
|
+ </ScrollView>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ listItem = ({item, index}) => {
|
|
|
+ return (
|
|
|
+ <Button
|
|
|
+ key={index}
|
|
|
+ text={item}
|
|
|
+ style={styles.stationItemView}
|
|
|
+ textStyle={styles.stationItemText}
|
|
|
+ onClick={() => this.changeChargeBox(item)}
|
|
|
+ />
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const styles = StyleSheet.create({
|
|
|
+ container: {
|
|
|
+ flex: 1,
|
|
|
+ backgroundColor: pageBackground
|
|
|
+ },
|
|
|
+ contentView: {
|
|
|
+ marginTop: -30,
|
|
|
+ borderTopLeftRadius: 30,
|
|
|
+ borderTopRightRadius: 30,
|
|
|
+ ...$padding(8, 16, 16),
|
|
|
+ backgroundColor: colorLight
|
|
|
+ },
|
|
|
+ headerView: {
|
|
|
+ paddingTop: 16,
|
|
|
+ paddingLeft: 16,
|
|
|
+ paddingRight: 8,
|
|
|
+ paddingBottom: 30,
|
|
|
+ flexDirection: 'row',
|
|
|
+ backgroundColor: '#F5F5F5'
|
|
|
+ },
|
|
|
+ headerImage: {
|
|
|
+ width: 123,
|
|
|
+ height: 101
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ color: '#056A94',
|
|
|
+ fontSize: 18,
|
|
|
+ paddingTop: 16,
|
|
|
+ paddingBottom: 8
|
|
|
+ },
|
|
|
+ content: {
|
|
|
+ color: '#056A94',
|
|
|
+ fontSize: 14
|
|
|
+ },
|
|
|
+ typeTitle: {
|
|
|
+ color: '#000',
|
|
|
+ fontSize: 16,
|
|
|
+ paddingTop: 16,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ paddingBottom: 10
|
|
|
+ },
|
|
|
+ stationView: {
|
|
|
+ paddingLeft: 12,
|
|
|
+ alignItems: 'center',
|
|
|
+ flexDirection: 'row'
|
|
|
+ },
|
|
|
+ textStation: {
|
|
|
+ flex: 1,
|
|
|
+ color: textPrimary,
|
|
|
+ fontSize: 15,
|
|
|
+ paddingLeft: 8
|
|
|
+ },
|
|
|
+ pickerView: {
|
|
|
+ //width: $vw(60),
|
|
|
+ height: 44,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: '#999',
|
|
|
+ borderRadius: 6,
|
|
|
+ overflow: 'hidden',
|
|
|
+ justifyContent: 'center',
|
|
|
+ backgroundColor: '#F5F5F5'
|
|
|
+ },
|
|
|
+ inputView: {
|
|
|
+ flex: 1,
|
|
|
+ color: textPrimary,
|
|
|
+ fontSize: 15
|
|
|
+ },
|
|
|
+ pickerViewInfo: {
|
|
|
+ height: 44,
|
|
|
+ paddingLeft: 16,
|
|
|
+ paddingRight: 8,
|
|
|
+ alignItems: 'center',
|
|
|
+ flexDirection: 'row'
|
|
|
+ },
|
|
|
+ searchView: {
|
|
|
+ height: 44,
|
|
|
+ paddingLeft: 12,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: '#999',
|
|
|
+ borderRadius: 6,
|
|
|
+ ...$margin(8, 16, 16),
|
|
|
+ overflow: 'hidden',
|
|
|
+ alignItems: 'center',
|
|
|
+ flexDirection: 'row',
|
|
|
+ backgroundColor: '#F5F5F5'
|
|
|
+ },
|
|
|
+ feedbackInput: {
|
|
|
+ color: textPrimary,
|
|
|
+ minHeight: 100,
|
|
|
+ borderWidth: 1,
|
|
|
+ borderColor: '#999',
|
|
|
+ borderRadius: 6,
|
|
|
+ paddingLeft: 10,
|
|
|
+ paddingRight: 10,
|
|
|
+ backgroundColor: '#F5F5F5'
|
|
|
+ },
|
|
|
+ uploadGroup: {
|
|
|
+ //paddingTop: 16,
|
|
|
+ alignItems: 'center',
|
|
|
+ flexDirection: 'row',
|
|
|
+ justifyContent: 'center'
|
|
|
+ },
|
|
|
+ uploadView: {
|
|
|
+ flex: 1,
|
|
|
+ alignItems: 'center'
|
|
|
+ },
|
|
|
+ uploadIcon: {
|
|
|
+ width: $vw(29),
|
|
|
+ height: $vw(29),
|
|
|
+ borderRadius: 6
|
|
|
+ },
|
|
|
+ button: {
|
|
|
+ marginTop: 32,
|
|
|
+ marginBottom: 16,
|
|
|
+ borderRadius: 4
|
|
|
+ },
|
|
|
+ stationList: {
|
|
|
+ height: $vh(35)
|
|
|
+ },
|
|
|
+ noResult: {
|
|
|
+ color: textCancel,
|
|
|
+ fontSize: 12,
|
|
|
+ textAlign: 'center',
|
|
|
+ paddingTop: 32
|
|
|
+ },
|
|
|
+ stationItemView: {
|
|
|
+ borderRadius: 0,
|
|
|
+ backgroundColor: colorLight,
|
|
|
+ borderTopWidth: 1,
|
|
|
+ borderTopColor: '#F0F0F0'
|
|
|
+ },
|
|
|
+ stationItemText: {
|
|
|
+ flex: 1,
|
|
|
+ color: textPrimary,
|
|
|
+ fontSize: 15
|
|
|
+ },
|
|
|
+ seachingIcon: {
|
|
|
+ width: 50,
|
|
|
+ height: 50,
|
|
|
+ marginTop: 20
|
|
|
+ }
|
|
|
+})
|