Feedback.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /**
  2. * Feedback 页面
  3. * @邠心vbe on 2021/04/28
  4. */
  5. import React from 'react';
  6. import { View, Text, StyleSheet, ScrollView, TextInput, Image, Pressable, FlatList } from 'react-native';
  7. import Button from '../../components/Button';
  8. import apiUpload from '../../api/apiUpload';
  9. import { host } from '../../api/http';
  10. import apiUser from '../../api/apiUser';
  11. import Dialog from '../../components/Dialog';
  12. import ImagePicker from 'react-native-image-crop-picker';
  13. import Dropdown from '../../components/Dropdown';
  14. import Modal from 'react-native-modal';
  15. import { UploadThemes } from '../../components/ThemesConfig';
  16. import apiBase from '../../api/apiBase.js';
  17. const options = {
  18. cropping: false,
  19. multiple: false,
  20. minFiles: 1,
  21. maxFiles: 3,
  22. mediaType: 'photo',
  23. writeTempFile: false,
  24. compressImageQuality: 0.8,
  25. compressImageMaxWidth: 720,
  26. compressImageMaxHeight: 1280,
  27. ...UploadThemes
  28. }
  29. export default class Feedback extends React.Component {
  30. constructor(props) {
  31. super(props);
  32. this.state= {
  33. typeList: [],
  34. feedback: '',
  35. searchId: '',
  36. connectorId: '',
  37. chargeBoxId: '',
  38. typeOfFeedback: '',
  39. imageUrl: ['', '', ''],
  40. chargeBoxList: [],
  41. connectorList: [],
  42. showDialog: false,
  43. searching: false
  44. }
  45. }
  46. componentDidMount() {
  47. this.pageShow = true;
  48. this.props.navigation.addListener('blur', () => {
  49. this.pageShow = false;
  50. });
  51. apiUser.getTypeOfFeedback().then(res => {
  52. if (res.success && res.data.length > 0) {
  53. this.setState({
  54. typeList: res.data
  55. });
  56. } else {
  57. if (this.pageShow) {
  58. this.noTypeDialog();
  59. }
  60. }
  61. }).catch(err => {
  62. if (this.pageShow) {
  63. this.noTypeDialog();
  64. }
  65. });
  66. this.getChargeBox("");
  67. }
  68. noTypeDialog() {
  69. setTimeout(() => {
  70. if (this.pageShow) {
  71. Dialog.showResultDialog('Can not fetch feedback type!', 'OK', back => {
  72. goBack();
  73. });
  74. }
  75. }, 500);
  76. }
  77. changeType(type, index) {
  78. this.setState({
  79. typeOfFeedback: type
  80. })
  81. }
  82. listChargeBox(searchId) {
  83. this.setState({
  84. searching: true,
  85. searchId: searchId
  86. }, () => {
  87. setTimeout(() => {
  88. this.getChargeBox(searchId)
  89. }, 400);
  90. });
  91. }
  92. getChargeBox(searchId) {
  93. if (searchId != this.state.searchId) {
  94. return;
  95. }
  96. apiBase.listChargeBox(this.state.searchId).then(res => {
  97. if (res.data) {
  98. this.setState({
  99. chargeBoxList: res.data,
  100. searching: false
  101. })
  102. } else {
  103. this.setState({
  104. chargeBoxList: [],
  105. searching: false,
  106. connectorId: ""
  107. })
  108. }
  109. }).catch(err => {
  110. this.setState({
  111. chargeBoxList: [],
  112. searching: false,
  113. connectorId: ""
  114. })
  115. })
  116. }
  117. listConnector() {
  118. //Dialog.showProgressDialog()
  119. apiBase.listConnector(this.state.chargeBoxId).then(res => {
  120. if (res.data) {
  121. this.setState({
  122. connectorList: res.data
  123. })
  124. } else {
  125. this.setState({
  126. connectorList: []
  127. })
  128. }
  129. }).catch(err => {
  130. this.setState({
  131. connectorList: []
  132. })
  133. })
  134. }
  135. uploadImage(index) {
  136. ImagePicker.openPicker(options).then(image => {
  137. if (image.path) {
  138. apiUpload.uploadImage(image.path, image.mime, 'FEEDBACK').then(res => {
  139. if (res.success && res.data.picturePath) {
  140. let imageUrl = this.state.imageUrl;
  141. imageUrl[index] = res.data.picturePath;
  142. this.setState({
  143. imageUrl: imageUrl
  144. });
  145. } else {
  146. toastShort('Upload failed, please retry');
  147. }
  148. }).catch(err => {
  149. toastShort(err);
  150. });
  151. }
  152. }).catch(err => {
  153. //console.log(err);
  154. });
  155. }
  156. submitFeedback() {
  157. if (this.state.typeOfFeedback == '') {
  158. toastShort('Please select type of feedback');
  159. return;
  160. }
  161. if (this.state.feedback == '') {
  162. toastShort('Please type feedback content');
  163. return;
  164. }
  165. const params = {
  166. "typeOfFeedback": this.state.typeOfFeedback,
  167. "feedback": this.state.feedback,
  168. "feedbackImgOne": this.state.imageUrl[0],
  169. "feedbackImgTwo": this.state.imageUrl[1],
  170. "feedbackImgThree": this.state.imageUrl[2],
  171. "chargeBoxId": this.state.chargeBoxId,
  172. "connectorId": this.state.connectorId
  173. }
  174. Dialog.showProgressDialog();
  175. apiUser.feedback(params).then(res => {
  176. Dialog.dismissLoading();
  177. Dialog.showResultDialog('Send feedback successfully!', 'OK', back => {
  178. goBack();
  179. });
  180. }).catch(err => {
  181. Dialog.dismissLoading();
  182. toastShort(err);
  183. });
  184. }
  185. changeChargeBox(id) {
  186. if (id) {
  187. this.setState({
  188. showDialog: false,
  189. chargeBoxId: id
  190. }, () => this.listConnector())
  191. } else {
  192. this.setState({
  193. showDialog: false
  194. })
  195. }
  196. }
  197. changeConnector(id) {
  198. this.setState({
  199. connectorId: id
  200. })
  201. }
  202. render() {
  203. return (
  204. <ScrollView
  205. style={styles.container}
  206. keyboardShouldPersistTaps='handled'
  207. contentInsetAdjustmentBehavior='automatic'>
  208. <View style={styles.headerView}>
  209. <View style={ui.flex1}>
  210. <Text style={styles.title}>Have something to tell us?</Text>
  211. <Text style={styles.content}>Please let us know below!</Text>
  212. </View>
  213. <Image
  214. style={styles.headerImage}
  215. resizeMode="contain"
  216. source={require('../../images/top-feedback.png')}/>
  217. </View>
  218. <View style={styles.contentView}>
  219. <Text style={styles.typeTitle}>Type of Feedback</Text>
  220. <View style={styles.pickerView}>
  221. <Dropdown
  222. style={styles.pickerViewInfo}
  223. title='Type of Feedback'
  224. list={this.state.typeList}
  225. value={this.state.typeOfFeedback}
  226. nameKey={'value'}
  227. valueKey={'key'}
  228. placeholder='Select'
  229. onChange={(value, index) => this.changeType(value, index)}/>
  230. </View>
  231. <Text style={styles.typeTitle}>Please fill in here (500 words)</Text>
  232. <TextInput
  233. style={styles.feedbackInput}
  234. multiline={true}
  235. maxLength={1000}
  236. numberOfLines={8}
  237. textAlignVertical='top'
  238. onChangeText={text => {
  239. this.setState({
  240. feedback: text
  241. });
  242. }}/>
  243. <Text style={styles.typeTitle}>Please upload relevant images</Text>
  244. <View
  245. style={styles.uploadGroup}>
  246. { this.state.imageUrl.map((item, index) => {
  247. return (
  248. <Pressable
  249. key={index}
  250. style={styles.uploadView}
  251. onPress={() => {
  252. this.uploadImage(index)
  253. }}>
  254. { item == ''
  255. ? <Image
  256. style={styles.uploadIcon}
  257. source={require('../../images/icon/ic-add-photo.png')}/>
  258. : <Image
  259. style={styles.uploadIcon}
  260. defaultSource={require('../../images/icon/icon-upload-default.png')}
  261. source={{uri: host + item}}/>
  262. }
  263. </Pressable>
  264. );
  265. })
  266. }
  267. </View>
  268. {this.state.typeOfFeedback == "csf" && <>
  269. <Text style={styles.typeTitle}>Which charging station?</Text>
  270. <Pressable
  271. style={[styles.pickerView, styles.stationView]}
  272. android_ripple={ripple}
  273. onPress={() => this.setState({showDialog: true})}>
  274. <MaterialIcons
  275. name="search"
  276. size={24}
  277. color="#00638C"
  278. />
  279. <Text style={styles.textStation}>{this.state.chargeBoxId}</Text>
  280. </Pressable>
  281. <Text style={styles.typeTitle}>Which connecter?</Text>
  282. <View style={styles.pickerView}>
  283. <Dropdown
  284. style={styles.pickerViewInfo}
  285. title="Select a Connecter"
  286. list={this.state.connectorList}
  287. value={this.state.connectorId}
  288. placeholder="Select"
  289. onChange={value => this.changeConnector(value)}/>
  290. </View>
  291. </>}
  292. <Button
  293. style={styles.button}
  294. text='Submit Feedback'
  295. elevation={1.5}
  296. onClick={() => {
  297. this.submitFeedback();
  298. }}/>
  299. </View>
  300. <Modal
  301. style={{zIndex: 900}}
  302. isVisible={this.state.showDialog}
  303. avoidKeyboard={true}
  304. animationIn={"fadeIn"}
  305. animationOut={"fadeOut"}
  306. useNativeDriver={true}
  307. onBackdropPress={() => this.changeChargeBox()}
  308. onBackButtonPress={() => this.changeChargeBox()}>
  309. <View style={Dialog.styles.modalDialog}>
  310. <View style={[styles.pickerView, styles.stationView]}>
  311. <MaterialIcons
  312. name="search"
  313. size={24}
  314. color="#00638C"
  315. />
  316. <TextInput
  317. style={styles.inputView}
  318. maxLength={50}
  319. placeholder="Searching for ChargeBoxId"
  320. onChangeText={text => this.listChargeBox(text)}
  321. />
  322. </View>
  323. { this.state.searching
  324. ? <View style={[styles.stationList, ui.center]}>
  325. <Image
  326. style={styles.seachingIcon}
  327. source={require('../../images/icon/search-loading.gif')}/>
  328. </View>
  329. : <FlatList
  330. style={styles.stationList}
  331. data={this.state.chargeBoxList}
  332. keyExtractor={(item,index) => index}
  333. renderItem={this.listItem}
  334. ListEmptyComponent={<Text style={styles.noResult}>No Station</Text>}
  335. />
  336. }
  337. </View>
  338. </Modal>
  339. </ScrollView>
  340. );
  341. }
  342. listItem = ({item, index}) => {
  343. return (
  344. <Button
  345. key={index}
  346. text={item}
  347. style={styles.stationItemView}
  348. textStyle={styles.stationItemText}
  349. onClick={() => this.changeChargeBox(item)}
  350. />
  351. )
  352. }
  353. }
  354. const styles = StyleSheet.create({
  355. container: {
  356. flex: 1,
  357. backgroundColor: pageBackground
  358. },
  359. contentView: {
  360. marginTop: -30,
  361. borderTopLeftRadius: 30,
  362. borderTopRightRadius: 30,
  363. ...$padding(8, 16, 16),
  364. backgroundColor: colorLight
  365. },
  366. headerView: {
  367. paddingTop: 16,
  368. paddingLeft: 16,
  369. paddingRight: 8,
  370. paddingBottom: 30,
  371. flexDirection: 'row',
  372. backgroundColor: '#F5F5F5'
  373. },
  374. headerImage: {
  375. width: 123,
  376. height: 101
  377. },
  378. title: {
  379. color: '#056A94',
  380. fontSize: 18,
  381. paddingTop: 16,
  382. paddingBottom: 8
  383. },
  384. content: {
  385. color: '#056A94',
  386. fontSize: 14
  387. },
  388. typeTitle: {
  389. color: '#000',
  390. fontSize: 16,
  391. paddingTop: 16,
  392. fontWeight: 'bold',
  393. paddingBottom: 10
  394. },
  395. stationView: {
  396. paddingLeft: 12,
  397. alignItems: 'center',
  398. flexDirection: 'row'
  399. },
  400. textStation: {
  401. flex: 1,
  402. color: textPrimary,
  403. fontSize: 15,
  404. paddingLeft: 8
  405. },
  406. pickerView: {
  407. //width: $vw(60),
  408. height: 44,
  409. borderWidth: 1,
  410. borderColor: '#999',
  411. borderRadius: 6,
  412. marginTop: 8,
  413. marginBottom: 8,
  414. overflow: 'hidden',
  415. justifyContent: 'center',
  416. backgroundColor: '#F5F5F5'
  417. },
  418. inputView: {
  419. flex: 1,
  420. color: textPrimary,
  421. fontSize: 15
  422. },
  423. pickerViewInfo: {
  424. height: 44,
  425. paddingLeft: 16,
  426. paddingRight: 32,
  427. alignItems: 'center',
  428. flexDirection: 'row'
  429. },
  430. feedbackInput: {
  431. color: textPrimary,
  432. minHeight: 100,
  433. borderWidth: 1,
  434. borderColor: '#999',
  435. borderRadius: 6,
  436. paddingLeft: 10,
  437. paddingRight: 10,
  438. backgroundColor: '#F5F5F5'
  439. },
  440. uploadGroup: {
  441. //paddingTop: 16,
  442. alignItems: 'center',
  443. flexDirection: 'row',
  444. justifyContent: 'center'
  445. },
  446. uploadView: {
  447. flex: 1,
  448. alignItems: 'center'
  449. },
  450. uploadIcon: {
  451. width: $vw(29),
  452. height: $vw(29),
  453. borderRadius: 6
  454. },
  455. button: {
  456. marginTop: 32,
  457. marginBottom: 16,
  458. borderRadius: 4
  459. },
  460. stationList: {
  461. height: $vh(35)
  462. },
  463. noResult: {
  464. color: textCancel,
  465. fontSize: 12,
  466. textAlign: 'center',
  467. paddingTop: 32
  468. },
  469. stationItemView: {
  470. borderRadius: 0,
  471. backgroundColor: colorLight,
  472. borderTopWidth: 1,
  473. borderTopColor: '#F0F0F0'
  474. },
  475. stationItemText: {
  476. flex: 1,
  477. color: textPrimary,
  478. fontSize: 15
  479. },
  480. seachingIcon: {
  481. width: 100,
  482. height: 100
  483. }
  484. })