@ecolovo/uniapp-components 2.0.1 → 2.0.3
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/components/aspect/aspect.vue +4 -2
- package/components/badge/badge.scss +3 -1
- package/components/badge/badge.vue +15 -11
- package/components/button/button.vue +100 -27
- package/components/checkbox/checkbox.scss +1 -1
- package/components/checkbox/checkbox.vue +54 -23
- package/components/checkbox-group/checkbox-group.vue +11 -4
- package/components/checkbox-group2/checkbox-group2.vue +23 -8
- package/components/checkbox-group2/checkbox2.vue +11 -4
- package/components/data-list/data-list.vue +25 -5
- package/components/date-picker/date-picker-view.vue +45 -16
- package/components/date-picker/date-picker.vue +58 -18
- package/components/dialog/dialog-content.vue +39 -12
- package/components/dialog/dialog.vue +30 -19
- package/components/dropdown/dropdown.vue +29 -6
- package/components/empty/empty.vue +10 -3
- package/components/flex/flex.vue +16 -6
- package/components/form-item/form-item.vue +13 -4
- package/components/icon/icon.scss +9 -0
- package/components/icon/icon.vue +46 -8
- package/components/image/image.vue +13 -8
- package/components/index.d.ts +50 -0
- package/components/index.js +50 -0
- package/components/index.ts +50 -0
- package/components/input/input.vue +13 -3
- package/components/input/password-input.vue +2 -2
- package/components/list-page/list-page.vue +24 -6
- package/components/load-more/load-more.vue +7 -2
- package/components/loading/animations/loading-animation3.vue +10 -5
- package/components/loading/animations/loading-animation4.vue +65 -8
- package/components/loading/loading.vue +7 -6
- package/components/message-box/message-box.d.ts +18 -0
- package/components/message-box/message-box.js +1 -0
- package/components/message-box/message-box.ts +18 -0
- package/components/message-box/message-box.vue +53 -21
- package/components/number-dialog/number-dialog.vue +61 -21
- package/components/number-input/number-input.vue +53 -18
- package/components/numpad/numpad.vue +11 -5
- package/components/page/page-body.scss +1 -1
- package/components/page/page-body.vue +79 -11
- package/components/page/page-footer.vue +11 -5
- package/components/page/page-header.vue +49 -24
- package/components/page/page.scss +2 -1
- package/components/page/page.vue +74 -29
- package/components/picker/picker-popup.vue +96 -68
- package/components/picker/picker.vue +11 -5
- package/components/popover/popover-content.vue +59 -19
- package/components/popover/popover-mask.vue +18 -11
- package/components/popover/popover.vue +13 -3
- package/components/popup/popup-content.vue +35 -11
- package/components/popup/popup.vue +36 -17
- package/components/price/price.scss +1 -0
- package/components/price/price.vue +18 -7
- package/components/search/search.vue +24 -10
- package/components/searching/searching.scss +14 -0
- package/components/searching/searching.vue +17 -10
- package/components/selector/selector.vue +10 -3
- package/components/switch/switch.vue +31 -10
- package/components/tab/tab.vue +41 -8
- package/components/textarea/textarea.vue +55 -13
- package/composable/component-name.js +2 -1
- package/composable/component-name.ts +2 -1
- package/composable/message-box.d.ts +35 -0
- package/composable/message-box.js +56 -0
- package/composable/message-box.ts +98 -0
- package/configs/index.d.ts +8 -1
- package/configs/index.js +14 -3
- package/configs/index.ts +8 -1
- package/easyCom.js +10 -5
- package/easyCom.ts +10 -5
- package/globals.d.ts +36 -0
- package/index.d.ts +5 -0
- package/index.js +5 -0
- package/index.ts +5 -0
- package/package.json +14 -6
- package/styles/global.scss +123 -2
- package/styles/reset.scss +5 -0
- package/styles/utils/css-var.scss +11 -11
- package/styles/utils/index.scss +0 -1
- package/styles/var/component.scss +23 -20
- package/styles/var/theme.scss +3 -3
- package/utils/date.d.ts +7 -2
- package/utils/date.js +31 -10
- package/utils/date.ts +20 -6
- package/utils/price.d.ts +4 -1
- package/utils/price.js +12 -7
- package/utils/price.ts +8 -3
- package/utils/round.js +4 -1
- package/utils/round.ts +4 -1
- package/utils/windowInfo.d.ts +0 -1
- package/styles/utils/z-index.scss +0 -10
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<view
|
|
2
|
+
<view
|
|
3
|
+
:class="[...classNames, $style['PageFooter']]"
|
|
4
|
+
:style="inlineStyle"
|
|
5
|
+
@touchmove.stop.prevent
|
|
6
|
+
>
|
|
3
7
|
<view :class="$style['PageFooter--content']">
|
|
4
8
|
<slot></slot>
|
|
5
9
|
</view>
|
|
@@ -38,8 +42,6 @@ const inlineStyle = computed(() => {
|
|
|
38
42
|
`--safe-area-bottom: ${safeAreaInsetBottom}px;`,
|
|
39
43
|
].join('');
|
|
40
44
|
});
|
|
41
|
-
|
|
42
|
-
const doNothing = () => {};
|
|
43
45
|
</script>
|
|
44
46
|
|
|
45
47
|
<style lang="scss" module>
|
|
@@ -48,7 +50,11 @@ const doNothing = () => {};
|
|
|
48
50
|
|
|
49
51
|
// 定义外部变量
|
|
50
52
|
%external-vars {
|
|
51
|
-
@include UTILS__DEFINE_COMPONENT_VARS(
|
|
53
|
+
@include UTILS__DEFINE_COMPONENT_VARS('page-footer', $page-footer);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:host {
|
|
57
|
+
display: block;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
.PageFooter {
|
|
@@ -60,7 +66,7 @@ const doNothing = () => {};
|
|
|
60
66
|
background: UTILS__LOCAL_VAR(background);
|
|
61
67
|
padding-bottom: var(--safe-area-bottom);
|
|
62
68
|
box-sizing: border-box;
|
|
63
|
-
z-index:
|
|
69
|
+
z-index: UTILS__GLOBAL_VAR(z-index, page-footer);
|
|
64
70
|
box-shadow: 0 -4rpx 4rpx 4rpx #ccc;
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
:class="[
|
|
4
4
|
...classNames,
|
|
5
5
|
$style['PageHeader'],
|
|
6
|
-
theme === 'dark'
|
|
6
|
+
theme === 'dark'
|
|
7
|
+
? $style['PageHeader--dark']
|
|
8
|
+
: $style['PageHeader--light'],
|
|
9
|
+
{ [$style['PageHeader--fixed']]: fixed },
|
|
7
10
|
]"
|
|
8
11
|
:style="inlineCssVar"
|
|
9
12
|
>
|
|
@@ -57,10 +60,14 @@
|
|
|
57
60
|
/**
|
|
58
61
|
* 页面头部部分容器组件,不单独使用,通过 page 组件间接引入
|
|
59
62
|
*/
|
|
60
|
-
import { computed } from 'vue';
|
|
63
|
+
import { computed, onMounted, ref } from 'vue';
|
|
61
64
|
import useClassNames from '../../composable/component-name';
|
|
62
65
|
import { homePath } from '../../configs';
|
|
63
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
navBarHeight,
|
|
68
|
+
navBarTopOffset,
|
|
69
|
+
menuButtonWidthInCss,
|
|
70
|
+
} from '../../utils/windowInfo';
|
|
64
71
|
import IconCpn from '../icon/icon.vue';
|
|
65
72
|
|
|
66
73
|
defineOptions({
|
|
@@ -73,55 +80,62 @@ defineOptions({
|
|
|
73
80
|
const classNames = useClassNames('page-header');
|
|
74
81
|
|
|
75
82
|
type _Props = {
|
|
76
|
-
/**
|
|
83
|
+
/** 导航栏标题 */
|
|
77
84
|
title?: string;
|
|
78
|
-
/**
|
|
85
|
+
/** 导航栏标题是否居中显示 */
|
|
79
86
|
center?: boolean;
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
/**
|
|
87
|
+
/** 是否使用固定布局 */
|
|
88
|
+
fixed?: boolean;
|
|
89
|
+
/** 主题,只能是 'light' 和 'dark' 中的一个 */
|
|
90
|
+
theme?: 'dark' | 'light';
|
|
91
|
+
/** 导航按钮类型: 0 = 不显示,1 = 返回,2 = 首页,3 = 返回|首页 */
|
|
83
92
|
navType?: 0 | 1 | 2 | 3;
|
|
84
93
|
};
|
|
85
94
|
|
|
86
95
|
const props = withDefaults(defineProps<_Props>(), {
|
|
87
96
|
title: '',
|
|
88
97
|
center: false,
|
|
89
|
-
|
|
98
|
+
fixed: false,
|
|
99
|
+
theme: 'light',
|
|
90
100
|
navType: undefined,
|
|
91
101
|
});
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
/** 当前页面栈 */
|
|
104
|
+
const currentPages = ref<ReturnType<typeof getCurrentPages>>([]);
|
|
105
|
+
/** 当前页面路由 */
|
|
106
|
+
const currentPageRoute = computed(() => {
|
|
107
|
+
return currentPages.value.slice(-1)[0]?.route || '';
|
|
108
|
+
});
|
|
96
109
|
|
|
97
110
|
/** 是否显示返回按钮 */
|
|
98
111
|
const hasBack = computed(() => {
|
|
99
112
|
// 如果指定了导航按钮类型,则根据类型判断
|
|
100
113
|
if (props.navType !== undefined) return !!(props.navType & 1);
|
|
101
|
-
const pages = getCurrentPages();
|
|
102
114
|
// 在首页不显示返回按钮
|
|
103
|
-
if (homePath === '/' +
|
|
115
|
+
if (homePath === '/' + currentPageRoute.value) return false;
|
|
104
116
|
// 页面栈只有一个时不显示返回按钮
|
|
105
|
-
if (
|
|
117
|
+
if (currentPages.value.length <= 1) return false;
|
|
106
118
|
// 其他情况均显示返回按钮
|
|
107
119
|
return true;
|
|
108
120
|
});
|
|
121
|
+
|
|
109
122
|
/** 是否显示首页按钮 */
|
|
110
123
|
const hasHome = computed(() => {
|
|
111
124
|
// 如果指定了导航按钮类型,则根据类型判断
|
|
112
125
|
if (props.navType !== undefined) return !!(props.navType & 2);
|
|
113
|
-
const pages = getCurrentPages();
|
|
114
126
|
// 在首页不显示返回首页按钮
|
|
115
|
-
if (homePath === '/' +
|
|
127
|
+
if (homePath === '/' + currentPageRoute.value) return false;
|
|
116
128
|
// 页面栈只有一个时显示返回按钮
|
|
117
|
-
if (
|
|
129
|
+
if (currentPages.value.length === 1) return true;
|
|
118
130
|
// 其他情况均不显示返回按钮
|
|
119
131
|
return false;
|
|
120
132
|
});
|
|
133
|
+
|
|
121
134
|
/** 导航按钮个数 */
|
|
122
135
|
const iconCount = computed(() => {
|
|
123
136
|
return (hasBack.value ? 1 : 0) + (hasHome.value ? 1 : 0);
|
|
124
137
|
});
|
|
138
|
+
|
|
125
139
|
const inlineCssVar = computed(() => {
|
|
126
140
|
return [
|
|
127
141
|
// 页面头部上边缘相较于顶部的距离
|
|
@@ -132,6 +146,10 @@ const inlineCssVar = computed(() => {
|
|
|
132
146
|
`--menu-button-width: ${menuButtonWidthInCss};`,
|
|
133
147
|
].join(' ');
|
|
134
148
|
});
|
|
149
|
+
|
|
150
|
+
onMounted(() => {
|
|
151
|
+
currentPages.value = getCurrentPages() || [];
|
|
152
|
+
});
|
|
135
153
|
</script>
|
|
136
154
|
|
|
137
155
|
<style lang="scss" module>
|
|
@@ -140,18 +158,26 @@ const inlineCssVar = computed(() => {
|
|
|
140
158
|
|
|
141
159
|
// 定义外部变量
|
|
142
160
|
%external-vars {
|
|
143
|
-
@include UTILS__DEFINE_COMPONENT_VARS(
|
|
161
|
+
@include UTILS__DEFINE_COMPONENT_VARS('page-header', $page-header);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
:host {
|
|
165
|
+
display: block;
|
|
144
166
|
}
|
|
145
167
|
|
|
146
168
|
.PageHeader {
|
|
147
169
|
@extend %external-vars;
|
|
148
|
-
position: fixed;
|
|
149
|
-
top: 0;
|
|
150
|
-
left: 0;
|
|
151
170
|
width: 100%;
|
|
152
171
|
overflow: hidden; // BFC
|
|
153
172
|
background: UTILS__LOCAL_VAR(background);
|
|
154
|
-
|
|
173
|
+
}
|
|
174
|
+
// 固定布局
|
|
175
|
+
.PageHeader--fixed {
|
|
176
|
+
position: fixed;
|
|
177
|
+
top: 0;
|
|
178
|
+
left: 0;
|
|
179
|
+
z-index: UTILS__GLOBAL_VAR(z-index, page-header);
|
|
180
|
+
padding-top: var(--page-header-top);
|
|
155
181
|
}
|
|
156
182
|
|
|
157
183
|
// 亮色主题对应样式
|
|
@@ -168,7 +194,6 @@ const inlineCssVar = computed(() => {
|
|
|
168
194
|
position: relative;
|
|
169
195
|
font-size: UTILS__LOCAL_VAR(font-size);
|
|
170
196
|
font-weight: 600;
|
|
171
|
-
margin-top: var(--page-header-top);
|
|
172
197
|
width: 100%;
|
|
173
198
|
height: var(--page-header-height);
|
|
174
199
|
line-height: var(--page-header-height);
|
package/components/page/page.vue
CHANGED
|
@@ -1,40 +1,59 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view :class="[...classNames, $style['Page']]" :style="inlineStyle">
|
|
3
3
|
<slot name="header">
|
|
4
|
-
<page-header-cpn
|
|
4
|
+
<page-header-cpn
|
|
5
|
+
v-if="hasHeader"
|
|
6
|
+
fixed
|
|
7
|
+
:theme="theme"
|
|
8
|
+
:title="title"
|
|
9
|
+
>
|
|
5
10
|
<template #extra>
|
|
6
11
|
<slot name="header-extra"></slot>
|
|
7
12
|
</template>
|
|
8
13
|
</page-header-cpn>
|
|
9
14
|
</slot>
|
|
10
15
|
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
<slot name="body">
|
|
17
|
+
<page-body-cpn
|
|
18
|
+
ref="pageBodyRef"
|
|
19
|
+
:has-header="hasHeader"
|
|
20
|
+
:has-footer="hasFooter && !!$slots.footer"
|
|
21
|
+
:use-scroll="useScroll"
|
|
22
|
+
:is-tab-page="tabbar"
|
|
23
|
+
:enable-refresh="enableRefresh"
|
|
24
|
+
:refreshing="refreshing"
|
|
25
|
+
@scroll="$emit('scroll', $event)"
|
|
26
|
+
@refresh="$emit('refresh')"
|
|
27
|
+
@reach-bottom="$emit('reach-bottom')"
|
|
28
|
+
>
|
|
29
|
+
<slot></slot>
|
|
30
|
+
</page-body-cpn>
|
|
31
|
+
</slot>
|
|
32
|
+
|
|
33
|
+
<!-- 固定布局内容插槽 -->
|
|
34
|
+
<slot name="fixed"></slot>
|
|
35
|
+
|
|
36
|
+
<page-footer-cpn
|
|
37
|
+
v-if="hasFooter && $slots.footer"
|
|
16
38
|
:is-tab-page="tabbar"
|
|
17
|
-
:enable-refresh="enableRefresh"
|
|
18
|
-
:refreshing="refreshing"
|
|
19
|
-
@scroll="$emit('scroll', $event)"
|
|
20
|
-
@refresh="$emit('refresh')"
|
|
21
|
-
@reach-bottom="$emit('reach-bottom')"
|
|
22
39
|
>
|
|
23
|
-
<slot></slot>
|
|
24
|
-
</page-body-cpn>
|
|
25
|
-
|
|
26
|
-
<page-footer-cpn v-if="hasFooter && $slots.footer" :is-tab-page="tabbar">
|
|
27
40
|
<slot name="footer"></slot>
|
|
28
41
|
</page-footer-cpn>
|
|
29
42
|
|
|
30
|
-
<
|
|
43
|
+
<message-box-cpn ref="pageMessageBoxRef" />
|
|
44
|
+
|
|
45
|
+
<loading-cpn
|
|
46
|
+
v-if="loading"
|
|
47
|
+
:title="loading === true ? '加载中...' : loading"
|
|
48
|
+
/>
|
|
31
49
|
</view>
|
|
32
50
|
</template>
|
|
33
51
|
|
|
34
52
|
<script lang="ts" setup>
|
|
35
|
-
import { computed } from 'vue';
|
|
53
|
+
import { computed, provide, ref } from 'vue';
|
|
36
54
|
import useClassNames from '../../composable/component-name';
|
|
37
55
|
import LoadingCpn from '../loading/loading.vue';
|
|
56
|
+
import MessageBoxCpn from '../message-box/message-box.vue';
|
|
38
57
|
import PageBodyCpn from './page-body.vue';
|
|
39
58
|
import PageFooterCpn from './page-footer.vue';
|
|
40
59
|
import PageHeaderCpn from './page-header.vue';
|
|
@@ -45,6 +64,7 @@ import {
|
|
|
45
64
|
navBarTopOffset,
|
|
46
65
|
safeAreaInsetBottom,
|
|
47
66
|
} from '../../utils/windowInfo';
|
|
67
|
+
import { useMessageBox } from '../../composable/message-box';
|
|
48
68
|
|
|
49
69
|
defineOptions({
|
|
50
70
|
options: {
|
|
@@ -98,23 +118,36 @@ withDefaults(defineProps<_Props>(), {
|
|
|
98
118
|
tabbar: false,
|
|
99
119
|
});
|
|
100
120
|
|
|
101
|
-
type ScrollEvent = {
|
|
102
|
-
detail: {
|
|
103
|
-
scrollLeft: number;
|
|
104
|
-
scrollTop: number;
|
|
105
|
-
scrollHeight: number;
|
|
106
|
-
scrollWidth: number;
|
|
107
|
-
deltaX: number;
|
|
108
|
-
deltaY: number;
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
-
|
|
112
121
|
defineEmits<{
|
|
113
122
|
(event: 'scroll', value: ScrollEvent): void;
|
|
114
123
|
(event: 'refresh'): void;
|
|
115
124
|
(event: 'reach-bottom'): void;
|
|
116
125
|
}>();
|
|
117
126
|
|
|
127
|
+
const pageBodyRef = ref<InstanceType<typeof PageBodyCpn>>();
|
|
128
|
+
const pageMessageBoxRef = ref<InstanceType<typeof MessageBoxCpn>>();
|
|
129
|
+
provide('ecolovo/page/messageBoxRef', pageMessageBoxRef);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 滚动到顶部
|
|
133
|
+
* @param position 滚动到与顶部的距离
|
|
134
|
+
* @param animation 是否有滚动动画
|
|
135
|
+
*/
|
|
136
|
+
const scrollToTop = (position = 0, animation = true) => {
|
|
137
|
+
pageBodyRef.value?.scrollToTop(position, animation);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 滚动到指定 id 的元素位置
|
|
142
|
+
* @param viewId 目标元素的 id
|
|
143
|
+
* @param animation 是否有滚动动画
|
|
144
|
+
*/
|
|
145
|
+
const scrollIntoView = (viewId: string, animation = true) => {
|
|
146
|
+
pageBodyRef.value?.scrollIntoView(viewId, animation);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const { show, showConfirm, showMessageBox } = useMessageBox(pageMessageBoxRef);
|
|
150
|
+
|
|
118
151
|
const inlineStyle = computed(() => {
|
|
119
152
|
return [
|
|
120
153
|
// 顶部安全区高度
|
|
@@ -131,6 +164,14 @@ const inlineStyle = computed(() => {
|
|
|
131
164
|
`--menu-button-width: ${menuButtonWidthInCss};`,
|
|
132
165
|
].join('');
|
|
133
166
|
});
|
|
167
|
+
|
|
168
|
+
defineExpose({
|
|
169
|
+
scrollToTop,
|
|
170
|
+
scrollIntoView,
|
|
171
|
+
show,
|
|
172
|
+
showConfirm,
|
|
173
|
+
showMessageBox,
|
|
174
|
+
});
|
|
134
175
|
</script>
|
|
135
176
|
|
|
136
177
|
<style lang="scss" module>
|
|
@@ -139,7 +180,11 @@ const inlineStyle = computed(() => {
|
|
|
139
180
|
|
|
140
181
|
// 定义外部变量
|
|
141
182
|
%external-vars {
|
|
142
|
-
@include UTILS__DEFINE_COMPONENT_VARS(
|
|
183
|
+
@include UTILS__DEFINE_COMPONENT_VARS('page', $page);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
:host {
|
|
187
|
+
display: block;
|
|
143
188
|
}
|
|
144
189
|
|
|
145
190
|
.Page {
|
|
@@ -19,11 +19,19 @@
|
|
|
19
19
|
<view :class="$style['PickerPopup--title']">
|
|
20
20
|
<slot name="title">
|
|
21
21
|
<text>{{ title }}</text>
|
|
22
|
-
<text v-if="multiple"
|
|
23
|
-
|
|
22
|
+
<text v-if="multiple"
|
|
23
|
+
>({{ tempCheckedCount }})</text
|
|
24
|
+
>
|
|
25
|
+
<text
|
|
26
|
+
:class="$style['PickerPopup--sub-title']"
|
|
27
|
+
>{{ subTitle }}</text
|
|
28
|
+
>
|
|
24
29
|
</slot>
|
|
25
30
|
</view>
|
|
26
|
-
<view
|
|
31
|
+
<view
|
|
32
|
+
v-if="searchable"
|
|
33
|
+
:class="$style['PickerPopup--search-wrapper']"
|
|
34
|
+
>
|
|
27
35
|
<!-- 搜索框 -->
|
|
28
36
|
<search-cpn
|
|
29
37
|
:placeholder="searchPlaceholder"
|
|
@@ -40,22 +48,34 @@
|
|
|
40
48
|
<view
|
|
41
49
|
v-for="item in filteredList"
|
|
42
50
|
:key="item.option[valueKey]"
|
|
43
|
-
:class="[
|
|
51
|
+
:class="[
|
|
52
|
+
$style['PickerPopup--option'],
|
|
53
|
+
{ [$style['PickerPopup--checked']]: item.checked },
|
|
54
|
+
]"
|
|
44
55
|
@click="checkPickerItem(item.option)"
|
|
45
56
|
>
|
|
46
57
|
<icon-cpn
|
|
47
58
|
v-if="multiple"
|
|
48
|
-
:icon="
|
|
59
|
+
:icon="
|
|
60
|
+
item.checked
|
|
61
|
+
? 'checkbox-checked'
|
|
62
|
+
: 'checkbox-unchecked'
|
|
63
|
+
"
|
|
49
64
|
:class="$style['PickerPopup--option-multi']"
|
|
50
65
|
/>
|
|
51
|
-
<text :class="$style['PickerPopup--option-text']">{{
|
|
66
|
+
<text :class="$style['PickerPopup--option-text']">{{
|
|
67
|
+
item.option[labelKey]
|
|
68
|
+
}}</text>
|
|
52
69
|
<icon-cpn
|
|
53
70
|
v-if="!multiple && item.checked"
|
|
54
71
|
icon="radio-checked"
|
|
55
72
|
:class="$style['PickerPopup--option-single']"
|
|
56
73
|
/>
|
|
57
74
|
</view>
|
|
58
|
-
<view
|
|
75
|
+
<view
|
|
76
|
+
v-if="pagingData && pagingData.data.length > 0"
|
|
77
|
+
:class="$style['PickerPopup--load-more-wrapper']"
|
|
78
|
+
>
|
|
59
79
|
<load-more-cpn
|
|
60
80
|
:loading="loadingMore"
|
|
61
81
|
:end="!pagingData.hasMore"
|
|
@@ -63,7 +83,10 @@
|
|
|
63
83
|
@load-more="loadMore()"
|
|
64
84
|
/>
|
|
65
85
|
</view>
|
|
66
|
-
<empty-cpn
|
|
86
|
+
<empty-cpn
|
|
87
|
+
v-if="filteredList.length === 0"
|
|
88
|
+
:class="$style['PickerPopup--empty']"
|
|
89
|
+
/>
|
|
67
90
|
</popup-content-cpn>
|
|
68
91
|
</template>
|
|
69
92
|
</popup-cpn>
|
|
@@ -71,7 +94,7 @@
|
|
|
71
94
|
|
|
72
95
|
<script lang="ts" setup>
|
|
73
96
|
import { computed, ref, watch } from 'vue';
|
|
74
|
-
import { PagingData } from '@ecolovo/utils
|
|
97
|
+
import type { PagingData } from '@ecolovo/utils';
|
|
75
98
|
import useClassNames from '../../composable/component-name';
|
|
76
99
|
import EmptyCpn from '../empty/empty.vue';
|
|
77
100
|
import IconCpn from '../icon/icon.vue';
|
|
@@ -127,6 +150,7 @@ const props = withDefaults(defineProps<_Props>(), {
|
|
|
127
150
|
searchPlaceholder: '请输入',
|
|
128
151
|
options: () => [],
|
|
129
152
|
pagingData: null,
|
|
153
|
+
loadingMore: false,
|
|
130
154
|
checked: () => [],
|
|
131
155
|
valueKey: 'id',
|
|
132
156
|
labelKey: 'label',
|
|
@@ -167,7 +191,9 @@ const setOptionList = () => {
|
|
|
167
191
|
const filteredList = computed(() => {
|
|
168
192
|
const text = filterText.value.trim();
|
|
169
193
|
return text
|
|
170
|
-
? optionList.value.filter((item) =>
|
|
194
|
+
? optionList.value.filter((item) =>
|
|
195
|
+
(item.option[props.labelKey] as string).includes(text)
|
|
196
|
+
)
|
|
171
197
|
: optionList.value;
|
|
172
198
|
});
|
|
173
199
|
|
|
@@ -187,7 +213,9 @@ watch(
|
|
|
187
213
|
|
|
188
214
|
/** 勾选选项 */
|
|
189
215
|
const checkPickerItem = (option: _Option) => {
|
|
190
|
-
const index = optionList.value.findIndex(
|
|
216
|
+
const index = optionList.value.findIndex(
|
|
217
|
+
(item) => item.option[props.valueKey] === option[props.valueKey]
|
|
218
|
+
);
|
|
191
219
|
if (index < 0) return;
|
|
192
220
|
|
|
193
221
|
if (!props.multiple) {
|
|
@@ -240,65 +268,65 @@ const onConfirm = () => {
|
|
|
240
268
|
<style lang="scss" module>
|
|
241
269
|
@use '../../styles/utils' as *;
|
|
242
270
|
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
:host {
|
|
272
|
+
display: block;
|
|
273
|
+
}
|
|
245
274
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
+
.PickerPopup--header {
|
|
276
|
+
position: relative;
|
|
277
|
+
box-sizing: border-box;
|
|
278
|
+
height: UTILS__LOCAL_VAR(header-height);
|
|
279
|
+
padding: 20rpx 30rpx 0rpx;
|
|
280
|
+
display: flex;
|
|
281
|
+
align-items: center;
|
|
282
|
+
}
|
|
283
|
+
.PickerPopup--title {
|
|
284
|
+
flex: 1;
|
|
285
|
+
font-size: UTILS__GLOBAL_VAR(font-size, large);
|
|
286
|
+
font-weight: 600;
|
|
287
|
+
}
|
|
288
|
+
.PickerPopup--sub-title {
|
|
289
|
+
margin-left: 10rpx;
|
|
290
|
+
color: UTILS__GLOBAL_VAR(color, placeholder);
|
|
291
|
+
font-size: UTILS__GLOBAL_VAR(font-size, normal);
|
|
292
|
+
font-weight: 400;
|
|
293
|
+
}
|
|
294
|
+
.PickerPopup--search-wrapper {
|
|
295
|
+
flex: none;
|
|
296
|
+
height: 88rpx;
|
|
297
|
+
display: flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
#{UTILS__VAR_NAME(search, height)}: 72rpx;
|
|
300
|
+
}
|
|
301
|
+
.PickerPopup--search {
|
|
302
|
+
flex: 1;
|
|
303
|
+
}
|
|
275
304
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
.PickerPopup--option-multi {
|
|
286
|
-
flex: none;
|
|
287
|
-
margin-right: 12rpx;
|
|
288
|
-
}
|
|
289
|
-
.PickerPopup--option-single {
|
|
290
|
-
flex: none;
|
|
291
|
-
margin-left: 12rpx;
|
|
292
|
-
}
|
|
293
|
-
.PickerPopup--option-text {
|
|
294
|
-
@include UTILS__ELLIPSIS();
|
|
295
|
-
}
|
|
296
|
-
.PickerPopup--empty {
|
|
297
|
-
margin: auto auto;
|
|
298
|
-
}
|
|
299
|
-
.PickerPopup--load-more-wrapper {
|
|
300
|
-
position: sticky;
|
|
301
|
-
top: 100%;
|
|
305
|
+
.PickerPopup--option {
|
|
306
|
+
display: flex;
|
|
307
|
+
justify-content: start;
|
|
308
|
+
align-items: center;
|
|
309
|
+
padding: 20rpx 0;
|
|
310
|
+
&.PickerPopup--checked {
|
|
311
|
+
color: UTILS__GLOBAL_VAR(theme, primary);
|
|
302
312
|
}
|
|
303
313
|
}
|
|
314
|
+
.PickerPopup--option-multi {
|
|
315
|
+
flex: none;
|
|
316
|
+
margin-right: 12rpx;
|
|
317
|
+
}
|
|
318
|
+
.PickerPopup--option-single {
|
|
319
|
+
flex: none;
|
|
320
|
+
margin-left: 12rpx;
|
|
321
|
+
}
|
|
322
|
+
.PickerPopup--option-text {
|
|
323
|
+
@include UTILS__ELLIPSIS();
|
|
324
|
+
}
|
|
325
|
+
.PickerPopup--empty {
|
|
326
|
+
margin: auto auto;
|
|
327
|
+
}
|
|
328
|
+
.PickerPopup--load-more-wrapper {
|
|
329
|
+
position: sticky;
|
|
330
|
+
top: 100%;
|
|
331
|
+
}
|
|
304
332
|
</style>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</template>
|
|
26
26
|
|
|
27
27
|
<script lang="ts" setup>
|
|
28
|
-
import isEqual from 'lodash-es
|
|
28
|
+
import { isEqual } from 'lodash-es';
|
|
29
29
|
import { computed, inject, ref } from 'vue';
|
|
30
30
|
import useClassNames from '../../composable/component-name';
|
|
31
31
|
import SelectorCpn from '../selector/selector.vue';
|
|
@@ -102,15 +102,21 @@ const isReadonly = computed(() => props.readonly || isFormReadonly.value);
|
|
|
102
102
|
|
|
103
103
|
const checkedOptions = computed(() => {
|
|
104
104
|
if (props.multiple) {
|
|
105
|
-
return props.options.filter((item) =>
|
|
105
|
+
return props.options.filter((item) =>
|
|
106
|
+
(props.modelValue as unknown[])?.includes(item[props.valueKey])
|
|
107
|
+
);
|
|
106
108
|
}
|
|
107
|
-
return props.options.filter(
|
|
109
|
+
return props.options.filter(
|
|
110
|
+
(item) => item[props.valueKey] === props.modelValue
|
|
111
|
+
);
|
|
108
112
|
});
|
|
109
113
|
|
|
110
114
|
const valueStr = computed(() => {
|
|
111
115
|
if (props.valueText) return props.valueText;
|
|
112
116
|
if (props.modelValue === null) return '';
|
|
113
|
-
return checkedOptions.value
|
|
117
|
+
return checkedOptions.value
|
|
118
|
+
.map((item) => item[props.labelKey] as string)
|
|
119
|
+
.join(props.joinText);
|
|
114
120
|
});
|
|
115
121
|
|
|
116
122
|
const showPicker = () => {
|
|
@@ -137,7 +143,7 @@ const onConfirm = (checked: _Option | _Option[]) => {
|
|
|
137
143
|
</script>
|
|
138
144
|
|
|
139
145
|
<style lang="scss" module>
|
|
140
|
-
@use '../../styles/
|
|
146
|
+
@use '../../styles/utils' as *;
|
|
141
147
|
|
|
142
148
|
.Picker {
|
|
143
149
|
width: 100%;
|