Add dedicated Help screen to profile tab with navigation integration.
Refactor SettingsScreen to navigate to HelpScreen instead of showing placeholder alert.
Fix API upload method to use correct field names ("file" for documents, "image" for media)
based on endpoint path, resolving upload failures for file uploads.
398 lines
12 KiB
TypeScript
398 lines
12 KiB
TypeScript
/**
|
||
* 帮助与反馈页面 HelpScreen
|
||
* 威友 - 常见问题、官方联系方式
|
||
* 扁平化设计风格,可折叠问答,参考关于我们、设置页面
|
||
*/
|
||
|
||
import React, { useMemo, useState, useCallback } from 'react';
|
||
import {
|
||
View,
|
||
StyleSheet,
|
||
TouchableOpacity,
|
||
ScrollView,
|
||
Linking,
|
||
Alert,
|
||
} from 'react-native';
|
||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||
import { StatusBar } from 'expo-status-bar';
|
||
import { useRouter } from 'expo-router';
|
||
import {
|
||
useAppColors,
|
||
spacing,
|
||
fontSizes,
|
||
borderRadius,
|
||
type AppColors,
|
||
} from '../../theme';
|
||
import { Text, SimpleHeader } from '../../components/common';
|
||
import { useResponsive, useResponsiveSpacing } from '../../hooks';
|
||
|
||
const CONTENT_MAX_WIDTH = 720;
|
||
|
||
// 威友橙主题色
|
||
const THEME_COLORS = {
|
||
primary: '#FF6B35',
|
||
primaryLight: '#FFF1EA',
|
||
};
|
||
|
||
// 官方联系邮箱
|
||
const CONTACT_EMAIL = 'system@qczlit.cn';
|
||
|
||
interface FAQItem {
|
||
q: string;
|
||
a: string;
|
||
/** 补充提示(如注意事项),与正文区分展示 */
|
||
note?: string;
|
||
}
|
||
|
||
interface FAQSection {
|
||
key: string;
|
||
title: string;
|
||
icon: string;
|
||
items: FAQItem[];
|
||
}
|
||
|
||
// 常见问题内容
|
||
const FAQ_SECTIONS: FAQSection[] = [
|
||
{
|
||
key: 'account',
|
||
title: '账号与认证',
|
||
icon: 'account-check-outline',
|
||
items: [
|
||
{
|
||
q: '如何通过身份认证?',
|
||
a: '进入「我的」页面,点击右上角齿轮状设置图标,选择「身份认证」。按提示填写学号/工号、真实姓名,并上传有效佐证材料。提交后通常等待 1-2 小时即可完成审核。',
|
||
},
|
||
{
|
||
q: '忘记密码怎么找回?',
|
||
a: '在登录页点击「忘记密码」,通过已绑定的手机号或校内邮箱进行重置。若均未绑定,请联系客服人工处理。',
|
||
},
|
||
{
|
||
q: '如何绑定手机号?',
|
||
a: '进入「我的」页面,点击「编辑资料」,选择「手机号」,输入手机号码并完成验证即可绑定。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: 'campus',
|
||
title: '校园工具',
|
||
icon: 'school-outline',
|
||
items: [
|
||
{
|
||
q: '如何查看课表?',
|
||
a: '进入「应用」页面,点击「课表」,点击左上角齿轮状设置图标,选择「同步教务系统」。输入学号/工号及教务系统密码即可同步。',
|
||
note: '该密码与统一身份认证密码不同。如遗忘,可登录新教务系统,进入「个人中心」→「修改密码」进行重置,默认密码为身份证号码后 6 位。',
|
||
},
|
||
{
|
||
q: '如何查找学习资料?',
|
||
a: '进入「应用」页面,点击「学习资料」,可按学科分类浏览,也可点击右上角搜索图标快速查找所需资料。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: 'notification',
|
||
title: '消息与通知',
|
||
icon: 'bell-outline',
|
||
items: [
|
||
{
|
||
q: '如何及时收到消息通知?',
|
||
a: '请先在手机系统「设置」→「通知管理」或「应用管理」中,找到本 App,开启通知权限及自启动/后台运行权限;然后进入 App 内「我的」页面,点击右上角齿轮状设置图标,进入「通知管理」,确认所需消息提醒均已开启。设置完成后重启 App 即可生效。',
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
function createHelpStyles(colors: AppColors) {
|
||
return StyleSheet.create({
|
||
container: {
|
||
flex: 1,
|
||
backgroundColor: colors.background.paper,
|
||
},
|
||
scrollContent: {
|
||
paddingVertical: spacing.lg,
|
||
},
|
||
content: {
|
||
maxWidth: CONTENT_MAX_WIDTH,
|
||
alignSelf: 'center',
|
||
width: '100%',
|
||
},
|
||
|
||
// 分组
|
||
section: {
|
||
marginBottom: spacing.xl,
|
||
},
|
||
sectionHeader: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
paddingHorizontal: spacing['2xl'],
|
||
marginBottom: spacing.sm,
|
||
marginTop: spacing.sm,
|
||
},
|
||
sectionIcon: {
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: borderRadius.sm,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
marginRight: spacing.sm,
|
||
},
|
||
sectionTitle: {
|
||
fontWeight: '600',
|
||
fontSize: fontSizes.sm,
|
||
color: colors.text.secondary,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: 0.5,
|
||
},
|
||
|
||
// 问答卡片
|
||
faqList: {
|
||
backgroundColor: colors.background.paper,
|
||
borderTopWidth: StyleSheet.hairlineWidth,
|
||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||
borderColor: colors.divider,
|
||
overflow: 'hidden',
|
||
},
|
||
faqItem: {
|
||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||
borderBottomColor: colors.divider,
|
||
},
|
||
faqItemLast: {
|
||
borderBottomWidth: 0,
|
||
},
|
||
faqQuestion: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
paddingVertical: 16,
|
||
paddingHorizontal: spacing['2xl'],
|
||
},
|
||
questionText: {
|
||
flex: 1,
|
||
fontSize: fontSizes.lg,
|
||
fontWeight: '500',
|
||
color: colors.text.primary,
|
||
lineHeight: fontSizes.lg * 1.4,
|
||
},
|
||
faqAnswer: {
|
||
paddingHorizontal: spacing['2xl'],
|
||
paddingBottom: 16,
|
||
},
|
||
answerText: {
|
||
fontSize: fontSizes.md,
|
||
lineHeight: fontSizes.md * 1.7,
|
||
color: colors.text.secondary,
|
||
},
|
||
|
||
// 补充提示
|
||
noteContainer: {
|
||
flexDirection: 'row',
|
||
alignItems: 'flex-start',
|
||
marginTop: spacing.md,
|
||
padding: spacing.md,
|
||
backgroundColor: THEME_COLORS.primaryLight,
|
||
borderRadius: borderRadius.md,
|
||
gap: spacing.sm,
|
||
},
|
||
noteText: {
|
||
flex: 1,
|
||
fontSize: fontSizes.sm,
|
||
lineHeight: fontSizes.sm * 1.6,
|
||
color: colors.text.secondary,
|
||
},
|
||
|
||
// 联系我们
|
||
contactCard: {
|
||
marginHorizontal: spacing['2xl'],
|
||
marginTop: spacing.sm,
|
||
padding: spacing.xl,
|
||
borderRadius: borderRadius.xl,
|
||
backgroundColor: colors.background.default,
|
||
alignItems: 'center',
|
||
},
|
||
contactIconWrap: {
|
||
width: 52,
|
||
height: 52,
|
||
borderRadius: borderRadius.full,
|
||
backgroundColor: THEME_COLORS.primary,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
marginBottom: spacing.md,
|
||
},
|
||
contactTitle: {
|
||
fontSize: fontSizes.xl,
|
||
fontWeight: '600',
|
||
color: colors.text.primary,
|
||
marginBottom: spacing.xs,
|
||
},
|
||
contactDesc: {
|
||
fontSize: fontSizes.md,
|
||
color: colors.text.secondary,
|
||
textAlign: 'center',
|
||
marginBottom: spacing.lg,
|
||
lineHeight: fontSizes.md * 1.6,
|
||
},
|
||
contactButton: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
paddingVertical: spacing.md,
|
||
paddingHorizontal: spacing.xl,
|
||
borderRadius: borderRadius.full,
|
||
backgroundColor: THEME_COLORS.primary,
|
||
gap: spacing.sm,
|
||
},
|
||
contactButtonText: {
|
||
fontSize: fontSizes.md,
|
||
fontWeight: '600',
|
||
color: '#fff',
|
||
},
|
||
|
||
// 底部版权
|
||
footer: {
|
||
alignItems: 'center',
|
||
marginTop: spacing.xl,
|
||
paddingBottom: spacing.xl,
|
||
},
|
||
footerText: {
|
||
fontSize: fontSizes.sm,
|
||
color: colors.text.hint,
|
||
textAlign: 'center',
|
||
},
|
||
});
|
||
}
|
||
|
||
export const HelpScreen: React.FC = () => {
|
||
const colors = useAppColors();
|
||
const styles = useMemo(() => createHelpStyles(colors), [colors]);
|
||
const insets = useSafeAreaInsets();
|
||
const router = useRouter();
|
||
const { isMobile } = useResponsive();
|
||
const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 });
|
||
|
||
// 当前展开的问题 key(同一时刻仅展开一个)
|
||
const [expandedKey, setExpandedKey] = useState<string | null>(
|
||
`${FAQ_SECTIONS[0].key}-0`,
|
||
);
|
||
|
||
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
|
||
|
||
const toggleItem = useCallback((key: string) => {
|
||
setExpandedKey((prev) => (prev === key ? null : key));
|
||
}, []);
|
||
|
||
const handleContactEmail = useCallback(async () => {
|
||
const url = `mailto:${CONTACT_EMAIL}`;
|
||
try {
|
||
const canOpen = await Linking.canOpenURL(url);
|
||
if (canOpen) {
|
||
await Linking.openURL(url);
|
||
} else {
|
||
Alert.alert('联系我们', `请发送邮件至:${CONTACT_EMAIL}`);
|
||
}
|
||
} catch {
|
||
Alert.alert('联系我们', `请发送邮件至:${CONTACT_EMAIL}`);
|
||
}
|
||
}, []);
|
||
|
||
const renderFAQItem = (
|
||
section: FAQSection,
|
||
item: FAQItem,
|
||
index: number,
|
||
) => {
|
||
const key = `${section.key}-${index}`;
|
||
const isExpanded = expandedKey === key;
|
||
const isLast = index === section.items.length - 1;
|
||
|
||
return (
|
||
<View
|
||
key={key}
|
||
style={[styles.faqItem, isLast && styles.faqItemLast]}
|
||
>
|
||
<TouchableOpacity
|
||
style={styles.faqQuestion}
|
||
onPress={() => toggleItem(key)}
|
||
activeOpacity={0.7}
|
||
>
|
||
<Text style={styles.questionText}>{item.q}</Text>
|
||
<MaterialCommunityIcons
|
||
name={isExpanded ? 'chevron-up' : 'chevron-down'}
|
||
size={20}
|
||
color={colors.text.hint}
|
||
/>
|
||
</TouchableOpacity>
|
||
{isExpanded && (
|
||
<View style={styles.faqAnswer}>
|
||
<Text style={styles.answerText}>{item.a}</Text>
|
||
{item.note && (
|
||
<View style={styles.noteContainer}>
|
||
<MaterialCommunityIcons
|
||
name="alert-circle-outline"
|
||
size={16}
|
||
color={THEME_COLORS.primary}
|
||
/>
|
||
<Text style={styles.noteText}>{item.note}</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
)}
|
||
</View>
|
||
);
|
||
};
|
||
|
||
const renderSection = (section: FAQSection) => (
|
||
<View key={section.key} style={styles.section}>
|
||
<View style={styles.sectionHeader}>
|
||
<View style={[styles.sectionIcon, { backgroundColor: THEME_COLORS.primaryLight }]}>
|
||
<MaterialCommunityIcons name={section.icon as any} size={14} color={THEME_COLORS.primary} />
|
||
</View>
|
||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
||
</View>
|
||
<View style={styles.faqList}>
|
||
{section.items.map((item, index) => renderFAQItem(section, item, index))}
|
||
</View>
|
||
</View>
|
||
);
|
||
|
||
return (
|
||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||
<StatusBar style="auto" />
|
||
<SimpleHeader title="帮助与反馈" onBack={() => router.back()} />
|
||
<ScrollView
|
||
contentContainerStyle={[
|
||
styles.scrollContent,
|
||
{ paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding },
|
||
]}
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
<View style={styles.content}>
|
||
{FAQ_SECTIONS.map(renderSection)}
|
||
|
||
{/* 联系我们 */}
|
||
<View style={styles.contactCard}>
|
||
<View style={styles.contactIconWrap}>
|
||
<MaterialCommunityIcons name="email-outline" size={26} color="#fff" />
|
||
</View>
|
||
<Text style={styles.contactTitle}>没有找到答案?</Text>
|
||
<Text style={styles.contactDesc}>
|
||
如果以上内容未能解决您的问题,欢迎通过邮件与官方团队联系,我们会尽快为您处理。
|
||
</Text>
|
||
<TouchableOpacity
|
||
style={styles.contactButton}
|
||
onPress={handleContactEmail}
|
||
activeOpacity={0.8}
|
||
>
|
||
<MaterialCommunityIcons name="email-fast-outline" size={18} color="#fff" />
|
||
<Text style={styles.contactButtonText}>{CONTACT_EMAIL}</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
<View style={styles.footer}>
|
||
<Text style={styles.footerText}>© 2026 青春之旅电子信息科技(威海)有限公司</Text>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
);
|
||
};
|
||
|
||
export default HelpScreen;
|