RuleModal.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
  2. import {
  3. Dimensions,
  4. ImageBackground,
  5. Modal,
  6. ScrollView,
  7. StyleSheet,
  8. Text,
  9. TouchableOpacity,
  10. View,
  11. } from 'react-native';
  12. import { Images } from '@/constants/images';
  13. import { getParamConfig } from '@/services/user';
  14. const { width: SCREEN_WIDTH } = Dimensions.get('window');
  15. export interface RuleModalRef {
  16. show: () => void;
  17. close: () => void;
  18. }
  19. export const RuleModal = forwardRef<RuleModalRef>((_, ref) => {
  20. const [visible, setVisible] = useState(false);
  21. const [rules, setRules] = useState('');
  22. useImperativeHandle(ref, () => ({
  23. show: () => {
  24. setVisible(true);
  25. loadRules();
  26. },
  27. close: () => setVisible(false),
  28. }));
  29. const loadRules = async () => {
  30. try {
  31. const res = await getParamConfig('baoxiang_rule');
  32. // 增强清洗:移除 style/script,去除 HTML 标签,并压缩多余空行
  33. const rawData = res?.data || '';
  34. const text = rawData
  35. .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
  36. .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
  37. .replace(/<[^>]+>/g, '')
  38. .replace(/&nbsp;/g, ' ')
  39. .replace(/\n\s*\n/g, '\n') // 将多个空行压缩为一个
  40. .trim() || '平台发货零门槛,全国统一运费15元/单,满五件享快递包邮服务,默认5个工作日内完成发货。';
  41. setRules(text);
  42. } catch (error) {
  43. console.error('加载规则失败:', error);
  44. setRules('平台发货零门槛,全国统一运费15元/单,满五件享快递包邮服务,默认5个工作日内完成发货。');
  45. }
  46. };
  47. useEffect(() => {
  48. if (visible && !rules) loadRules();
  49. }, [visible]);
  50. return (
  51. <Modal visible={visible} transparent animationType="fade" onRequestClose={() => setVisible(false)}>
  52. <View style={styles.overlay}>
  53. <View style={styles.container}>
  54. {/* 内容区域 */}
  55. <ImageBackground
  56. source={{ uri: Images.mine.dialogContentBg }}
  57. style={styles.contentBg}
  58. resizeMode="stretch"
  59. >
  60. <Text style={styles.title}>玩法规则</Text>
  61. <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
  62. <Text style={styles.ruleText}>{rules}</Text>
  63. <Text style={[styles.ruleText, { marginTop: 15, color: '#999', fontSize: 12 }]}>特别声明:本抽奖活动及相关福利由艾斯潮盒官方发起,与苹果公司(Apple Inc.)无关。苹果公司不是活动的赞助商,也没有以任何形式参与。</Text>
  64. </ScrollView>
  65. </ImageBackground>
  66. {/* 关闭按钮 - 移到下方 */}
  67. <TouchableOpacity onPress={() => setVisible(false)} style={styles.closeBtn}>
  68. <ImageBackground source={{ uri: Images.common.closeBut }} style={styles.closeIcon} resizeMode="contain" />
  69. </TouchableOpacity>
  70. </View>
  71. </View>
  72. </Modal>
  73. );
  74. });
  75. const styles = StyleSheet.create({
  76. overlay: {
  77. flex: 1,
  78. backgroundColor: 'rgba(0,0,0,0.6)',
  79. justifyContent: 'center',
  80. alignItems: 'center',
  81. },
  82. container: {
  83. width: '100%',
  84. alignItems: 'center',
  85. justifyContent: 'center',
  86. },
  87. contentBg: {
  88. width: SCREEN_WIDTH * 0.85,
  89. height: SCREEN_WIDTH * 0.85 * 1.25,
  90. paddingTop: 85,
  91. paddingHorizontal: 35,
  92. paddingBottom: 50,
  93. justifyContent: 'flex-start',
  94. marginBottom: 30, // 给下方按钮留出空间
  95. },
  96. closeBtn: {
  97. width: 45,
  98. height: 45,
  99. justifyContent: 'center',
  100. alignItems: 'center',
  101. zIndex: 10,
  102. },
  103. closeIcon: {
  104. width: '100%',
  105. height: '100%',
  106. },
  107. title: {
  108. fontSize: 18,
  109. fontWeight: 'bold',
  110. color: '#333',
  111. textAlign: 'center',
  112. marginBottom: 15,
  113. },
  114. scrollView: {
  115. flex: 1,
  116. },
  117. ruleText: {
  118. fontSize: 14,
  119. color: '#333',
  120. lineHeight: 20, // 降低行间距
  121. },
  122. });