@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,434 @@
1
+ <template>
2
+ <snake-popup
3
+ v-model="popupValue"
4
+ mode="center"
5
+ :zoom="effectiveConfig.zoom"
6
+ :z-index="uZIndex"
7
+ :close-on-click-overlay="effectiveConfig.maskCloseAble"
8
+ :custom-style="popupStyle"
9
+ :custom-class="effectiveConfig.customClass"
10
+ @close="popupClose"
11
+ >
12
+ <view class="snake-modal" :style="toStyle(effectiveConfig.customStyle)">
13
+ <view
14
+ v-if="effectiveConfig.showTitle"
15
+ class="snake-modal__title snake-modal__line-1"
16
+ :style="toStyle(effectiveConfig.titleStyle)"
17
+ >
18
+ {{ effectiveConfig.title }}
19
+ </view>
20
+ <view class="snake-modal__content">
21
+ <view v-if="slots.default" :style="toStyle(effectiveConfig.contentStyle)">
22
+ <slot />
23
+ </view>
24
+ <view v-else class="snake-modal__content__message" :style="toStyle(effectiveConfig.contentStyle)">
25
+ <text>{{ effectiveConfig.content }}</text>
26
+ </view>
27
+ </view>
28
+ <view
29
+ v-if="effectiveConfig.showCancelButton || effectiveConfig.showConfirmButton"
30
+ class="snake-modal__footer"
31
+ >
32
+ <view
33
+ v-if="effectiveConfig.showCancelButton"
34
+ :hover-stay-time="100"
35
+ hover-class="snake-modal__btn--hover"
36
+ class="snake-modal__footer__button"
37
+ :style="toStyle(cancelBtnStyle)"
38
+ @tap="cancel"
39
+ >
40
+ {{ effectiveConfig.cancelText }}
41
+ </view>
42
+ <view
43
+ v-if="effectiveConfig.showConfirmButton || slots['confirm-button']"
44
+ :class="[
45
+ 'snake-modal__footer__button',
46
+ effectiveConfig.showCancelButton ? 'snake-modal__footer__button--border-left' : ''
47
+ ]"
48
+ :hover-stay-time="100"
49
+ :hover-class="effectiveConfig.asyncClose ? 'none' : 'snake-modal__btn--hover'"
50
+ :style="toStyle(confirmBtnStyle)"
51
+ @tap="confirm"
52
+ >
53
+ <slot v-if="slots['confirm-button']" name="confirm-button"></slot>
54
+ <template v-else>
55
+ <snake-loading
56
+ v-if="loading"
57
+ type="circular"
58
+ :size="20"
59
+ :color="effectiveConfig.confirmColor"
60
+ ></snake-loading>
61
+ <template v-else>{{ effectiveConfig.confirmText }}</template>
62
+ </template>
63
+ </view>
64
+ </view>
65
+ </view>
66
+ </snake-popup>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ /**
71
+ * @component Modal
72
+ * @description 弹出模态框,支持声明式 v-model 与函数式 useModal() 调用,用于消息提示、消息确认及页面内交互。
73
+ * @see /components/modal
74
+ */
75
+
76
+ import { computed, onBeforeUnmount, onMounted, ref, useSlots, watch } from 'vue';
77
+ import { onPageHide, onPageShow } from '@dcloudio/uni-app';
78
+ import zIndex from '../../libs/config/zIndex';
79
+ import { addUnit } from '../../libs/function/unit';
80
+ import { toStyle } from '../../libs/function/style';
81
+ import { useLocale } from '../../libs/hooks/useLocale';
82
+ import { emitter } from '../../libs/function/emitter';
83
+ import Popup from '../snake-popup/snake-popup.vue';
84
+ import SnakeLoading from '../snake-loading/snake-loading.vue';
85
+ import { modalProps } from './types';
86
+ import type { ModalEmits } from './types';
87
+ import {
88
+ SNAKE_MODAL_EVENT_CLEAR_LOADING,
89
+ SNAKE_MODAL_EVENT_HIDE,
90
+ SNAKE_MODAL_EVENT_SHOW,
91
+ SNAKE_MODAL_GLOBAL_EVENT_CLEAR_LOADING,
92
+ SNAKE_MODAL_GLOBAL_EVENT_HIDE,
93
+ SNAKE_MODAL_GLOBAL_EVENT_SHOW,
94
+ getEventWithCurrentPage,
95
+ type ModalPayload
96
+ } from '../../libs/config/modal';
97
+
98
+ defineOptions({ name: 'Modal', options: { addGlobalClass: true, virtualHost: true, styleIsolation: 'shared' } });
99
+
100
+ const props = defineProps(modalProps);
101
+ const emit = defineEmits<ModalEmits>();
102
+ const slots = useSlots();
103
+
104
+ const { t } = useLocale('modal');
105
+
106
+ // 是否已经初始化事件监听
107
+ const isInit = ref(false);
108
+ // 确认按钮是否正在加载中
109
+ const loading = ref(false);
110
+ const isGlobal = computed(() => !!props.global);
111
+ const isPage = computed(() => !!props.page);
112
+ const showEvent = computed(() =>
113
+ isGlobal.value
114
+ ? SNAKE_MODAL_GLOBAL_EVENT_SHOW
115
+ : isPage.value
116
+ ? getEventWithCurrentPage(SNAKE_MODAL_EVENT_SHOW, props.page)
117
+ : ''
118
+ );
119
+ const hideEvent = computed(() =>
120
+ isGlobal.value
121
+ ? SNAKE_MODAL_GLOBAL_EVENT_HIDE
122
+ : isPage.value
123
+ ? getEventWithCurrentPage(SNAKE_MODAL_EVENT_HIDE, props.page)
124
+ : ''
125
+ );
126
+ const clearLoadingEvent = computed(() =>
127
+ isGlobal.value
128
+ ? SNAKE_MODAL_GLOBAL_EVENT_CLEAR_LOADING
129
+ : isPage.value
130
+ ? getEventWithCurrentPage(SNAKE_MODAL_EVENT_CLEAR_LOADING, props.page)
131
+ : ''
132
+ );
133
+
134
+ // 存储用户传入的回调函数
135
+ let userOnConfirm: (() => void) | null = null;
136
+ let userOnCancel: (() => void) | null = null;
137
+
138
+ // 需要与 effectiveConfig 合并的 props 键名列表(用于函数式调用)
139
+ const MERGE_PROPS_KEYS = [
140
+ 'title',
141
+ 'content',
142
+ 'showTitle',
143
+ 'showConfirmButton',
144
+ 'showCancelButton',
145
+ 'confirmText',
146
+ 'cancelText',
147
+ 'confirmColor',
148
+ 'cancelColor',
149
+ 'confirmStyle',
150
+ 'cancelStyle',
151
+ 'titleStyle',
152
+ 'contentStyle',
153
+ 'asyncClose',
154
+ 'borderRadius',
155
+ 'width',
156
+ 'zoom',
157
+ 'maskCloseAble',
158
+ 'negativeTop',
159
+ 'zIndex',
160
+ 'customStyle',
161
+ 'customClass'
162
+ ] as const;
163
+
164
+ // 函数式调用时的临时配置
165
+ const tempConfig = ref<Partial<ModalPayload>>({});
166
+
167
+ // 函数式调用时的内部显示状态(用于 global/page 模式)
168
+ const internalShow = ref(false);
169
+
170
+ // 国际化计算属性
171
+ const getTitle = computed(() => props.title || t('title'));
172
+ const getConfirmText = computed(() => props.confirmText || t('confirmText'));
173
+ const getCancelText = computed(() => props.cancelText || t('cancelText'));
174
+
175
+ // 有效的配置(函数式调用时合并 tempConfig 和 props,v-model 时使用 props)
176
+ const effectiveConfig = computed(() => {
177
+ if (Object.keys(tempConfig.value).length > 0) {
178
+ const result: Record<string, any> = {};
179
+ for (const key of MERGE_PROPS_KEYS) {
180
+ result[key] = (tempConfig.value as Record<string, any>)[key] ?? (props as Record<string, any>)[key];
181
+ }
182
+ result.zIndex = tempConfig.value.zIndex ?? props.zIndex ?? zIndex.popup;
183
+ result.title = result.title || t('title');
184
+ result.confirmText = result.confirmText || t('confirmText');
185
+ result.cancelText = result.cancelText || t('cancelText');
186
+ return result;
187
+ }
188
+ return {
189
+ ...props,
190
+ title: getTitle.value,
191
+ confirmText: getConfirmText.value,
192
+ cancelText: getCancelText.value
193
+ };
194
+ });
195
+
196
+ // 取消按钮样式
197
+ const cancelBtnStyle = computed(() =>
198
+ Object.assign({ color: effectiveConfig.value.cancelColor }, effectiveConfig.value.cancelStyle)
199
+ );
200
+ // 确认按钮样式
201
+ const confirmBtnStyle = computed(() =>
202
+ Object.assign({ color: effectiveConfig.value.confirmColor }, effectiveConfig.value.confirmStyle)
203
+ );
204
+ // 弹窗层级
205
+ const uZIndex = computed(() => Number(effectiveConfig.value.zIndex || zIndex.popup));
206
+ // 弹窗自定义样式(宽度 / 圆角 / 键盘避让)
207
+ const popupStyle = computed(() => {
208
+ const style: Record<string, string> = {
209
+ width: addUnit(effectiveConfig.value.width, 'rpx'),
210
+ borderRadius: addUnit(effectiveConfig.value.borderRadius, 'rpx')
211
+ };
212
+ if (effectiveConfig.value.negativeTop) {
213
+ style.marginTop = `-${addUnit(effectiveConfig.value.negativeTop, 'rpx')}`;
214
+ }
215
+ return style;
216
+ });
217
+ // 是否使用内部状态控制显示(global/page 模式)
218
+ const useInternalShow = computed(() => isGlobal.value || isPage.value);
219
+ // 最终显示状态
220
+ const finalShow = computed(() => {
221
+ if (useInternalShow.value) {
222
+ return internalShow.value;
223
+ }
224
+ return props.modelValue;
225
+ });
226
+ // snake-popup 绑定的值
227
+ const popupValue = computed({
228
+ get: () => finalShow.value,
229
+ set: (val: boolean) => {
230
+ if (useInternalShow.value) {
231
+ internalShow.value = val;
232
+ } else {
233
+ emit('update:modelValue', val);
234
+ }
235
+ }
236
+ });
237
+
238
+ // 异步关闭时,外部修改 v-model 为 false 后重置 loading,避免下次打开状态混乱
239
+ watch(
240
+ () => popupValue.value,
241
+ n => {
242
+ if (n === true) loading.value = false;
243
+ }
244
+ );
245
+
246
+ /**
247
+ * 确认按钮点击事件
248
+ */
249
+ function confirm() {
250
+ const onConfirm = userOnConfirm;
251
+ if (effectiveConfig.value.asyncClose) {
252
+ loading.value = true;
253
+ } else {
254
+ popupValue.value = false;
255
+ // 延迟重置配置,避免闪屏
256
+ setTimeout(() => resetTempConfig(), 300);
257
+ }
258
+ emit('confirm');
259
+ onConfirm?.();
260
+ }
261
+
262
+ /**
263
+ * 取消按钮点击事件
264
+ */
265
+ function cancel() {
266
+ const onCancel = userOnCancel;
267
+ // 延迟重置配置,避免闪屏
268
+ setTimeout(() => resetTempConfig(), 300);
269
+ emit('cancel');
270
+ popupValue.value = false;
271
+ onCancel?.();
272
+ // 避免确认按钮文字恢复成默认值后 modal 还没消失
273
+ setTimeout(() => {
274
+ loading.value = false;
275
+ }, 300);
276
+ }
277
+
278
+ /**
279
+ * 点击遮罩关闭 modal
280
+ */
281
+ function popupClose() {
282
+ popupValue.value = false;
283
+ resetTempConfig();
284
+ }
285
+
286
+ /**
287
+ * 清除加载中的状态
288
+ */
289
+ function clearLoading() {
290
+ loading.value = false;
291
+ }
292
+
293
+ /**
294
+ * 函数式调用显示 modal
295
+ */
296
+ function onServiceShow(payload: ModalPayload) {
297
+ userOnConfirm = payload.onConfirm ?? null;
298
+ userOnCancel = payload.onCancel ?? null;
299
+
300
+ // 只保存用户传入的配置(过滤掉回调)
301
+ const config = { ...payload };
302
+ delete config.onConfirm;
303
+ delete config.onCancel;
304
+ tempConfig.value = config;
305
+
306
+ internalShow.value = true;
307
+ }
308
+
309
+ /**
310
+ * 函数式调用关闭 modal
311
+ */
312
+ function onServiceHide() {
313
+ internalShow.value = false;
314
+ resetTempConfig();
315
+ }
316
+
317
+ /**
318
+ * 重置临时配置
319
+ */
320
+ function resetTempConfig() {
321
+ tempConfig.value = {};
322
+ userOnConfirm = null;
323
+ userOnCancel = null;
324
+ }
325
+
326
+ // 开始监听事件
327
+ function startListeners() {
328
+ if (isInit.value) {
329
+ return;
330
+ }
331
+ isInit.value = true;
332
+ if (showEvent.value) {
333
+ emitter.on(showEvent.value, onServiceShow);
334
+ }
335
+ if (hideEvent.value) {
336
+ emitter.on(hideEvent.value, onServiceHide);
337
+ }
338
+ if (clearLoadingEvent.value) {
339
+ emitter.on(clearLoadingEvent.value, clearLoading);
340
+ }
341
+ }
342
+
343
+ // 停止监听事件
344
+ function stopListeners() {
345
+ if (!isInit.value) {
346
+ return;
347
+ }
348
+ isInit.value = false;
349
+ if (showEvent.value) {
350
+ emitter.off(showEvent.value, onServiceShow);
351
+ }
352
+ if (hideEvent.value) {
353
+ emitter.off(hideEvent.value, onServiceHide);
354
+ }
355
+ if (clearLoadingEvent.value) {
356
+ emitter.off(clearLoadingEvent.value, clearLoading);
357
+ }
358
+ }
359
+
360
+ onPageShow(() => {
361
+ startListeners();
362
+ });
363
+
364
+ onPageHide(() => {
365
+ stopListeners();
366
+ });
367
+
368
+ onMounted(() => {
369
+ startListeners();
370
+ });
371
+
372
+ onBeforeUnmount(() => {
373
+ stopListeners();
374
+ });
375
+
376
+ defineExpose({
377
+ clearLoading
378
+ });
379
+ </script>
380
+
381
+ <style scoped lang="scss">
382
+ .snake-modal {
383
+ height: auto;
384
+ overflow: hidden;
385
+ font-size: 32rpx;
386
+ background-color: var(--snake-color-surface, #fff);
387
+
388
+ &__btn--hover {
389
+ background-color: rgb(230, 230, 230);
390
+ }
391
+
392
+ &__line-1 {
393
+ overflow: hidden;
394
+ white-space: nowrap;
395
+ text-overflow: ellipsis;
396
+ }
397
+
398
+ &__title {
399
+ padding-top: 48rpx;
400
+ font-weight: 500;
401
+ text-align: center;
402
+ color: var(--snake-color-text, #303133);
403
+ }
404
+
405
+ &__content {
406
+ &__message {
407
+ padding: 48rpx;
408
+ font-size: 30rpx;
409
+ text-align: center;
410
+ color: var(--snake-color-text-secondary, #909399);
411
+ }
412
+ }
413
+
414
+ &__footer {
415
+ display: flex;
416
+ border-top: 1rpx solid var(--snake-color-border, #f6f6f6);
417
+
418
+ &__button {
419
+ flex: 1;
420
+ height: 100rpx;
421
+ line-height: 100rpx;
422
+ font-size: 32rpx;
423
+ box-sizing: border-box;
424
+ cursor: pointer;
425
+ text-align: center;
426
+ border-radius: 4rpx;
427
+
428
+ &--border-left {
429
+ border-left: 1rpx solid var(--snake-color-border, #f6f6f6);
430
+ }
431
+ }
432
+ }
433
+ }
434
+ </style>
@@ -0,0 +1,71 @@
1
+ import type { ExtractPropTypes, PropType } from 'vue';
2
+
3
+ /** modalProps 运行时属性及默认值。 */
4
+ export const modalProps = {
5
+ /** 根节点自定义样式。 */
6
+ customStyle: {
7
+ type: [String, Object, Array] as PropType<string | Record<string, any> | unknown[]>,
8
+ default: () => ({})
9
+ },
10
+ /** 根节点自定义类名。 */
11
+ customClass: { type: String, default: '' },
12
+ /** 是否显示模态框(v-model)。 */
13
+ modelValue: { type: Boolean, default: false },
14
+ /** 层级 z-index。 */
15
+ zIndex: { type: [Number, String] as PropType<number | string>, default: '' },
16
+ /** 标题。 */
17
+ title: { type: String, default: '' },
18
+ /** 弹窗宽度,单位 rpx。 */
19
+ width: { type: [Number, String] as PropType<number | string>, default: 600 },
20
+ /** 弹窗内容。 */
21
+ content: { type: String, default: '' },
22
+ /** 是否显示标题。 */
23
+ showTitle: { type: Boolean, default: true },
24
+ /** 是否显示确认按钮。 */
25
+ showConfirmButton: { type: Boolean, default: true },
26
+ /** 是否显示取消按钮。 */
27
+ showCancelButton: { type: Boolean, default: false },
28
+ /** 确认按钮文案。 */
29
+ confirmText: { type: String, default: '' },
30
+ /** 取消按钮文案。 */
31
+ cancelText: { type: String, default: '' },
32
+ /** 确认按钮颜色。 */
33
+ confirmColor: { type: String, default: 'var(--snake-color-primary, #06D290)' },
34
+ /** 取消文字颜色。 */
35
+ cancelColor: { type: String, default: 'var(--snake-color-text-secondary, #909399)' },
36
+ /** 弹窗圆角值,单位 rpx。 */
37
+ borderRadius: { type: [Number, String] as PropType<number | string>, default: 16 },
38
+ /** 标题样式。 */
39
+ titleStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
40
+ /** 内容样式。 */
41
+ contentStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
42
+ /** 取消按钮样式。 */
43
+ cancelStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
44
+ /** 确定按钮样式。 */
45
+ confirmStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
46
+ /** 是否开启缩放效果。 */
47
+ zoom: { type: Boolean, default: true },
48
+ /** 是否异步关闭,只对确定按钮有效。 */
49
+ asyncClose: { type: Boolean, default: false },
50
+ /** 是否允许点击遮罩关闭。 */
51
+ maskCloseAble: { type: Boolean, default: false },
52
+ /** 往上偏移的值,避免和键盘重合。 */
53
+ negativeTop: { type: [String, Number] as PropType<number | string>, default: 0 },
54
+ /** 是否作为全局根部 modal(配合 useModal() 使用)。 */
55
+ global: { type: Boolean, default: false },
56
+ /** 是否作为页面级 modal(配合 useModal({ page: true }) 使用)。 */
57
+ page: { type: [Boolean, String] as PropType<boolean | string>, default: false }
58
+ };
59
+
60
+ /** Modal 组件公开属性。 */
61
+ export type ModalProps = ExtractPropTypes<typeof modalProps>;
62
+
63
+ /** Modal 组件的事件契约。 */
64
+ export type ModalEmits = {
65
+ /** update:modelValue 事件携带的参数。 */
66
+ 'update:modelValue': [value: boolean];
67
+ /** confirm 事件。 */
68
+ confirm: [];
69
+ /** cancel 事件。 */
70
+ cancel: [];
71
+ };
@@ -0,0 +1,194 @@
1
+ <template>
2
+ <view class="snake-navbar-host">
3
+ <view
4
+ :class="['snake-navbar', { 'is-fixed': fixed, 'has-border': borderBottom }, customClass]"
5
+ :style="rootStyle"
6
+ >
7
+ <view class="snake-navbar__status" :style="{ height: statusBarHeight + 'px' }" />
8
+ <view class="snake-navbar__inner" :style="innerStyle">
9
+ <view class="snake-navbar__left">
10
+ <view
11
+ v-if="isBack"
12
+ class="snake-navbar__back"
13
+ hover-class="snake-navbar__hit--hover"
14
+ @click="goBack"
15
+ >
16
+ <snake-icon :name="backIconName" :size="backIconSize ?? 'inherit'" :color="backIconColor" />
17
+ <text v-if="backText" class="snake-navbar__back-text" :style="backTextStyle">
18
+ {{ backText }}
19
+ </text>
20
+ </view>
21
+ <slot name="left" />
22
+ </view>
23
+
24
+ <view v-if="title" class="snake-navbar__title" :style="titleStyle">{{ title }}</view>
25
+ <view v-else class="snake-navbar__content"><slot /></view>
26
+
27
+ <view class="snake-navbar__right"><slot name="right" /></view>
28
+ </view>
29
+ </view>
30
+ <view
31
+ v-if="fixed && !immersive"
32
+ class="snake-navbar__placeholder"
33
+ :style="{ height: placeholderHeight + 'px' }"
34
+ />
35
+ </view>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ /**
40
+ * @component Navbar
41
+ * @description 用于页面顶部导航,支持状态栏安全区、返回操作、固定定位、标题及左右插槽。
42
+ * @see /components/navbar
43
+ */
44
+
45
+ import { computed } from 'vue';
46
+ import zIndex from '../../libs/config/zIndex';
47
+ import { addUnit } from '../../libs/function/unit';
48
+ import Icon from '../snake-icon/snake-icon.vue';
49
+ import type { NavbarEmits, NavbarProps } from './types';
50
+
51
+ defineOptions({ name: 'Navbar', options: { virtualHost: true, styleIsolation: 'shared' } });
52
+
53
+ const props = withDefaults(defineProps<NavbarProps>(), {
54
+ title: '',
55
+ height: 44,
56
+ backIconColor: 'var(--snake-color-text, #303133)',
57
+ backIconName: 'arrow-left',
58
+ backIconSize: 40,
59
+ backText: '',
60
+ backTextStyle: '',
61
+ titleWidth: 360,
62
+ titleColor: 'var(--snake-color-text, #303133)',
63
+ titleSize: 32,
64
+ titleBold: false,
65
+ isBack: true,
66
+ background: 'var(--snake-color-surface, #fff)',
67
+ fixed: true,
68
+ immersive: false,
69
+ safeAreaInsetTop: true,
70
+ borderBottom: true,
71
+ zIndex: zIndex.navbar,
72
+ customBack: null,
73
+ customClass: '',
74
+ customStyle: ''
75
+ });
76
+ const emit = defineEmits<NavbarEmits>();
77
+
78
+ const systemInfo = uni.getSystemInfoSync();
79
+ const statusBarHeight = computed(() => (props.safeAreaInsetTop ? Number(systemInfo.statusBarHeight) || 0 : 0));
80
+ const navbarHeight = computed(() => Number.parseFloat(String(props.height)) || 44);
81
+ const placeholderHeight = computed(() => navbarHeight.value + statusBarHeight.value);
82
+ const backgroundStyle = computed(() =>
83
+ typeof props.background === 'string' ? { background: props.background } : props.background
84
+ );
85
+ const rootStyle = computed(() => [
86
+ backgroundStyle.value,
87
+ { zIndex: Number(props.zIndex) || zIndex.navbar },
88
+ props.customStyle
89
+ ]);
90
+ const innerStyle = computed(() => ({ height: navbarHeight.value + 'px' }));
91
+ const titleStyle = computed(() => ({
92
+ width: addUnit(props.titleWidth, 'rpx'),
93
+ color: props.titleColor,
94
+ fontSize: addUnit(props.titleSize, 'rpx'),
95
+ fontWeight: props.titleBold ? '700' : '500'
96
+ }));
97
+
98
+ function goBack(event: unknown) {
99
+ emit('back', event);
100
+ if (props.customBack) {
101
+ props.customBack();
102
+ return;
103
+ }
104
+ uni.navigateBack();
105
+ }
106
+ </script>
107
+
108
+ <style scoped>
109
+ .snake-navbar-host,
110
+ .snake-navbar {
111
+ box-sizing: border-box;
112
+ width: 100%;
113
+ }
114
+ .snake-navbar {
115
+ position: relative;
116
+ }
117
+ .snake-navbar.is-fixed {
118
+ position: fixed;
119
+ top: 0;
120
+ right: 0;
121
+ left: 0;
122
+ }
123
+ .snake-navbar.has-border {
124
+ border-bottom: 1rpx solid var(--snake-color-divider, #ebeef5);
125
+ }
126
+ .snake-navbar__status {
127
+ width: 100%;
128
+ }
129
+ .snake-navbar__inner {
130
+ position: relative;
131
+ display: flex;
132
+ box-sizing: border-box;
133
+ align-items: center;
134
+ justify-content: space-between;
135
+ width: 100%;
136
+ }
137
+ .snake-navbar__left,
138
+ .snake-navbar__right {
139
+ position: relative;
140
+ z-index: 2;
141
+ display: flex;
142
+ flex: 1;
143
+ align-items: center;
144
+ min-width: 0;
145
+ height: 100%;
146
+ }
147
+ .snake-navbar__right {
148
+ justify-content: flex-end;
149
+ }
150
+ .snake-navbar__back {
151
+ display: flex;
152
+ align-items: center;
153
+ min-width: 88rpx;
154
+ height: 100%;
155
+ padding: 0 20rpx;
156
+ }
157
+ .snake-navbar__back-text {
158
+ overflow: hidden;
159
+ margin-left: 4rpx;
160
+ font-size: 28rpx;
161
+ line-height: 1;
162
+ text-overflow: ellipsis;
163
+ white-space: nowrap;
164
+ }
165
+ .snake-navbar__title,
166
+ .snake-navbar__content {
167
+ position: absolute;
168
+ top: 0;
169
+ bottom: 0;
170
+ left: 50%;
171
+ z-index: 1;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ max-width: calc(100% - 192rpx);
176
+ text-align: center;
177
+ transform: translateX(-50%);
178
+ }
179
+ .snake-navbar__title {
180
+ overflow: hidden;
181
+ line-height: 1.2;
182
+ text-overflow: ellipsis;
183
+ white-space: nowrap;
184
+ }
185
+ .snake-navbar__content {
186
+ width: calc(100% - 192rpx);
187
+ }
188
+ .snake-navbar__hit--hover {
189
+ background: rgba(0, 0, 0, 0.06);
190
+ }
191
+ .snake-navbar__placeholder {
192
+ width: 100%;
193
+ }
194
+ </style>