agreement.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <template>
  2. <view class="agreement-container">
  3. <!-- <view class="agreement-header">
  4. <view class="back-btn" @click="goBack">
  5. <text class="iconfont icon-back">&#xe679;</text>
  6. </view>
  7. <view class="title">用户协议</view>
  8. <view class="placeholder"></view>
  9. </view> -->
  10. <scroll-view scroll-y class="agreement-content">
  11. <!-- <view class="agreement-title">《用户协议》</view> -->
  12. <!-- 使用v-if控制加载状态 -->
  13. <view v-if="loading" class="loading">
  14. <text>加载中...</text>
  15. </view>
  16. <!-- 使用v-else-if显示错误信息 -->
  17. <view v-else-if="error" class="error">
  18. <text>{{errorMsg}}</text>
  19. <button class="retry-btn" @click="fetchAgreement">重新加载</button>
  20. </view>
  21. <!-- 使用v-else和v-html动态渲染协议内容 -->
  22. <view v-else class="markdown-content" v-html="parsedContent"></view>
  23. </scroll-view>
  24. </view>
  25. </template>
  26. <script>
  27. import { getUserAgreement } from '@/api/user';
  28. import { apiBaseUrl } from '@/common/config.js';
  29. export default {
  30. data() {
  31. return {
  32. agreementContent: '',
  33. loading: true,
  34. error: false,
  35. errorMsg: '加载失败,请重试'
  36. }
  37. },
  38. computed: {
  39. parsedContent() {
  40. if (!this.agreementContent) return '';
  41. return this.parseMarkdown(this.agreementContent);
  42. }
  43. },
  44. onLoad() {
  45. this.fetchAgreement();
  46. },
  47. methods: {
  48. // 简单的 markdown 解析函数
  49. parseMarkdown(markdown) {
  50. if (!markdown) return '';
  51. let html = markdown
  52. // 标题解析
  53. .replace(/^### (.*$)/gim, '<h3>$1</h3>')
  54. .replace(/^## (.*$)/gim, '<h2>$1</h2>')
  55. .replace(/^# (.*$)/gim, '<h1>$1</h1>')
  56. // 粗体
  57. .replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
  58. // 斜体
  59. .replace(/\*(.*)\*/gim, '<em>$1</em>')
  60. // 链接
  61. .replace(/\[([^\]]*)\]\(([^\)]*)\)/gim, '<a href="$2">$1</a>')
  62. // 代码块
  63. .replace(/```(.*?)```/gims, '<pre><code>$1</code></pre>')
  64. // 行内代码
  65. .replace(/`([^`]*)`/gim, '<code>$1</code>')
  66. // 列表项
  67. .replace(/^\* (.*$)/gim, '<li>$1</li>')
  68. .replace(/^\d+\. (.*$)/gim, '<li>$1</li>')
  69. // 换行处理
  70. .replace(/\n\n/gim, '</p><p>')
  71. .replace(/\n/gim, '<br>');
  72. // 包装列表
  73. html = html.replace(/(<li>.*<\/li>)/gim, '<ul>$1</ul>');
  74. // 包装段落
  75. if (html && !html.startsWith('<h') && !html.startsWith('<ul') && !html.startsWith('<ol')) {
  76. html = '<p>' + html + '</p>';
  77. }
  78. return html;
  79. },
  80. goBack() {
  81. uni.navigateBack();
  82. },
  83. async fetchAgreement() {
  84. this.loading = true;
  85. this.error = false;
  86. const res = await uni.request({
  87. url: `${apiBaseUrl}/api/public/agreements/terms_of_service/`,
  88. method: 'GET',
  89. });
  90. console.log(res);
  91. if(res.statusCode==200){
  92. this.loading = false;
  93. this.agreementContent = res.data.content || '';
  94. }
  95. /* getUserAgreement()
  96. .then(res => {
  97. console.log(res);
  98. this.loading = false;
  99. // 假设API返回的数据中有content字段包含协议内容
  100. this.agreementContent = res.content || '';
  101. console.log(this.agreementContent);
  102. })
  103. .catch(err => {
  104. console.error('获取用户协议失败:', err);
  105. this.error = true;
  106. this.errorMsg = '网络异常,请稍后重试';
  107. })
  108. .finally(() => {
  109. this.loading = false;
  110. }); */
  111. }
  112. }
  113. }
  114. </script>
  115. <style>
  116. .agreement-container {
  117. /* display: flex;
  118. flex-direction: column; */
  119. height: 100vh;
  120. background-color: #fff;
  121. }
  122. .agreement-header {
  123. display: flex;
  124. align-items: center;
  125. justify-content: space-between;
  126. height: 90rpx;
  127. background-color: #fff;
  128. padding: 0 30rpx;
  129. position: relative;
  130. border-bottom: 1rpx solid #eee;
  131. }
  132. .back-btn {
  133. width: 60rpx;
  134. height: 60rpx;
  135. display: flex;
  136. align-items: center;
  137. justify-content: center;
  138. }
  139. .icon-back {
  140. font-size: 40rpx;
  141. color: #333;
  142. }
  143. .title {
  144. font-size: 34rpx;
  145. font-weight: 500;
  146. color: #333;
  147. }
  148. .placeholder {
  149. width: 60rpx;
  150. }
  151. .agreement-content {
  152. width: 100%;
  153. padding: 20rpx 30rpx;
  154. box-sizing: border-box;
  155. background-color: #fff;
  156. }
  157. .agreement-title {
  158. font-size: 36rpx;
  159. font-weight: bold;
  160. text-align: center;
  161. margin-bottom: 40rpx;
  162. color: #333;
  163. }
  164. .section {
  165. margin-bottom: 30rpx;
  166. }
  167. .section-title {
  168. font-size: 32rpx;
  169. font-weight: 500;
  170. color: #333;
  171. margin-bottom: 20rpx;
  172. }
  173. .section-content {
  174. font-size: 28rpx;
  175. color: #666;
  176. line-height: 1.6;
  177. }
  178. .paragraph {
  179. display: block;
  180. margin-bottom: 20rpx;
  181. text-align: justify;
  182. }
  183. .loading, .error {
  184. display: flex;
  185. flex-direction: column;
  186. align-items: center;
  187. justify-content: center;
  188. padding: 40rpx;
  189. color: #666;
  190. font-size: 28rpx;
  191. }
  192. .error {
  193. color: #ff4d4f;
  194. }
  195. .retry-btn {
  196. margin-top: 20rpx;
  197. padding: 10rpx 30rpx;
  198. background-color: #1890ff;
  199. color: #fff;
  200. border-radius: 8rpx;
  201. font-size: 28rpx;
  202. }
  203. /* Markdown 样式 */
  204. .markdown-content {
  205. font-size: 28rpx;
  206. line-height: 1.6;
  207. color: #333;
  208. }
  209. .markdown-content h1 {
  210. font-size: 36rpx;
  211. font-weight: bold;
  212. margin: 30rpx 0 20rpx 0;
  213. color: #333;
  214. }
  215. .markdown-content h2 {
  216. font-size: 32rpx;
  217. font-weight: bold;
  218. margin: 25rpx 0 15rpx 0;
  219. color: #333;
  220. }
  221. .markdown-content h3 {
  222. font-size: 30rpx;
  223. font-weight: bold;
  224. margin: 20rpx 0 10rpx 0;
  225. color: #333;
  226. }
  227. .markdown-content p {
  228. margin-bottom: 20rpx;
  229. text-align: justify;
  230. }
  231. .markdown-content strong {
  232. font-weight: bold;
  233. color: #333;
  234. }
  235. .markdown-content em {
  236. font-style: italic;
  237. }
  238. .markdown-content a {
  239. color: #1890ff;
  240. text-decoration: underline;
  241. }
  242. .markdown-content code {
  243. background-color: #f5f5f5;
  244. padding: 2rpx 8rpx;
  245. border-radius: 4rpx;
  246. font-family: 'Courier New', monospace;
  247. font-size: 24rpx;
  248. }
  249. .markdown-content pre {
  250. background-color: #f5f5f5;
  251. padding: 20rpx;
  252. border-radius: 8rpx;
  253. margin: 20rpx 0;
  254. overflow-x: auto;
  255. }
  256. .markdown-content pre code {
  257. background: none;
  258. padding: 0;
  259. font-size: 24rpx;
  260. line-height: 1.4;
  261. }
  262. .markdown-content ul {
  263. margin: 20rpx 0;
  264. padding-left: 40rpx;
  265. }
  266. .markdown-content li {
  267. margin-bottom: 10rpx;
  268. list-style-type: disc;
  269. }
  270. </style>