Feedback.js 13 KB

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