RuleModal.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { Images } from '@/constants/images';
  2. import { getParamConfig } from '@/services/user';
  3. import { Image, 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. <View style={styles.header}>
  56. <TouchableOpacity style={styles.closeBtn} onPress={() => setVisible(false)}>
  57. <Image source={{ uri: Images.common.closeBut }} style={styles.closeIcon} />
  58. </TouchableOpacity>
  59. </View>
  60. <ImageBackground
  61. source={{ uri: Images.mine.dialogContentBg }}
  62. style={styles.bg}
  63. resizeMode="stretch"
  64. >
  65. <View style={styles.content}>
  66. {loading ? (
  67. <ActivityIndicator color="#000" style={{ marginTop: 50 }} />
  68. ) : (
  69. <WebView
  70. originWhitelist={['*']}
  71. source={{ html: content }}
  72. style={styles.webview}
  73. showsVerticalScrollIndicator={false}
  74. />
  75. )}
  76. </View>
  77. </ImageBackground>
  78. </View>
  79. </View>
  80. </Modal>
  81. );
  82. });
  83. const styles = StyleSheet.create({
  84. overlay: {
  85. flex: 1,
  86. backgroundColor: 'rgba(0,0,0,0.6)',
  87. justifyContent: 'center',
  88. alignItems: 'center',
  89. },
  90. container: {
  91. width: width * 0.9,
  92. alignItems: 'center',
  93. },
  94. header: {
  95. width: '100%',
  96. alignItems: 'flex-end',
  97. marginBottom: -20, // Overlap
  98. zIndex: 10,
  99. paddingRight: 20,
  100. },
  101. closeBtn: {
  102. padding: 10,
  103. },
  104. closeIcon: {
  105. width: 30,
  106. height: 30,
  107. },
  108. bg: {
  109. width: '100%',
  110. height: 400, // Adjust height
  111. paddingTop: 60,
  112. paddingHorizontal: 30,
  113. paddingBottom: 30,
  114. },
  115. content: {
  116. flex: 1,
  117. overflow: 'hidden',
  118. },
  119. webview: {
  120. flex: 1,
  121. backgroundColor: 'transparent',
  122. }
  123. });