巴青农资商城

AgreementBlock.vue 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <view v-if="visible" class="agreement-card">
  3. <view class="agreement-card__inner" @click="toggle">
  4. <view :class="['agreement-card__box', { 'agreement-card__box--on': modelValue }]">
  5. <u-icon v-if="modelValue" name="checkmark" color="#fff" size="12" />
  6. </view>
  7. <view class="agreement-card__text">
  8. <template v-if="useStructured">
  9. <text class="agreement-card__plain">我已阅读并同意</text>
  10. <text
  11. v-if="hasContent"
  12. class="agreement-card__link"
  13. @click.stop="openPopup"
  14. >《{{ agreementTitle }}》</text>
  15. <!-- <text v-if="displayVersion" class="agreement-card__ver">{{ displayVersion }}</text> -->
  16. </template>
  17. <text v-else class="agreement-card__fallback">{{ checkboxLabel }}</text>
  18. </view>
  19. </view>
  20. <!-- <text v-if="hasContent && useStructured" class="agreement-card__action" @click="openPopup">
  21. 查看全文
  22. </text> -->
  23. <u-popup :show="popupShow" mode="bottom" round="16" @close="popupShow = false">
  24. <view class="agreement-popup">
  25. <view class="agreement-popup__head">
  26. <text class="agreement-popup__title">{{ agreementTitle || '服务协议' }}</text>
  27. <u-icon name="close" size="20" @click="popupShow = false" />
  28. </view>
  29. <scroll-view v-if="popupShow" class="agreement-popup__scroll" scroll-y>
  30. <rich-text class="agreement-rich" :nodes="richHtml" />
  31. </scroll-view>
  32. </view>
  33. </u-popup>
  34. </view>
  35. </template>
  36. <script setup>
  37. import { ref, computed } from 'vue'
  38. import { prepareRichHtml } from '@/utils/htmlContent'
  39. const props = defineProps({
  40. modelValue: { type: Boolean, default: false },
  41. checkboxLabel: { type: String, default: '我已阅读并同意相关服务协议' },
  42. agreementTitle: { type: String, default: '' },
  43. versionLabel: { type: String, default: '' },
  44. content: { type: String, default: '' },
  45. enabled: { type: Boolean, default: true }
  46. })
  47. const emit = defineEmits(['update:modelValue'])
  48. const popupShow = ref(false)
  49. const visible = computed(() => props.enabled)
  50. const hasContent = computed(() => !!(props.content || '').trim())
  51. /** 有协议标题时用分行排版,避免整段 checkboxLabel 撑破布局 */
  52. const useStructured = computed(() => !!(props.agreementTitle || '').trim())
  53. /**
  54. * 与商品详情一致用 rich-text 渲染 HTML(平台 Quill 输出)
  55. * up-parse 在 Vue3 下内部仍用 this.$set,易出现标签被当纯文本显示
  56. */
  57. const richHtml = computed(() => prepareRichHtml(props.content))
  58. function toggle() {
  59. emit('update:modelValue', !props.modelValue)
  60. }
  61. function openPopup() {
  62. if (hasContent.value) {
  63. popupShow.value = true
  64. }
  65. }
  66. </script>
  67. <style lang="scss" scoped>
  68. .agreement-card {
  69. margin-top: 24rpx;
  70. // padding: 20rpx 24rpx;
  71. background: #f5faf6;
  72. border: 1rpx solid #e8f5e9;
  73. border-radius: 12rpx;
  74. box-sizing: border-box;
  75. }
  76. .agreement-card__inner {
  77. display: flex;
  78. align-items: flex-start;
  79. }
  80. .agreement-card__box {
  81. width: 36rpx;
  82. height: 36rpx;
  83. margin-top: 2rpx;
  84. margin-right: 16rpx;
  85. border: 2rpx solid #a5d6a7;
  86. border-radius: 8rpx;
  87. display: flex;
  88. align-items: center;
  89. justify-content: center;
  90. flex-shrink: 0;
  91. background: #fff;
  92. }
  93. .agreement-card__box--on {
  94. background: #2e7d32;
  95. border-color: #2e7d32;
  96. }
  97. .agreement-card__text {
  98. flex: 1;
  99. min-width: 0;
  100. display: flex;
  101. flex-wrap: wrap;
  102. align-items: center;
  103. line-height: 40rpx;
  104. }
  105. .agreement-card__plain,
  106. .agreement-card__ver {
  107. font-size: 24rpx;
  108. color: #666;
  109. }
  110. .agreement-card__ver {
  111. margin-left: 4rpx;
  112. }
  113. .agreement-card__link {
  114. font-size: 24rpx;
  115. color: #2e7d32;
  116. font-weight: 500;
  117. text-decoration: underline;
  118. }
  119. .agreement-card__fallback {
  120. font-size: 24rpx;
  121. color: #666;
  122. line-height: 1.6;
  123. word-break: break-all;
  124. white-space: normal;
  125. }
  126. .agreement-card__action {
  127. display: block;
  128. margin-top: 12rpx;
  129. margin-left: 52rpx;
  130. font-size: 24rpx;
  131. color: #43a047;
  132. }
  133. .agreement-popup {
  134. max-height: 70vh;
  135. display: flex;
  136. flex-direction: column;
  137. }
  138. .agreement-popup__head {
  139. display: flex;
  140. align-items: center;
  141. justify-content: space-between;
  142. padding: 28rpx 32rpx;
  143. border-bottom: 1rpx solid #eee;
  144. }
  145. .agreement-popup__title {
  146. flex: 1;
  147. margin-right: 16rpx;
  148. font-size: 30rpx;
  149. font-weight: 600;
  150. color: #333;
  151. }
  152. .agreement-popup__scroll {
  153. max-height: 60vh;
  154. padding: 24rpx 32rpx 48rpx;
  155. box-sizing: border-box;
  156. }
  157. .agreement-rich {
  158. display: block;
  159. width: 100%;
  160. font-size: 28rpx;
  161. color: #444;
  162. line-height: 1.7;
  163. word-break: break-word;
  164. }
  165. </style>