@puresnake/snake-ui 0.0.0-stage → 0.0.2

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 +83 -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 +134 -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,33 @@
1
+ <template>
2
+ <view class="cms-v2-divider" :style="rootStyle">
3
+ <view v-if="data.mode === 'line'" class="cms-v2-divider__line" :style="lineStyle" />
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ /** @component CmsV2Divider @description CMS v2 空白与分割线内部渲染器。 @see /cms/overview */
9
+ import { computed } from 'vue';
10
+ import { rpx, type V2Record } from './shared';
11
+ const props = defineProps<{ data: V2Record }>();
12
+ const rootStyle = computed(() => ({
13
+ height: rpx(props.data.height, 30),
14
+ padding: Number(props.data.margin) === 1 ? '0 30rpx' : 0,
15
+ backgroundColor: props.data.style?.backgroundColor || '#fff'
16
+ }));
17
+ const lineStyle = computed(() => ({
18
+ borderTop: `2rpx ${['solid', 'dashed', 'dotted'].includes(props.data.lineStyle) ? props.data.lineStyle : 'solid'} ${props.data.style?.lineColor || '#e5e5e5'}`
19
+ }));
20
+ </script>
21
+
22
+ <style scoped>
23
+ .cms-v2-divider {
24
+ display: flex;
25
+ box-sizing: border-box;
26
+ align-items: center;
27
+ width: 100%;
28
+ }
29
+ .cms-v2-divider__line {
30
+ width: 100%;
31
+ height: 0;
32
+ }
33
+ </style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <image
3
+ v-if="data.src"
4
+ class="cms-v2-floatbutton"
5
+ :style="buttonStyle"
6
+ :src="data.src"
7
+ mode="aspectFit"
8
+ @click="openV2Link(data.link, linkService, pageInfo)"
9
+ />
10
+ </template>
11
+ <script setup lang="ts">
12
+ /** @component CmsV2Floatbutton @description CMS v2 悬浮按钮内部渲染器。 @see /cms/overview */
13
+ import { computed } from 'vue';
14
+ import { openV2Link, rpx, type V2BlockProps } from './shared';
15
+ const props = defineProps<V2BlockProps>();
16
+ const buttonStyle = computed(() => ({
17
+ width: rpx(props.data.style?.width, 76),
18
+ height: rpx(props.data.style?.height, 76),
19
+ right: `${Number(props.data.position?.right) || 0}%`,
20
+ bottom: `${Number(props.data.position?.bottom) || 20}%`
21
+ }));
22
+ </script>
23
+ <style scoped>
24
+ .cms-v2-floatbutton {
25
+ position: fixed;
26
+ z-index: 20;
27
+ }
28
+ </style>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <view class="cms-v2-goods" :style="rootStyle">
3
+ <snake-activity-list
4
+ v-if="data.bars.activitySession && data.display.cardType === 'auction'"
5
+ :list="activityList"
6
+ :current="activityCurrent"
7
+ @change="$emit('activity-change', $event)"
8
+ />
9
+ <snake-sticky
10
+ v-if="showBars"
11
+ :offset-top="stickyOffsetTop"
12
+ :custom-nav-height="stickyCustomNavHeight"
13
+ :scroll-top="scrollTop"
14
+ :disabled="!data.bars.sticky"
15
+ :bg-color="data.style.backgroundColor"
16
+ :z-index="19"
17
+ >
18
+ <scroll-view v-if="data.bars.tabs.enabled && tabs.length" scroll-x class="cms-v2-goods__tabs">
19
+ <view
20
+ v-for="(tab, index) in tabs"
21
+ :key="tab.id ?? index"
22
+ class="cms-v2-goods__tab"
23
+ :class="{ 'cms-v2-goods__tab--active': index === tabsCurrent }"
24
+ @click="$emit('tab-change', index, tabs)"
25
+ >
26
+ {{ tab.label }}
27
+ </view>
28
+ </scroll-view>
29
+ <scroll-view
30
+ v-if="data.bars.filter === 'custom' && data.bars.filterList.length"
31
+ scroll-x
32
+ class="cms-v2-goods__filters"
33
+ >
34
+ <view class="cms-v2-goods__filters-inner">
35
+ <view
36
+ v-for="(item, index) in data.bars.filterList"
37
+ :key="item.id || index"
38
+ class="cms-v2-goods__filter-chip"
39
+ >
40
+ {{ item.name }}
41
+ </view>
42
+ </view>
43
+ </scroll-view>
44
+ <snake-filter-sorts
45
+ v-else-if="data.bars.filter === true"
46
+ :goods-list-length="goodsList.length"
47
+ :filters="filtersData"
48
+ :bg-color="data.style.backgroundColor"
49
+ :show-tabs="data.bars.tabs.enabled && !!tabs.length"
50
+ :sort="data.query.sort"
51
+ :custom-style="{ padding: '0 20rpx' }"
52
+ @change="$emit('filter-change', $event)"
53
+ />
54
+ </snake-sticky>
55
+ <snake-goods-list
56
+ :goods-list="goodsList"
57
+ :list-type="data.display?.cardType || 'sale'"
58
+ :commodity-type="Number(data.display?.columns) === 3 ? 1 : 0"
59
+ :attendance-show="data.display?.showAttendance !== false"
60
+ :page-margin="data.style?.pageMargin"
61
+ :image-margin="data.style?.gap"
62
+ :bg-color="data.style?.backgroundColor"
63
+ :assets="assets"
64
+ :layout="data.display.layout"
65
+ :card-radius="data.style.radius"
66
+ :show-tag="data.display.showTag"
67
+ @item-click="(item: Record<string, any>, index: number) => $emit('item-click', item, index)"
68
+ @handler-offer="(item: Record<string, any>, index: number) => $emit('offer-click', item, index)"
69
+ />
70
+ <snake-loadmore v-if="goodsList.length" :status="loadingStatus" :bg-color="data.style?.backgroundColor" />
71
+ <snake-empty
72
+ v-else-if="status !== 'initial-loading'"
73
+ :icon="data.empty?.image"
74
+ :text="data.empty?.text || '列表为空'"
75
+ :text-color="data.empty?.color || '#000'"
76
+ />
77
+ </view>
78
+ </template>
79
+ <script setup lang="ts">
80
+ /** @component CmsV2GoodsList @description CMS v2 商品楼层内部渲染器。 @see /cms/overview */
81
+ import { computed } from 'vue';
82
+ import Empty from '../../../snake-empty/snake-empty.vue';
83
+ import ActivityList from '../../../snake-activity-list/snake-activity-list.vue';
84
+ import FilterSorts from '../../../snake-filter-sorts/snake-filter-sorts.vue';
85
+ import GoodsList from '../../../snake-goods-list/snake-goods-list.vue';
86
+ import Loadmore from '../../../snake-loadmore/snake-loadmore.vue';
87
+ import Sticky from '../../../snake-sticky/snake-sticky.vue';
88
+ import type { CmsAssets, CmsStatus } from '../../../../libs/cms';
89
+ import type { LoadmoreStatus } from '../../../snake-loadmore/types';
90
+ const props = defineProps<{
91
+ data: Record<string, any>;
92
+ goodsList: Record<string, any>[];
93
+ tabsCurrent: number;
94
+ loadingStatus: LoadmoreStatus;
95
+ status: CmsStatus;
96
+ assets?: CmsAssets;
97
+ filtersData: Record<string, any>;
98
+ activityList: Record<string, any>[];
99
+ activityCurrent: string | number;
100
+ scrollTop: number;
101
+ stickyOffsetTop: number;
102
+ stickyCustomNavHeight: number;
103
+ }>();
104
+ defineEmits<{
105
+ 'tab-change': [index: number, list: Record<string, any>[]];
106
+ 'item-click': [item: Record<string, any>, index: number];
107
+ 'offer-click': [item: Record<string, any>, index: number];
108
+ 'activity-change': [item: Record<string, any>];
109
+ 'filter-change': [params: Record<string, any>];
110
+ }>();
111
+ const tabs = computed(() => (Array.isArray(props.data.bars?.tabs?.list) ? props.data.bars.tabs.list : []));
112
+ const showBars = computed(
113
+ () => (props.data.bars?.tabs?.enabled && tabs.value.length) || Boolean(props.data.bars?.filter)
114
+ );
115
+ const rootStyle = computed(() => ({ backgroundColor: props.data.style?.backgroundColor || '#f8f8f8' }));
116
+ </script>
117
+ <style scoped>
118
+ .cms-v2-goods__tabs {
119
+ width: 100%;
120
+ white-space: nowrap;
121
+ }
122
+ .cms-v2-goods__tab {
123
+ display: inline-block;
124
+ padding: 20rpx 28rpx;
125
+ color: #606266;
126
+ }
127
+ .cms-v2-goods__tab--active {
128
+ color: var(--snake-color-primary, #06d290);
129
+ font-weight: 600;
130
+ }
131
+ .cms-v2-goods__filters {
132
+ width: 100%;
133
+ white-space: nowrap;
134
+ }
135
+ .cms-v2-goods__filters-inner {
136
+ display: inline-flex;
137
+ gap: 16rpx;
138
+ min-width: 100%;
139
+ padding: 16rpx 20rpx;
140
+ box-sizing: border-box;
141
+ }
142
+ .cms-v2-goods__filter-chip {
143
+ flex: 0 0 auto;
144
+ padding: 10rpx 24rpx;
145
+ border-radius: 999rpx;
146
+ background: rgba(0, 0, 0, 0.05);
147
+ font-size: 24rpx;
148
+ line-height: 32rpx;
149
+ }
150
+ </style>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <view class="cms-v2-hotzone">
3
+ <image class="cms-v2-hotzone__image" :src="data.image || ''" mode="widthFix" />
4
+ <view
5
+ v-for="(zone, index) in zones"
6
+ :key="zone.id ?? index"
7
+ class="cms-v2-hotzone__zone"
8
+ :style="zoneStyle(zone)"
9
+ @click="openV2Link(zone, linkService, pageInfo)"
10
+ />
11
+ </view>
12
+ </template>
13
+ <script setup lang="ts">
14
+ /** @component CmsV2Hotzone @description CMS v2 热区图片内部渲染器。 @see /cms/overview */
15
+ import { computed } from 'vue';
16
+ import { openV2Link, type V2BlockProps, type V2Record } from './shared';
17
+ const props = defineProps<V2BlockProps>();
18
+ const zones = computed(() => (Array.isArray(props.data.zones) ? props.data.zones : []));
19
+ const percent = (value: unknown) => `${Math.min(100, Math.max(0, Number(value) || 0))}%`;
20
+ const zoneStyle = (zone: V2Record) => ({
21
+ top: percent(zone.top),
22
+ left: percent(zone.left),
23
+ width: percent(zone.width),
24
+ height: percent(zone.height)
25
+ });
26
+ </script>
27
+ <style scoped>
28
+ .cms-v2-hotzone {
29
+ position: relative;
30
+ width: 100%;
31
+ line-height: 0;
32
+ }
33
+ .cms-v2-hotzone__image {
34
+ display: block;
35
+ width: 100%;
36
+ }
37
+ .cms-v2-hotzone__zone {
38
+ position: absolute;
39
+ z-index: 1;
40
+ }
41
+ </style>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <view class="cms-v2-images" :style="rootStyle">
3
+ <view class="cms-v2-images__grid" :style="gridStyle">
4
+ <image
5
+ v-for="(item, index) in items"
6
+ :key="item.id ?? index"
7
+ class="cms-v2-images__image"
8
+ :src="item.src || ''"
9
+ :mode="imageMode"
10
+ :style="imageStyle"
11
+ @click="openV2Link(item.link, linkService, pageInfo)"
12
+ />
13
+ </view>
14
+ </view>
15
+ </template>
16
+ <script setup lang="ts">
17
+ /** @component CmsV2Images @description CMS v2 单列与网格图片内部渲染器。 @see /cms/overview */
18
+ import { computed } from 'vue';
19
+ import { blockStyle, openV2Link, rpx, type V2BlockProps } from './shared';
20
+ const props = defineProps<V2BlockProps>();
21
+ const items = computed(() => (Array.isArray(props.data.items) ? props.data.items : []));
22
+ const rootStyle = computed(() => blockStyle(props.data.style));
23
+ const imageMode = computed(() => (props.data.style?.heightMode === 'fixed' ? 'aspectFill' : 'widthFix'));
24
+ const imageStyle = computed(() =>
25
+ props.data.style?.heightMode === 'fixed' ? { height: rpx(props.data.style?.height, 360) } : undefined
26
+ );
27
+ const gridStyle = computed(() => ({
28
+ gridTemplateColumns: `repeat(${props.data.layout?.mode === 'grid' ? Math.min(4, Math.max(1, Number(props.data.layout?.columns) || 2)) : 1}, minmax(0, 1fr))`,
29
+ gap: rpx(props.data.style?.gap)
30
+ }));
31
+ </script>
32
+ <style scoped>
33
+ .cms-v2-images__grid {
34
+ display: grid;
35
+ width: 100%;
36
+ }
37
+ .cms-v2-images__image {
38
+ display: block;
39
+ width: 100%;
40
+ }
41
+ </style>
@@ -0,0 +1,93 @@
1
+ <template>
2
+ <view class="cms-v2-magiccube" :style="rootStyle">
3
+ <view class="cms-v2-magiccube__grid" :style="gridStyle">
4
+ <image
5
+ v-for="(cell, index) in cells"
6
+ :key="index"
7
+ class="cms-v2-magiccube__cell"
8
+ :style="cellStyle(index)"
9
+ :src="activeImage(cell).src || ''"
10
+ mode="aspectFill"
11
+ @click="openV2Link(activeImage(cell).link, linkService, pageInfo)"
12
+ />
13
+ </view>
14
+ </view>
15
+ </template>
16
+ <script setup lang="ts">
17
+ /** @component CmsV2Magiccube @description CMS v2 魔方布局与定时投放内部渲染器。 @see /cms/overview */
18
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
19
+ import { blockStyle, openV2Link, rpx, type V2BlockProps, type V2Record } from './shared';
20
+ const props = defineProps<V2BlockProps>();
21
+ const now = ref(new Date());
22
+ const slideIndex = ref(0);
23
+ let scheduleTimer: ReturnType<typeof setInterval> | undefined;
24
+ let slideTimer: ReturnType<typeof setInterval> | undefined;
25
+ const layouts: Record<string, { template: string; columns: string; rows: string }> = {
26
+ grid2x2: { template: '"a b" "c d"', columns: '1fr 1fr', rows: '1fr 1fr' },
27
+ left1right2: { template: '"a b" "a c"', columns: '1fr 1fr', rows: '1fr 1fr' },
28
+ top1bottom2: { template: '"a a" "b c"', columns: '1fr 1fr', rows: '1fr 1fr' },
29
+ left1right3: { template: '"a b b" "a c d"', columns: '1fr 1fr 1fr', rows: '1fr 1fr' },
30
+ left2right1: { template: '"a c" "b c"', columns: '1fr 1fr', rows: '1fr 1fr' },
31
+ left3right1: { template: '"a a d" "b c d"', columns: '1fr 1fr 1fr', rows: '1fr 1fr' }
32
+ };
33
+ const cells = computed(() => (Array.isArray(props.data.cells) ? props.data.cells : []));
34
+ const rootStyle = computed(() => ({ ...blockStyle(props.data.style), height: rpx(props.data.style?.height, 188) }));
35
+ const gridStyle = computed(() => {
36
+ const layout = layouts[props.data.layout];
37
+ return layout
38
+ ? {
39
+ gridTemplateAreas: layout.template,
40
+ gridTemplateColumns: layout.columns,
41
+ gridTemplateRows: layout.rows,
42
+ gap: rpx(props.data.style?.gap)
43
+ }
44
+ : {
45
+ gridTemplateColumns: `repeat(${Math.min(6, Math.max(1, Number(props.data.columns) || 4))}, 1fr)`,
46
+ gap: rpx(props.data.style?.gap)
47
+ };
48
+ });
49
+ const cellStyle = (index: number) => ({
50
+ ...(props.data.layout === 'row' || !layouts[props.data.layout]
51
+ ? {}
52
+ : { gridArea: String.fromCharCode(97 + index) }),
53
+ borderRadius: rpx(props.data.style?.radius),
54
+ overflow: 'hidden'
55
+ });
56
+ function activeImage(cell: V2Record): V2Record {
57
+ const current = now.value;
58
+ const weekday = current.getDay() || 7;
59
+ const time = `${String(current.getHours()).padStart(2, '0')}:${String(current.getMinutes()).padStart(2, '0')}`;
60
+ const schedules = Array.isArray(cell.schedules) ? cell.schedules : [];
61
+ const active = schedules.filter(
62
+ (item: V2Record) =>
63
+ (!Array.isArray(item.weeks) || item.weeks.includes(weekday)) &&
64
+ (!item.startTime || item.startTime <= time) &&
65
+ (!item.endTime || item.endTime >= time)
66
+ );
67
+ return active.length ? active[slideIndex.value % active.length] : cell;
68
+ }
69
+ onMounted(() => {
70
+ scheduleTimer = setInterval(() => (now.value = new Date()), 30_000);
71
+ slideTimer = setInterval(
72
+ () => (slideIndex.value += 1),
73
+ Math.max(1000, Number(props.data.play?.interval || 3) * 1000)
74
+ );
75
+ });
76
+ onBeforeUnmount(() => {
77
+ clearInterval(scheduleTimer);
78
+ clearInterval(slideTimer);
79
+ });
80
+ </script>
81
+ <style scoped>
82
+ .cms-v2-magiccube__grid {
83
+ display: grid;
84
+ width: 100%;
85
+ height: 100%;
86
+ }
87
+ .cms-v2-magiccube__cell {
88
+ width: 100%;
89
+ height: 100%;
90
+ min-width: 0;
91
+ min-height: 0;
92
+ }
93
+ </style>
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <view class="cms-v2-notice" :style="rootStyle" @click="handleClick">
3
+ <text v-if="data.isLeftIcon !== false" class="cms-v2-notice__icon">📢</text>
4
+ <swiper v-if="data.direction === 'column'" class="cms-v2-notice__swiper" vertical autoplay circular>
5
+ <swiper-item
6
+ v-for="(item, index) in items"
7
+ :key="index"
8
+ @click.stop="openV2Link(item.link, linkService, pageInfo)"
9
+ >
10
+ <text class="cms-v2-notice__text" :style="textStyle">{{ item.text }}</text>
11
+ </swiper-item>
12
+ </swiper>
13
+ <view v-else class="cms-v2-notice__row">
14
+ <view class="cms-v2-notice__marquee" :style="marqueeStyle">
15
+ <text class="cms-v2-notice__text">{{ data.noticeText }}</text>
16
+ </view>
17
+ </view>
18
+ </view>
19
+ </template>
20
+ <script setup lang="ts">
21
+ /** @component CmsV2Notice @description CMS v2 公告内部渲染器。 @see /cms/overview */
22
+ import { computed } from 'vue';
23
+ import { openV2Link, rpx, type V2BlockProps } from './shared';
24
+ const props = defineProps<V2BlockProps>();
25
+ const items = computed(() => (Array.isArray(props.data.textList) ? props.data.textList : []));
26
+ const rootStyle = computed(() => ({
27
+ height: rpx(props.data.style?.height, 72),
28
+ margin: `0 ${rpx(props.data.style?.pageMargin, 10)}`,
29
+ color: props.data.style?.textColor || '#2d57e7',
30
+ backgroundColor: props.data.style?.backgroundColor || '#eaf1ff'
31
+ }));
32
+ const marqueeStyle = computed(() => ({
33
+ animationDuration: `${Math.max(8, String(props.data.noticeText || '').length * 0.35)}s`,
34
+ lineHeight: rpx(props.data.style?.height, 72)
35
+ }));
36
+ const textStyle = computed(() => ({ lineHeight: rpx(props.data.style?.height, 72) }));
37
+ const handleClick = () =>
38
+ props.data.direction === 'column' ? undefined : openV2Link(props.data.link, props.linkService, props.pageInfo);
39
+ </script>
40
+ <style scoped>
41
+ .cms-v2-notice {
42
+ display: flex;
43
+ box-sizing: border-box;
44
+ align-items: center;
45
+ overflow: hidden;
46
+ padding: 0 20rpx;
47
+ }
48
+ .cms-v2-notice__icon {
49
+ margin-right: 12rpx;
50
+ }
51
+ .cms-v2-notice__swiper,
52
+ .cms-v2-notice__row {
53
+ flex: 1;
54
+ height: 100%;
55
+ overflow: hidden;
56
+ }
57
+ .cms-v2-notice__text {
58
+ white-space: nowrap;
59
+ }
60
+ .cms-v2-notice__marquee {
61
+ display: inline-block;
62
+ min-width: 100%;
63
+ padding-left: 100%;
64
+ animation: cms-v2-notice-marquee 12s linear infinite;
65
+ }
66
+ @keyframes cms-v2-notice-marquee {
67
+ from {
68
+ transform: translateX(0);
69
+ }
70
+ to {
71
+ transform: translateX(-100%);
72
+ }
73
+ }
74
+ @media (prefers-reduced-motion: reduce) {
75
+ .cms-v2-notice__marquee {
76
+ padding-left: 0;
77
+ animation: none;
78
+ }
79
+ }
80
+ </style>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <snake-popup
3
+ v-if="data.image"
4
+ v-model="show"
5
+ :mode="data.popupMode === 'center' ? 'center' : 'bottom'"
6
+ :bg-color="data.popupMode === 'center' ? 'transparent' : '#fff'"
7
+ safe-area-inset-bottom
8
+ close-on-click-overlay
9
+ >
10
+ <view class="cms-v2-popup">
11
+ <image class="cms-v2-popup__image" :src="data.image || ''" mode="widthFix" @click="handleImageClick" />
12
+ <view class="cms-v2-popup__close" @click="show = false">×</view>
13
+ </view>
14
+ </snake-popup>
15
+ </template>
16
+ <script setup lang="ts">
17
+ /** @component CmsV2Popup @description CMS v2 页面弹窗内部渲染器。 @see /cms/overview */
18
+ import { ref } from 'vue';
19
+ import Popup from '../../../snake-popup/snake-popup.vue';
20
+ import { openV2Link, type V2BlockProps } from './shared';
21
+ const props = defineProps<V2BlockProps>();
22
+ const show = ref(true);
23
+ const handleImageClick = async () => {
24
+ await openV2Link(props.data.link, props.linkService, props.pageInfo);
25
+ show.value = false;
26
+ };
27
+ </script>
28
+ <style scoped>
29
+ .cms-v2-popup {
30
+ position: relative;
31
+ width: 560rpx;
32
+ max-width: 100%;
33
+ }
34
+ .cms-v2-popup__image {
35
+ display: block;
36
+ width: 100%;
37
+ }
38
+ .cms-v2-popup__close {
39
+ position: absolute;
40
+ top: -70rpx;
41
+ right: 0;
42
+ color: #fff;
43
+ font-size: 60rpx;
44
+ line-height: 60rpx;
45
+ }
46
+ </style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <view class="cms-v2-richtext" :style="rootStyle"><rich-text :nodes="safeContent" /></view>
3
+ </template>
4
+ <script setup lang="ts">
5
+ /** @component CmsV2Richtext @description CMS v2 富文本内部渲染器。 @see /cms/overview */
6
+ import { computed } from 'vue';
7
+ import { sanitizeRichText, type V2Record } from './shared';
8
+ const props = defineProps<{ data: V2Record }>();
9
+ const rootStyle = computed(() => ({ backgroundColor: props.data.style?.backgroundColor || '#fff' }));
10
+ const safeContent = computed(() => sanitizeRichText(props.data.content));
11
+ </script>
12
+ <style scoped>
13
+ .cms-v2-richtext {
14
+ box-sizing: border-box;
15
+ width: 100%;
16
+ overflow: hidden;
17
+ }
18
+ </style>
@@ -0,0 +1,115 @@
1
+ <template>
2
+ <view class="cms-v2-search" :style="rootStyle">
3
+ <view class="cms-v2-search__box" :style="boxStyle" @click="handleBoxClick">
4
+ <text class="cms-v2-search__symbol">⌕</text>
5
+ <input
6
+ class="cms-v2-search__input"
7
+ :type="data.inputType === 'number' ? 'number' : 'text'"
8
+ :placeholder="placeholder"
9
+ :disabled="!data.isSearch"
10
+ :value="keyword"
11
+ @input="handleInput"
12
+ @click.stop
13
+ @confirm="submitSearch"
14
+ />
15
+ <image
16
+ v-if="data.rightImage?.show && data.rightImage?.src"
17
+ class="cms-v2-search__right"
18
+ :src="data.rightImage.src"
19
+ mode="aspectFit"
20
+ :style="rightImageStyle"
21
+ @click.stop="openV2Link(data.rightImage.link, linkService, pageInfo)"
22
+ />
23
+ </view>
24
+ <image
25
+ v-for="(icon, index) in icons"
26
+ :key="index"
27
+ class="cms-v2-search__icon"
28
+ :src="icon.src || ''"
29
+ mode="aspectFit"
30
+ :style="iconStyle(icon)"
31
+ @click="openV2Link(icon.link, linkService, pageInfo)"
32
+ />
33
+ </view>
34
+ </template>
35
+ <script setup lang="ts">
36
+ /** @component CmsV2Search @description CMS v2 搜索框内部渲染器。 @see /cms/overview */
37
+ import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
38
+ import { openV2Link, rpx, type V2BlockProps } from './shared';
39
+ const props = defineProps<V2BlockProps>();
40
+ const icons = computed(() => (Array.isArray(props.data.icons) ? props.data.icons.slice(0, 2) : []));
41
+ const hotwordIndex = ref(0);
42
+ const hotwords = computed(() => (Array.isArray(props.data.textList) ? props.data.textList : []));
43
+ const placeholder = computed(
44
+ () => hotwords.value[hotwordIndex.value]?.text || props.data.searchText || '请输入搜索内容'
45
+ );
46
+ const keyword = ref('');
47
+ let hotwordTimer: ReturnType<typeof setInterval> | undefined;
48
+ const rootStyle = computed(() => ({
49
+ padding: `0 ${rpx(props.data.style?.pageMargin)}`,
50
+ backgroundColor: props.data.style?.backgroundColor || '#f9f9f9'
51
+ }));
52
+ const boxStyle = computed(() => ({
53
+ height: rpx(props.data.style?.height, 60),
54
+ color: props.data.style?.textColor || '#969799',
55
+ backgroundColor: props.data.style?.borderBgColor || '#fff',
56
+ border:
57
+ props.data.style?.showBorder === false
58
+ ? 'none'
59
+ : `${rpx(props.data.style?.borderSize, 1)} solid ${props.data.style?.borderColor || '#000'}`,
60
+ borderRadius: rpx(props.data.style?.borderRadius)
61
+ }));
62
+ const rightImageStyle = computed(() => ({
63
+ width: rpx(props.data.rightImage?.width, 60),
64
+ height: rpx(props.data.rightImage?.height, 60),
65
+ borderRadius: rpx(props.data.rightImage?.borderRadius),
66
+ marginRight: rpx(props.data.rightImage?.marginRight)
67
+ }));
68
+ const iconStyle = (icon: Record<string, any>) => ({ width: rpx(icon.width, 60), height: rpx(icon.height, 60) });
69
+ const handleInput = (event: any) => {
70
+ keyword.value = String(event?.detail?.value || '');
71
+ };
72
+ const handleBoxClick = () => {
73
+ if (!props.data.isSearch) return openV2Link(props.data.link, props.linkService, props.pageInfo);
74
+ };
75
+ const submitSearch = () => {
76
+ const hotword = hotwords.value[hotwordIndex.value];
77
+ const value = keyword.value.trim() || hotword?.text || '';
78
+ return openV2Link(hotword?.link || props.data.link, props.linkService, props.pageInfo, { keyword: value });
79
+ };
80
+ onMounted(() => {
81
+ hotwordTimer = setInterval(() => {
82
+ if (!keyword.value && hotwords.value.length > 1)
83
+ hotwordIndex.value = (hotwordIndex.value + 1) % hotwords.value.length;
84
+ }, 3000);
85
+ });
86
+ onBeforeUnmount(() => clearInterval(hotwordTimer));
87
+ </script>
88
+ <style scoped>
89
+ .cms-v2-search {
90
+ display: flex;
91
+ box-sizing: border-box;
92
+ align-items: center;
93
+ gap: 12rpx;
94
+ width: 100%;
95
+ }
96
+ .cms-v2-search__box {
97
+ display: flex;
98
+ flex: 1;
99
+ align-items: center;
100
+ height: 100%;
101
+ padding: 0 16rpx;
102
+ }
103
+ .cms-v2-search__input {
104
+ flex: 1;
105
+ min-width: 0;
106
+ }
107
+ .cms-v2-search__symbol {
108
+ margin-right: 10rpx;
109
+ font-size: 32rpx;
110
+ }
111
+ .cms-v2-search__icon {
112
+ width: 60rpx;
113
+ height: 60rpx;
114
+ }
115
+ </style>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <view class="cms-v2-video" :style="rootStyle">
3
+ <video
4
+ class="cms-v2-video__media"
5
+ :style="mediaStyle"
6
+ :src="data.src || ''"
7
+ :poster="data.coverUrl || ''"
8
+ :autoplay="!!data.autoplay"
9
+ />
10
+ </view>
11
+ </template>
12
+ <script setup lang="ts">
13
+ /** @component CmsV2Video @description CMS v2 视频内部渲染器。 @see /cms/overview */
14
+ import { computed } from 'vue';
15
+ import { blockStyle, mediaHeightStyle, type V2Record } from './shared';
16
+ const props = defineProps<{ data: V2Record }>();
17
+ const rootStyle = computed(() => blockStyle(props.data.style));
18
+ const mediaStyle = computed(() => mediaHeightStyle(props.data.style));
19
+ </script>
20
+ <style scoped>
21
+ .cms-v2-video__media {
22
+ display: block;
23
+ width: 100%;
24
+ }
25
+ </style>