@puresnake/snake-ui 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/LICENSE +21 -0
  2. package/changelog.md +1 -0
  3. package/components/snake-activity-list/snake-activity-list.vue +365 -0
  4. package/components/snake-activity-list/types.ts +29 -0
  5. package/components/snake-auxiliary-segmentation/snake-auxiliary-segmentation.vue +44 -0
  6. package/components/snake-auxiliary-segmentation/types.ts +5 -0
  7. package/components/snake-button/snake-button.vue +357 -0
  8. package/components/snake-button/types.ts +103 -0
  9. package/components/snake-button-sort/snake-button-sort.vue +72 -0
  10. package/components/snake-button-sort/types.ts +22 -0
  11. package/components/snake-calendar/snake-calendar.vue +998 -0
  12. package/components/snake-calendar/types.ts +189 -0
  13. package/components/snake-cell-group/snake-cell-group.vue +55 -0
  14. package/components/snake-cell-group/types.ts +19 -0
  15. package/components/snake-cell-item/snake-cell-item.vue +159 -0
  16. package/components/snake-cell-item/types.ts +66 -0
  17. package/components/snake-checkbox/snake-checkbox.vue +264 -0
  18. package/components/snake-checkbox/types.ts +70 -0
  19. package/components/snake-checkbox-group/snake-checkbox-group.vue +154 -0
  20. package/components/snake-checkbox-group/types.ts +52 -0
  21. package/components/snake-cms/blocks/v2/cms-v2-carousel.vue +87 -0
  22. package/components/snake-cms/blocks/v2/cms-v2-coupon.vue +25 -0
  23. package/components/snake-cms/blocks/v2/cms-v2-custom.vue +12 -0
  24. package/components/snake-cms/blocks/v2/cms-v2-divider.vue +33 -0
  25. package/components/snake-cms/blocks/v2/cms-v2-floatbutton.vue +28 -0
  26. package/components/snake-cms/blocks/v2/cms-v2-goods-list.vue +150 -0
  27. package/components/snake-cms/blocks/v2/cms-v2-hotzone.vue +41 -0
  28. package/components/snake-cms/blocks/v2/cms-v2-images.vue +41 -0
  29. package/components/snake-cms/blocks/v2/cms-v2-magiccube.vue +93 -0
  30. package/components/snake-cms/blocks/v2/cms-v2-notice.vue +80 -0
  31. package/components/snake-cms/blocks/v2/cms-v2-popup.vue +46 -0
  32. package/components/snake-cms/blocks/v2/cms-v2-richtext.vue +18 -0
  33. package/components/snake-cms/blocks/v2/cms-v2-search.vue +115 -0
  34. package/components/snake-cms/blocks/v2/cms-v2-video.vue +25 -0
  35. package/components/snake-cms/blocks/v2/shared.ts +84 -0
  36. package/components/snake-cms/snake-cms.vue +409 -0
  37. package/components/snake-cms/types.ts +50 -0
  38. package/components/snake-commodity-search/snake-commodity-search.vue +234 -0
  39. package/components/snake-commodity-search/types.ts +11 -0
  40. package/components/snake-config-provider/snake-config-provider.vue +61 -0
  41. package/components/snake-config-provider/types.ts +22 -0
  42. package/components/snake-count-down/snake-count-down.vue +231 -0
  43. package/components/snake-count-down/types.ts +103 -0
  44. package/components/snake-coupon/snake-coupon.vue +209 -0
  45. package/components/snake-coupon/types.ts +21 -0
  46. package/components/snake-custom-module/snake-custom-module.vue +25 -0
  47. package/components/snake-custom-module/types.ts +11 -0
  48. package/components/snake-drop-menu/snake-drop-menu.vue +123 -0
  49. package/components/snake-drop-menu/types.ts +95 -0
  50. package/components/snake-drop-menu-item/snake-drop-menu-item.vue +181 -0
  51. package/components/snake-drop-menu-item/types.ts +48 -0
  52. package/components/snake-empty/snake-empty.vue +100 -0
  53. package/components/snake-empty/types.ts +48 -0
  54. package/components/snake-filter-sorts/filter-page.vue +695 -0
  55. package/components/snake-filter-sorts/snake-filter-sorts.vue +185 -0
  56. package/components/snake-filter-sorts/types.ts +57 -0
  57. package/components/snake-floating-bubble/snake-floating-bubble.vue +157 -0
  58. package/components/snake-floating-bubble/types.ts +43 -0
  59. package/components/snake-follow/snake-follow.vue +18 -0
  60. package/components/snake-follow/types.ts +2 -0
  61. package/components/snake-form/snake-form.vue +124 -0
  62. package/components/snake-form/types.ts +96 -0
  63. package/components/snake-form-item/snake-form-item.vue +331 -0
  64. package/components/snake-form-item/types.ts +70 -0
  65. package/components/snake-goods-list/snake-goods-list.vue +792 -0
  66. package/components/snake-goods-list/types.ts +55 -0
  67. package/components/snake-graphic-navigation/snake-graphic-navigation.vue +198 -0
  68. package/components/snake-graphic-navigation/types.ts +43 -0
  69. package/components/snake-icon/icon-names.ts +222 -0
  70. package/components/snake-icon/snake-icon.vue +220 -0
  71. package/components/snake-icon/types.ts +45 -0
  72. package/components/snake-image/snake-image.vue +230 -0
  73. package/components/snake-image/types.ts +70 -0
  74. package/components/snake-images/snake-images.vue +80 -0
  75. package/components/snake-images/types.ts +5 -0
  76. package/components/snake-index-anchor/snake-index-anchor.vue +83 -0
  77. package/components/snake-index-anchor/types.ts +14 -0
  78. package/components/snake-index-list/anchor-registry.ts +80 -0
  79. package/components/snake-index-list/snake-index-list.vue +259 -0
  80. package/components/snake-index-list/types.ts +115 -0
  81. package/components/snake-index-list-panel/snake-index-list-panel.vue +277 -0
  82. package/components/snake-index-list-panel/types.ts +10 -0
  83. package/components/snake-input/snake-input.vue +225 -0
  84. package/components/snake-input/types.ts +99 -0
  85. package/components/snake-jump-applet/snake-jump-applet.vue +99 -0
  86. package/components/snake-jump-applet/types.ts +17 -0
  87. package/components/snake-line/snake-line.vue +42 -0
  88. package/components/snake-line/types.ts +30 -0
  89. package/components/snake-line-progress/snake-line-progress.vue +105 -0
  90. package/components/snake-line-progress/types.ts +26 -0
  91. package/components/snake-link/snake-link.vue +74 -0
  92. package/components/snake-link/types.ts +30 -0
  93. package/components/snake-list-switching/components/tree-select/index.vue +156 -0
  94. package/components/snake-list-switching/snake-list-switching.vue +1285 -0
  95. package/components/snake-list-switching/types.ts +11 -0
  96. package/components/snake-loading/snake-loading.vue +241 -0
  97. package/components/snake-loading/types.ts +34 -0
  98. package/components/snake-loadmore/snake-loadmore.vue +103 -0
  99. package/components/snake-loadmore/types.ts +56 -0
  100. package/components/snake-magic-cube/snake-magic-cube.vue +622 -0
  101. package/components/snake-magic-cube/types.ts +5 -0
  102. package/components/snake-modal/snake-modal.vue +434 -0
  103. package/components/snake-modal/types.ts +71 -0
  104. package/components/snake-navbar/snake-navbar.vue +194 -0
  105. package/components/snake-navbar/types.ts +54 -0
  106. package/components/snake-no-network/snake-no-network.vue +125 -0
  107. package/components/snake-no-network/types.ts +27 -0
  108. package/components/snake-notice/snake-notice.vue +44 -0
  109. package/components/snake-notice/types.ts +5 -0
  110. package/components/snake-notice-bar/snake-notice-bar.vue +336 -0
  111. package/components/snake-notice-bar/types.ts +82 -0
  112. package/components/snake-overlay/snake-overlay.vue +73 -0
  113. package/components/snake-overlay/types.ts +35 -0
  114. package/components/snake-page-popup/snake-page-popup.vue +146 -0
  115. package/components/snake-page-popup/types.ts +28 -0
  116. package/components/snake-parse/audio/audio.vue +286 -0
  117. package/components/snake-parse/audio/context.js +11 -0
  118. package/components/snake-parse/audio/index.js +34 -0
  119. package/components/snake-parse/node/node.vue +830 -0
  120. package/components/snake-parse/parser.js +1480 -0
  121. package/components/snake-parse/snake-parse.vue +548 -0
  122. package/components/snake-parse/static/app-plus/snake-parse/js/handler.js +109 -0
  123. package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.js +0 -0
  124. package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.min.js +1 -0
  125. package/components/snake-parse/static/app-plus/snake-parse/local.html +3 -0
  126. package/components/snake-parse/style/index.js +132 -0
  127. package/components/snake-parse/style/parser.js +175 -0
  128. package/components/snake-parse/types.ts +49 -0
  129. package/components/snake-picture-ads/snake-picture-ads.vue +292 -0
  130. package/components/snake-picture-ads/types.ts +5 -0
  131. package/components/snake-popup/snake-popup.vue +348 -0
  132. package/components/snake-popup/types.ts +88 -0
  133. package/components/snake-qr-code/qrcode.ts +1214 -0
  134. package/components/snake-qr-code/snake-qr-code.vue +408 -0
  135. package/components/snake-qr-code/types.ts +78 -0
  136. package/components/snake-radio/snake-radio.vue +271 -0
  137. package/components/snake-radio/types.ts +64 -0
  138. package/components/snake-radio-group/snake-radio-group.vue +98 -0
  139. package/components/snake-radio-group/types.ts +48 -0
  140. package/components/snake-read-more/snake-read-more.vue +140 -0
  141. package/components/snake-read-more/types.ts +53 -0
  142. package/components/snake-rich-text/snake-rich-text.vue +24 -0
  143. package/components/snake-rich-text/types.ts +5 -0
  144. package/components/snake-root-portal/snake-root-portal.vue +92 -0
  145. package/components/snake-root-portal/types.ts +9 -0
  146. package/components/snake-search/snake-search.vue +202 -0
  147. package/components/snake-search/types.ts +84 -0
  148. package/components/snake-select/snake-select.vue +371 -0
  149. package/components/snake-select/types.ts +75 -0
  150. package/components/snake-skeleton/snake-skeleton.vue +104 -0
  151. package/components/snake-skeleton/types.ts +32 -0
  152. package/components/snake-skeleton-item/snake-skeleton-item.vue +90 -0
  153. package/components/snake-skeleton-item/types.ts +30 -0
  154. package/components/snake-slider/snake-slider.vue +249 -0
  155. package/components/snake-slider/types.ts +59 -0
  156. package/components/snake-sticky/snake-sticky.vue +178 -0
  157. package/components/snake-sticky/types.ts +48 -0
  158. package/components/snake-subsection/snake-subsection.vue +188 -0
  159. package/components/snake-subsection/types.ts +54 -0
  160. package/components/snake-swiper/snake-swiper.vue +229 -0
  161. package/components/snake-swiper/types.ts +79 -0
  162. package/components/snake-switch/snake-switch.vue +100 -0
  163. package/components/snake-switch/types.ts +41 -0
  164. package/components/snake-tabbar/snake-tabbar.vue +306 -0
  165. package/components/snake-tabbar/types.ts +93 -0
  166. package/components/snake-tabs/snake-tabs.vue +305 -0
  167. package/components/snake-tabs/types.ts +79 -0
  168. package/components/snake-text/snake-text.vue +292 -0
  169. package/components/snake-text/types.ts +100 -0
  170. package/components/snake-textarea/snake-textarea.vue +171 -0
  171. package/components/snake-textarea/types.ts +79 -0
  172. package/components/snake-toast/snake-toast.vue +330 -0
  173. package/components/snake-toast/types.ts +55 -0
  174. package/components/snake-tooltip/position.ts +33 -0
  175. package/components/snake-tooltip/snake-tooltip.vue +339 -0
  176. package/components/snake-tooltip/types.ts +80 -0
  177. package/components/snake-transition/snake-transition.vue +315 -0
  178. package/components/snake-transition/types.ts +65 -0
  179. package/components/snake-upload/snake-upload.vue +1801 -0
  180. package/components/snake-upload/types.ts +256 -0
  181. package/components/snake-videos/snake-videos.vue +122 -0
  182. package/components/snake-videos/types.ts +7 -0
  183. package/iconfont.css +881 -0
  184. package/index.d.ts +7 -0
  185. package/index.scss +26 -0
  186. package/index.ts +94 -0
  187. package/libs/cms/blocks.ts +103 -0
  188. package/libs/cms/config.ts +25 -0
  189. package/libs/cms/index.ts +7 -0
  190. package/libs/cms/normalize.ts +135 -0
  191. package/libs/cms/resolve.ts +12 -0
  192. package/libs/cms/types.ts +164 -0
  193. package/libs/cms/use-commerce.ts +385 -0
  194. package/libs/cms/v2.ts +193 -0
  195. package/libs/config/index.ts +4 -0
  196. package/libs/config/modal.ts +93 -0
  197. package/libs/config/state.ts +33 -0
  198. package/libs/config/theme.ts +351 -0
  199. package/libs/config/toast.ts +71 -0
  200. package/libs/config/types.ts +135 -0
  201. package/libs/config/zIndex.ts +44 -0
  202. package/libs/css/color.scss +105 -0
  203. package/libs/css/common.scss +171 -0
  204. package/libs/css/style.h5.scss +7 -0
  205. package/libs/css/style.mp.scss +7 -0
  206. package/libs/css/style.theme.scss +70 -0
  207. package/libs/css/style.vue.scss +140 -0
  208. package/libs/form/async-validator.min.d.ts +75 -0
  209. package/libs/form/async-validator.min.js +3 -0
  210. package/libs/form/index.ts +3 -0
  211. package/libs/form/path.ts +40 -0
  212. package/libs/form/types.ts +51 -0
  213. package/libs/form/validate.ts +149 -0
  214. package/libs/foundation.ts +96 -0
  215. package/libs/function/$parent.ts +33 -0
  216. package/libs/function/clipboard.ts +13 -0
  217. package/libs/function/color.ts +55 -0
  218. package/libs/function/debounce.ts +34 -0
  219. package/libs/function/deepClone.ts +30 -0
  220. package/libs/function/deepMerge.ts +25 -0
  221. package/libs/function/emitter.ts +30 -0
  222. package/libs/function/format.ts +27 -0
  223. package/libs/function/getParent.ts +53 -0
  224. package/libs/function/getRect.ts +29 -0
  225. package/libs/function/guid.ts +11 -0
  226. package/libs/function/index.ts +31 -0
  227. package/libs/function/queryParams.ts +73 -0
  228. package/libs/function/random.ts +6 -0
  229. package/libs/function/randomArray.ts +14 -0
  230. package/libs/function/route.ts +155 -0
  231. package/libs/function/sleep.ts +7 -0
  232. package/libs/function/style.ts +58 -0
  233. package/libs/function/styleUtils.ts +83 -0
  234. package/libs/function/system.ts +14 -0
  235. package/libs/function/test.ts +202 -0
  236. package/libs/function/throttle.ts +36 -0
  237. package/libs/function/timeFormat.ts +27 -0
  238. package/libs/function/timeFrom.ts +27 -0
  239. package/libs/function/toast.ts +10 -0
  240. package/libs/function/trim.ts +11 -0
  241. package/libs/function/type2icon.ts +11 -0
  242. package/libs/function/unit.ts +45 -0
  243. package/libs/hooks/index.ts +11 -0
  244. package/libs/hooks/useChildren.ts +125 -0
  245. package/libs/hooks/useColor.ts +5 -0
  246. package/libs/hooks/useDebounce.ts +5 -0
  247. package/libs/hooks/useLocale.ts +13 -0
  248. package/libs/hooks/useModal.ts +131 -0
  249. package/libs/hooks/useParent.ts +62 -0
  250. package/libs/hooks/useRect.ts +44 -0
  251. package/libs/hooks/useRouter.ts +5 -0
  252. package/libs/hooks/useTheme.ts +31 -0
  253. package/libs/hooks/useThrottle.ts +5 -0
  254. package/libs/hooks/useToast.ts +116 -0
  255. package/libs/index.ts +18 -0
  256. package/libs/request/auto-http.ts +1 -0
  257. package/libs/request/index.ts +343 -0
  258. package/libs/runtime/accessors.ts +55 -0
  259. package/libs/runtime/context.ts +40 -0
  260. package/libs/runtime/index.ts +18 -0
  261. package/libs/runtime/types.ts +49 -0
  262. package/libs/util/calendar.ts +6 -0
  263. package/locale/index.ts +130 -0
  264. package/locale/lang/en-US.ts +67 -0
  265. package/locale/lang/zh-CN.ts +67 -0
  266. package/package.json +130 -5
  267. package/plugins/index.ts +1 -0
  268. package/plugins/root/README.md +86 -0
  269. package/plugins/root/index.ts +123 -0
  270. package/plugins/root/page.ts +77 -0
  271. package/plugins/root/root.ts +53 -0
  272. package/plugins/root/utils.ts +137 -0
  273. package/readme.md +79 -0
  274. package/theme.scss +118 -0
  275. package/types/components.d.ts +236 -0
  276. package/types/global.d.ts +21 -0
  277. package/types/index.d.ts +6 -0
  278. package/types/uni-app.d.ts +16 -0
  279. package/README.md +0 -3
@@ -0,0 +1,695 @@
1
+ <template>
2
+ <view class="filter-container">
3
+ <view class="scroll-panel" id="scroll-panel">
4
+ <view class="list-box">
5
+ <view class="left">
6
+ <scroll-view
7
+ scroll-y
8
+ :style="{ height: scrollHeight + 'px' }"
9
+ :scroll-into-view="leftIntoView"
10
+ :scroll-with-animation="false"
11
+ >
12
+ <view
13
+ class="item"
14
+ v-for="(item, index) in filterList"
15
+ :key="index"
16
+ :class="{ active: index == leftIndex }"
17
+ :id="'left-' + index"
18
+ :data-index="index"
19
+ @tap.stop="leftTap"
20
+ >
21
+ {{ item.name }}
22
+ </view>
23
+ </scroll-view>
24
+ </view>
25
+ <view class="main">
26
+ <scroll-view
27
+ v-if="filterList.length - 1 !== leftIndex"
28
+ scroll-y
29
+ :style="{ height: scrollHeight + 'px' }"
30
+ :scroll-into-view="scrollInto"
31
+ :scroll-with-animation="false"
32
+ @scroll="mainScroll"
33
+ >
34
+ <view
35
+ class="item main-item"
36
+ v-for="(item, index) in filterList"
37
+ :key="index"
38
+ :id="'item-' + index"
39
+ >
40
+ <view class="title" v-if="item.value !== 'brandFilter'">
41
+ <text>{{ item.name }}</text>
42
+ <snake-tooltip
43
+ v-if="item.desc"
44
+ placement="bottom-start"
45
+ :offset="10"
46
+ custom-class="filter-page__tooltip-wrap"
47
+ custom-style="--snake-tooltip-background: #eafff5"
48
+ content-style="width: calc(100vw - 238rpx); padding: 14rpx"
49
+ >
50
+ <snake-icon
51
+ name="question-circle"
52
+ size="26rpx"
53
+ custom-class="filter-page__tooltip-icon"
54
+ ></snake-icon>
55
+ <template #content>
56
+ <snake-parse :content="item.desc"></snake-parse>
57
+ </template>
58
+ </snake-tooltip>
59
+ </view>
60
+ <view class="price-input-box" v-if="item.value === 'priceFilter'">
61
+ <input
62
+ class="price-input-item"
63
+ placeholder="最低价"
64
+ v-model="priceValue.low"
65
+ placeholder-style="color: var(--snake-color-text-muted, #acacb7)"
66
+ @input="inputChange($event, item, 'low')"
67
+ />
68
+ <view class="line"></view>
69
+ <input
70
+ class="price-input-item"
71
+ placeholder="最高价"
72
+ v-model="priceValue.high"
73
+ placeholder-style="color: var(--snake-color-text-muted, #acacb7)"
74
+ @input="inputChange($event, item, 'high')"
75
+ />
76
+ </view>
77
+
78
+ <view class="tabs" v-if="item.value !== 'brandFilter'">
79
+ <block v-for="(item2, index2) in item.children" :key="index2">
80
+ <view
81
+ class="tabs-item filter-page__ellipsis"
82
+ :class="{
83
+ 'tabs-item--active': filterParams[item.value].includes(item2.value)
84
+ }"
85
+ @click="selectHandle(item, item2)"
86
+ >
87
+ {{ item2.label }}
88
+ </view>
89
+ </block>
90
+ </view>
91
+ </view>
92
+ <view class="fill-last" :style="{ height: fillHeight + 'px' }"></view>
93
+ </scroll-view>
94
+
95
+ <view v-if="filterList.length - 1 === leftIndex" class="filter-page__brand-panel">
96
+ <view class="title" v-if="filterList[leftIndex]">
97
+ <text>{{ filterList[leftIndex].name }}</text>
98
+ <text class="filter-page__count">
99
+ (已选{{ filterParams[filterList[leftIndex].value].length }}个)
100
+ </text>
101
+ </view>
102
+
103
+ <snake-index-list-panel
104
+ ref="uIndexListRef"
105
+ :indexList="indexList"
106
+ :customStyle="{ flex: 1, minHeight: 0, height: 'auto' }"
107
+ activeColor="var(--snake-color-text, #26273a)"
108
+ inactiveColor="var(--snake-color-text-muted, #acacb7)"
109
+ >
110
+ <block v-for="(item, index) in brandGroupList" :key="index">
111
+ <snake-index-anchor
112
+ :index="indexList[index]"
113
+ :text="indexList[index]"
114
+ :customStyle="{ height: '44rpx', padding: '0 10px' }"
115
+ ></snake-index-anchor>
116
+
117
+ <template v-for="(cell, cellIndex) in item" :key="cellIndex">
118
+ <view
119
+ class="filter-page__brand-cell"
120
+ @click.stop="
121
+ selectHandle(filterList[leftIndex], {
122
+ value: cell
123
+ })
124
+ "
125
+ >
126
+ <view class="filter-page__brand-name" hover-class="u-hover-class">
127
+ {{ cell }}
128
+ </view>
129
+ <snake-icon
130
+ v-if="filterParams.brandFilter.includes(cell)"
131
+ name="checkmark"
132
+ size="28rpx"
133
+ color="var(--snake-color-primary, #06d290)"
134
+ custom-class="filter-page__brand-check"
135
+ ></snake-icon>
136
+ </view>
137
+ </template>
138
+ <view class="filter-page__spacer-sm"></view>
139
+ </block>
140
+ <view class="filter-page__spacer-lg"></view>
141
+ </snake-index-list-panel>
142
+ </view>
143
+ </view>
144
+ </view>
145
+ </view>
146
+
147
+ <!-- 底部按钮 -->
148
+ <view class="filter-footer">
149
+ <view class="filter-page__save-row" @click="isDefaultSave = !isDefaultSave">
150
+ <snake-icon
151
+ v-if="isDefaultSave"
152
+ name="checkbox-mark"
153
+ size="28rpx"
154
+ color="var(--snake-color-primary, #06d290)"
155
+ ></snake-icon>
156
+ <snake-icon
157
+ v-else
158
+ name="circle"
159
+ size="28rpx"
160
+ color="var(--snake-color-text-muted, #acacb7)"
161
+ ></snake-icon>
162
+ <text class="filter-page__save-text">保存为默认筛选</text>
163
+ </view>
164
+
165
+ <view class="action">
166
+ <snake-button
167
+ type="primary"
168
+ color="var(--snake-color-primary, #06D290)"
169
+ plain
170
+ customStyle="width: 168rpx; height: 76rpx;"
171
+ @click="resetFilter"
172
+ >
173
+ 重置
174
+ </snake-button>
175
+ <snake-button
176
+ type="primary"
177
+ customStyle="width: 228rpx; height: 76rpx; margin-left: 20rpx;"
178
+ @click="confirmFilter"
179
+ >
180
+ 确定
181
+ </snake-button>
182
+ </view>
183
+ </view>
184
+ </view>
185
+ </template>
186
+
187
+ <script>
188
+ /**
189
+ * @component FilterPage
190
+ * @description 筛选排序组件内部使用的条件选择面板,负责选项联动、默认值恢复和结果确认。
191
+ */
192
+
193
+ import { createCacheClient } from '../../libs/runtime';
194
+ import IndexAnchor from '../snake-index-anchor/snake-index-anchor.vue';
195
+ import IndexListPanel from '../snake-index-list-panel/snake-index-list-panel.vue';
196
+ const cacheManager = createCacheClient('snake-filter-sorts');
197
+ export default {
198
+ emits: ['confirm'],
199
+ options: {
200
+ virtualHost: true,
201
+ addGlobalClass: true,
202
+ styleIsolation: 'shared'
203
+ },
204
+ components: { SnakeIndexAnchor: IndexAnchor, SnakeIndexListPanel: IndexListPanel },
205
+ props: {
206
+ show: {
207
+ type: Boolean,
208
+ default: false
209
+ },
210
+ filters: {
211
+ type: Object,
212
+ default: () => ({})
213
+ },
214
+ defaultParams: {
215
+ type: Object,
216
+ default: () => ({})
217
+ }
218
+ },
219
+ data() {
220
+ return {
221
+ scrollHeight: 400,
222
+ scrollTopSize: 0,
223
+ fillHeight: 0, // 填充高度,用于最后一项低于滚动区域时使用
224
+ leftArray: [],
225
+ mainArray: [],
226
+ topArr: [],
227
+ leftIndex: 0,
228
+ scrollInto: '',
229
+ isDefaultSave: false,
230
+ tagStyle: {
231
+ text: ''
232
+ },
233
+ // 价格筛选
234
+ priceValue: {
235
+ low: '',
236
+ high: ''
237
+ },
238
+ brandValues: [],
239
+ filterList: [],
240
+ filterParams: {}
241
+ };
242
+ },
243
+ computed: {
244
+ /* 计算左侧滚动位置定位 */
245
+ leftIntoView() {
246
+ return `left-${this.leftIndex > 3 ? this.leftIndex - 3 : 0}`;
247
+ },
248
+ /**
249
+ * 获取品牌分组列表
250
+ * @returns {Array} 品牌数据数组,按 hot -> 字母顺序 -> # 的顺序排列
251
+ */
252
+ brandGroupList() {
253
+ const brandData = this.filters?.brandFilter;
254
+ const indexList = Object.keys(brandData);
255
+ const filteredIndexes = indexList.filter(e => !['hot', '#'].includes(e));
256
+
257
+ const result = filteredIndexes.map(key => brandData[key]);
258
+
259
+ // 如果存在 hot 分组,将其插入到开头
260
+ if (indexList.includes('hot')) {
261
+ result.unshift(brandData.hot);
262
+ }
263
+
264
+ // 如果存在 # 分组,将其追加到末尾
265
+ if (indexList.includes('#')) {
266
+ result.push(brandData['#']);
267
+ }
268
+
269
+ return result;
270
+ },
271
+ /**
272
+ * 获取索引列表
273
+ * @returns {Array} 索引数组,按 hot -> 字母顺序 -> # 的顺序排列
274
+ */
275
+ indexList() {
276
+ const indexList = Object.keys(this.filters?.brandFilter);
277
+ const filteredIndexes = indexList.filter(e => !['hot', '#'].includes(e));
278
+
279
+ const result = [...filteredIndexes];
280
+
281
+ // 如果存在 hot 分组,将其插入到开头
282
+ if (indexList.includes('hot')) {
283
+ result.unshift('hot');
284
+ }
285
+
286
+ // 如果存在 # 分组,将其追加到末尾
287
+ if (indexList.includes('#')) {
288
+ result.push('#');
289
+ }
290
+
291
+ return result;
292
+ }
293
+ },
294
+ watch: {
295
+ leftIndex(newVal) {
296
+ if (newVal !== this.filterList.length - 1) return;
297
+ this.$nextTick(() => this.$refs.uIndexListRef?.update?.());
298
+ },
299
+ show: {
300
+ async handler(newVal) {
301
+ // #ifdef H5
302
+ document.body.style.overflow = newVal ? 'hidden' : 'unset';
303
+ // #endif
304
+
305
+ if (newVal) {
306
+ await uni.$snake.sleep(500);
307
+ await this.initScrollView();
308
+ await this.initData();
309
+ await uni.$snake.sleep(200);
310
+ await this.getElementTop();
311
+ if (this.leftIndex > 0) {
312
+ this.scrollInto = '';
313
+ this.$nextTick(() => {
314
+ this.scrollInto = `item-${this.leftIndex}`;
315
+ });
316
+ }
317
+ }
318
+ },
319
+ immediate: true,
320
+ deep: true
321
+ }
322
+ },
323
+ methods: {
324
+ initData() {
325
+ const filterParamsCache = cacheManager.get('filterParams') || {};
326
+
327
+ const mainFilter = [
328
+ ...(this.filters?.mainFilter || []),
329
+ {
330
+ name: '品牌',
331
+ value: 'brandFilter'
332
+ }
333
+ ];
334
+
335
+ this.filterList = mainFilter;
336
+
337
+ const obj = {};
338
+ this.filterList.forEach(item => {
339
+ obj[item.value] = (filterParamsCache?.[item.value]?.length && filterParamsCache[item.value]) || [];
340
+ if (!obj[item.value].length) {
341
+ const value = this.defaultParams?.[item.value] || [];
342
+ obj[item.value] = Array.isArray(value) ? value : value.split?.(',');
343
+ }
344
+ this.filterParams = { ...obj };
345
+ });
346
+ },
347
+ /* 初始化滚动区域 */
348
+ initScrollView() {
349
+ return new Promise(resolve => {
350
+ let view = uni.createSelectorQuery().in(this);
351
+
352
+ // #ifdef MP-ALIPAY
353
+ view = uni.createSelectorQuery();
354
+ // #endif
355
+
356
+ view.select('#scroll-panel')
357
+ .boundingClientRect(res => {
358
+ // virtualHost 下 mp 端 selector 可能返回 null,回退默认高度避免 initData 中断
359
+ this.scrollTopSize = res?.top || 0;
360
+ this.scrollHeight = res?.height || this.scrollHeight;
361
+ setTimeout(() => {
362
+ resolve();
363
+ }, 100);
364
+ })
365
+ .exec();
366
+ });
367
+ },
368
+ /* 获取元素顶部信息 */
369
+ getElementTop() {
370
+ new Promise(resolve => {
371
+ let view = uni.createSelectorQuery().in(this);
372
+
373
+ // #ifdef MP-ALIPAY
374
+ view = uni.createSelectorQuery();
375
+ // #endif
376
+
377
+ view.selectAll('.main-item')
378
+ .boundingClientRect(data => {
379
+ resolve(data);
380
+ })
381
+ .exec();
382
+ }).then(res => {
383
+ const list = res || [];
384
+ const topArr = list.map(item => {
385
+ return item.top - this.scrollTopSize; /* 减去滚动容器距离顶部的距离 */
386
+ });
387
+ this.topArr = topArr;
388
+
389
+ /* 获取最后一项的高度,设置填充高度。判断和填充时做了 +-20 的操作,是为了滚动时更好的定位 */
390
+ const last = list[list.length - 1]?.height || 0;
391
+ if (last - 20 < this.scrollHeight) {
392
+ this.fillHeight = this.scrollHeight - last + 20;
393
+ }
394
+ });
395
+ },
396
+ /* 主区域滚动监听 */
397
+ mainScroll(e) {
398
+ // 节流方法
399
+ clearTimeout(this.mainThrottle);
400
+ this.mainThrottle = setTimeout(() => {
401
+ scrollFn();
402
+ }, 10);
403
+
404
+ const scrollFn = () => {
405
+ const top = e.detail.scrollTop;
406
+ let index = 0;
407
+ /* 查找当前滚动距离 */
408
+ for (let i = this.topArr.length - 1; i >= 0; i--) {
409
+ /* 在部分安卓设备上,因手机逻辑分辨率与rpx单位计算不是整数,滚动距离与有误差,增加2px来完善该问题 */
410
+ if (top + 2 >= this.topArr[i]) {
411
+ index = i;
412
+ break;
413
+ }
414
+ }
415
+ this.leftIndex = index < 0 ? 0 : index;
416
+ };
417
+ },
418
+ /* 左侧导航点击 */
419
+ leftTap(e) {
420
+ const index = e.currentTarget.dataset.index;
421
+
422
+ if (this.filterList.length - 1 === this.leftIndex) {
423
+ this.leftIndex = index;
424
+ }
425
+
426
+ this.scrollInto = `item-${index}`;
427
+ },
428
+ selectHandle(item, item2) {
429
+ if (this.filterParams[item.value].includes(item2.value)) {
430
+ this.filterParams[item.value].splice(this.filterParams[item.value].indexOf(item2.value), 1);
431
+ } else {
432
+ if (item.type === 'selector') this.filterParams[item.value] = [];
433
+ this.filterParams[item.value].push(item2.value);
434
+ }
435
+
436
+ if (item.value === 'priceFilter') {
437
+ this.selectPrice({
438
+ low: item2.value.split('-')[0],
439
+ high: item2.value.split('-')[1]
440
+ });
441
+ }
442
+ },
443
+ inputChange(e, item, propName) {
444
+ const value = e.detail.value;
445
+ this.priceValue[propName] = value;
446
+ this.filterParams[item.value] = [`${this.priceValue?.low || 0}-${this.priceValue?.high || 0}`];
447
+ },
448
+ // 价格选择
449
+ selectPrice(item) {
450
+ if (this.priceValue.low === item.low && this.priceValue.high === item.high) {
451
+ this.priceValue = {};
452
+ } else {
453
+ this.priceValue = { ...item };
454
+ }
455
+ },
456
+ // 重置
457
+ resetFilter() {
458
+ Object.entries(this.filterParams).forEach(([key, value]) => {
459
+ if (value?.length) {
460
+ this.filterParams[key] = [];
461
+ }
462
+ });
463
+ cacheManager.delete('filterParams');
464
+ },
465
+ // 更多筛选 确认
466
+ confirmFilter() {
467
+ const params = {};
468
+ Object.entries(this.filterParams).forEach(([key, value]) => {
469
+ if (value?.length) {
470
+ params[key] = value.join(',');
471
+ }
472
+ });
473
+ // 缓存48小时
474
+ if (this.isDefaultSave) {
475
+ cacheManager.set('filterParams', this.filterParams, 60 * 60 * 48);
476
+ }
477
+ this.$emit('confirm', params);
478
+ }
479
+ }
480
+ };
481
+ </script>
482
+
483
+ <style lang="scss" scoped>
484
+ /* 品牌勾选区(原依赖宿主 UnoCSS 原子类,迁移为组件语义类) */
485
+ .filter-page__brand-cell {
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: space-between;
489
+ height: 40rpx;
490
+ margin-top: 10rpx;
491
+ padding-left: 10rpx;
492
+ background-color: var(--snake-color-surface, #fff);
493
+ }
494
+
495
+ .filter-page__brand-name {
496
+ font-size: 24rpx;
497
+ }
498
+
499
+ .filter-page__brand-check {
500
+ padding-right: 96rpx;
501
+ }
502
+
503
+ .filter-page__spacer-sm {
504
+ height: 20rpx;
505
+ }
506
+
507
+ .filter-page__spacer-lg {
508
+ height: 120rpx;
509
+ }
510
+
511
+ /* 底部保存区 */
512
+ .filter-page__save-row {
513
+ display: flex;
514
+ align-items: center;
515
+ }
516
+
517
+ .filter-page__save-text {
518
+ margin-left: 10rpx;
519
+ font-size: 24rpx;
520
+ }
521
+
522
+ /* 工具提示 */
523
+ .filter-page__tooltip-icon {
524
+ margin-left: 10rpx;
525
+ }
526
+
527
+ /* 保持旧筛选提示框的水平位置,避免固定宽度从图标处向右溢出屏幕。 */
528
+ ::v-deep .filter-page__tooltip-wrap .snake-tooltip__panel--bottom-start {
529
+ transform: translateX(-100rpx);
530
+ }
531
+
532
+ ::v-deep .filter-page__tooltip-wrap .snake-tooltip__panel--bottom-start .snake-tooltip__arrow {
533
+ left: 112rpx;
534
+ }
535
+
536
+ .filter-page__ellipsis {
537
+ overflow: hidden;
538
+ white-space: nowrap;
539
+ text-overflow: ellipsis;
540
+ }
541
+
542
+ .filter-page__count {
543
+ font-weight: 300;
544
+ }
545
+
546
+ .filter-container {
547
+ display: flex;
548
+ flex-direction: column;
549
+ height: 100%;
550
+ background: var(--snake-color-surface, #fff);
551
+
552
+ & > view {
553
+ width: 100%;
554
+ }
555
+
556
+ .scroll-panel {
557
+ flex-grow: 1;
558
+ height: 0;
559
+ overflow: hidden;
560
+ }
561
+ }
562
+
563
+ .list-box {
564
+ display: flex;
565
+ flex-direction: row;
566
+ height: 100%;
567
+ min-height: 0;
568
+ font-size: 28rpx;
569
+
570
+ .left {
571
+ box-sizing: border-box;
572
+ width: 188rpx;
573
+ line-height: normal;
574
+ background: var(--snake-color-background, #f6f6f6);
575
+
576
+ .item {
577
+ display: flex;
578
+ align-items: center;
579
+ justify-content: center;
580
+ height: 80rpx;
581
+ font-family: PingFangSC-Regular;
582
+ font-size: 26rpx;
583
+ font-weight: 400;
584
+ color: var(--snake-color-text-secondary, #909399);
585
+
586
+ &.active {
587
+ font-family: PingFangSC-Medium;
588
+ font-weight: 500;
589
+ color: var(--snake-color-primary, #06d290);
590
+ }
591
+ }
592
+ }
593
+
594
+ .main {
595
+ box-sizing: border-box;
596
+ flex-grow: 1;
597
+ height: 100%;
598
+ min-height: 0;
599
+ width: 0;
600
+ padding-left: 20rpx;
601
+ background-color: var(--snake-color-surface, #fff);
602
+
603
+ .title {
604
+ display: flex;
605
+ align-items: center;
606
+ padding: 20rpx 0;
607
+ font-family: PingFangSC-Medium;
608
+ font-size: 26rpx;
609
+ font-weight: 500;
610
+ line-height: normal;
611
+ color: var(--snake-color-text, #26273a);
612
+ background-color: var(--snake-color-surface, #fff);
613
+ }
614
+
615
+ .tabs {
616
+ display: flex;
617
+ flex-wrap: wrap;
618
+ align-content: center;
619
+ align-items: center;
620
+ justify-content: flex-start;
621
+
622
+ .tabs-item {
623
+ box-sizing: border-box;
624
+ width: 166rpx;
625
+ height: 60rpx;
626
+ margin-right: 10rpx;
627
+ margin-bottom: 10rpx;
628
+ font-family: PingFangSC-Regular;
629
+ font-size: 24rpx;
630
+ font-weight: 400;
631
+ line-height: 60rpx;
632
+ text-align: center;
633
+ background: var(--snake-color-surface-muted, #f6f6f6);
634
+ border: 1px solid var(--snake-color-border, #f6f6f6);
635
+ border-radius: 2rpx;
636
+ }
637
+
638
+ .tabs-item--active {
639
+ font-family: PingFangSC-Medium;
640
+ font-weight: 500;
641
+ color: var(--snake-color-primary, #06d290);
642
+ background: var(--snake-color-primary-light, rgba(6, 210, 144, 0.1));
643
+ border: 1px solid var(--snake-color-primary, #06d290);
644
+ }
645
+ }
646
+ }
647
+ }
648
+
649
+ .filter-page__brand-panel {
650
+ display: flex;
651
+ flex-direction: column;
652
+ height: 100%;
653
+ min-height: 0;
654
+ }
655
+
656
+ .filter-footer {
657
+ box-sizing: border-box;
658
+ display: flex;
659
+ align-items: center;
660
+ justify-content: space-between;
661
+ width: 100%;
662
+ height: 116rpx;
663
+ padding: 0 22rpx;
664
+ background: var(--snake-color-surface, #fff);
665
+ box-shadow: 0 -4px 12px 0 var(--snake-color-shadow, #00000008);
666
+
667
+ .action {
668
+ display: flex;
669
+ align-items: center;
670
+ }
671
+ }
672
+
673
+ .price-input-box {
674
+ display: flex;
675
+ align-items: center;
676
+ margin-bottom: 20rpx;
677
+
678
+ .price-input-item {
679
+ box-sizing: border-box;
680
+ width: 229rpx;
681
+ height: 60rpx;
682
+ font-size: 26rpx;
683
+ color: var(--snake-color-text-muted, #acacb7);
684
+ text-align: center;
685
+ background-color: var(--snake-color-surface-muted, #f6f6f6);
686
+ border-radius: 2rpx;
687
+ }
688
+ .line {
689
+ width: 24rpx;
690
+ height: 4rpx;
691
+ margin: 0 20rpx;
692
+ background-color: #000;
693
+ }
694
+ }
695
+ </style>