@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.
- package/LICENSE +21 -0
- package/changelog.md +1 -0
- package/components/snake-activity-list/snake-activity-list.vue +365 -0
- package/components/snake-activity-list/types.ts +29 -0
- package/components/snake-auxiliary-segmentation/snake-auxiliary-segmentation.vue +44 -0
- package/components/snake-auxiliary-segmentation/types.ts +5 -0
- package/components/snake-button/snake-button.vue +357 -0
- package/components/snake-button/types.ts +103 -0
- package/components/snake-button-sort/snake-button-sort.vue +72 -0
- package/components/snake-button-sort/types.ts +22 -0
- package/components/snake-calendar/snake-calendar.vue +998 -0
- package/components/snake-calendar/types.ts +189 -0
- package/components/snake-cell-group/snake-cell-group.vue +55 -0
- package/components/snake-cell-group/types.ts +19 -0
- package/components/snake-cell-item/snake-cell-item.vue +159 -0
- package/components/snake-cell-item/types.ts +66 -0
- package/components/snake-checkbox/snake-checkbox.vue +264 -0
- package/components/snake-checkbox/types.ts +70 -0
- package/components/snake-checkbox-group/snake-checkbox-group.vue +154 -0
- package/components/snake-checkbox-group/types.ts +52 -0
- package/components/snake-cms/blocks/v2/cms-v2-carousel.vue +87 -0
- package/components/snake-cms/blocks/v2/cms-v2-coupon.vue +25 -0
- package/components/snake-cms/blocks/v2/cms-v2-custom.vue +12 -0
- package/components/snake-cms/blocks/v2/cms-v2-divider.vue +33 -0
- package/components/snake-cms/blocks/v2/cms-v2-floatbutton.vue +28 -0
- package/components/snake-cms/blocks/v2/cms-v2-goods-list.vue +150 -0
- package/components/snake-cms/blocks/v2/cms-v2-hotzone.vue +41 -0
- package/components/snake-cms/blocks/v2/cms-v2-images.vue +41 -0
- package/components/snake-cms/blocks/v2/cms-v2-magiccube.vue +93 -0
- package/components/snake-cms/blocks/v2/cms-v2-notice.vue +80 -0
- package/components/snake-cms/blocks/v2/cms-v2-popup.vue +46 -0
- package/components/snake-cms/blocks/v2/cms-v2-richtext.vue +18 -0
- package/components/snake-cms/blocks/v2/cms-v2-search.vue +115 -0
- package/components/snake-cms/blocks/v2/cms-v2-video.vue +25 -0
- package/components/snake-cms/blocks/v2/shared.ts +84 -0
- package/components/snake-cms/snake-cms.vue +409 -0
- package/components/snake-cms/types.ts +50 -0
- package/components/snake-commodity-search/snake-commodity-search.vue +234 -0
- package/components/snake-commodity-search/types.ts +11 -0
- package/components/snake-config-provider/snake-config-provider.vue +61 -0
- package/components/snake-config-provider/types.ts +22 -0
- package/components/snake-count-down/snake-count-down.vue +231 -0
- package/components/snake-count-down/types.ts +103 -0
- package/components/snake-coupon/snake-coupon.vue +209 -0
- package/components/snake-coupon/types.ts +21 -0
- package/components/snake-custom-module/snake-custom-module.vue +25 -0
- package/components/snake-custom-module/types.ts +11 -0
- package/components/snake-drop-menu/snake-drop-menu.vue +123 -0
- package/components/snake-drop-menu/types.ts +95 -0
- package/components/snake-drop-menu-item/snake-drop-menu-item.vue +181 -0
- package/components/snake-drop-menu-item/types.ts +48 -0
- package/components/snake-empty/snake-empty.vue +100 -0
- package/components/snake-empty/types.ts +48 -0
- package/components/snake-filter-sorts/filter-page.vue +695 -0
- package/components/snake-filter-sorts/snake-filter-sorts.vue +185 -0
- package/components/snake-filter-sorts/types.ts +57 -0
- package/components/snake-floating-bubble/snake-floating-bubble.vue +157 -0
- package/components/snake-floating-bubble/types.ts +43 -0
- package/components/snake-follow/snake-follow.vue +18 -0
- package/components/snake-follow/types.ts +2 -0
- package/components/snake-form/snake-form.vue +124 -0
- package/components/snake-form/types.ts +96 -0
- package/components/snake-form-item/snake-form-item.vue +331 -0
- package/components/snake-form-item/types.ts +70 -0
- package/components/snake-goods-list/snake-goods-list.vue +792 -0
- package/components/snake-goods-list/types.ts +55 -0
- package/components/snake-graphic-navigation/snake-graphic-navigation.vue +198 -0
- package/components/snake-graphic-navigation/types.ts +43 -0
- package/components/snake-icon/icon-names.ts +222 -0
- package/components/snake-icon/snake-icon.vue +220 -0
- package/components/snake-icon/types.ts +45 -0
- package/components/snake-image/snake-image.vue +230 -0
- package/components/snake-image/types.ts +70 -0
- package/components/snake-images/snake-images.vue +80 -0
- package/components/snake-images/types.ts +5 -0
- package/components/snake-index-anchor/snake-index-anchor.vue +83 -0
- package/components/snake-index-anchor/types.ts +14 -0
- package/components/snake-index-list/anchor-registry.ts +80 -0
- package/components/snake-index-list/snake-index-list.vue +259 -0
- package/components/snake-index-list/types.ts +115 -0
- package/components/snake-index-list-panel/snake-index-list-panel.vue +277 -0
- package/components/snake-index-list-panel/types.ts +10 -0
- package/components/snake-input/snake-input.vue +225 -0
- package/components/snake-input/types.ts +99 -0
- package/components/snake-jump-applet/snake-jump-applet.vue +99 -0
- package/components/snake-jump-applet/types.ts +17 -0
- package/components/snake-line/snake-line.vue +42 -0
- package/components/snake-line/types.ts +30 -0
- package/components/snake-line-progress/snake-line-progress.vue +105 -0
- package/components/snake-line-progress/types.ts +26 -0
- package/components/snake-link/snake-link.vue +74 -0
- package/components/snake-link/types.ts +30 -0
- package/components/snake-list-switching/components/tree-select/index.vue +156 -0
- package/components/snake-list-switching/snake-list-switching.vue +1285 -0
- package/components/snake-list-switching/types.ts +11 -0
- package/components/snake-loading/snake-loading.vue +241 -0
- package/components/snake-loading/types.ts +34 -0
- package/components/snake-loadmore/snake-loadmore.vue +103 -0
- package/components/snake-loadmore/types.ts +56 -0
- package/components/snake-magic-cube/snake-magic-cube.vue +622 -0
- package/components/snake-magic-cube/types.ts +5 -0
- package/components/snake-modal/snake-modal.vue +434 -0
- package/components/snake-modal/types.ts +71 -0
- package/components/snake-navbar/snake-navbar.vue +194 -0
- package/components/snake-navbar/types.ts +54 -0
- package/components/snake-no-network/snake-no-network.vue +125 -0
- package/components/snake-no-network/types.ts +27 -0
- package/components/snake-notice/snake-notice.vue +44 -0
- package/components/snake-notice/types.ts +5 -0
- package/components/snake-notice-bar/snake-notice-bar.vue +336 -0
- package/components/snake-notice-bar/types.ts +82 -0
- package/components/snake-overlay/snake-overlay.vue +73 -0
- package/components/snake-overlay/types.ts +35 -0
- package/components/snake-page-popup/snake-page-popup.vue +146 -0
- package/components/snake-page-popup/types.ts +28 -0
- package/components/snake-parse/audio/audio.vue +286 -0
- package/components/snake-parse/audio/context.js +11 -0
- package/components/snake-parse/audio/index.js +34 -0
- package/components/snake-parse/node/node.vue +830 -0
- package/components/snake-parse/parser.js +1480 -0
- package/components/snake-parse/snake-parse.vue +548 -0
- package/components/snake-parse/static/app-plus/snake-parse/js/handler.js +109 -0
- package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.js +0 -0
- package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.min.js +1 -0
- package/components/snake-parse/static/app-plus/snake-parse/local.html +3 -0
- package/components/snake-parse/style/index.js +132 -0
- package/components/snake-parse/style/parser.js +175 -0
- package/components/snake-parse/types.ts +49 -0
- package/components/snake-picture-ads/snake-picture-ads.vue +292 -0
- package/components/snake-picture-ads/types.ts +5 -0
- package/components/snake-popup/snake-popup.vue +348 -0
- package/components/snake-popup/types.ts +88 -0
- package/components/snake-qr-code/qrcode.ts +1214 -0
- package/components/snake-qr-code/snake-qr-code.vue +408 -0
- package/components/snake-qr-code/types.ts +78 -0
- package/components/snake-radio/snake-radio.vue +271 -0
- package/components/snake-radio/types.ts +64 -0
- package/components/snake-radio-group/snake-radio-group.vue +98 -0
- package/components/snake-radio-group/types.ts +48 -0
- package/components/snake-read-more/snake-read-more.vue +140 -0
- package/components/snake-read-more/types.ts +53 -0
- package/components/snake-rich-text/snake-rich-text.vue +24 -0
- package/components/snake-rich-text/types.ts +5 -0
- package/components/snake-root-portal/snake-root-portal.vue +92 -0
- package/components/snake-root-portal/types.ts +9 -0
- package/components/snake-search/snake-search.vue +202 -0
- package/components/snake-search/types.ts +84 -0
- package/components/snake-select/snake-select.vue +371 -0
- package/components/snake-select/types.ts +75 -0
- package/components/snake-skeleton/snake-skeleton.vue +104 -0
- package/components/snake-skeleton/types.ts +32 -0
- package/components/snake-skeleton-item/snake-skeleton-item.vue +90 -0
- package/components/snake-skeleton-item/types.ts +30 -0
- package/components/snake-slider/snake-slider.vue +249 -0
- package/components/snake-slider/types.ts +59 -0
- package/components/snake-sticky/snake-sticky.vue +178 -0
- package/components/snake-sticky/types.ts +48 -0
- package/components/snake-subsection/snake-subsection.vue +188 -0
- package/components/snake-subsection/types.ts +54 -0
- package/components/snake-swiper/snake-swiper.vue +229 -0
- package/components/snake-swiper/types.ts +79 -0
- package/components/snake-switch/snake-switch.vue +100 -0
- package/components/snake-switch/types.ts +41 -0
- package/components/snake-tabbar/snake-tabbar.vue +306 -0
- package/components/snake-tabbar/types.ts +93 -0
- package/components/snake-tabs/snake-tabs.vue +305 -0
- package/components/snake-tabs/types.ts +79 -0
- package/components/snake-text/snake-text.vue +292 -0
- package/components/snake-text/types.ts +100 -0
- package/components/snake-textarea/snake-textarea.vue +171 -0
- package/components/snake-textarea/types.ts +79 -0
- package/components/snake-toast/snake-toast.vue +330 -0
- package/components/snake-toast/types.ts +55 -0
- package/components/snake-tooltip/position.ts +33 -0
- package/components/snake-tooltip/snake-tooltip.vue +339 -0
- package/components/snake-tooltip/types.ts +80 -0
- package/components/snake-transition/snake-transition.vue +315 -0
- package/components/snake-transition/types.ts +65 -0
- package/components/snake-upload/snake-upload.vue +1801 -0
- package/components/snake-upload/types.ts +256 -0
- package/components/snake-videos/snake-videos.vue +122 -0
- package/components/snake-videos/types.ts +7 -0
- package/iconfont.css +881 -0
- package/index.d.ts +7 -0
- package/index.scss +26 -0
- package/index.ts +94 -0
- package/libs/cms/blocks.ts +103 -0
- package/libs/cms/config.ts +25 -0
- package/libs/cms/index.ts +7 -0
- package/libs/cms/normalize.ts +135 -0
- package/libs/cms/resolve.ts +12 -0
- package/libs/cms/types.ts +164 -0
- package/libs/cms/use-commerce.ts +385 -0
- package/libs/cms/v2.ts +193 -0
- package/libs/config/index.ts +4 -0
- package/libs/config/modal.ts +93 -0
- package/libs/config/state.ts +33 -0
- package/libs/config/theme.ts +351 -0
- package/libs/config/toast.ts +71 -0
- package/libs/config/types.ts +135 -0
- package/libs/config/zIndex.ts +44 -0
- package/libs/css/color.scss +105 -0
- package/libs/css/common.scss +171 -0
- package/libs/css/style.h5.scss +7 -0
- package/libs/css/style.mp.scss +7 -0
- package/libs/css/style.theme.scss +70 -0
- package/libs/css/style.vue.scss +140 -0
- package/libs/form/async-validator.min.d.ts +75 -0
- package/libs/form/async-validator.min.js +3 -0
- package/libs/form/index.ts +3 -0
- package/libs/form/path.ts +40 -0
- package/libs/form/types.ts +51 -0
- package/libs/form/validate.ts +149 -0
- package/libs/foundation.ts +96 -0
- package/libs/function/$parent.ts +33 -0
- package/libs/function/clipboard.ts +13 -0
- package/libs/function/color.ts +55 -0
- package/libs/function/debounce.ts +34 -0
- package/libs/function/deepClone.ts +30 -0
- package/libs/function/deepMerge.ts +25 -0
- package/libs/function/emitter.ts +30 -0
- package/libs/function/format.ts +27 -0
- package/libs/function/getParent.ts +53 -0
- package/libs/function/getRect.ts +29 -0
- package/libs/function/guid.ts +11 -0
- package/libs/function/index.ts +31 -0
- package/libs/function/queryParams.ts +73 -0
- package/libs/function/random.ts +6 -0
- package/libs/function/randomArray.ts +14 -0
- package/libs/function/route.ts +155 -0
- package/libs/function/sleep.ts +7 -0
- package/libs/function/style.ts +58 -0
- package/libs/function/styleUtils.ts +83 -0
- package/libs/function/system.ts +14 -0
- package/libs/function/test.ts +202 -0
- package/libs/function/throttle.ts +36 -0
- package/libs/function/timeFormat.ts +27 -0
- package/libs/function/timeFrom.ts +27 -0
- package/libs/function/toast.ts +10 -0
- package/libs/function/trim.ts +11 -0
- package/libs/function/type2icon.ts +11 -0
- package/libs/function/unit.ts +45 -0
- package/libs/hooks/index.ts +11 -0
- package/libs/hooks/useChildren.ts +125 -0
- package/libs/hooks/useColor.ts +5 -0
- package/libs/hooks/useDebounce.ts +5 -0
- package/libs/hooks/useLocale.ts +13 -0
- package/libs/hooks/useModal.ts +131 -0
- package/libs/hooks/useParent.ts +62 -0
- package/libs/hooks/useRect.ts +44 -0
- package/libs/hooks/useRouter.ts +5 -0
- package/libs/hooks/useTheme.ts +31 -0
- package/libs/hooks/useThrottle.ts +5 -0
- package/libs/hooks/useToast.ts +116 -0
- package/libs/index.ts +18 -0
- package/libs/request/auto-http.ts +1 -0
- package/libs/request/index.ts +343 -0
- package/libs/runtime/accessors.ts +55 -0
- package/libs/runtime/context.ts +40 -0
- package/libs/runtime/index.ts +18 -0
- package/libs/runtime/types.ts +49 -0
- package/libs/util/calendar.ts +6 -0
- package/locale/index.ts +130 -0
- package/locale/lang/en-US.ts +67 -0
- package/locale/lang/zh-CN.ts +67 -0
- package/package.json +130 -5
- package/plugins/index.ts +1 -0
- package/plugins/root/README.md +86 -0
- package/plugins/root/index.ts +123 -0
- package/plugins/root/page.ts +77 -0
- package/plugins/root/root.ts +53 -0
- package/plugins/root/utils.ts +137 -0
- package/readme.md +79 -0
- package/theme.scss +118 -0
- package/types/components.d.ts +236 -0
- package/types/global.d.ts +21 -0
- package/types/index.d.ts +6 -0
- package/types/uni-app.d.ts +16 -0
- package/README.md +0 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { parseSFC, findNode } from './utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 页面模板转换:将页面内容包裹在 <global-root-view> 中
|
|
5
|
+
*
|
|
6
|
+
* <global-root-view> 是 App.root.vue 注册的全局组件,
|
|
7
|
+
* 其模板中包含 <slot />,页面内容通过插槽传入。
|
|
8
|
+
*
|
|
9
|
+
* 处理逻辑:
|
|
10
|
+
* 1. 解析 SFC,定位 <template> 标签的起止位置
|
|
11
|
+
* 2. 提取模板内容,查找 PageMeta 节点(如有则提取到包裹层外部)
|
|
12
|
+
* 3. 将剩余内容包裹在 <global-root-view>...</global-root-view> 中
|
|
13
|
+
*
|
|
14
|
+
* 降级机制:当 vue/compiler-sfc 不可用(如 HBuilderX 项目)时,
|
|
15
|
+
* 使用正则匹配 <page-meta> 标签替代 AST 解析。
|
|
16
|
+
*/
|
|
17
|
+
export async function transformPage(code: string) {
|
|
18
|
+
const sfc = await parseSFC(code);
|
|
19
|
+
|
|
20
|
+
// 用正则定位 <template> 开头标签
|
|
21
|
+
const openMatch = code.match(/<template\b[^>]*>/);
|
|
22
|
+
if (!openMatch) return null;
|
|
23
|
+
|
|
24
|
+
// 使用 lastIndexOf 定位最后一个 </template>,即根 <template> 的闭合标签
|
|
25
|
+
// 避免匹配到模板内具名插槽 <template #slot> 的闭合标签
|
|
26
|
+
const closeTagStart = code.lastIndexOf('</template>');
|
|
27
|
+
if (closeTagStart === -1) return null;
|
|
28
|
+
|
|
29
|
+
const openTagEnd = openMatch.index! + openMatch[0].length;
|
|
30
|
+
const content = code.slice(openTagEnd, closeTagStart);
|
|
31
|
+
|
|
32
|
+
let pageMetaSource = '';
|
|
33
|
+
let newContent = content;
|
|
34
|
+
|
|
35
|
+
if (sfc?.template) {
|
|
36
|
+
// AST 模式:通过 vue/compiler-sfc 解析,精确定位 PageMeta 节点
|
|
37
|
+
const pageMetaNode = findNode(sfc, 'PageMeta');
|
|
38
|
+
if (pageMetaNode) {
|
|
39
|
+
pageMetaSource = pageMetaNode.loc.source;
|
|
40
|
+
const metaStart = pageMetaNode.loc.start.offset;
|
|
41
|
+
const metaEnd = pageMetaNode.loc.end.offset;
|
|
42
|
+
|
|
43
|
+
// pageMetaNode.loc 的 offset 是相对于整个文件的,转换为相对于 content 的位置
|
|
44
|
+
const metaStartInContent = metaStart - openTagEnd;
|
|
45
|
+
const metaEndInContent = metaEnd - openTagEnd;
|
|
46
|
+
|
|
47
|
+
newContent = content.slice(0, metaStartInContent) + content.slice(metaEndInContent);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
// 降级模式:vue/compiler-sfc 不可用,用正则匹配 <page-meta> 标签
|
|
51
|
+
const pageMetaMatch = content.match(/<page-meta\b[^>]*>[\s\S]*?<\/page-meta>/);
|
|
52
|
+
if (pageMetaMatch) {
|
|
53
|
+
pageMetaSource = pageMetaMatch[0];
|
|
54
|
+
newContent = content.replace(pageMetaMatch[0], '');
|
|
55
|
+
} else {
|
|
56
|
+
// 也匹配自闭合的 <page-meta ... />
|
|
57
|
+
const selfClosingMatch = content.match(/<page-meta\b[^>]*\/>/);
|
|
58
|
+
if (selfClosingMatch) {
|
|
59
|
+
pageMetaSource = selfClosingMatch[0];
|
|
60
|
+
newContent = content.replace(selfClosingMatch[0], '');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// page-meta 放在包裹层外部,内容包裹在 <global-root-view> 中
|
|
66
|
+
const wrappedContent = `\n${pageMetaSource}\n<global-root-view>${newContent}</global-root-view>\n`;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
code: code.slice(0, openTagEnd) + wrappedContent + code.slice(closeTagStart),
|
|
70
|
+
map: {
|
|
71
|
+
version: 3 as const,
|
|
72
|
+
sources: [] as string[],
|
|
73
|
+
names: [] as string[],
|
|
74
|
+
mappings: ''
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建一个最小的 sourcemap,用于满足 Vite/Rollup 的 sourcemap 要求。
|
|
3
|
+
*
|
|
4
|
+
* 这是一个空映射的 sourcemap(mappings 为空字符串),不提供精确的源码映射,
|
|
5
|
+
* 但可以避免 "Sourcemap is likely to be incorrect" 警告。
|
|
6
|
+
* 调试器会回退到显示转换后的代码,不影响功能。
|
|
7
|
+
*
|
|
8
|
+
* 纯 JS 实现,无需第三方依赖。
|
|
9
|
+
*/
|
|
10
|
+
function createEmptySourcemap() {
|
|
11
|
+
return {
|
|
12
|
+
version: 3 as const,
|
|
13
|
+
sources: [] as string[],
|
|
14
|
+
names: [] as string[],
|
|
15
|
+
mappings: ''
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 向 main.ts 注入 App.root.vue 的导入和全局注册
|
|
21
|
+
*
|
|
22
|
+
* 流程:
|
|
23
|
+
* 1. 在文件顶部插入 `import GlobalRootView from "./App.root.vue"`
|
|
24
|
+
* 2. 在 createSSRApp 之后插入 `app.component("global-root-view", GlobalRootView)`
|
|
25
|
+
*
|
|
26
|
+
* 注:GlobalRootView 仅为 main.ts 中的局部变量名,
|
|
27
|
+
* 实际组件就是 App.root.vue,页面通过 <global-root-view> 标签使用。
|
|
28
|
+
*/
|
|
29
|
+
export function registerRootApp(code: string, fileName: string = 'App.root') {
|
|
30
|
+
const importCode = `import GlobalRootView from "./${fileName}.vue";`;
|
|
31
|
+
const vueUseComponentCode = `app.component("global-root-view", GlobalRootView);`;
|
|
32
|
+
|
|
33
|
+
let newCode = importCode + '\n' + code;
|
|
34
|
+
|
|
35
|
+
// 在 createSSRApp 赋值语句之后插入组件注册代码
|
|
36
|
+
const ssrAppMatch = newCode.match(/(const\s+app\s*=\s*createSSRApp\([^)]+\);)/);
|
|
37
|
+
if (ssrAppMatch) {
|
|
38
|
+
newCode = newCode.replace(/(const\s+app\s*=\s*createSSRApp\([^)]+\);)/, `$1\n ${vueUseComponentCode}`);
|
|
39
|
+
} else {
|
|
40
|
+
// 兜底:在 return { 之前插入
|
|
41
|
+
newCode = newCode.replace(/(createApp[\s\S]*?)(return\s*\{)/, `$1${vueUseComponentCode}\n $2`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { code: newCode, map: createEmptySourcemap() };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* App.root.vue 模板中已使用 <slot /> 作为子内容插槽,
|
|
49
|
+
* 无需替换,直接透传。
|
|
50
|
+
*/
|
|
51
|
+
export function rebuildRootApp(code: string) {
|
|
52
|
+
return { code, map: createEmptySourcemap() };
|
|
53
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { normalizePath } from 'vite';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse Vue SFC file, return descriptor
|
|
7
|
+
*
|
|
8
|
+
* When vue/compiler-sfc is not available (e.g. HBuilderX projects where
|
|
9
|
+
* vue is not in node_modules), returns null instead of throwing.
|
|
10
|
+
* Callers should fall back to regex-based matching.
|
|
11
|
+
*/
|
|
12
|
+
export async function parseSFC(code: string) {
|
|
13
|
+
try {
|
|
14
|
+
const { parse } = await import('vue/compiler-sfc');
|
|
15
|
+
return parse(code, { pad: 'space' }).descriptor;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Strip JSON comments (single-line and multi-line)
|
|
23
|
+
*/
|
|
24
|
+
export function stripJsonComments(str: string): string {
|
|
25
|
+
return str.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Convert pages.json path to absolute file path
|
|
30
|
+
*/
|
|
31
|
+
export function formatPagePath(root: string, path: string) {
|
|
32
|
+
return normalizePath(`${join(root, path)}.vue`).replace(/^[A-Z]:/, match => match.toLowerCase());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Load pages.json and resolve all page paths (main + subPackages)
|
|
37
|
+
*/
|
|
38
|
+
export function loadPagesJson(path: string, rootPath: string): string[] {
|
|
39
|
+
const raw = readFileSync(path, 'utf-8');
|
|
40
|
+
const { pages = [], subPackages = [] } = JSON.parse(stripJsonComments(raw));
|
|
41
|
+
|
|
42
|
+
return [
|
|
43
|
+
...pages.map((page: any) => formatPagePath(rootPath, page.path)),
|
|
44
|
+
...subPackages
|
|
45
|
+
.map(({ pages: subPages = [], root = '' }: any) => {
|
|
46
|
+
return subPages.map((page: any) => formatPagePath(join(rootPath, root), page.path));
|
|
47
|
+
})
|
|
48
|
+
.flat()
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* camelCase to kebab-case: PageMeta -> page-meta
|
|
54
|
+
*/
|
|
55
|
+
export function toKebabCase(str: string) {
|
|
56
|
+
return str
|
|
57
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
58
|
+
.replace(/[_\s]+/g, '-')
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* kebab-case to PascalCase: page-meta -> PageMeta
|
|
64
|
+
*/
|
|
65
|
+
export function toPascalCase(str: string) {
|
|
66
|
+
return str.replace(/(^\w|-+\w)/g, match => match.toUpperCase().replace(/-/g, ''));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface TagNode {
|
|
70
|
+
loc: {
|
|
71
|
+
source: string;
|
|
72
|
+
start: { offset: number };
|
|
73
|
+
end: { offset: number };
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Find a tag node in SFC template AST by tag name
|
|
79
|
+
* Supports both kebab-case and PascalCase
|
|
80
|
+
*/
|
|
81
|
+
export function findNode(sfc: any, rawTagName: string): TagNode | undefined {
|
|
82
|
+
const templateSource = sfc.template?.content;
|
|
83
|
+
if (!templateSource) return;
|
|
84
|
+
|
|
85
|
+
let tagName = '';
|
|
86
|
+
if (templateSource.includes(`<${toKebabCase(rawTagName)}`)) {
|
|
87
|
+
tagName = toKebabCase(rawTagName);
|
|
88
|
+
} else if (templateSource.includes(`<${toPascalCase(rawTagName)}`)) {
|
|
89
|
+
tagName = toPascalCase(rawTagName);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!tagName) return;
|
|
93
|
+
|
|
94
|
+
const nodeAst = sfc.template?.ast;
|
|
95
|
+
if (!nodeAst) return;
|
|
96
|
+
|
|
97
|
+
// Recursively traverse AST to find the target node
|
|
98
|
+
const traverse = (nodes: any): TagNode | undefined => {
|
|
99
|
+
for (const node of nodes) {
|
|
100
|
+
if (node.type === 1) {
|
|
101
|
+
if (node.tag === tagName) return node;
|
|
102
|
+
if (node.children?.length) {
|
|
103
|
+
const found = traverse(node.children);
|
|
104
|
+
if (found) return found;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return traverse(nodeAst.children);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Normalize platform-specific page paths (.mp-weixin.vue -> .vue)
|
|
116
|
+
*/
|
|
117
|
+
export function normalizePlatformPath(id: string) {
|
|
118
|
+
const platform = process.env.UNI_PLATFORM;
|
|
119
|
+
if (!platform) return id;
|
|
120
|
+
|
|
121
|
+
const regex = new RegExp(`\\.${platform}\\.vue$`);
|
|
122
|
+
if (regex.test(id)) {
|
|
123
|
+
return id.replace(`.${platform}.`, '.');
|
|
124
|
+
}
|
|
125
|
+
return id;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Debounce utility
|
|
130
|
+
*/
|
|
131
|
+
export function debounce<T extends (...args: any[]) => void>(fn: T, delay: number) {
|
|
132
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
133
|
+
return (...args: Parameters<T>) => {
|
|
134
|
+
if (timer) clearTimeout(timer);
|
|
135
|
+
timer = setTimeout(() => fn(...args), delay);
|
|
136
|
+
};
|
|
137
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Snake UI
|
|
2
|
+
|
|
3
|
+
Snake UI 是面向 uni-app 商城与 CMS 场景的 Vue 3 组件库,支持 H5、微信小程序和 App。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @puresnake/snake-ui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
在 `main.ts` 安装组件库,并按业务需要传入适配器:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import SnakeUI from '@puresnake/snake-ui';
|
|
15
|
+
|
|
16
|
+
app.use(SnakeUI, {
|
|
17
|
+
theme: { defaultDarkMode: 'auto' },
|
|
18
|
+
locale: { defaultLocale: 'zh-CN' },
|
|
19
|
+
http: { config: { baseUrl: '/api' } },
|
|
20
|
+
request: { get, post },
|
|
21
|
+
cache: { get, set, delete: remove },
|
|
22
|
+
config: { baseUrl: '/api' }
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Snake UI 不依赖其他 UI 组件库,不需要额外导入第三方主题样式。底座内置 Snake 绿色明暗主题、`zh-CN` / `en-US`、基于 `uni.request` 的 HTTP 客户端和常用 hooks / 工具。
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { http, setDarkMode, setLocale } from '@puresnake/snake-ui';
|
|
30
|
+
|
|
31
|
+
setDarkMode('auto');
|
|
32
|
+
setLocale('en-US');
|
|
33
|
+
const data = await http.get('/goods');
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
如需统一覆盖品牌色、文字色和圆角,可在应用入口导入可选主题变量:
|
|
37
|
+
|
|
38
|
+
```scss
|
|
39
|
+
@import '@puresnake/snake-ui/theme.scss';
|
|
40
|
+
|
|
41
|
+
:root,
|
|
42
|
+
page {
|
|
43
|
+
--snake-color-primary: #06d290;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## easycom
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"easycom": {
|
|
52
|
+
"autoscan": true,
|
|
53
|
+
"custom": {
|
|
54
|
+
"^snake-(.*)": "@puresnake/snake-ui/components/snake-$1/snake-$1.vue"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<snake-button type="primary">确定</snake-button>
|
|
62
|
+
<snake-popup v-model="visible" mode="bottom">...</snake-popup>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 手动导入
|
|
66
|
+
|
|
67
|
+
组件推荐使用 easycom 标签;如需手动导入,按组件路径引入:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import Button from '@puresnake/snake-ui/components/snake-button/snake-button.vue';
|
|
71
|
+
import type { ButtonProps, PopupMode } from '@puresnake/snake-ui';
|
|
72
|
+
import { addUnit, navTo, sleep, toast } from '@puresnake/snake-ui/libs';
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 公开基础组件
|
|
76
|
+
|
|
77
|
+
`snake-icon`、`snake-popup`、`snake-overlay`、`snake-button`、`snake-image`、`snake-tabs`、`snake-sticky`、`snake-notice-bar`、`snake-loadmore`、`snake-empty`、`snake-swiper`、`snake-button-sort`、`snake-drop-menu`、`snake-drop-menu-item`、`snake-index-list`、`snake-index-list-panel`、`snake-index-anchor`、`snake-form`、`snake-form-item`、`snake-input`、`snake-textarea`、`snake-checkbox`、`snake-checkbox-group`、`snake-radio`、`snake-radio-group`、`snake-switch`、`snake-slider`。
|
|
78
|
+
|
|
79
|
+
CMS 商品、优惠券和跳转能力通过 `app.use(SnakeUI, { cms: { services: ... } })` 显式注入。纯展示 CMS 不要求请求适配器。
|
package/theme.scss
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* Snake UI theme tokens. Import from uni.scss for SCSS variables. */
|
|
2
|
+
/* 品牌绿七档自有体系 + 语义色与中性色默认主题 */
|
|
3
|
+
:root,
|
|
4
|
+
page {
|
|
5
|
+
--snake-color-primary: #06d290;
|
|
6
|
+
--snake-color-primary-rgb: 6, 210, 144;
|
|
7
|
+
--snake-color-primary-dark: #2ebd7c;
|
|
8
|
+
--snake-color-primary-dark-rgb: 46, 189, 124;
|
|
9
|
+
--snake-color-primary-deep: #26273a;
|
|
10
|
+
--snake-color-primary-deep-rgb: 38, 39, 58;
|
|
11
|
+
--snake-color-primary-deeper: #22242e;
|
|
12
|
+
--snake-color-primary-deeper-rgb: 34, 36, 46;
|
|
13
|
+
--snake-color-primary-disabled: #a6ebd3;
|
|
14
|
+
--snake-color-primary-light: #eeffe0;
|
|
15
|
+
--snake-color-primary-light-rgb: 238, 255, 224;
|
|
16
|
+
--snake-color-primary-ink: #ffffff;
|
|
17
|
+
--snake-color-success: #19be6b;
|
|
18
|
+
--snake-color-success-rgb: 25, 190, 107;
|
|
19
|
+
--snake-color-success-dark: #18b566;
|
|
20
|
+
--snake-color-success-disabled: #71d5a1;
|
|
21
|
+
--snake-color-success-light: #dbf1e1;
|
|
22
|
+
--snake-color-warning: #ff9900;
|
|
23
|
+
--snake-color-warning-rgb: 255, 153, 0;
|
|
24
|
+
--snake-color-warning-dark: #f29100;
|
|
25
|
+
--snake-color-warning-disabled: #fcbd71;
|
|
26
|
+
--snake-color-warning-light: #fdf6ec;
|
|
27
|
+
--snake-color-error: #fa3534;
|
|
28
|
+
--snake-color-error-rgb: 250, 53, 52;
|
|
29
|
+
--snake-color-error-dark: #dd6161;
|
|
30
|
+
--snake-color-error-disabled: #fab6b6;
|
|
31
|
+
--snake-color-error-light: #fef0f0;
|
|
32
|
+
--snake-color-info: #909399;
|
|
33
|
+
--snake-color-info-rgb: 144, 147, 153;
|
|
34
|
+
--snake-color-info-dark: #82848a;
|
|
35
|
+
--snake-color-info-disabled: #c8c9cc;
|
|
36
|
+
--snake-color-info-light: #f4f4f5;
|
|
37
|
+
--snake-color-white: #ffffff;
|
|
38
|
+
--snake-color-black: #000000;
|
|
39
|
+
--snake-color-text: #303133;
|
|
40
|
+
--snake-color-text-rgb: 48, 49, 51;
|
|
41
|
+
--snake-color-text-secondary: #606266;
|
|
42
|
+
--snake-color-text-secondary-rgb: 96, 98, 102;
|
|
43
|
+
--snake-color-text-muted: #909399;
|
|
44
|
+
--snake-color-text-light: #c0c4cc;
|
|
45
|
+
--snake-color-border: #dcdfe6;
|
|
46
|
+
--snake-color-border-rgb: 220, 223, 230;
|
|
47
|
+
--snake-color-divider: #e4e7ed;
|
|
48
|
+
--snake-color-mask: rgba(0, 0, 0, 0.4);
|
|
49
|
+
--snake-color-shadow: rgba(0, 0, 0, 0.1);
|
|
50
|
+
--snake-color-background: #f6f6f6;
|
|
51
|
+
--snake-color-surface: #ffffff;
|
|
52
|
+
--snake-color-surface-rgb: 255, 255, 255;
|
|
53
|
+
--snake-color-surface-muted: #f1f1f1;
|
|
54
|
+
--snake-color-disabled-bg: #f1f1f1;
|
|
55
|
+
--snake-color-form-error: #fa3534;
|
|
56
|
+
--snake-control-height-small: 64rpx;
|
|
57
|
+
--snake-control-height: 80rpx;
|
|
58
|
+
--snake-control-height-large: 92rpx;
|
|
59
|
+
--snake-color-notice: #ff9900;
|
|
60
|
+
--snake-color-notice-background: #fdf6ec;
|
|
61
|
+
--snake-radius-small: 8rpx;
|
|
62
|
+
--snake-radius-base: 12rpx;
|
|
63
|
+
--snake-radius-large: 20rpx;
|
|
64
|
+
--snake-duration-fast: 180ms;
|
|
65
|
+
--snake-duration-base: 300ms;
|
|
66
|
+
--snake-easing-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
67
|
+
--snake-z-index-overlay: 10070;
|
|
68
|
+
--snake-z-index-popup: 10075;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
$snake-color-primary: var(--snake-color-primary) !default;
|
|
72
|
+
$snake-color-primary-dark: var(--snake-color-primary-dark) !default;
|
|
73
|
+
$snake-color-primary-deep: var(--snake-color-primary-deep) !default;
|
|
74
|
+
$snake-color-primary-deeper: var(--snake-color-primary-deeper) !default;
|
|
75
|
+
$snake-color-primary-disabled: var(--snake-color-primary-disabled) !default;
|
|
76
|
+
$snake-color-primary-light: var(--snake-color-primary-light) !default;
|
|
77
|
+
$snake-color-primary-ink: var(--snake-color-primary-ink) !default;
|
|
78
|
+
$snake-color-success: var(--snake-color-success) !default;
|
|
79
|
+
$snake-color-success-dark: var(--snake-color-success-dark) !default;
|
|
80
|
+
$snake-color-success-disabled: var(--snake-color-success-disabled) !default;
|
|
81
|
+
$snake-color-success-light: var(--snake-color-success-light) !default;
|
|
82
|
+
$snake-color-warning: var(--snake-color-warning) !default;
|
|
83
|
+
$snake-color-warning-dark: var(--snake-color-warning-dark) !default;
|
|
84
|
+
$snake-color-warning-disabled: var(--snake-color-warning-disabled) !default;
|
|
85
|
+
$snake-color-warning-light: var(--snake-color-warning-light) !default;
|
|
86
|
+
$snake-color-error: var(--snake-color-error) !default;
|
|
87
|
+
$snake-color-error-dark: var(--snake-color-error-dark) !default;
|
|
88
|
+
$snake-color-error-disabled: var(--snake-color-error-disabled) !default;
|
|
89
|
+
$snake-color-error-light: var(--snake-color-error-light) !default;
|
|
90
|
+
$snake-color-info: var(--snake-color-info) !default;
|
|
91
|
+
$snake-color-info-dark: var(--snake-color-info-dark) !default;
|
|
92
|
+
$snake-color-info-disabled: var(--snake-color-info-disabled) !default;
|
|
93
|
+
$snake-color-info-light: var(--snake-color-info-light) !default;
|
|
94
|
+
$snake-color-white: var(--snake-color-white) !default;
|
|
95
|
+
$snake-color-black: var(--snake-color-black) !default;
|
|
96
|
+
$snake-color-text: var(--snake-color-text) !default;
|
|
97
|
+
$snake-color-text-secondary: var(--snake-color-text-secondary) !default;
|
|
98
|
+
$snake-color-text-muted: var(--snake-color-text-muted) !default;
|
|
99
|
+
$snake-color-text-light: var(--snake-color-text-light) !default;
|
|
100
|
+
$snake-color-border: var(--snake-color-border) !default;
|
|
101
|
+
$snake-color-divider: var(--snake-color-divider) !default;
|
|
102
|
+
$snake-color-mask: var(--snake-color-mask) !default;
|
|
103
|
+
$snake-color-shadow: var(--snake-color-shadow) !default;
|
|
104
|
+
$snake-color-background: var(--snake-color-background) !default;
|
|
105
|
+
$snake-color-surface: var(--snake-color-surface) !default;
|
|
106
|
+
$snake-color-surface-muted: var(--snake-color-surface-muted) !default;
|
|
107
|
+
$snake-color-disabled-bg: var(--snake-color-disabled-bg) !default;
|
|
108
|
+
$snake-color-form-error: var(--snake-color-form-error) !default;
|
|
109
|
+
$snake-color-notice: var(--snake-color-notice) !default;
|
|
110
|
+
$snake-color-notice-background: var(--snake-color-notice-background) !default;
|
|
111
|
+
$snake-radius-small: 8rpx !default;
|
|
112
|
+
$snake-radius-base: 12rpx !default;
|
|
113
|
+
$snake-radius-large: 20rpx !default;
|
|
114
|
+
$snake-duration-fast: 180ms !default;
|
|
115
|
+
$snake-duration-base: 300ms !default;
|
|
116
|
+
$snake-easing-standard: cubic-bezier(0.2, 0.8, 0.2, 1) !default;
|
|
117
|
+
$snake-z-index-overlay: 10070 !default;
|
|
118
|
+
$snake-z-index-popup: 10075 !default;
|