index.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import { useRouter } from 'expo-router';
  2. import React, { useState } from 'react';
  3. import {
  4. Alert,
  5. ImageBackground,
  6. ScrollView,
  7. StatusBar,
  8. StyleSheet,
  9. Text,
  10. TextInput,
  11. TouchableOpacity,
  12. View,
  13. } from 'react-native';
  14. import { useSafeAreaInsets } from 'react-native-safe-area-context';
  15. import { Images } from '@/constants/images';
  16. import { submitFeedback } from '@/services/base';
  17. const FEEDBACK_TYPES = ['BUG', '建议', '投诉', '其他'];
  18. export default function FeedbackScreen() {
  19. const router = useRouter();
  20. const insets = useSafeAreaInsets();
  21. const [feedbackType, setFeedbackType] = useState('BUG');
  22. const [feedbackContent, setFeedbackContent] = useState('');
  23. const [loading, setLoading] = useState(false);
  24. const maxContentLength = 3000;
  25. const handleBack = () => {
  26. router.back();
  27. };
  28. const handleSubmit = async () => {
  29. if (!feedbackContent.trim()) {
  30. Alert.alert('提示', '反馈内容是必填的!');
  31. return;
  32. }
  33. try {
  34. setLoading(true);
  35. await submitFeedback({
  36. type: feedbackType,
  37. text: feedbackContent.trim(),
  38. });
  39. Alert.alert('提示', '提交成功!', [
  40. { text: '确定', onPress: () => router.back() }
  41. ]);
  42. } catch (error) {
  43. console.error('提交失败:', error);
  44. Alert.alert('提示', '提交失败');
  45. } finally {
  46. setLoading(false);
  47. }
  48. };
  49. return (
  50. <ImageBackground
  51. source={{ uri: Images.mine.kaixinMineBg }}
  52. style={styles.container}
  53. resizeMode="cover"
  54. >
  55. <StatusBar barStyle="light-content" />
  56. {/* 顶部导航 */}
  57. <View style={[styles.header, { paddingTop: insets.top }]}>
  58. <TouchableOpacity style={styles.backBtn} onPress={handleBack}>
  59. <Text style={styles.backIcon}>‹</Text>
  60. </TouchableOpacity>
  61. <Text style={styles.headerTitle}>意见反馈</Text>
  62. <View style={styles.placeholder} />
  63. </View>
  64. <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
  65. <View style={styles.content}>
  66. <View style={styles.card}>
  67. {/* 类型选择 */}
  68. <View style={styles.typeSelector}>
  69. {FEEDBACK_TYPES.map((type) => (
  70. <TouchableOpacity
  71. key={type}
  72. style={[
  73. styles.typeItem,
  74. feedbackType === type && styles.typeItemActive
  75. ]}
  76. onPress={() => setFeedbackType(type)}
  77. >
  78. <Text style={[
  79. styles.typeText,
  80. feedbackType === type && styles.typeTextActive
  81. ]}>
  82. {type}
  83. </Text>
  84. </TouchableOpacity>
  85. ))}
  86. </View>
  87. {/* 投诉提示 */}
  88. <View style={styles.complaintTip}>
  89. <Text style={styles.complaintTipText}>
  90. 此投诉为本小程序自有投诉渠道,非微信官方投诉渠道
  91. </Text>
  92. </View>
  93. {/* 输入框 */}
  94. <View style={styles.inputWrapper}>
  95. <TextInput
  96. style={styles.input}
  97. value={feedbackContent}
  98. onChangeText={setFeedbackContent}
  99. placeholder="请输入您的反馈内容,我们将及时为您处理。"
  100. placeholderTextColor="#999"
  101. multiline
  102. maxLength={maxContentLength}
  103. />
  104. <View style={styles.inputCount}>
  105. <Text style={[
  106. styles.countNum,
  107. feedbackContent.length > 0 && styles.countNumActive
  108. ]}>
  109. {feedbackContent.length}
  110. </Text>
  111. <Text style={styles.countText}>/{maxContentLength}</Text>
  112. </View>
  113. </View>
  114. {/* 提交按钮 */}
  115. <TouchableOpacity
  116. style={styles.submitBtn}
  117. onPress={handleSubmit}
  118. disabled={loading}
  119. >
  120. <Text style={styles.submitBtnText}>
  121. {loading ? '提交中...' : '提交'}
  122. </Text>
  123. </TouchableOpacity>
  124. </View>
  125. </View>
  126. </ScrollView>
  127. </ImageBackground>
  128. );
  129. }
  130. const styles = StyleSheet.create({
  131. container: {
  132. flex: 1,
  133. },
  134. header: {
  135. flexDirection: 'row',
  136. alignItems: 'center',
  137. justifyContent: 'space-between',
  138. paddingHorizontal: 10,
  139. height: 80,
  140. },
  141. backBtn: {
  142. width: 40,
  143. height: 40,
  144. justifyContent: 'center',
  145. alignItems: 'center',
  146. },
  147. backIcon: {
  148. fontSize: 32,
  149. color: '#fff',
  150. fontWeight: 'bold',
  151. },
  152. headerTitle: {
  153. fontSize: 16,
  154. fontWeight: 'bold',
  155. color: '#fff',
  156. },
  157. placeholder: {
  158. width: 40,
  159. },
  160. scrollView: {
  161. flex: 1,
  162. },
  163. content: {
  164. paddingHorizontal: 15,
  165. paddingTop: 10,
  166. paddingBottom: 30,
  167. },
  168. card: {
  169. backgroundColor: '#fff',
  170. borderRadius: 15,
  171. padding: 15,
  172. },
  173. typeSelector: {
  174. flexDirection: 'row',
  175. marginBottom: 10,
  176. },
  177. typeItem: {
  178. paddingHorizontal: 15,
  179. paddingVertical: 8,
  180. marginRight: 10,
  181. borderRadius: 20,
  182. backgroundColor: '#f8f8f8',
  183. },
  184. typeItemActive: {
  185. backgroundColor: '#FC7D2E',
  186. },
  187. typeText: {
  188. fontSize: 14,
  189. color: '#666',
  190. },
  191. typeTextActive: {
  192. color: '#fff',
  193. },
  194. complaintTip: {
  195. backgroundColor: '#fff7e6',
  196. borderWidth: 1,
  197. borderColor: '#ffd591',
  198. borderRadius: 4,
  199. padding: 10,
  200. marginBottom: 15,
  201. },
  202. complaintTipText: {
  203. fontSize: 13,
  204. color: '#fa8c16',
  205. },
  206. inputWrapper: {
  207. position: 'relative',
  208. marginBottom: 20,
  209. },
  210. input: {
  211. height: 150,
  212. backgroundColor: '#f8f8f8',
  213. borderRadius: 8,
  214. padding: 12,
  215. fontSize: 14,
  216. color: '#333',
  217. textAlignVertical: 'top',
  218. },
  219. inputCount: {
  220. position: 'absolute',
  221. bottom: 10,
  222. right: 10,
  223. flexDirection: 'row',
  224. },
  225. countNum: {
  226. fontSize: 12,
  227. color: '#999',
  228. },
  229. countNumActive: {
  230. color: '#1FA4FF',
  231. },
  232. countText: {
  233. fontSize: 12,
  234. color: '#999',
  235. },
  236. submitBtn: {
  237. backgroundColor: '#FC7D2E',
  238. height: 44,
  239. borderRadius: 22,
  240. justifyContent: 'center',
  241. alignItems: 'center',
  242. },
  243. submitBtnText: {
  244. fontSize: 16,
  245. color: '#fff',
  246. fontWeight: '500',
  247. },
  248. });