RuleModal.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { Images } from '@/constants/images';
  2. import { getParamConfig } from '@/services/user';
  3. import { ImageBackground } from 'expo-image';
  4. import React, { forwardRef, useImperativeHandle, useState } from 'react';
  5. import { ActivityIndicator, Dimensions, Modal, StyleSheet, TouchableOpacity, View } from 'react-native';
  6. import { WebView } from 'react-native-webview';
  7. const { width } = Dimensions.get('window');
  8. export interface RuleModalRef {
  9. show: (ruleKey?: string) => void;
  10. close: () => void;
  11. }
  12. export const RuleModal = forwardRef<RuleModalRef, {}>((props, ref) => {
  13. const [visible, setVisible] = useState(false);
  14. const [content, setContent] = useState('');
  15. const [loading, setLoading] = useState(false);
  16. useImperativeHandle(ref, () => ({
  17. show: (ruleKey = 'baoxiang_rule') => {
  18. setVisible(true);
  19. loadRule(ruleKey);
  20. },
  21. close: () => setVisible(false),
  22. }));
  23. const loadRule = async (key: string) => {
  24. setLoading(true);
  25. try {
  26. const res = await getParamConfig(key);
  27. if (res && res.data) {
  28. // Simple HTML wrap
  29. const html = `
  30. <html>
  31. <head>
  32. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  33. <style>
  34. body { font-size: 14px; color: #333; padding: 10px; word-wrap: break-word; }
  35. img { max-width: 100%; height: auto; }
  36. p { margin: 5px 0; }
  37. </style>
  38. </head>
  39. <body>${res.data}</body>
  40. </html>
  41. `;
  42. setContent(html);
  43. }
  44. } catch (e) {
  45. console.error(e);
  46. setContent('<p>加载失败</p>');
  47. } finally {
  48. setLoading(false);
  49. }
  50. };
  51. return (
  52. <Modal visible={visible} transparent animationType="fade" onRequestClose={() => setVisible(false)}>
  53. <View style={styles.overlay}>
  54. <View style={styles.container}>
  55. <ImageBackground
  56. source={{ uri: Images.mine.dialogContentBg }}
  57. style={styles.bg}
  58. resizeMode="stretch"
  59. >
  60. <View style={styles.content}>
  61. {loading ? (
  62. <ActivityIndicator color="#000" style={{ marginTop: 50 }} />
  63. ) : (
  64. <WebView
  65. originWhitelist={['*']}
  66. source={{ html: content }}
  67. style={styles.webview}
  68. showsVerticalScrollIndicator={false}
  69. />
  70. )}
  71. </View>
  72. </ImageBackground>
  73. {/* Close Button Moved to Bottom */}
  74. <TouchableOpacity style={styles.closeBtn} onPress={() => setVisible(false)}>
  75. <ImageBackground source={{ uri: Images.common.closeBut }} style={styles.closeIcon} resizeMode="contain" />
  76. </TouchableOpacity>
  77. </View>
  78. </View>
  79. </Modal>
  80. );
  81. });
  82. const styles = StyleSheet.create({
  83. overlay: {
  84. flex: 1,
  85. backgroundColor: 'rgba(0,0,0,0.6)',
  86. justifyContent: 'center',
  87. alignItems: 'center',
  88. },
  89. container: {
  90. width: width * 0.9,
  91. alignItems: 'center',
  92. },
  93. bg: {
  94. width: '100%',
  95. height: 400, // Adjust height
  96. paddingTop: 60,
  97. paddingHorizontal: 30,
  98. paddingBottom: 30,
  99. marginBottom: 30, // Space for button
  100. },
  101. content: {
  102. flex: 1,
  103. overflow: 'hidden',
  104. },
  105. webview: {
  106. flex: 1,
  107. backgroundColor: 'transparent',
  108. },
  109. closeBtn: {
  110. width: 45,
  111. height: 45,
  112. justifyContent: 'center',
  113. alignItems: 'center',
  114. zIndex: 10,
  115. },
  116. closeIcon: {
  117. width: '100%',
  118. height: '100%',
  119. }
  120. });