Feedback.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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. defaultSource={require('../../images/icon/icon-upload-default.png')}
  264. source={{uri: utils.getImageUrl(item)}}/>
  265. }
  266. </Pressable>
  267. );
  268. })
  269. }
  270. </View>
  271. {this.state.typeOfFeedback == "csf" && <>
  272. <Text style={styles.typeTitle}>{$t('feedback.labelStation')}</Text>
  273. <Button
  274. style={styles.pickerView}
  275. viewStyle={[styles.stationView, ui.flex1]}
  276. onClick={() => this.setState({showDialog: true})}>
  277. <MaterialIcons
  278. name="search"
  279. size={24}
  280. color="#00638C"
  281. />
  282. <Text style={styles.textStation}>{this.state.chargeBoxId}</Text>
  283. </Button>
  284. <Text style={styles.typeTitle}>{$t('feedback.labelConnector')}</Text>
  285. <View style={styles.pickerView}>
  286. <Dropdown
  287. style={styles.pickerViewInfo}
  288. title={$t('feedback.selectConnector')}
  289. list={this.state.connectorList}
  290. value={this.state.connectorId}
  291. placeholder={$t('common.select')}
  292. onChange={value => this.changeConnector(value)}/>
  293. </View>
  294. </>}
  295. <Button
  296. style={styles.button}
  297. text={$t('feedback.submitFeedback')}
  298. elevation={1.5}
  299. onClick={() => {
  300. this.submitFeedback();
  301. }}/>
  302. </View>
  303. <MyModal
  304. visible={this.state.showDialog}
  305. onLayerPress={() => this.changeChargeBox()}>
  306. <View style={$padding(8, 0)}>
  307. <View style={styles.searchView}>
  308. <MaterialIcons
  309. name="search"
  310. size={24}
  311. color="#00638C"
  312. />
  313. <TextInput
  314. style={styles.inputView}
  315. maxLength={50}
  316. placeholder={$t('feedback.searchingChargeBox')}
  317. onChangeText={text => this.listChargeBox(text)}
  318. />
  319. </View>
  320. { this.state.searching
  321. ? <VbeSkeleton
  322. style={styles.stationList}
  323. layout={[
  324. {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
  325. {width: "80%", height: 20, ...$margin(14, 0, 14, 16)},
  326. {width: "80%", height: 20, ...$margin(14, 0, 14, 16)}
  327. ]}/>
  328. : <FlatList
  329. style={styles.stationList}
  330. data={this.state.chargeBoxList}
  331. keyExtractor={(item,index) => index}
  332. renderItem={this.listItem}
  333. ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
  334. />
  335. }
  336. </View>
  337. </MyModal>
  338. </ScrollView>
  339. );
  340. }
  341. listItem = ({item, index}) => {
  342. return (
  343. <Button
  344. key={index}
  345. text={item}
  346. style={styles.stationItemView}
  347. textStyle={styles.stationItemText}
  348. onClick={() => this.changeChargeBox(item)}
  349. />
  350. )
  351. }
  352. }
  353. const styles = StyleSheet.create({
  354. container: {
  355. flex: 1,
  356. backgroundColor: pageBackground
  357. },
  358. contentView: {
  359. marginTop: -30,
  360. borderTopLeftRadius: 30,
  361. borderTopRightRadius: 30,
  362. ...$padding(8, 16, 16),
  363. backgroundColor: colorLight
  364. },
  365. headerView: {
  366. paddingTop: 16,
  367. paddingLeft: 16,
  368. paddingRight: 8,
  369. paddingBottom: 30,
  370. flexDirection: 'row',
  371. backgroundColor: '#F5F5F5'
  372. },
  373. headerImage: {
  374. width: 123,
  375. height: 101
  376. },
  377. title: {
  378. color: '#056A94',
  379. fontSize: 18,
  380. paddingTop: 16,
  381. paddingBottom: 8
  382. },
  383. content: {
  384. color: '#056A94',
  385. fontSize: 14
  386. },
  387. typeTitle: {
  388. color: '#000',
  389. fontSize: 16,
  390. paddingTop: 16,
  391. fontWeight: 'bold',
  392. paddingBottom: 10
  393. },
  394. stationView: {
  395. paddingLeft: 12,
  396. alignItems: 'center',
  397. flexDirection: 'row'
  398. },
  399. textStation: {
  400. flex: 1,
  401. color: textPrimary,
  402. fontSize: 15,
  403. paddingLeft: 8
  404. },
  405. pickerView: {
  406. //width: $vw(60),
  407. height: 44,
  408. borderWidth: 1,
  409. borderColor: '#999',
  410. borderRadius: 6,
  411. overflow: 'hidden',
  412. justifyContent: 'center',
  413. backgroundColor: '#F5F5F5'
  414. },
  415. inputView: {
  416. flex: 1,
  417. color: textPrimary,
  418. fontSize: 15
  419. },
  420. pickerViewInfo: {
  421. height: 44,
  422. paddingLeft: 16,
  423. paddingRight: 8,
  424. alignItems: 'center',
  425. flexDirection: 'row'
  426. },
  427. searchView: {
  428. height: 44,
  429. paddingLeft: 12,
  430. borderWidth: 1,
  431. borderColor: '#999',
  432. borderRadius: 6,
  433. ...$margin(8, 16, 16),
  434. overflow: 'hidden',
  435. alignItems: 'center',
  436. flexDirection: 'row',
  437. backgroundColor: '#F5F5F5'
  438. },
  439. feedbackInput: {
  440. color: textPrimary,
  441. minHeight: 100,
  442. borderWidth: 1,
  443. borderColor: '#999',
  444. borderRadius: 6,
  445. paddingLeft: 10,
  446. paddingRight: 10,
  447. backgroundColor: '#F5F5F5'
  448. },
  449. uploadGroup: {
  450. //paddingTop: 16,
  451. alignItems: 'center',
  452. flexDirection: 'row',
  453. justifyContent: 'center'
  454. },
  455. uploadView: {
  456. flex: 1,
  457. alignItems: 'center'
  458. },
  459. uploadIcon: {
  460. width: $vw(29),
  461. height: $vw(29),
  462. borderRadius: 6
  463. },
  464. button: {
  465. marginTop: 32,
  466. marginBottom: 16,
  467. borderRadius: 4
  468. },
  469. stationList: {
  470. height: $vh(35)
  471. },
  472. noResult: {
  473. color: textCancel,
  474. fontSize: 12,
  475. textAlign: 'center',
  476. paddingTop: 32
  477. },
  478. stationItemView: {
  479. borderRadius: 0,
  480. backgroundColor: colorLight,
  481. borderTopWidth: 1,
  482. borderTopColor: '#F0F0F0'
  483. },
  484. stationItemText: {
  485. flex: 1,
  486. color: textPrimary,
  487. fontSize: 15
  488. },
  489. seachingIcon: {
  490. width: 50,
  491. height: 50,
  492. marginTop: 20
  493. }
  494. })