帮助中心已经xml文档

This commit is contained in:
tx 2024-12-31 13:59:12 +08:00
parent 52c771c22a
commit 0cd54ba8ec
6 changed files with 234 additions and 27 deletions

View File

@ -19,6 +19,7 @@ import ShareDetailScreen from '../views/device/KeyDetail';
import ExpiredKeysScreen from '../views/device/ExpiredKeysScreen';
import deviceDetailSet from '../views/device/deviceDetailSet';
import HelpCenterPage from '../views/user/HelpCenterPage';
import XmlRenderPage from '../views/user/XmlRenderPage';
const Stack = createStackNavigator<HomeStackParamList>();
const createScreenOptions = (title: string): StackNavigationOptions => {
@ -82,6 +83,13 @@ export default function HomeStackNavigator({ navigation, route }: Props) {
headerShown: false,
}}
/>
<Stack.Screen
name="XmlRenderPage"
component={XmlRenderPage}
options={{
headerShown: false,
}}
/>
<Stack.Screen
name="HelpCenterPage"
component={HelpCenterPage}

View File

@ -32,6 +32,7 @@ export type HomeStackParamList = {
ExpiredKeysScreen: undefined;
deviceDetailSet: undefined;
HelpCenterPage: undefined;
XmlRenderPage: undefined;
};
// 导航属性类型

View File

@ -140,5 +140,8 @@ export const apiService = {
getArticleList: (classifyId: string) => api.get('/app/article/list?classifyId=' + classifyId),
// 获取客服电话
getServerPhone: () => api.get('/app/getServerPhone'),
getArticleById: (id: string) => api.get('/app/article/' + id),
// updateKeyExpiration: (keyId: string, expirationTime: string) => api.put('/appVerify/updateKeyExpiration', { keyId, expirationTime }),
};

View File

@ -169,6 +169,9 @@ const NormaIndex: React.FC = () => {
}
};
const toXmlRenderPage = () => {
navigation.navigate('XmlRenderPage', { id: 28 });
};
return (
<View style={styles.pageContainer}>
<StatusBar
@ -212,7 +215,7 @@ const NormaIndex: React.FC = () => {
<Text style={styles.KmLi_tits1}></Text>
</View>
</View>
<View style={styles.KmLi}>
{/* <View style={styles.KmLi}>
<View style={styles.KmLi_top}>
<Text style={styles.titTxt}>110</Text>
<Text style={styles.KmLi_tits}>km</Text>
@ -224,7 +227,7 @@ const NormaIndex: React.FC = () => {
/>
<Text style={styles.KmLi_tits1}></Text>
</View>
</View>
</View> */}
</View>
<DeviceControl defaultDevice={defaultDevice} onDeviceUpdate={handleDeviceUpdate} />
@ -252,10 +255,12 @@ const NormaIndex: React.FC = () => {
/>
</TouchableOpacity>
<TouchableOpacity onPress={toXmlRenderPage}>
<Image
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/u849NsNxdtzxhUkUJnfW' }}
style={styles.otherImg}
/>
</TouchableOpacity>
</View>
</>
)}
@ -461,6 +466,7 @@ const styles = StyleSheet.create({
},
KmLi_tits: {
marginLeft: rpx(10),
paddingBottom: rpx(10),
fontSize: rpx(24),
color: '#3D3D3D',

View File

@ -1,8 +1,12 @@
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, ScrollView, Image, TouchableOpacity, Alert, Modal, Text, ImageBackground } from 'react-native';
import React, { useState, useEffect, useRef } from 'react';
import { View, StyleSheet, ScrollView, Image, TouchableOpacity, Modal, Text, ImageBackground, Platform, NativeModules, Linking } from 'react-native';
import { TopNavigation, TopNavigationAction, Icon, Button } from '@ui-kitten/components';
import { apiService } from '../../utils/api';
import { rpx } from '../../utils/rpx';
import RNFS from 'react-native-fs';
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
import { captureRef } from 'react-native-view-shot';
import Toast from 'react-native-toast-message';
const BackIcon = (props: any) => (
<Icon {...props} name='arrow-back' />
@ -15,21 +19,22 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
const [wordlist, setWordlist] = useState([]);
const [tabindex, setTabindex] = useState(0);
const [isLoading, setIsLoading] = useState(true);
const qrRef = useRef();
useEffect(() => {
const initData = async () => {
try {
setIsLoading(true);
const classifyResponse = await apiService.getClassifyList();
console.log('分类数据:', classifyResponse); // 添加日志
console.log('分类数据:', classifyResponse);
if (classifyResponse?.data?.length > 0) {
setClassifyList(classifyResponse.data);
const firstClassifyId = classifyResponse.data[0].classifyId;
console.log('第一个分类ID:', firstClassifyId); // 添加日志
console.log('第一个分类ID:', firstClassifyId);
const articleResponse = await apiService.getArticleList(firstClassifyId);
console.log('文章列表数据:', articleResponse); // 添加日志
console.log('文章列表数据:', articleResponse);
if (articleResponse?.rows) {
setWordlist(articleResponse.rows);
@ -66,9 +71,9 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
try {
setIsLoading(true);
console.log('获取文章列表分类ID:', id); // 添加日志
console.log('获取文章列表分类ID:', id);
const response = await apiService.getArticleList(id);
console.log('获取到的文章列表:', response); // 添加日志
console.log('获取到的文章列表:', response);
if (response?.rows) {
setWordlist(response.rows);
@ -88,7 +93,14 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
};
const callPhone = () => {
Alert.alert('拨打电话', `拨打电话: ${phone.serverPhone}`);
const phoneNumber = phone.serverPhone;
if (phoneNumber) {
Linking.openURL(`tel:${phoneNumber}`).catch(err =>
console.error('拨打电话失败:', err)
);
} else {
showToast('电话号码无效', 'error');
}
};
const changeTab = (index: number, id: string) => {
@ -97,11 +109,11 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
};
const topage = (item: any) => {
navigation.navigate('ArticlePage', { id: item.articleId });
navigation.navigate('XmlRenderPage', { id: item.articleId });
};
const renderWordList = () => {
console.log('当前wordlist数据:', wordlist); // 添加日志
console.log('当前wordlist数据:', wordlist);
if (isLoading) {
return (
@ -122,7 +134,7 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
return (
<View style={styles.wordListContainer}>
{wordlist.map((item, index) => {
console.log('渲染列表项:', item.title); // 添加日志
console.log('渲染列表项:', item.title);
return (
<TouchableOpacity
key={item.articleId || index}
@ -143,6 +155,72 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
);
};
const saveWechatQrcode = async () => {
try {
const writePermission = await request(
Platform.select({
android: PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
default: PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
})
);
if (writePermission !== RESULTS.GRANTED) {
showToast('请在设置中授予存储权限', 'error');
return;
}
const fileName = `wechat_qrcode_${Date.now()}.jpg`;
const tempPath = `${RNFS.CachesDirectoryPath}/${fileName}`;
const uri = await captureRef(qrRef, {
format: 'jpg',
quality: 1,
result: 'base64',
});
await RNFS.writeFile(tempPath, uri, 'base64');
await Promise.race([
new Promise((resolve, reject) => {
NativeModules.MediaScanner.scanFile(
tempPath,
'image/jpeg',
(error) => {
if (error) {
console.warn('MediaScanner warning:', error);
resolve(true);
} else {
resolve(true);
}
}
);
}),
new Promise((resolve) => setTimeout(resolve, 3000))
]);
showToast('二维码已保存到相册', 'success');
setShowQrcode(false); // 确保遮罩关闭
RNFS.unlink(tempPath).catch(e => {
console.warn('清理临时文件失败:', e);
});
} catch (error) {
console.error('Save error:', error);
showToast('文件可能已保存到相册,请检查', 'info');
}
};
const showToast = (message: string, type: 'success' | 'error' | 'info' = 'info') => {
Toast.show({
type,
text1: message,
position: 'top',
topOffset: Platform.OS === 'ios' ? 60 : 20,
visibilityTime: 2000,
});
};
return (
<ImageBackground
source={{ uri: 'https://lxnapi.ccttiot.com/bike/img/static/uYRs7Cv2Pbp95w3KjGO3' }}
@ -210,7 +288,6 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
onPress={() => changeTab(index, item.classifyId)}
>
<Text style={styles.txt}>{item.classifyName}</Text>
{/* <Text style={styles.txt}>{wordlist[0].title}</Text> */}
<View style={[styles.botBor, tabindex === index && styles.botBorActive]}></View>
</TouchableOpacity>
))}
@ -228,10 +305,10 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
activeOpacity={1}
onPress={() => setShowQrcode(false)}
>
<View style={styles.qrcodeBox}>
<View style={styles.qrcodeBox} >
<Text style={styles.qrcodeTitle}></Text>
<Image source={{ uri: phone.serverWx }} style={styles.qrcodeImg} />
<TouchableOpacity style={styles.qrcodeBtn}>
<Image source={{ uri: phone.serverWx }} style={styles.qrcodeImg} ref={qrRef}/>
<TouchableOpacity style={styles.qrcodeBtn} onPress={saveWechatQrcode}>
<Text style={styles.qrcodeBtnText}></Text>
</TouchableOpacity>
<TouchableOpacity
@ -244,6 +321,7 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => {
</TouchableOpacity>
</Modal>
</View>
<Toast />
</ImageBackground>
);
};
@ -374,7 +452,7 @@ const styles = StyleSheet.create({
},
wordListContainer: {
marginTop: rpx(20),
paddingHorizontal: rpx(20), // 添加水平内边距
paddingHorizontal: rpx(20),
},
qsLi: {
flexDirection: 'row',
@ -383,18 +461,17 @@ const styles = StyleSheet.create({
borderBottomWidth: rpx(2),
borderBottomColor: 'rgba(216, 216, 216, 0.44)',
paddingVertical: rpx(26),
// 移除 width: '100%'
},
lastQsLi: {
borderBottomWidth: rpx(2),
borderBottomColor: '#FFFFFF',
},
qsLiTxt: {
flex: 1, // 让文本占据剩余空间
flex: 1,
fontWeight: '400',
fontSize: rpx(28),
color: '#3D3D3D',
marginRight: rpx(20), // 改用 marginRight 替代 paddingRight
marginRight: rpx(20),
},
icon: {
width: rpx(32),

View File

@ -0,0 +1,112 @@
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';
import { TopNavigation, TopNavigationAction, Icon, IconElement } from '@ui-kitten/components';
import { useRoute, useNavigation } from '@react-navigation/native';
import { apiService } from '../../utils/api';
import { rpx } from '../../utils/rpx';
const BackIcon = (props): IconElement => (
<Icon
{...props}
name='arrow-back'
fill='#000000'
/>
);
const XmlRenderPage = () => {
const navigation = useNavigation();
const route = useRoute();
const [xmlContent, setXmlContent] = useState('');
const [title, setTitle] = useState('详情');
const [loading, setLoading] = useState(true);
const { id } = route.params;
const navigateBack = () => {
navigation.goBack();
};
const BackAction = () => (
<TopNavigationAction
icon={BackIcon}
onPress={navigateBack}
/>
);
const replaceImgWithImage = (content) => {
// 替换所有的&nbsp;为 \u00A0
content = content.replace(/&nbsp;/g, '\u00A0');
// 替换 <img> 标签的宽度和高度样式
content = content.replace(/<img([^>]*)>/g, (match, group1) => {
// 查找并移除可能存在的尾部斜杠
let cleanedGroup = group1.replace(/\s*\/$/, '');
return `<img style="width: 85vw; height: auto;" ${cleanedGroup} />`;
});
return content;
};
const fetchArticleById = async () => {
console.log(id, 'id');
const response = await apiService.getArticleById(id);
console.log(response, '1111111111111');
const modifiedContent = replaceImgWithImage(response.data.content);
setXmlContent(modifiedContent);
setTitle(response.data.title || '详情');
setLoading(false);
}
useEffect(() => {
fetchArticleById();
}, []);
return (
<View style={styles.container}>
<TopNavigation
title={title}
alignment='center'
accessoryLeft={BackAction}
style={styles.header}
/>
{loading ? (
<ActivityIndicator size="large" color="#0000ff" style={styles.loading} />
) : (
<WebView
originWhitelist={['*']}
source={{ html: xmlContent }}
style={styles.webview}
scalesPageToFit={false}
injectedJavaScript={`
const meta = document.createElement('meta');
meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1');
meta.setAttribute('name', 'viewport');
document.getElementsByTagName('head')[0].appendChild(meta);
`}
/>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
backgroundColor: 'transparent',
elevation: 0,
shadowOpacity: 0,
},
webview: {
width: rpx(750),
flex: 1,
},
loading: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default XmlRenderPage;