@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,385 @@
|
|
|
1
|
+
import { computed, ref } from 'vue';
|
|
2
|
+
import type { CmsNormalizedComponent, CmsOptions, CmsPageInfo, CmsStateChange, CmsStatus } from './types';
|
|
3
|
+
import { CmsError } from './types';
|
|
4
|
+
import { getRuntimeOptions } from '../runtime';
|
|
5
|
+
|
|
6
|
+
type Dictionary = Record<string, any>;
|
|
7
|
+
|
|
8
|
+
export interface CmsCommerceControllerOptions {
|
|
9
|
+
pageInfo: () => CmsPageInfo;
|
|
10
|
+
component: () => CmsNormalizedComponent | undefined;
|
|
11
|
+
options: () => CmsOptions;
|
|
12
|
+
onStateChange?: (state: CmsStateChange) => void;
|
|
13
|
+
onError?: (error: CmsError) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function valuesToQuery(input: unknown): Dictionary {
|
|
17
|
+
if (!input || typeof input !== 'object') return {};
|
|
18
|
+
return Object.fromEntries(Object.entries(input as Dictionary).map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useCmsCommerce(input: CmsCommerceControllerOptions) {
|
|
22
|
+
const goodsList = ref<Dictionary[]>([]);
|
|
23
|
+
const filtersData = ref<Dictionary>({});
|
|
24
|
+
const activityList = ref<Dictionary[]>([]);
|
|
25
|
+
const activityCurrent = ref<string | number>('001');
|
|
26
|
+
const tabsCurrent = ref(0);
|
|
27
|
+
const auctionSetParams = ref<Dictionary>({});
|
|
28
|
+
const status = ref<CmsStatus>('idle');
|
|
29
|
+
const page = ref(1);
|
|
30
|
+
const goodsParams = ref<Dictionary>({});
|
|
31
|
+
const filterParams = ref<Dictionary>({});
|
|
32
|
+
const tabsParams = ref<Dictionary>({});
|
|
33
|
+
// 每次初始化、刷新或失效都会推进代际;异步请求返回时只允许当前代际写入状态,避免旧响应覆盖新筛选结果。
|
|
34
|
+
let generation = 0;
|
|
35
|
+
let busy = false;
|
|
36
|
+
|
|
37
|
+
function styleOf(component: CmsNormalizedComponent): Dictionary {
|
|
38
|
+
if (component.version !== 2) return component.setStyle;
|
|
39
|
+
const data = component.props || {};
|
|
40
|
+
const query = data.query || {};
|
|
41
|
+
const display = data.display || {};
|
|
42
|
+
const bars = data.bars || {};
|
|
43
|
+
const style = data.style || {};
|
|
44
|
+
return {
|
|
45
|
+
goodsListType: query.source === 'activity' ? 'activity' : 'activityCondition',
|
|
46
|
+
listType: display.cardType || 'sale',
|
|
47
|
+
subType: display.subType || '',
|
|
48
|
+
attendanceShow: display.showAttendance !== false,
|
|
49
|
+
commodityType: Number(display.columns) === 3 ? 1 : 0,
|
|
50
|
+
activitySessionShow: Boolean(bars.activitySession),
|
|
51
|
+
filterShow: bars.filter,
|
|
52
|
+
priceFilterShow: Boolean(bars.priceFilter),
|
|
53
|
+
tabShow: Boolean(bars.tabs?.enabled),
|
|
54
|
+
tabslist: Array.isArray(bars.tabs?.list) ? bars.tabs.list : [],
|
|
55
|
+
stickyDisabled: !bars.sticky,
|
|
56
|
+
pageMargin: style.pageMargin,
|
|
57
|
+
imageMargin: style.gap,
|
|
58
|
+
backgroundColor: style.backgroundColor,
|
|
59
|
+
listMode: data.empty?.mode,
|
|
60
|
+
emptySrc: data.empty?.image,
|
|
61
|
+
emptyText: data.empty?.text,
|
|
62
|
+
emptyColor: data.empty?.color,
|
|
63
|
+
filterParams: {
|
|
64
|
+
...query,
|
|
65
|
+
pageSize: query.pageSize || 20,
|
|
66
|
+
activityUserRule: query.userRule,
|
|
67
|
+
sellingDay: query.sellingDays,
|
|
68
|
+
brandNames: Array.isArray(query.brands) ? query.brands.map((brand: Dictionary) => brand.name) : [],
|
|
69
|
+
genderFilter: query.gender
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const goodsLoadingStatus = computed(() => {
|
|
75
|
+
if (status.value === 'initial-loading' || status.value === 'loading-more') return 'loading';
|
|
76
|
+
if (status.value === 'no-more') return 'nomore';
|
|
77
|
+
return 'loadmore';
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function snapshot(): CmsStateChange {
|
|
81
|
+
return { status: status.value, page: page.value, itemCount: goodsList.value.length };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function setStatus(next: CmsStatus): void {
|
|
85
|
+
status.value = next;
|
|
86
|
+
input.onStateChange?.(snapshot());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function context(component: CmsNormalizedComponent, currentGeneration: number) {
|
|
90
|
+
return { component, pageInfo: input.pageInfo(), generation: currentGeneration };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function cacheKey(component: CmsNormalizedComponent): string {
|
|
94
|
+
const pageInfo = input.pageInfo();
|
|
95
|
+
const pageId = pageInfo.pageId ?? pageInfo.id ?? 'default';
|
|
96
|
+
return `${input.options().cachePrefix || 'snake-ui:cms'}:${pageId}:${component.id}:filters`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function restoreFilters(component: CmsNormalizedComponent): void {
|
|
100
|
+
const cache = getRuntimeOptions().cache;
|
|
101
|
+
if (!cache) return;
|
|
102
|
+
// 优先读取按页面和组件隔离的新键;旧的全局 filterParams 键仅用于兼容存量缓存,写入始终使用新键。
|
|
103
|
+
const cached = cache.get(cacheKey(component)) ?? cache.get('filterParams');
|
|
104
|
+
filterParams.value = valuesToQuery(cached);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function persistFilters(component: CmsNormalizedComponent, value: Dictionary): void {
|
|
108
|
+
getRuntimeOptions().cache?.set(cacheKey(component), value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildFilterRequest(component: CmsNormalizedComponent, mode = ''): Dictionary {
|
|
112
|
+
const style = styleOf(component);
|
|
113
|
+
const params: Dictionary = {};
|
|
114
|
+
if (style.goodsListType === 'activity' || goodsParams.value.activityId) {
|
|
115
|
+
params.activityId = style.filterParams?.activityId || goodsParams.value.activityId || '';
|
|
116
|
+
}
|
|
117
|
+
if (style.listType === 'auction' && !['forSale', 'downBuy'].includes(mode)) {
|
|
118
|
+
params.activityType = 'auction';
|
|
119
|
+
params.subType = style.subType || '';
|
|
120
|
+
}
|
|
121
|
+
if (goodsParams.value.categoryId) params.categoryId = goodsParams.value.categoryId;
|
|
122
|
+
return params;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildGoodsRequest(component: CmsNormalizedComponent, mode = ''): Dictionary {
|
|
126
|
+
const style = styleOf(component);
|
|
127
|
+
const configured = style.filterParams || {};
|
|
128
|
+
const params: Dictionary = {
|
|
129
|
+
page: page.value,
|
|
130
|
+
size: configured.pageSize || 10,
|
|
131
|
+
key: style.key || '',
|
|
132
|
+
low: configured.low || '',
|
|
133
|
+
high: configured.high || '',
|
|
134
|
+
sellingDay: configured.sellingDay || '',
|
|
135
|
+
cateId: configured.categoryId || goodsParams.value.categoryId || '',
|
|
136
|
+
sort: configured.sort,
|
|
137
|
+
showOutOfStock: goodsParams.value.activityStatus === 'init' ? 1 : configured.showOutOfStock || 0
|
|
138
|
+
};
|
|
139
|
+
if (style.goodsListType === 'activity') params.activityId = configured.activityId || '';
|
|
140
|
+
if (goodsParams.value.activityId) params.activityId = goodsParams.value.activityId;
|
|
141
|
+
if (style.goodsListType === 'activityCondition' && configured.activityType) {
|
|
142
|
+
params.activityType = configured.activityType;
|
|
143
|
+
params.activityStatus = configured.activityStatus;
|
|
144
|
+
params.activityUserRule = configured.activityUserRule || '';
|
|
145
|
+
params.activitySubType = style.subType || '';
|
|
146
|
+
}
|
|
147
|
+
if (goodsParams.value.activityStatus) params.activityStatus = goodsParams.value.activityStatus;
|
|
148
|
+
Object.assign(params, tabsParams.value, filterParams.value);
|
|
149
|
+
for (const [target, source] of [
|
|
150
|
+
['brandFilter', configured.brandNames],
|
|
151
|
+
['genderFilter', configured.genderFilter]
|
|
152
|
+
] as const) {
|
|
153
|
+
if (!Array.isArray(source) || !source.length) continue;
|
|
154
|
+
params[target] = params[target] ? `${params[target]},${source.join(',')}` : source.join(',');
|
|
155
|
+
}
|
|
156
|
+
if (['forSale', 'downBuy'].includes(mode)) {
|
|
157
|
+
delete params.activityType;
|
|
158
|
+
delete params.activityStatus;
|
|
159
|
+
delete params.activitySubType;
|
|
160
|
+
}
|
|
161
|
+
if (mode === 'downBuy') params.activityType = 'down_buy';
|
|
162
|
+
return params;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function loadActivities(component: CmsNormalizedComponent, currentGeneration: number): Promise<void> {
|
|
166
|
+
const service = input.options().services?.commerce;
|
|
167
|
+
const style = styleOf(component);
|
|
168
|
+
if (!service?.getActivities || !style.activitySessionShow || style.listType !== 'auction') return;
|
|
169
|
+
let result;
|
|
170
|
+
try {
|
|
171
|
+
result = await service.getActivities({
|
|
172
|
+
...context(component, currentGeneration),
|
|
173
|
+
params: { type: 'auction', status: 'initAndIng', subType: style.subType || '' }
|
|
174
|
+
});
|
|
175
|
+
} catch (cause) {
|
|
176
|
+
throw new CmsError('activities', 'CMS activities failed to load.', cause);
|
|
177
|
+
}
|
|
178
|
+
if (currentGeneration !== generation) return;
|
|
179
|
+
const items = result.items || [];
|
|
180
|
+
// “全部”是基于首个活动构造的前端虚拟项,id=001 不对应真实活动,请求时会转换为空 activityId。
|
|
181
|
+
const all = items.length ? [{ ...items[0], id: '001', squareImage: input.options().assets?.activityAll }, ...items] : [];
|
|
182
|
+
activityList.value = all;
|
|
183
|
+
auctionSetParams.value = all[0] || {};
|
|
184
|
+
const requested = input.pageInfo().activityId;
|
|
185
|
+
const selected = requested === undefined ? undefined : all.find(item => item.id === requested);
|
|
186
|
+
if (selected) {
|
|
187
|
+
activityCurrent.value = selected.id;
|
|
188
|
+
auctionSetParams.value = selected;
|
|
189
|
+
goodsParams.value.activityId = selected.id === '001' ? '' : selected.id;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function loadFilters(component: CmsNormalizedComponent, currentGeneration: number, mode = '') {
|
|
194
|
+
const service = input.options().services?.commerce;
|
|
195
|
+
const style = styleOf(component);
|
|
196
|
+
if (!service?.getFilters || !(style.filterShow || style.filterShow === 'custom')) return;
|
|
197
|
+
let result;
|
|
198
|
+
try {
|
|
199
|
+
result = await service.getFilters({
|
|
200
|
+
...context(component, currentGeneration),
|
|
201
|
+
params: buildFilterRequest(component, mode)
|
|
202
|
+
});
|
|
203
|
+
} catch (cause) {
|
|
204
|
+
throw new CmsError('filters', 'CMS filters failed to load.', cause);
|
|
205
|
+
}
|
|
206
|
+
if (currentGeneration === generation) filtersData.value = result || {};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function loadGoods(component: CmsNormalizedComponent, currentGeneration: number, append: boolean) {
|
|
210
|
+
const service = input.options().services?.commerce;
|
|
211
|
+
if (!service) throw new CmsError('goods', 'A CMS commerce service is required for goodslist.');
|
|
212
|
+
const request = buildGoodsRequest(component);
|
|
213
|
+
let result;
|
|
214
|
+
try {
|
|
215
|
+
result = await service.getGoods({ ...context(component, currentGeneration), params: request });
|
|
216
|
+
} catch (cause) {
|
|
217
|
+
throw new CmsError('goods', 'CMS goods failed to load.', cause);
|
|
218
|
+
}
|
|
219
|
+
if (currentGeneration !== generation) return;
|
|
220
|
+
const style = styleOf(component);
|
|
221
|
+
// 首次无商品时,forSale/downBuy 可改写活动参数再次请求,作为运营配置的降级展示策略。
|
|
222
|
+
if (!append && (result.total || 0) <= 0 && ['forSale', 'downBuy'].includes(style.listMode)) {
|
|
223
|
+
const mode = style.listMode;
|
|
224
|
+
await loadFilters(component, currentGeneration, mode);
|
|
225
|
+
try {
|
|
226
|
+
result = await service.getGoods({
|
|
227
|
+
...context(component, currentGeneration),
|
|
228
|
+
params: buildGoodsRequest(component, mode)
|
|
229
|
+
});
|
|
230
|
+
} catch (cause) {
|
|
231
|
+
throw new CmsError('goods', 'CMS fallback goods failed to load.', cause);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (currentGeneration !== generation) return;
|
|
235
|
+
const items = result.items || [];
|
|
236
|
+
goodsList.value = append ? goodsList.value.concat(items) : items;
|
|
237
|
+
const pageSize = Number(style.filterParams?.pageSize || 10);
|
|
238
|
+
const hasMore = result.hasMore ?? items.length >= pageSize;
|
|
239
|
+
if (hasMore) {
|
|
240
|
+
page.value += 1;
|
|
241
|
+
setStatus('ready');
|
|
242
|
+
} else {
|
|
243
|
+
setStatus(goodsList.value.length ? 'no-more' : 'empty');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function fail(stage: CmsError['stage'], cause: unknown): void {
|
|
248
|
+
const error = cause instanceof CmsError ? cause : new CmsError(stage, cause instanceof Error ? cause.message : String(cause), cause);
|
|
249
|
+
setStatus('error');
|
|
250
|
+
input.onError?.(error);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function init(): Promise<void> {
|
|
254
|
+
const component = input.component();
|
|
255
|
+
generation += 1;
|
|
256
|
+
const currentGeneration = generation;
|
|
257
|
+
page.value = 1;
|
|
258
|
+
goodsList.value = [];
|
|
259
|
+
filtersData.value = {};
|
|
260
|
+
activityList.value = [];
|
|
261
|
+
goodsParams.value = {};
|
|
262
|
+
tabsParams.value = {};
|
|
263
|
+
activityCurrent.value = '001';
|
|
264
|
+
tabsCurrent.value = 0;
|
|
265
|
+
if (!component) {
|
|
266
|
+
setStatus('ready');
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
restoreFilters(component);
|
|
270
|
+
busy = true;
|
|
271
|
+
setStatus('initial-loading');
|
|
272
|
+
try {
|
|
273
|
+
await loadActivities(component, currentGeneration);
|
|
274
|
+
await loadFilters(component, currentGeneration);
|
|
275
|
+
await loadGoods(component, currentGeneration, false);
|
|
276
|
+
} catch (cause) {
|
|
277
|
+
if (currentGeneration === generation) fail('goods', cause);
|
|
278
|
+
} finally {
|
|
279
|
+
if (currentGeneration === generation) busy = false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function loadMore(): Promise<void> {
|
|
284
|
+
const component = input.component();
|
|
285
|
+
if (!component || busy || status.value !== 'ready') return;
|
|
286
|
+
const currentGeneration = generation;
|
|
287
|
+
busy = true;
|
|
288
|
+
setStatus('loading-more');
|
|
289
|
+
try {
|
|
290
|
+
await loadGoods(component, currentGeneration, true);
|
|
291
|
+
} catch (cause) {
|
|
292
|
+
if (currentGeneration === generation) fail('goods', cause);
|
|
293
|
+
} finally {
|
|
294
|
+
if (currentGeneration === generation) busy = false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function refresh(): Promise<void> {
|
|
299
|
+
const component = input.component();
|
|
300
|
+
if (!component || busy) return;
|
|
301
|
+
generation += 1;
|
|
302
|
+
const currentGeneration = generation;
|
|
303
|
+
page.value = 1;
|
|
304
|
+
goodsList.value = [];
|
|
305
|
+
busy = true;
|
|
306
|
+
setStatus('initial-loading');
|
|
307
|
+
try {
|
|
308
|
+
await loadFilters(component, currentGeneration);
|
|
309
|
+
await loadGoods(component, currentGeneration, false);
|
|
310
|
+
} catch (cause) {
|
|
311
|
+
if (currentGeneration === generation) fail('goods', cause);
|
|
312
|
+
} finally {
|
|
313
|
+
if (currentGeneration === generation) busy = false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function retry(): Promise<void> {
|
|
318
|
+
await init();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function changeActivity(item: Dictionary): Promise<void> {
|
|
322
|
+
if (activityCurrent.value === item.id) return;
|
|
323
|
+
activityCurrent.value = item.id;
|
|
324
|
+
goodsParams.value.activityId = item.id === '001' ? '' : item.id;
|
|
325
|
+
goodsParams.value.activityStatus = item.status || '';
|
|
326
|
+
auctionSetParams.value = item;
|
|
327
|
+
await refresh();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function changeTab(index: number, list: Dictionary[]): Promise<void> {
|
|
331
|
+
tabsCurrent.value = index;
|
|
332
|
+
const item = list[index] || {};
|
|
333
|
+
goodsParams.value.categoryId = item.paramType === 'categoryId' || !item.paramType ? item.value || '' : '';
|
|
334
|
+
filterParams.value = {};
|
|
335
|
+
tabsParams.value = item.paramType
|
|
336
|
+
? { [item.paramType]: item.value }
|
|
337
|
+
: Object.fromEntries((item.customList || []).map((entry: Dictionary) => [entry.customLabel, entry.customValue]));
|
|
338
|
+
await refresh();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function confirmFilter(params: Dictionary): Promise<void> {
|
|
342
|
+
const component = input.component();
|
|
343
|
+
filterParams.value = { ...params };
|
|
344
|
+
if (component) persistFilters(component, params);
|
|
345
|
+
await refresh();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function navigateGoods(item: Dictionary, index: number): Promise<void> {
|
|
349
|
+
const component = input.component();
|
|
350
|
+
const service = input.options().services?.commerce;
|
|
351
|
+
if (!component || !service?.navigateGoods) return;
|
|
352
|
+
await service.navigateGoods({ ...context(component, generation), item, index });
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function refreshQuotePay(price: unknown, index: number): void {
|
|
356
|
+
const activityInfo = goodsList.value[index]?.activityInfo;
|
|
357
|
+
if (activityInfo && typeof activityInfo === 'object') activityInfo.myPrice = price;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function invalidate(): void {
|
|
361
|
+
generation += 1;
|
|
362
|
+
busy = false;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
goodsList,
|
|
367
|
+
filtersData,
|
|
368
|
+
activityList,
|
|
369
|
+
activityCurrent,
|
|
370
|
+
tabsCurrent,
|
|
371
|
+
auctionSetParams,
|
|
372
|
+
status,
|
|
373
|
+
goodsLoadingStatus,
|
|
374
|
+
init,
|
|
375
|
+
loadMore,
|
|
376
|
+
refresh,
|
|
377
|
+
retry,
|
|
378
|
+
changeActivity,
|
|
379
|
+
changeTab,
|
|
380
|
+
confirmFilter,
|
|
381
|
+
navigateGoods,
|
|
382
|
+
refreshQuotePay,
|
|
383
|
+
invalidate
|
|
384
|
+
};
|
|
385
|
+
}
|
package/libs/cms/v2.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { CmsV2BuiltinComponent } from './blocks';
|
|
2
|
+
|
|
3
|
+
type Dictionary = Record<string, any>;
|
|
4
|
+
|
|
5
|
+
const link = () => ({ httpType: 10, http: '' });
|
|
6
|
+
const mediaStyle = () => ({
|
|
7
|
+
heightMode: 'auto',
|
|
8
|
+
height: 200,
|
|
9
|
+
pageMargin: 0,
|
|
10
|
+
imageRadius: 0,
|
|
11
|
+
backgroundColor: '#FFFFFF'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const cmsV2Defaults: Record<CmsV2BuiltinComponent, Dictionary> = {
|
|
15
|
+
'v2.carousel': {
|
|
16
|
+
play: { autoplay: true, interval: 3, loop: true },
|
|
17
|
+
indicator: { type: 'bullets', color: '#1989fa' },
|
|
18
|
+
style: mediaStyle(),
|
|
19
|
+
items: []
|
|
20
|
+
},
|
|
21
|
+
'v2.coupon': {
|
|
22
|
+
mode: 'page',
|
|
23
|
+
popupMode: 'bottom',
|
|
24
|
+
coupon: null,
|
|
25
|
+
before: { image: '', link: link() },
|
|
26
|
+
after: { image: '', link: link() }
|
|
27
|
+
},
|
|
28
|
+
'v2.custom': { slotName: 'custom' },
|
|
29
|
+
'v2.divider': {
|
|
30
|
+
mode: 'blank',
|
|
31
|
+
height: 30,
|
|
32
|
+
lineStyle: 'solid',
|
|
33
|
+
margin: 0,
|
|
34
|
+
style: { lineColor: 'rgb(229, 229, 229)', backgroundColor: 'rgb(255, 255, 255)' }
|
|
35
|
+
},
|
|
36
|
+
'v2.floatbutton': {
|
|
37
|
+
src: '',
|
|
38
|
+
link: link(),
|
|
39
|
+
position: { right: 0, bottom: 20 },
|
|
40
|
+
style: { width: 76, height: 76 }
|
|
41
|
+
},
|
|
42
|
+
'v2.goodslist': {
|
|
43
|
+
audience: 'all',
|
|
44
|
+
query: {
|
|
45
|
+
source: 'activity',
|
|
46
|
+
activityId: '',
|
|
47
|
+
activityType: '',
|
|
48
|
+
activityStatus: 'ing',
|
|
49
|
+
userRule: 'all',
|
|
50
|
+
gender: [],
|
|
51
|
+
low: '',
|
|
52
|
+
high: '',
|
|
53
|
+
categoryId: '',
|
|
54
|
+
brands: [],
|
|
55
|
+
sellingDays: '',
|
|
56
|
+
sort: 'auto',
|
|
57
|
+
pageSize: 20,
|
|
58
|
+
showOutOfStock: false
|
|
59
|
+
},
|
|
60
|
+
display: {
|
|
61
|
+
layout: 'grid',
|
|
62
|
+
columns: 2,
|
|
63
|
+
cardType: 'sale',
|
|
64
|
+
subType: '',
|
|
65
|
+
showAttendance: true,
|
|
66
|
+
showTag: false
|
|
67
|
+
},
|
|
68
|
+
bars: {
|
|
69
|
+
filter: false,
|
|
70
|
+
filterList: [],
|
|
71
|
+
priceFilter: true,
|
|
72
|
+
tabs: { enabled: false, list: [] },
|
|
73
|
+
activitySession: false,
|
|
74
|
+
sticky: false
|
|
75
|
+
},
|
|
76
|
+
style: { pageMargin: 5, gap: 10, radius: 0, backgroundColor: '#F8F8F8' },
|
|
77
|
+
empty: { mode: 'default', image: '', text: '列表为空', color: '#000000' }
|
|
78
|
+
},
|
|
79
|
+
'v2.hotzone': { image: '', zones: [] },
|
|
80
|
+
'v2.images': {
|
|
81
|
+
layout: { mode: 'single', columns: 2 },
|
|
82
|
+
style: { ...mediaStyle(), gap: 8 },
|
|
83
|
+
items: []
|
|
84
|
+
},
|
|
85
|
+
'v2.magiccube': {
|
|
86
|
+
layout: 'row',
|
|
87
|
+
columns: 4,
|
|
88
|
+
cells: [],
|
|
89
|
+
play: { interval: 3 },
|
|
90
|
+
style: { pageMargin: 0, gap: 0, radius: 0, height: 188, backgroundColor: '#FFFFFF' }
|
|
91
|
+
},
|
|
92
|
+
'v2.notice': {
|
|
93
|
+
direction: 'row',
|
|
94
|
+
noticeText: '请填写内容,如果过长,将会在手机上滚动显示',
|
|
95
|
+
link: link(),
|
|
96
|
+
textList: [],
|
|
97
|
+
isLeftIcon: true,
|
|
98
|
+
style: {
|
|
99
|
+
height: 72,
|
|
100
|
+
pageMargin: 10,
|
|
101
|
+
backgroundColor: 'rgb(234, 241, 255)',
|
|
102
|
+
textColor: 'rgb(45, 87, 231)'
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
'v2.popup': { image: '', link: link(), popupMode: 'bottom' },
|
|
106
|
+
'v2.richtext': { content: '', style: { backgroundColor: '#FFFFFF' } },
|
|
107
|
+
'v2.search': {
|
|
108
|
+
searchText: '请输入你想要搜索的商品名称/货号/关键词',
|
|
109
|
+
link: link(),
|
|
110
|
+
isSearch: false,
|
|
111
|
+
inputType: 'text',
|
|
112
|
+
textList: [],
|
|
113
|
+
rightImage: { show: false, src: '', link: link(), width: 60, height: 60, borderRadius: 0, marginRight: 0 },
|
|
114
|
+
icons: [],
|
|
115
|
+
style: {
|
|
116
|
+
position: 0,
|
|
117
|
+
height: 60,
|
|
118
|
+
pageMargin: 0,
|
|
119
|
+
borderRadius: 0,
|
|
120
|
+
backgroundColor: 'rgb(249, 249, 249)',
|
|
121
|
+
borderBgColor: 'rgb(255, 255, 255)',
|
|
122
|
+
textColor: 'rgb(150, 151, 153)',
|
|
123
|
+
borderSize: 1,
|
|
124
|
+
borderColor: 'rgb(0, 0, 0)',
|
|
125
|
+
showBorder: true
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
'v2.video': { src: '', coverUrl: '', autoplay: false, style: mediaStyle() }
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const enums: Record<string, readonly unknown[]> = {
|
|
132
|
+
'v2.carousel:indicator.type': ['none', 'bullets', 'fraction', 'progressbar'],
|
|
133
|
+
'v2.coupon:mode': ['page', 'popup'],
|
|
134
|
+
'v2.coupon:popupMode': ['bottom', 'center'],
|
|
135
|
+
'v2.divider:mode': ['blank', 'line'],
|
|
136
|
+
'v2.divider:lineStyle': ['solid', 'dashed', 'dotted'],
|
|
137
|
+
'v2.goodslist:audience': ['all', 'new', 'old'],
|
|
138
|
+
'v2.goodslist:query.source': ['activity', 'condition'],
|
|
139
|
+
'v2.goodslist:display.layout': ['grid', 'swipe'],
|
|
140
|
+
'v2.goodslist:display.columns': [2, 3],
|
|
141
|
+
'v2.goodslist:display.cardType': ['sale', 'auction', 'new'],
|
|
142
|
+
'v2.images:layout.mode': ['single', 'grid'],
|
|
143
|
+
'v2.notice:direction': ['row', 'column'],
|
|
144
|
+
'v2.popup:popupMode': ['bottom', 'center'],
|
|
145
|
+
'v2.search:inputType': ['text', 'number'],
|
|
146
|
+
'v2.search:style.position': [0, 1]
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const limits: Record<string, [number, number]> = {
|
|
150
|
+
'v2.carousel:play.interval': [1, 3600],
|
|
151
|
+
'v2.goodslist:query.pageSize': [1, 50],
|
|
152
|
+
'v2.images:layout.columns': [1, 4],
|
|
153
|
+
'v2.magiccube:columns': [1, 6],
|
|
154
|
+
'v2.search:style.height': [60, 80]
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
function clone<T>(value: T): T {
|
|
158
|
+
return JSON.parse(JSON.stringify(value));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function normalizeValue(value: unknown, fallback: any, component: string, path: string): any {
|
|
162
|
+
if (Array.isArray(fallback)) return Array.isArray(value) ? clone(value) : clone(fallback);
|
|
163
|
+
if (fallback !== null && typeof fallback === 'object') {
|
|
164
|
+
const source =
|
|
165
|
+
value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as Dictionary) : {};
|
|
166
|
+
return Object.fromEntries(
|
|
167
|
+
Object.entries(fallback).map(([key, nested]) => [
|
|
168
|
+
key,
|
|
169
|
+
normalizeValue(source[key], nested, component, path ? `${path}.${key}` : key)
|
|
170
|
+
])
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
if (fallback === null) return value === undefined ? null : clone(value);
|
|
174
|
+
const enumValues = enums[`${component}:${path}`];
|
|
175
|
+
if (typeof fallback === 'boolean') {
|
|
176
|
+
if (value === true || value === 1 || value === '1' || value === 'true') return true;
|
|
177
|
+
if (value === false || value === 0 || value === '0' || value === 'false') return false;
|
|
178
|
+
return fallback;
|
|
179
|
+
}
|
|
180
|
+
if (typeof fallback === 'number') {
|
|
181
|
+
const numeric = Number(value);
|
|
182
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
183
|
+
if (enumValues && !enumValues.includes(numeric)) return fallback;
|
|
184
|
+
const limit = limits[`${component}:${path}`];
|
|
185
|
+
return limit ? Math.min(limit[1], Math.max(limit[0], numeric)) : numeric;
|
|
186
|
+
}
|
|
187
|
+
const text = typeof value === 'string' || typeof value === 'number' ? String(value) : fallback;
|
|
188
|
+
return enumValues && !enumValues.includes(text) ? fallback : text;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function normalizeCmsV2Props(component: CmsV2BuiltinComponent, value: unknown): Dictionary {
|
|
192
|
+
return normalizeValue(value, cmsV2Defaults[component], component, '');
|
|
193
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snake-modal 函数式调用约定(事件名 + 载荷类型)。
|
|
3
|
+
* @description
|
|
4
|
+
* - useModal() 通过 emitter 派发事件
|
|
5
|
+
* - <snake-modal global /> / <snake-modal page /> 通过 emitter 监听并转调自身 show/hide
|
|
6
|
+
*
|
|
7
|
+
* 该模块放在 libs 层(而非 components),是为了让 `useModal` 组合式函数与
|
|
8
|
+
* `snake-modal` 组件共享同一套事件名与载荷,同时保持「libs 不反向依赖 components」的分层约束。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// 普通(页面级)modal 事件
|
|
12
|
+
export const SNAKE_MODAL_EVENT_SHOW = 'snake-ui:modal:show:';
|
|
13
|
+
export const SNAKE_MODAL_EVENT_HIDE = 'snake-ui:modal:hide:';
|
|
14
|
+
export const SNAKE_MODAL_EVENT_CLEAR_LOADING = 'snake-ui:modal:clear-loading:';
|
|
15
|
+
|
|
16
|
+
// 全局(App 根部)modal 事件,供 useModal() 使用
|
|
17
|
+
export const SNAKE_MODAL_GLOBAL_EVENT_SHOW = 'snake-ui:modal:global:show';
|
|
18
|
+
export const SNAKE_MODAL_GLOBAL_EVENT_HIDE = 'snake-ui:modal:global:hide';
|
|
19
|
+
export const SNAKE_MODAL_GLOBAL_EVENT_CLEAR_LOADING = 'snake-ui:modal:global:clear-loading';
|
|
20
|
+
|
|
21
|
+
/** 根据当前页面(或显式 pageId)拼接事件名。 */
|
|
22
|
+
export function getEventWithCurrentPage(event: string, page?: string | boolean): string {
|
|
23
|
+
if (page && typeof page === 'string' && page !== '') {
|
|
24
|
+
return event + page;
|
|
25
|
+
}
|
|
26
|
+
return event + getCurrentPage();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 获取当前页面路由。 */
|
|
30
|
+
function getCurrentPage(): string {
|
|
31
|
+
try {
|
|
32
|
+
const pages = getCurrentPages();
|
|
33
|
+
const currentPage = pages[pages.length - 1].route as string;
|
|
34
|
+
return currentPage || '';
|
|
35
|
+
} catch {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* snake-modal 函数式调用载荷类型。
|
|
42
|
+
* @description 与 `snake-modal` 组件的 props 一一对应(除 modelValue 外),额外支持确认/取消回调。
|
|
43
|
+
*/
|
|
44
|
+
export interface ModalPayload {
|
|
45
|
+
/** 标题。 */
|
|
46
|
+
title?: string;
|
|
47
|
+
/** 内容。 */
|
|
48
|
+
content?: string;
|
|
49
|
+
/** 是否显示标题。 */
|
|
50
|
+
showTitle?: boolean;
|
|
51
|
+
/** 是否显示确认按钮。 */
|
|
52
|
+
showConfirmButton?: boolean;
|
|
53
|
+
/** 是否显示取消按钮。 */
|
|
54
|
+
showCancelButton?: boolean;
|
|
55
|
+
/** 确认按钮文案。 */
|
|
56
|
+
confirmText?: string;
|
|
57
|
+
/** 取消按钮文案。 */
|
|
58
|
+
cancelText?: string;
|
|
59
|
+
/** 确认按钮颜色。 */
|
|
60
|
+
confirmColor?: string;
|
|
61
|
+
/** 取消文字颜色。 */
|
|
62
|
+
cancelColor?: string;
|
|
63
|
+
/** 弹窗圆角值,单位 rpx。 */
|
|
64
|
+
borderRadius?: number | string;
|
|
65
|
+
/** 弹窗宽度,单位 rpx。 */
|
|
66
|
+
width?: number | string;
|
|
67
|
+
/** 层级 z-index。 */
|
|
68
|
+
zIndex?: number | string;
|
|
69
|
+
/** 是否开启缩放效果。 */
|
|
70
|
+
zoom?: boolean;
|
|
71
|
+
/** 是否异步关闭,只对确定按钮有效。 */
|
|
72
|
+
asyncClose?: boolean;
|
|
73
|
+
/** 是否允许点击遮罩关闭。 */
|
|
74
|
+
maskCloseAble?: boolean;
|
|
75
|
+
/** 往上偏移的值,避免和键盘重合。 */
|
|
76
|
+
negativeTop?: number | string;
|
|
77
|
+
/** 根节点自定义样式。 */
|
|
78
|
+
customStyle?: string | Record<string, any> | unknown[];
|
|
79
|
+
/** 根节点自定义类名。 */
|
|
80
|
+
customClass?: string;
|
|
81
|
+
/** 标题样式。 */
|
|
82
|
+
titleStyle?: Record<string, any>;
|
|
83
|
+
/** 内容样式。 */
|
|
84
|
+
contentStyle?: Record<string, any>;
|
|
85
|
+
/** 取消按钮样式。 */
|
|
86
|
+
cancelStyle?: Record<string, any>;
|
|
87
|
+
/** 确定按钮样式。 */
|
|
88
|
+
confirmStyle?: Record<string, any>;
|
|
89
|
+
/** 确认回调。 */
|
|
90
|
+
onConfirm?: () => void;
|
|
91
|
+
/** 取消回调。 */
|
|
92
|
+
onCancel?: () => void;
|
|
93
|
+
}
|