Переглянути джерело

refactor(policy): 优化隐私政策和用户协议页面样式和内容展示

- 重新设计了页面布局和样式,使其更加美观和易读
- 增加了对内容的格式化处理,支持更好的文本排版
- 添加了响应式设计,确保在不同设备上都能良好显示
- 优化了错误处理,提高了页面的健壮性
fugui001 4 місяців тому
батько
коміт
627ad46a5f

+ 151 - 13
src/views/system/business/ofService/indexPreview.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="tos-container">
     <el-card>
-      <div v-html="tosContent"></div>
+      <div class="tos-content" v-html="tosContent"></div>
     </el-card>
   </div>
 </template>
@@ -22,18 +22,37 @@ const fetchTos = async () => {
   try {
     const res = await getTermsServiceListMax();
     // 处理获取到的内容,确保格式良好
-    const formattedContent = handleLineBreaks(res.data.content);
+    const formattedContent = handleContentFormatting(res.data.content);
     tosContent.value = DOMPurify.sanitize(formattedContent);
   } catch (error) {
     console.error('获取服务条款失败', error);
+    tosContent.value = '<p>服务条款内容加载失败</p>';
   }
 };
 
-// 确保文本内容中适当的换行符
-const handleLineBreaks = (content: string): string => {
-  let formattedContent = content.replace(/\s+/g, ' ');
-  // 在每80个非空白字符后插入一个<br>标签以强制换行
-  formattedContent = formattedContent.replace(/(\S{80})/g, '$1<br>');
+// 完善内容格式化处理
+const handleContentFormatting = (content: string): string => {
+  if (!content) return '<p>暂无内容</p>';
+
+  // 移除多余的空白字符,但保留必要的换行
+  let formattedContent = content
+    .replace(/\r\n/g, '\n')
+    .replace(/\r/g, '\n')
+    .replace(/\n\s*\n/g, '\n\n') // 合并多个空行为两个换行
+    .trim();
+
+  // 如果内容已经是HTML格式(包含标签),则不进行额外处理
+  if (/<[^>]+>/.test(formattedContent)) {
+    return formattedContent;
+  }
+
+  // 如果是纯文本,转换为基本的HTML格式
+  // 将连续的换行转换为段落
+  formattedContent = formattedContent
+    .split('\n\n')
+    .map((paragraph) => `<p>${paragraph.replace(/\n/g, '<br>')}</p>`)
+    .join('');
+
   return formattedContent;
 };
 </script>
@@ -41,23 +60,142 @@ const handleLineBreaks = (content: string): string => {
 <style scoped>
 .tos-container {
   padding: 20px;
-  word-break: break-word; /* 强制长单词换行 */
+  max-width: 800px;
+  margin: 0 auto;
 }
 
-/* 针对卡片内部内容的样式 */
-.tos-container div {
+.tos-content {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.6;
+  color: #333;
   word-break: break-word;
 }
 
+/* 标题样式 */
+.tos-content h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 24px 0 16px 0;
+  color: #2c3e50;
+  text-align: center;
+}
+
+.tos-content h2 {
+  font-size: 20px;
+  font-weight: bold;
+  margin: 20px 0 12px 0;
+  color: #34495e;
+  border-bottom: 1px solid #eee;
+  padding-bottom: 8px;
+}
+
+.tos-content h3 {
+  font-size: 18px;
+  font-weight: bold;
+  margin: 16px 0 10px 0;
+  color: #555;
+}
+
+/* 段落样式 */
+.tos-content p {
+  margin: 12px 0;
+  line-height: 1.7;
+}
+
+/* 列表样式 */
+.tos-content ul,
+.tos-content ol {
+  margin: 12px 0;
+  padding-left: 24px;
+}
+
+.tos-content li {
+  margin: 8px 0;
+  line-height: 1.6;
+}
+
+.tos-content ul li {
+  list-style-type: disc;
+}
+
+.tos-content ol li {
+  list-style-type: decimal;
+}
+
+/* 强调文本 */
+.tos-content strong {
+  font-weight: bold;
+  color: #2c3e50;
+}
+
+.tos-content em {
+  font-style: italic;
+}
+
+/* 链接样式 */
+.tos-content a {
+  color: #3498db;
+  text-decoration: none;
+}
+
+.tos-content a:hover {
+  text-decoration: underline;
+}
+
+/* 代码样式 */
+.tos-content code {
+  background-color: #f8f9fa;
+  padding: 2px 4px;
+  border-radius: 3px;
+  font-family: 'Courier New', monospace;
+  font-size: 13px;
+}
+
+/* 引用样式 */
+.tos-content blockquote {
+  border-left: 4px solid #ddd;
+  margin: 16px 0;
+  padding: 8px 16px;
+  background-color: #f9f9f9;
+  color: #666;
+}
+
 /* 响应式设计 */
 @media (max-width: 768px) {
   .tos-container {
-    padding: 15px; /* 减少内边距 */
+    padding: 15px;
+  }
+
+  .tos-content {
+    font-size: 13px;
+  }
+
+  .tos-content h1 {
+    font-size: 20px;
+    margin: 20px 0 14px 0;
+  }
+
+  .tos-content h2 {
+    font-size: 18px;
+    margin: 18px 0 10px 0;
+  }
+
+  .tos-content h3 {
+    font-size: 16px;
+    margin: 14px 0 8px 0;
   }
 
   .el-card {
-    width: 100%; /* 占满父容器宽度 */
-    margin: 0 auto; /* 居中对齐 */
+    width: 100%;
+    margin: 0 auto;
   }
 }
+
+/* 处理可能的空白内容 */
+.tos-content:empty::before {
+  content: "暂无内容";
+  color: #999;
+  font-style: italic;
+}
 </style>

+ 159 - 22
src/views/system/business/policy/indexPolicyPreview.vue

@@ -1,7 +1,7 @@
 <template>
-  <div class="tos-container">
+  <div class="policy-container">
     <el-card>
-      <div v-html="tosContent"></div>
+      <div class="policy-content" v-html="policyContent"></div>
     </el-card>
   </div>
 </template>
@@ -11,49 +11,186 @@ import { ref, onMounted } from 'vue';
 import DOMPurify from 'dompurify';
 import { getPrivacyPolicyListMax } from '@/api/system/business/policy';
 
-const tosContent = ref('');
-const language = ref('zh-CN');
+const policyContent = ref('');
 
 onMounted(() => {
-  fetchTos();
+  fetchPolicy();
 });
 
-const fetchTos = async () => {
+const fetchPolicy = async () => {
   try {
     const res = await getPrivacyPolicyListMax();
-    tosContent.value = DOMPurify.sanitize(handleLineBreaks(res.data.content));
+    policyContent.value = DOMPurify.sanitize(res.data.content || '<p>暂无隐私政策内容</p>');
   } catch (error) {
-    console.error('获取服务条款失败', error);
+    console.error('获取隐私政策失败', error);
+    policyContent.value = '<p>隐私政策内容加载失败</p>';
   }
 };
-
-const handleLineBreaks = (content: string): string => {
-  let formattedContent = content.replace(/\s+/g, ' ');
-  formattedContent = formattedContent.replace(/(\S{80})/g, '$1<br>');
-  return formattedContent;
-};
 </script>
 
 <style scoped>
-.tos-container {
+.policy-container {
   padding: 20px;
-  word-break: break-all;
-  white-space: pre-wrap;
+  max-width: 800px;
+  margin: 0 auto;
+}
+
+.policy-content {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.6;
+  color: #333;
+  word-wrap: break-word;
+}
+
+/* 标题样式 */
+.policy-content h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 24px 0 16px 0;
+  color: #2c3e50;
+  text-align: center;
+}
+
+.policy-content h2 {
+  font-size: 20px;
+  font-weight: bold;
+  margin: 20px 0 12px 0;
+  color: #34495e;
+  border-bottom: 1px solid #eee;
+  padding-bottom: 8px;
+}
+
+.policy-content h3 {
+  font-size: 18px;
+  font-weight: bold;
+  margin: 16px 0 10px 0;
+  color: #555;
+}
+
+/* 段落样式 */
+.policy-content p {
+  margin: 12px 0;
+  line-height: 1.7;
+}
+
+/* 列表样式 */
+.policy-content ul,
+.policy-content ol {
+  margin: 12px 0;
+  padding-left: 24px;
+}
+
+.policy-content li {
+  margin: 8px 0;
+  line-height: 1.6;
+}
+
+.policy-content ul li {
+  list-style-type: disc;
+}
+
+.policy-content ol li {
+  list-style-type: decimal;
+}
+
+/* 强调文本 */
+.policy-content strong {
+  font-weight: bold;
+  color: #2c3e50;
+}
+
+.policy-content em {
+  font-style: italic;
 }
 
-.tos-container div {
-  word-break: break-all;
-  white-space: pre-wrap;
+/* 链接样式 */
+.policy-content a {
+  color: #3498db;
+  text-decoration: none;
 }
 
+.policy-content a:hover {
+  text-decoration: underline;
+}
+
+/* 代码样式 */
+.policy-content code {
+  background-color: #f8f9fa;
+  padding: 2px 4px;
+  border-radius: 3px;
+  font-family: 'Courier New', monospace;
+  font-size: 13px;
+}
+
+/* 引用样式 */
+.policy-content blockquote {
+  border-left: 4px solid #ddd;
+  margin: 16px 0;
+  padding: 8px 16px;
+  background-color: #f9f9f9;
+  color: #666;
+}
+
+/* 表格样式 */
+.policy-content table {
+  width: 100%;
+  border-collapse: collapse;
+  margin: 16px 0;
+}
+
+.policy-content table th,
+.policy-content table td {
+  border: 1px solid #ddd;
+  padding: 8px 12px;
+  text-align: left;
+}
+
+.policy-content table th {
+  background-color: #f2f2f2;
+  font-weight: bold;
+}
+
+/* 响应式设计 */
 @media (max-width: 768px) {
-  .tos-container {
-    padding: 10px;
+  .policy-container {
+    padding: 15px;
+  }
+
+  .policy-content {
+    font-size: 13px;
+  }
+
+  .policy-content h1 {
+    font-size: 20px;
+    margin: 20px 0 14px 0;
+  }
+
+  .policy-content h2 {
+    font-size: 18px;
+    margin: 18px 0 10px 0;
+  }
+
+  .policy-content h3 {
+    font-size: 16px;
+    margin: 14px 0 8px 0;
   }
 
   .el-card {
     width: 100%;
     margin: 0 auto;
   }
+
+  .policy-content ul,
+  .policy-content ol {
+    padding-left: 20px;
+  }
+}
+
+/* 处理可能的空白内容 */
+.policy-content:empty::before {
+  content: "暂无隐私政策内容";
+  color: #999;
+  font-style: italic;
 }
 </style>