From 0cd54ba8ec767aa7f9aa2a30ccedb261d54287b2 Mon Sep 17 00:00:00 2001 From: tx <2622874537@qq.com> Date: Tue, 31 Dec 2024 13:59:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B8=AE=E5=8A=A9=E4=B8=AD=E5=BF=83=E5=B7=B2?= =?UTF-8?q?=E7=BB=8Fxml=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/navigation/HomeStack.tsx | 8 ++ src/navigation/types.tsx | 1 + src/utils/api.ts | 3 + src/views/Home/NormaIndex.tsx | 12 ++- src/views/user/HelpCenterPage.tsx | 125 ++++++++++++++++++++++++------ src/views/user/XmlRenderPage.tsx | 112 ++++++++++++++++++++++++++ 6 files changed, 234 insertions(+), 27 deletions(-) create mode 100644 src/views/user/XmlRenderPage.tsx diff --git a/src/navigation/HomeStack.tsx b/src/navigation/HomeStack.tsx index 447cb42..f8d2968 100644 --- a/src/navigation/HomeStack.tsx +++ b/src/navigation/HomeStack.tsx @@ -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(); const createScreenOptions = (title: string): StackNavigationOptions => { @@ -82,6 +83,13 @@ export default function HomeStackNavigator({ navigation, route }: Props) { headerShown: false, }} /> + 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 }), }; \ No newline at end of file diff --git a/src/views/Home/NormaIndex.tsx b/src/views/Home/NormaIndex.tsx index 333f734..5703a05 100644 --- a/src/views/Home/NormaIndex.tsx +++ b/src/views/Home/NormaIndex.tsx @@ -169,6 +169,9 @@ const NormaIndex: React.FC = () => { } }; + const toXmlRenderPage = () => { + navigation.navigate('XmlRenderPage', { id: 28 }); + }; return ( { 剩余里程 - + {/* 110 km @@ -224,7 +227,7 @@ const NormaIndex: React.FC = () => { /> 剩余里程 - + */} @@ -252,10 +255,12 @@ const NormaIndex: React.FC = () => { /> + + /> + )} @@ -461,6 +466,7 @@ const styles = StyleSheet.create({ }, KmLi_tits: { + marginLeft: rpx(10), paddingBottom: rpx(10), fontSize: rpx(24), color: '#3D3D3D', diff --git a/src/views/user/HelpCenterPage.tsx b/src/views/user/HelpCenterPage.tsx index fceb6a1..84f4ee8 100644 --- a/src/views/user/HelpCenterPage.tsx +++ b/src/views/user/HelpCenterPage.tsx @@ -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) => ( @@ -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 ( {wordlist.map((item, index) => { - console.log('渲染列表项:', item.title); // 添加日志 + console.log('渲染列表项:', item.title); return ( { ); }; + 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 ( { onPress={() => changeTab(index, item.classifyId)} > {item.classifyName} - {/* {wordlist[0].title} */} ))} @@ -228,10 +305,10 @@ const HelpCenterPage = ({ navigation }: { navigation: any }) => { activeOpacity={1} onPress={() => setShowQrcode(false)} > - + 微信客服 - - + + 保存二维码 { + ); }; const styles = StyleSheet.create({ - wordListWrapper: { - flex: 1, - width: '100%', - }, + wordListWrapper: { + flex: 1, + width: '100%', + }, background: { flex: 1, }, @@ -374,27 +452,26 @@ const styles = StyleSheet.create({ }, wordListContainer: { marginTop: rpx(20), - paddingHorizontal: rpx(20), // 添加水平内边距 + paddingHorizontal: rpx(20), }, - qsLi: { + qsLi: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', 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), diff --git a/src/views/user/XmlRenderPage.tsx b/src/views/user/XmlRenderPage.tsx new file mode 100644 index 0000000..77df7d3 --- /dev/null +++ b/src/views/user/XmlRenderPage.tsx @@ -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 => ( + +); + +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 = () => ( + + ); + + const replaceImgWithImage = (content) => { + // 替换所有的 为 \u00A0 + content = content.replace(/ /g, '\u00A0'); + + // 替换 标签的宽度和高度样式 + content = content.replace(/]*)>/g, (match, group1) => { + // 查找并移除可能存在的尾部斜杠 + let cleanedGroup = group1.replace(/\s*\/$/, ''); + return ``; + }); + + 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 ( + + + {loading ? ( + + ) : ( + + )} + + ); +}; + +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; \ No newline at end of file