爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
7.4 KiB

2 weeks ago
  1. <template>
  2. <!-- 回收规则弹窗 -->
  3. <view v-if="showRulePopup" class="rule-popup-mask" @click.self="handleRulePopupMaskClick">
  4. <view class="rule-popup">
  5. <view class="rule-popup-title">{{ ruleTitle }}</view>
  6. <scroll-view class="rule-popup-content" scroll-y @scroll="onRuleContentScroll"
  7. @scrolltolower="onRuleScrollToLower">
  8. <uv-parse :content="ruleHtml" @ready="onRuleContentReady"></uv-parse>
  9. <view class="rule-content-bottom-indicator"></view>
  10. </scroll-view>
  11. <view v-if="!hasScrolledToBottom" class="scroll-tip">请滚动到底部阅读完整内容</view>
  12. <button class="rule-popup-btn" :class="{ disabled: !hasScrolledToBottom }" :disabled="!hasScrolledToBottom"
  13. @click.stop="closeRulePopup">我知道了</button>
  14. </view>
  15. </view>
  16. <!-- 预约上门取件弹窗 -->
  17. <view v-if="showPickupConfirm" class="pickup-confirm-mask">
  18. <view class="pickup-confirm-popup">
  19. <view class="pickup-confirm-title">{{ confirmTitle }}</view>
  20. <view class="pickup-confirm-content">
  21. <uv-parse :content="confirmContent"></uv-parse>
  22. </view>
  23. <view class="pickup-confirm-btn-row">
  24. <button class="pickup-confirm-btn" @click="handlePickupCancel">{{ cancelText }}</button>
  25. <button class="pickup-confirm-btn agree" @click="handlePickupAgree">{{ confirmText }}</button>
  26. </view>
  27. </view>
  28. </view>
  29. </template>
  30. <script>
  31. export default {
  32. name: 'RulePopup',
  33. props: {
  34. // 回收规则弹窗标题
  35. ruleTitle: {
  36. type: String,
  37. default: '回收规则'
  38. },
  39. // 确认弹窗标题
  40. confirmTitle: {
  41. type: String,
  42. default: '温馨提示'
  43. },
  44. // 确认弹窗内容
  45. confirmContent: {
  46. type: String,
  47. default: ''
  48. },
  49. // 取消按钮文本
  50. cancelText: {
  51. type: String,
  52. default: '取消回收'
  53. },
  54. // 确认按钮文本
  55. confirmText: {
  56. type: String,
  57. default: '我同意'
  58. }
  59. },
  60. data() {
  61. return {
  62. showRulePopup: false,
  63. showPickupConfirm: false,
  64. ruleHtml: '',
  65. hasScrolledToBottom: false
  66. }
  67. },
  68. methods: {
  69. // 打开回收规则弹窗
  70. openRulePopup(ruleContent = '') {
  71. this.ruleHtml = ruleContent || '<p>暂无回收规则</p>'
  72. this.hasScrolledToBottom = false
  73. this.showRulePopup = true
  74. // 弹窗显示后主动判断内容是否需要滚动
  75. this.$nextTick(() => {
  76. const query = uni.createSelectorQuery().in(this)
  77. query.select('.rule-popup-content').boundingClientRect(rect => {
  78. if (rect && rect.height && rect.scrollHeight && rect.scrollHeight <= rect.height + 10) {
  79. this.hasScrolledToBottom = true
  80. }
  81. }).exec()
  82. })
  83. },
  84. // 打开确认弹窗
  85. openConfirmPopup() {
  86. this.showPickupConfirm = true
  87. },
  88. // 关闭回收规则弹窗
  89. closeRulePopup() {
  90. if (!this.hasScrolledToBottom) {
  91. uni.showToast({
  92. title: '请阅读完整回收规则',
  93. icon: 'none'
  94. })
  95. return
  96. }
  97. this.showRulePopup = false
  98. this.hasScrolledToBottom = false
  99. this.$emit('rule-confirm')
  100. },
  101. // 关闭确认弹窗
  102. closeConfirmPopup() {
  103. this.showPickupConfirm = false
  104. },
  105. // 处理规则弹窗遮罩点击
  106. handleRulePopupMaskClick() {
  107. if (!this.hasScrolledToBottom) {
  108. uni.showToast({
  109. title: '请阅读完整回收规则',
  110. icon: 'none'
  111. })
  112. return
  113. }
  114. this.closeRulePopup()
  115. },
  116. // 处理取消回收
  117. handlePickupCancel() {
  118. this.closeConfirmPopup()
  119. this.$emit('pickup-cancel')
  120. },
  121. // 处理确认回收
  122. handlePickupAgree() {
  123. this.closeConfirmPopup()
  124. this.$emit('pickup-confirm')
  125. },
  126. // 监听规则内容滚动
  127. onRuleContentScroll(e) {
  128. const { scrollTop, scrollHeight, clientHeight, height } = e.detail
  129. const h = clientHeight || height
  130. // 内容高度不够,无需滚动,直接允许
  131. if (scrollHeight <= h + 10) {
  132. this.hasScrolledToBottom = true
  133. return
  134. }
  135. if (scrollTop + h >= scrollHeight - 20) {
  136. this.hasScrolledToBottom = true
  137. }
  138. },
  139. // 规则内容滚动到底部
  140. onRuleScrollToLower() {
  141. this.hasScrolledToBottom = true
  142. },
  143. // 规则内容准备完成
  144. onRuleContentReady() {
  145. this.$nextTick(() => {
  146. const query = uni.createSelectorQuery().in(this)
  147. query.select('.rule-popup-content').boundingClientRect(rect => {
  148. query.select('.rule-popup-content').scrollOffset(scroll => {
  149. // 只有内容高度小于等于可视高度时才点亮
  150. if (scroll.scrollHeight <= rect.height + 1) {
  151. this.hasScrolledToBottom = true
  152. }
  153. })
  154. }).exec()
  155. })
  156. }
  157. }
  158. }
  159. </script>
  160. <style lang="scss" scoped>
  161. .rule-popup-mask {
  162. position: fixed;
  163. left: 0;
  164. right: 0;
  165. top: 0;
  166. bottom: 0;
  167. background: rgba(0, 0, 0, 0.35);
  168. z-index: 4000;
  169. display: flex;
  170. align-items: center;
  171. justify-content: center;
  172. }
  173. .rule-popup {
  174. width: 95vw;
  175. max-width: 750rpx;
  176. max-height: 85vh;
  177. background: #fff;
  178. border-radius: 48rpx;
  179. box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
  180. display: flex;
  181. flex-direction: column;
  182. align-items: center;
  183. position: relative;
  184. padding-bottom: 40rpx;
  185. }
  186. .rule-popup-title {
  187. font-size: 36rpx;
  188. color: #222;
  189. font-weight: bold;
  190. text-align: center;
  191. margin-top: 48rpx;
  192. margin-bottom: 16rpx;
  193. }
  194. .rule-popup-content {
  195. width: 100%;
  196. max-height: 60vh;
  197. min-height: 400rpx;
  198. padding: 0 40rpx;
  199. box-sizing: border-box;
  200. overflow-y: auto;
  201. scrollbar-width: none;
  202. /* Firefox */
  203. -ms-overflow-style: none;
  204. /* IE and Edge */
  205. &::-webkit-scrollbar {
  206. width: 0 !important;
  207. display: none;
  208. /* Chrome, Safari, Opera */
  209. }
  210. }
  211. .rule-content-bottom-indicator {
  212. height: 20rpx;
  213. width: 100%;
  214. }
  215. .scroll-tip {
  216. font-size: 24rpx;
  217. color: #ff6b35;
  218. text-align: center;
  219. margin: 16rpx 0 8rpx 0;
  220. animation: tipPulse 2s infinite;
  221. }
  222. @keyframes tipPulse {
  223. 0%,
  224. 100% {
  225. opacity: 1;
  226. }
  227. 50% {
  228. opacity: 0.6;
  229. }
  230. }
  231. .rule-popup-btn {
  232. width: 80%;
  233. height: 88rpx;
  234. background: linear-gradient(to right, #ffd01e, #ff8917);
  235. border-radius: 44rpx;
  236. color: #fff;
  237. font-size: 32rpx;
  238. font-weight: bold;
  239. display: flex;
  240. align-items: center;
  241. justify-content: center;
  242. border: none;
  243. margin: 0 auto;
  244. margin-top: 16rpx;
  245. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  246. transition: all 0.3s ease;
  247. &::after {
  248. border: none;
  249. }
  250. &:active {
  251. opacity: 0.9;
  252. }
  253. &.disabled {
  254. background: #ccc;
  255. color: #999;
  256. box-shadow: none;
  257. opacity: 0.6;
  258. }
  259. }
  260. /* 预约上门取件弹窗样式 */
  261. .pickup-confirm-mask {
  262. position: fixed;
  263. left: 0;
  264. right: 0;
  265. top: 0;
  266. bottom: 0;
  267. background: rgba(0, 0, 0, 0.35);
  268. z-index: 5000;
  269. display: flex;
  270. align-items: center;
  271. justify-content: center;
  272. }
  273. .pickup-confirm-popup {
  274. width: 90vw;
  275. max-width: 600rpx;
  276. background: #fff;
  277. border-radius: 48rpx;
  278. box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
  279. display: flex;
  280. flex-direction: column;
  281. align-items: center;
  282. position: relative;
  283. padding: 48rpx 36rpx 40rpx 36rpx;
  284. }
  285. .pickup-confirm-title {
  286. font-size: 36rpx;
  287. color: #222;
  288. font-weight: bold;
  289. text-align: center;
  290. margin-bottom: 24rpx;
  291. }
  292. .pickup-confirm-content {
  293. width: 100%;
  294. font-size: 26rpx;
  295. color: #333;
  296. text-align: left;
  297. line-height: 1.7;
  298. margin-bottom: 36rpx;
  299. }
  300. .pickup-confirm-btn-row {
  301. width: 100%;
  302. display: flex;
  303. justify-content: space-between;
  304. gap: 32rpx;
  305. }
  306. .pickup-confirm-btn {
  307. flex: 1;
  308. height: 88rpx;
  309. border-radius: 44rpx;
  310. font-size: 32rpx;
  311. font-weight: bold;
  312. display: flex;
  313. align-items: center;
  314. justify-content: center;
  315. border: 2rpx solid #ffd01e;
  316. background: #fff;
  317. color: #ff9c00;
  318. box-shadow: 0 4rpx 16rpx rgba(255, 156, 0, 0.08);
  319. &:not(.agree) {
  320. background: #fff0d2;
  321. }
  322. &.agree {
  323. background: linear-gradient(to right, #ffd01e, #ff8917);
  324. color: #fff;
  325. border: none;
  326. }
  327. }
  328. </style>