@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.
- package/LICENSE +21 -0
- package/changelog.md +83 -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 +134 -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,185 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
v-if="filters.topFilter"
|
|
4
|
+
id="filtersId"
|
|
5
|
+
class="filter-sorts"
|
|
6
|
+
:style="[{ backgroundColor: bgColor }, customStyle]"
|
|
7
|
+
>
|
|
8
|
+
<view class="filter-sorts__toolbar" role="toolbar" aria-label="商品排序与筛选">
|
|
9
|
+
<template v-for="(item, index) in filters.topFilter" :key="index">
|
|
10
|
+
<view
|
|
11
|
+
v-if="item.type === 'click'"
|
|
12
|
+
class="filter-sorts__item"
|
|
13
|
+
:class="{
|
|
14
|
+
'filter-sorts__item--active': filterParams['sort'] === item.value
|
|
15
|
+
}"
|
|
16
|
+
role="button"
|
|
17
|
+
tabindex="0"
|
|
18
|
+
:aria-pressed="filterParams['sort'] === item.value"
|
|
19
|
+
@click="sortSelectHandle('sort', item.value ?? '')"
|
|
20
|
+
@keydown.enter.prevent="sortSelectHandle('sort', item.value ?? '')"
|
|
21
|
+
@keydown.space.prevent="sortSelectHandle('sort', item.value ?? '')"
|
|
22
|
+
>
|
|
23
|
+
{{ item.name }}
|
|
24
|
+
</view>
|
|
25
|
+
<snake-button-sort
|
|
26
|
+
v-if="item.type === 'sort'"
|
|
27
|
+
:title="item.name"
|
|
28
|
+
@change="(e: any) => sortSelectHandle('price', e.value)"
|
|
29
|
+
></snake-button-sort>
|
|
30
|
+
</template>
|
|
31
|
+
<snake-drop-menu ref="dropMenuRef" custom-style="flex: 1; min-width: 0; background: transparent;">
|
|
32
|
+
<snake-drop-menu-item
|
|
33
|
+
title="筛选"
|
|
34
|
+
custom-style="height: 70vh"
|
|
35
|
+
:before-toggle="handleBeforeToggle"
|
|
36
|
+
@close="dropMenuClose"
|
|
37
|
+
>
|
|
38
|
+
<filterPage
|
|
39
|
+
:show="showDrawer"
|
|
40
|
+
:filters="filters"
|
|
41
|
+
:defaultParams="filterParams"
|
|
42
|
+
@confirm="filterConfirm"
|
|
43
|
+
></filterPage>
|
|
44
|
+
</snake-drop-menu-item>
|
|
45
|
+
</snake-drop-menu>
|
|
46
|
+
</view>
|
|
47
|
+
</view>
|
|
48
|
+
</template>
|
|
49
|
+
<script lang="ts" setup>
|
|
50
|
+
/**
|
|
51
|
+
* @component FilterSorts
|
|
52
|
+
* @description 组合顶部快捷排序、价格排序和多维筛选面板,并向父级输出统一查询参数。
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
import { getCurrentInstance, onMounted, ref } from 'vue';
|
|
56
|
+
import filterPage from './filter-page.vue';
|
|
57
|
+
import type { FilterSortsEmits, FilterSortsProps } from './types';
|
|
58
|
+
|
|
59
|
+
defineOptions({
|
|
60
|
+
name: 'filter-sorts',
|
|
61
|
+
options: {
|
|
62
|
+
virtualHost: true,
|
|
63
|
+
addGlobalClass: true,
|
|
64
|
+
styleIsolation: 'shared'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const props = withDefaults(defineProps<FilterSortsProps>(), {
|
|
69
|
+
filters: () => ({}),
|
|
70
|
+
defaultFilter: () => ({}),
|
|
71
|
+
bgColor: 'transparent',
|
|
72
|
+
sort: '',
|
|
73
|
+
showTabs: true,
|
|
74
|
+
goodsListLength: 0,
|
|
75
|
+
customStyle: () => ({})
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const emit = defineEmits<FilterSortsEmits>();
|
|
79
|
+
|
|
80
|
+
const instance = getCurrentInstance();
|
|
81
|
+
|
|
82
|
+
const dropMenuRef = ref<{ updateDropMenuRect: () => void; colseAll: () => void } | null>(null);
|
|
83
|
+
|
|
84
|
+
const showDrawer = ref(false);
|
|
85
|
+
const filterParams = ref<Record<string, string | number | boolean>>({
|
|
86
|
+
sort: 'auto',
|
|
87
|
+
price: ''
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const dropMenuOpen = () => {
|
|
91
|
+
showDrawer.value = true;
|
|
92
|
+
};
|
|
93
|
+
const dropMenuClose = async () => {
|
|
94
|
+
showDrawer.value = false;
|
|
95
|
+
};
|
|
96
|
+
// 异步打开
|
|
97
|
+
const handleBeforeToggle = async ({ resolve }: { resolve: (value: boolean) => void }) => {
|
|
98
|
+
let scrollTop = 0;
|
|
99
|
+
|
|
100
|
+
uni.createSelectorQuery()
|
|
101
|
+
.in(instance)
|
|
102
|
+
.selectViewport()
|
|
103
|
+
.scrollOffset(res => {
|
|
104
|
+
scrollTop = (res as UniApp.NodeInfo).scrollTop || 0;
|
|
105
|
+
})
|
|
106
|
+
.exec();
|
|
107
|
+
|
|
108
|
+
const query = uni.createSelectorQuery().in(instance);
|
|
109
|
+
query.select('#filtersId').boundingClientRect();
|
|
110
|
+
query.exec(res => {
|
|
111
|
+
const navbarHeight = 44;
|
|
112
|
+
const tabsHeight = props.showTabs ? 42 : 0;
|
|
113
|
+
scrollTop = scrollTop + (res[0]?.top || 0) - navbarHeight - tabsHeight;
|
|
114
|
+
// #ifndef H5
|
|
115
|
+
scrollTop = scrollTop - (uni.$snake.sys().statusBarHeight || 0);
|
|
116
|
+
// #endif
|
|
117
|
+
if (scrollTop > 0) {
|
|
118
|
+
uni.pageScrollTo({
|
|
119
|
+
scrollTop,
|
|
120
|
+
duration: 0,
|
|
121
|
+
success: () => {
|
|
122
|
+
dropMenuRef.value?.updateDropMenuRect();
|
|
123
|
+
dropMenuOpen();
|
|
124
|
+
resolve(true);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
dropMenuRef.value?.updateDropMenuRect();
|
|
129
|
+
dropMenuOpen();
|
|
130
|
+
resolve(true);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// 排序选择
|
|
136
|
+
const sortSelectHandle = (propName: string | number, value: string) => {
|
|
137
|
+
filterParams.value[propName] = value;
|
|
138
|
+
dropMenuRef.value?.colseAll();
|
|
139
|
+
emitChange();
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// 确认筛选
|
|
143
|
+
const filterConfirm = (e: Record<string, string | number | boolean>) => {
|
|
144
|
+
filterParams.value = {
|
|
145
|
+
sort: filterParams.value.sort,
|
|
146
|
+
price: filterParams.value.price,
|
|
147
|
+
...e
|
|
148
|
+
};
|
|
149
|
+
dropMenuRef.value?.colseAll();
|
|
150
|
+
emitChange();
|
|
151
|
+
};
|
|
152
|
+
const emitChange = () => {
|
|
153
|
+
emit('change', filterParams.value);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
onMounted(async () => {
|
|
157
|
+
await uni.$snake.sleep();
|
|
158
|
+
filterParams.value.sort = props.sort;
|
|
159
|
+
});
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<style lang="scss" scoped>
|
|
163
|
+
::v-deep .u-drawer-content {
|
|
164
|
+
height: 100% !important;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.filter-sorts__toolbar {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex: 1;
|
|
170
|
+
align-items: center;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.filter-sorts__item {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex: 1;
|
|
176
|
+
align-items: center;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
font-size: 26rpx;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.filter-sorts__item--active {
|
|
182
|
+
color: var(--snake-color-primary, #06d290);
|
|
183
|
+
font-weight: 500;
|
|
184
|
+
}
|
|
185
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { StyleValue } from '../../types/global';
|
|
2
|
+
|
|
3
|
+
/** Filter 可选项。 */
|
|
4
|
+
export interface FilterOption extends Record<string, unknown> {
|
|
5
|
+
/** 数据项名称或唯一标识。 */
|
|
6
|
+
name?: string;
|
|
7
|
+
/** 当前值。 */
|
|
8
|
+
value?: string | number;
|
|
9
|
+
/** selected 配置。 */
|
|
10
|
+
selected?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** FilterGroup 类型定义。 */
|
|
14
|
+
export interface FilterGroup extends Record<string, unknown> {
|
|
15
|
+
/** 数据项名称或唯一标识。 */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** 当前值。 */
|
|
18
|
+
value?: string;
|
|
19
|
+
/** 组件类型或展示模式。 */
|
|
20
|
+
type?: string;
|
|
21
|
+
/** 用于渲染的数据列表。 */
|
|
22
|
+
list?: FilterOption[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** FilterSorts 配置数据。 */
|
|
26
|
+
export interface FilterSortsData extends Record<string, unknown> {
|
|
27
|
+
/** top filter 配置。 */
|
|
28
|
+
topFilter?: FilterGroup[];
|
|
29
|
+
/** main filter 配置。 */
|
|
30
|
+
mainFilter?: FilterGroup[];
|
|
31
|
+
/** brand filter 配置。 */
|
|
32
|
+
brandFilter?: Record<string, FilterOption[]>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** FilterSorts 组件的公开属性。 */
|
|
36
|
+
export interface FilterSortsProps<Filters extends FilterSortsData = FilterSortsData> {
|
|
37
|
+
/** 筛选项配置数据。 */
|
|
38
|
+
filters?: Filters;
|
|
39
|
+
/** default filter 配置。 */
|
|
40
|
+
defaultFilter?: Record<string, any>;
|
|
41
|
+
/** 背景颜色。 */
|
|
42
|
+
bgColor?: string;
|
|
43
|
+
/** sort 配置。 */
|
|
44
|
+
sort?: string;
|
|
45
|
+
/** show tabs 配置。 */
|
|
46
|
+
showTabs?: boolean;
|
|
47
|
+
/** goods list length 配置。 */
|
|
48
|
+
goodsListLength?: number;
|
|
49
|
+
/** 根节点自定义样式。 */
|
|
50
|
+
customStyle?: StyleValue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** FilterSorts 组件的事件契约。 */
|
|
54
|
+
export type FilterSortsEmits = {
|
|
55
|
+
/** change 事件携带的参数。 */
|
|
56
|
+
change: [params: Record<string, string | number | boolean>];
|
|
57
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="floating-bubble"
|
|
4
|
+
:class="{ 'floating-bubble--slot': hasSlot }"
|
|
5
|
+
:animation="animationData"
|
|
6
|
+
:style="rootStyle"
|
|
7
|
+
v-if="visible"
|
|
8
|
+
>
|
|
9
|
+
<view class="floating-bubble__wrap" :style="wrapStyle" @click="handleNavigate">
|
|
10
|
+
<view v-if="!hasSlot" class="floating-bubble__close" @click.stop.prevent="close">
|
|
11
|
+
<snake-icon name="close-circle-fill" size="28" color="rgba(7, 7, 7, 0.2)" />
|
|
12
|
+
</view>
|
|
13
|
+
<image v-if="!hasSlot" class="floating-bubble__logo" :src="datas.src" />
|
|
14
|
+
<slot v-else />
|
|
15
|
+
</view>
|
|
16
|
+
</view>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* @component FloatingBubble
|
|
22
|
+
* @description 页面右侧的悬浮气泡。图片模式支持点击跳转与关闭(持久化),插槽模式承载自定义内容;滚动时向屏幕外收起,停止后恢复。
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { computed, getCurrentInstance, onBeforeUnmount, onMounted, ref, useSlots, watch } from 'vue';
|
|
26
|
+
import type { FloatingBubbleProps } from './types';
|
|
27
|
+
|
|
28
|
+
defineOptions({ name: 'FloatingBubble', options: { virtualHost: true, styleIsolation: 'shared' } });
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<FloatingBubbleProps>(), {
|
|
31
|
+
datas: () => ({}),
|
|
32
|
+
scrollTop: 0,
|
|
33
|
+
bottom: '80rpx',
|
|
34
|
+
background: 'transparent',
|
|
35
|
+
zIndex: 6
|
|
36
|
+
});
|
|
37
|
+
const emit = defineEmits<{ click: [] }>();
|
|
38
|
+
|
|
39
|
+
const slots = useSlots();
|
|
40
|
+
const hasSlot = computed(() => !!slots.default);
|
|
41
|
+
|
|
42
|
+
const instance = getCurrentInstance();
|
|
43
|
+
const closeList = ref<Array<string | number>>([]);
|
|
44
|
+
const animationData = ref<Record<string, unknown>>({});
|
|
45
|
+
const animation = ref<ReturnType<typeof uni.createAnimation>>();
|
|
46
|
+
const size = ref({ width: 0, right: 0 });
|
|
47
|
+
const off = ref(false);
|
|
48
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
49
|
+
|
|
50
|
+
const isShow = computed(() => !closeList.value.includes(props.datas?.uid ?? ''));
|
|
51
|
+
const visible = computed(() => (hasSlot.value || Boolean(props.datas?.src)) && isShow.value);
|
|
52
|
+
|
|
53
|
+
const rootStyle = computed(() => {
|
|
54
|
+
const style: Record<string, any> = { zIndex: props.zIndex };
|
|
55
|
+
if (hasSlot.value) {
|
|
56
|
+
style.bottom = props.bottom;
|
|
57
|
+
style.background = props.background;
|
|
58
|
+
} else {
|
|
59
|
+
style.right = `${props.datas?.right ?? 0}%`;
|
|
60
|
+
style.bottom = `${props.datas?.bottom ?? 20}%`;
|
|
61
|
+
}
|
|
62
|
+
return style;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const wrapStyle = computed(() =>
|
|
66
|
+
hasSlot.value ? {} : { width: `${props.datas?.width ?? 96}rpx`, height: `${props.datas?.height ?? 96}rpx` }
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
watch(() => props.scrollTop, declick);
|
|
70
|
+
|
|
71
|
+
onMounted(async () => {
|
|
72
|
+
closeList.value = uni.getStorageSync('suspensionList') || [];
|
|
73
|
+
animation.value = uni.createAnimation({ duration: 1000, timingFunction: 'ease' });
|
|
74
|
+
const rect = await uni.$snake.getRect('.floating-bubble', instance);
|
|
75
|
+
if (rect) size.value = { width: rect.width, right: rect.right ?? 0 };
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
onBeforeUnmount(() => clearTimeout(timer));
|
|
79
|
+
|
|
80
|
+
function declick() {
|
|
81
|
+
if (!off.value) {
|
|
82
|
+
rotateAndScale();
|
|
83
|
+
off.value = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function rotateAndScale() {
|
|
88
|
+
if (!animation.value) return;
|
|
89
|
+
const screenWidth = uni.$snake.sys().screenWidth;
|
|
90
|
+
const x = size.value.width / 2 + screenWidth - size.value.right;
|
|
91
|
+
animation.value.translateX(x).step();
|
|
92
|
+
animationData.value = animation.value.export();
|
|
93
|
+
timer = setTimeout(() => {
|
|
94
|
+
off.value = false;
|
|
95
|
+
norotateAndScale();
|
|
96
|
+
}, 2000);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function norotateAndScale() {
|
|
100
|
+
if (!animation.value) return;
|
|
101
|
+
animation.value.translateX(0).step();
|
|
102
|
+
animationData.value = animation.value.export();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function close() {
|
|
106
|
+
const uid = props.datas?.uid;
|
|
107
|
+
if (uid !== undefined && !closeList.value.includes(uid)) closeList.value.push(uid);
|
|
108
|
+
uni.setStorageSync('suspensionList', closeList.value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function reopen() {
|
|
112
|
+
const uid = props.datas?.uid;
|
|
113
|
+
if (uid === undefined) return;
|
|
114
|
+
closeList.value = closeList.value.filter(item => item !== uid);
|
|
115
|
+
uni.setStorageSync('suspensionList', closeList.value);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function handleNavigate() {
|
|
119
|
+
emit('click');
|
|
120
|
+
if (!hasSlot.value && props.datas?.url) uni.$snake.navTo(props.datas.url);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
defineExpose({ reopen });
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<style lang="scss" scoped>
|
|
127
|
+
.floating-bubble {
|
|
128
|
+
position: fixed;
|
|
129
|
+
right: 0;
|
|
130
|
+
bottom: 20%;
|
|
131
|
+
z-index: 6;
|
|
132
|
+
padding-bottom: 0rpx;
|
|
133
|
+
padding-bottom: constant(safe-area-inset-bottom);
|
|
134
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
135
|
+
|
|
136
|
+
&--slot {
|
|
137
|
+
right: 28rpx;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
&__wrap {
|
|
141
|
+
position: relative;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&__close {
|
|
145
|
+
position: absolute;
|
|
146
|
+
top: 0;
|
|
147
|
+
right: 0;
|
|
148
|
+
z-index: 12;
|
|
149
|
+
color: rgba(7, 7, 7, 0.2);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&__logo {
|
|
153
|
+
width: 100%;
|
|
154
|
+
height: 100%;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** FloatingBubble 配置数据。 */
|
|
2
|
+
export interface FloatingBubbleData {
|
|
3
|
+
/** uid 配置。 */
|
|
4
|
+
uid?: string | number;
|
|
5
|
+
/** 资源地址。 */
|
|
6
|
+
src?: string;
|
|
7
|
+
/** 点击后跳转地址。 */
|
|
8
|
+
url?: string;
|
|
9
|
+
/** right 配置。 */
|
|
10
|
+
right?: number;
|
|
11
|
+
/** bottom 配置。 */
|
|
12
|
+
bottom?: number;
|
|
13
|
+
/** 组件宽度。 */
|
|
14
|
+
width?: number;
|
|
15
|
+
/** 组件高度。 */
|
|
16
|
+
height?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** FloatingBubble 组件的公开属性。 */
|
|
20
|
+
export interface FloatingBubbleProps {
|
|
21
|
+
/** CMS 装修组件配置数据。 */
|
|
22
|
+
datas?: FloatingBubbleData;
|
|
23
|
+
/** 页面或容器当前滚动距离。 */
|
|
24
|
+
scrollTop?: number;
|
|
25
|
+
/** 插槽模式下底部定位偏移。 */
|
|
26
|
+
bottom?: string;
|
|
27
|
+
/** 插槽模式下容器背景。 */
|
|
28
|
+
background?: string;
|
|
29
|
+
/** 层级。 */
|
|
30
|
+
zIndex?: number | string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** FloatingBubble 实例公开的操作。 */
|
|
34
|
+
export interface FloatingBubbleExpose {
|
|
35
|
+
/** 重新打开已关闭的悬浮入口。 */
|
|
36
|
+
reopen(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** FloatingBubble 点击入口时发出的事件。 */
|
|
40
|
+
export interface FloatingBubbleEmits {
|
|
41
|
+
/** 用户点击悬浮入口。 */
|
|
42
|
+
click: [];
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view>
|
|
3
|
+
<!-- #ifdef MP-WEIXIN -->
|
|
4
|
+
<official-account></official-account>
|
|
5
|
+
<!-- #endif -->
|
|
6
|
+
</view>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
/**
|
|
11
|
+
* @component Follow
|
|
12
|
+
* @description 在微信小程序环境中展示官方账号关注组件。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
defineOptions({ name: 'Follow', options: { virtualHost: true, styleIsolation: 'shared' } });
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<style></style>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="snake-form" :class="customClass" :style="customStyle"><slot /></view>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* @component Form
|
|
8
|
+
* @description 管理表单数据、校验规则与字段实例。
|
|
9
|
+
* @see /components/form
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { computed, provide, ref, toRef } from 'vue';
|
|
13
|
+
import { toast } from '../../libs/function/toast';
|
|
14
|
+
import { formKey } from './types';
|
|
15
|
+
import type { FormCallback, FormError, FormProps, FormRules } from './types';
|
|
16
|
+
|
|
17
|
+
defineOptions({ name: 'SnakeForm', options: { virtualHost: true, styleIsolation: 'shared' } });
|
|
18
|
+
const props = withDefaults(defineProps<FormProps>(), {
|
|
19
|
+
model: () => ({}),
|
|
20
|
+
rules: () => ({}),
|
|
21
|
+
errorType: () => ['message', 'toast'],
|
|
22
|
+
borderBottom: true,
|
|
23
|
+
labelPosition: 'left',
|
|
24
|
+
labelWidth: 90,
|
|
25
|
+
labelAlign: 'left',
|
|
26
|
+
labelStyle: () => ({}),
|
|
27
|
+
size: 'default',
|
|
28
|
+
customClass: '',
|
|
29
|
+
customStyle: ''
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
interface FormField {
|
|
33
|
+
prop: string;
|
|
34
|
+
validation(trigger?: string, callback?: (message: string) => void): void;
|
|
35
|
+
resetField(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const fields = ref<FormField[]>([]);
|
|
39
|
+
const rules = ref<FormRules>(props.rules);
|
|
40
|
+
const model = computed(() => props.model);
|
|
41
|
+
|
|
42
|
+
function setRules(newRules: FormRules) {
|
|
43
|
+
rules.value = newRules;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addField(field: FormField) {
|
|
47
|
+
if (!fields.value.includes(field)) fields.value.push(field);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function removeField(field: Pick<FormField, 'prop'>) {
|
|
51
|
+
fields.value = fields.value.filter(item => item.prop !== field.prop);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resetFields() {
|
|
55
|
+
fields.value.forEach(field => field.resetField?.());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function runValidation(targetFields: FormField[], callback?: FormCallback, showToast = false): Promise<boolean> {
|
|
59
|
+
return new Promise(resolve => {
|
|
60
|
+
let valid = true;
|
|
61
|
+
let count = 0;
|
|
62
|
+
const errors: FormError[] = [];
|
|
63
|
+
if (targetFields.length === 0) {
|
|
64
|
+
resolve(true);
|
|
65
|
+
callback?.(true, []);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
targetFields.forEach(field => {
|
|
69
|
+
field.validation('', message => {
|
|
70
|
+
if (message) {
|
|
71
|
+
valid = false;
|
|
72
|
+
errors.push({ prop: field.prop, message });
|
|
73
|
+
}
|
|
74
|
+
if (++count !== targetFields.length) return;
|
|
75
|
+
resolve(valid);
|
|
76
|
+
if (
|
|
77
|
+
showToast &&
|
|
78
|
+
!props.errorType.includes('none') &&
|
|
79
|
+
props.errorType.includes('toast') &&
|
|
80
|
+
errors.length > 0
|
|
81
|
+
) {
|
|
82
|
+
toast(errors[0].message);
|
|
83
|
+
}
|
|
84
|
+
callback?.(valid, errors);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function validate(callback?: FormCallback): Promise<boolean> {
|
|
91
|
+
return runValidation(fields.value, callback, true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateField(prop: string | string[], callback?: FormCallback): Promise<boolean> {
|
|
95
|
+
const names = Array.isArray(prop) ? prop : [prop];
|
|
96
|
+
return runValidation(
|
|
97
|
+
fields.value.filter(field => names.includes(field.prop)),
|
|
98
|
+
callback
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Snake 输入控件通过注入读取表单尺寸和错误配置;字段生命周期由公开方法驱动。
|
|
103
|
+
provide(formKey, {
|
|
104
|
+
model: toRef(props, 'model'),
|
|
105
|
+
rules,
|
|
106
|
+
size: toRef(props, 'size'),
|
|
107
|
+
errorType: toRef(props, 'errorType'),
|
|
108
|
+
borderBottom: toRef(props, 'borderBottom'),
|
|
109
|
+
labelPosition: toRef(props, 'labelPosition'),
|
|
110
|
+
labelWidth: toRef(props, 'labelWidth'),
|
|
111
|
+
labelAlign: toRef(props, 'labelAlign'),
|
|
112
|
+
labelStyle: toRef(props, 'labelStyle'),
|
|
113
|
+
register: field => addField(field as unknown as FormField),
|
|
114
|
+
unregister: field => removeField(field)
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
defineExpose({ setRules, resetFields, validate, validateField, addField, removeField, fields, rules, props, model });
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<style scoped>
|
|
121
|
+
.snake-form {
|
|
122
|
+
width: 100%;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue';
|
|
2
|
+
import type { StyleValue } from '../../types/global';
|
|
3
|
+
import type { FormError, FormRules, FormTrigger } from '../../libs/form';
|
|
4
|
+
|
|
5
|
+
/** 表单控件尺寸。 */
|
|
6
|
+
export type FormSize = 'small' | 'default' | 'large';
|
|
7
|
+
/** 标签位置。 */
|
|
8
|
+
export type FormLabelPosition = 'left' | 'top';
|
|
9
|
+
/** 标签对齐方式。 */
|
|
10
|
+
export type FormLabelAlign = 'left' | 'center' | 'right';
|
|
11
|
+
/** 错误提示方式。 */
|
|
12
|
+
export type FormErrorType = 'message' | 'border' | 'border-bottom' | 'none' | 'toast';
|
|
13
|
+
|
|
14
|
+
/** Form 组件的公开属性。 */
|
|
15
|
+
export interface FormProps {
|
|
16
|
+
/** 表单数据对象。 */
|
|
17
|
+
model?: Record<string, any>;
|
|
18
|
+
/** 表单校验规则。 */
|
|
19
|
+
rules?: FormRules;
|
|
20
|
+
/** 错误提示方式。 */
|
|
21
|
+
errorType?: FormErrorType[];
|
|
22
|
+
/** 是否显示表单项底边。 */
|
|
23
|
+
borderBottom?: boolean;
|
|
24
|
+
/** 标签位置。 */
|
|
25
|
+
labelPosition?: FormLabelPosition;
|
|
26
|
+
/** 标签宽度。 */
|
|
27
|
+
labelWidth?: string | number;
|
|
28
|
+
/** 标签对齐方式。 */
|
|
29
|
+
labelAlign?: FormLabelAlign;
|
|
30
|
+
/** 标签自定义样式。 */
|
|
31
|
+
labelStyle?: Record<string, any>;
|
|
32
|
+
/** 表单控件尺寸。 */
|
|
33
|
+
size?: FormSize;
|
|
34
|
+
/** 根节点自定义类名。 */
|
|
35
|
+
customClass?: string;
|
|
36
|
+
/** 根节点自定义样式。 */
|
|
37
|
+
customStyle?: StyleValue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 表单校验回调。 */
|
|
41
|
+
export type FormCallback = (valid: boolean, errors: FormError[]) => void;
|
|
42
|
+
|
|
43
|
+
/** Form 通过模板 ref 暴露的接口。 */
|
|
44
|
+
export interface FormExpose {
|
|
45
|
+
/** 动态设置校验规则。 */
|
|
46
|
+
setRules(rules: FormRules): void;
|
|
47
|
+
/** 重置所有已注册字段。 */
|
|
48
|
+
resetFields(): void;
|
|
49
|
+
/** 校验全部字段。 */
|
|
50
|
+
validate(callback?: FormCallback): Promise<boolean>;
|
|
51
|
+
/** 校验一个或多个指定字段。 */
|
|
52
|
+
validateField(props: string | string[], callback?: FormCallback): Promise<boolean>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Form 注册的单个字段协议。 */
|
|
56
|
+
export interface FormField {
|
|
57
|
+
/** 字段在表单模型中的路径。 */
|
|
58
|
+
prop: string;
|
|
59
|
+
/** 按触发方式校验字段,返回错误列表。 */
|
|
60
|
+
validate(trigger?: FormTrigger): Promise<FormError[]>;
|
|
61
|
+
/** 将字段恢复为初始值。 */
|
|
62
|
+
reset(): void;
|
|
63
|
+
/** 清空字段的校验状态。 */
|
|
64
|
+
clear(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Form 向 form-item 注入的配对上下文(formKey 通道)。 */
|
|
68
|
+
export interface FormContext {
|
|
69
|
+
/** 表单数据对象。 */
|
|
70
|
+
model: Ref<Record<string, unknown>>;
|
|
71
|
+
/** 校验规则。 */
|
|
72
|
+
rules: Ref<FormRules>;
|
|
73
|
+
/** 控件尺寸。 */
|
|
74
|
+
size: Ref<FormSize>;
|
|
75
|
+
/** 错误提示方式。 */
|
|
76
|
+
errorType: Ref<FormErrorType[]>;
|
|
77
|
+
/** 是否显示表单项底边。 */
|
|
78
|
+
borderBottom: Ref<boolean>;
|
|
79
|
+
/** 标签位置。 */
|
|
80
|
+
labelPosition: Ref<FormLabelPosition>;
|
|
81
|
+
/** 标签宽度。 */
|
|
82
|
+
labelWidth: Ref<string | number>;
|
|
83
|
+
/** 标签对齐方式。 */
|
|
84
|
+
labelAlign: Ref<FormLabelAlign>;
|
|
85
|
+
/** 标签自定义样式。 */
|
|
86
|
+
labelStyle: Ref<StyleValue>;
|
|
87
|
+
/** 注册一个字段。 */
|
|
88
|
+
register(field: FormField): void;
|
|
89
|
+
/** 注销一个字段。 */
|
|
90
|
+
unregister(field: FormField): void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Form ↔ FormItem 配对注入键。 */
|
|
94
|
+
export const formKey: InjectionKey<FormContext> = Symbol('snake-form');
|
|
95
|
+
|
|
96
|
+
export type { FormError, FormRule, FormRules, FormTrigger } from '../../libs/form';
|