uniapp-dyckui 4.1.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/README.md +104 -0
- package/dist/assets/style.BFlsbpSj.css +1472 -0
- package/dist/index.cjs.js +1380 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.es.js +1380 -0
- package/dist/index.es.js.map +1 -0
- package/dist/src/components/MyComs/Button/index.d.ts +3 -0
- package/dist/src/components/MyComs/Button/index.vue.d.ts +93 -0
- package/dist/src/components/MyComs/Dialog/index.d.ts +3 -0
- package/dist/src/components/MyComs/Dialog/index.vue.d.ts +65 -0
- package/dist/src/components/MyComs/Divider/index.d.ts +3 -0
- package/dist/src/components/MyComs/Divider/index.vue.d.ts +53 -0
- package/dist/src/components/MyComs/DropdownSelect/dropdownSelect.d.ts +10 -0
- package/dist/src/components/MyComs/DropdownSelect/index.d.ts +4 -0
- package/dist/src/components/MyComs/DropdownSelect/index.vue.d.ts +26 -0
- package/dist/src/components/MyComs/DropdownSelect/type.d.ts +13 -0
- package/dist/src/components/MyComs/DropdownWithBadge/dropdownWithBadge.d.ts +5 -0
- package/dist/src/components/MyComs/DropdownWithBadge/index.d.ts +4 -0
- package/dist/src/components/MyComs/DropdownWithBadge/index.vue.d.ts +26 -0
- package/dist/src/components/MyComs/DropdownWithBadge/type.d.ts +9 -0
- package/dist/src/components/MyComs/FilterDrawer/hasBadge.d.ts +8 -0
- package/dist/src/components/MyComs/FilterDrawer/index.d.ts +5 -0
- package/dist/src/components/MyComs/FilterDrawer/index.vue.d.ts +26 -0
- package/dist/src/components/MyComs/FilterDrawer/type.d.ts +8 -0
- package/dist/src/components/MyComs/FilterDrawer/useFilterDrawer.d.ts +10 -0
- package/dist/src/components/MyComs/InfiniteScroll/index.d.ts +3 -0
- package/dist/src/components/MyComs/InfiniteScroll/index.vue.d.ts +65 -0
- package/dist/src/components/MyComs/Popup/index.d.ts +3 -0
- package/dist/src/components/MyComs/Popup/index.vue.d.ts +119 -0
- package/dist/src/components/MyComs/PullRefresh/index.d.ts +3 -0
- package/dist/src/components/MyComs/PullRefresh/index.vue.d.ts +117 -0
- package/dist/src/components/MyComs/Swiper/index.d.ts +3 -0
- package/dist/src/components/MyComs/Swiper/index.vue.d.ts +79 -0
- package/dist/src/components/MyComs/Toast/index.d.ts +3 -0
- package/dist/src/components/MyComs/Toast/index.vue.d.ts +108 -0
- package/dist/src/components/MyComs/index.d.ts +20 -0
- package/package.json +218 -0
- package/src/components/MyComs/Button/README.md +235 -0
- package/src/components/MyComs/Button/index.ts +3 -0
- package/src/components/MyComs/Button/index.vue +413 -0
- package/src/components/MyComs/Dialog/README.md +160 -0
- package/src/components/MyComs/Dialog/index.ts +2 -0
- package/src/components/MyComs/Dialog/index.vue +275 -0
- package/src/components/MyComs/Divider/README.md +0 -0
- package/src/components/MyComs/Divider/index.ts +2 -0
- package/src/components/MyComs/Divider/index.vue +106 -0
- package/src/components/MyComs/DropdownSelect/README.md +112 -0
- package/src/components/MyComs/DropdownSelect/dropdownSelect.less +75 -0
- package/src/components/MyComs/DropdownSelect/dropdownSelect.ts +59 -0
- package/src/components/MyComs/DropdownSelect/index.ts +4 -0
- package/src/components/MyComs/DropdownSelect/index.vue +88 -0
- package/src/components/MyComs/DropdownSelect/type.ts +15 -0
- package/src/components/MyComs/DropdownWithBadge/README.md +77 -0
- package/src/components/MyComs/DropdownWithBadge/dropdownWithBadge.less +11 -0
- package/src/components/MyComs/DropdownWithBadge/dropdownWithBadge.ts +10 -0
- package/src/components/MyComs/DropdownWithBadge/index.ts +4 -0
- package/src/components/MyComs/DropdownWithBadge/index.vue +39 -0
- package/src/components/MyComs/DropdownWithBadge/type.ts +12 -0
- package/src/components/MyComs/FilterDrawer/filterDrawer.less +117 -0
- package/src/components/MyComs/FilterDrawer/hasBadge.ts +41 -0
- package/src/components/MyComs/FilterDrawer/index.ts +5 -0
- package/src/components/MyComs/FilterDrawer/index.vue +53 -0
- package/src/components/MyComs/FilterDrawer/type.ts +9 -0
- package/src/components/MyComs/FilterDrawer/useFilterDrawer.ts +38 -0
- package/src/components/MyComs/InfiniteScroll/index.ts +2 -0
- package/src/components/MyComs/InfiniteScroll/index.vue +171 -0
- package/src/components/MyComs/Popup/README.md +684 -0
- package/src/components/MyComs/Popup/index.ts +2 -0
- package/src/components/MyComs/Popup/index.vue +835 -0
- package/src/components/MyComs/PullRefresh/README.md +600 -0
- package/src/components/MyComs/PullRefresh/index.ts +2 -0
- package/src/components/MyComs/PullRefresh/index.vue +599 -0
- package/src/components/MyComs/Swiper/README.md +202 -0
- package/src/components/MyComs/Swiper/index.ts +2 -0
- package/src/components/MyComs/Swiper/index.vue +245 -0
- package/src/components/MyComs/Toast/README.md +604 -0
- package/src/components/MyComs/Toast/index.ts +2 -0
- package/src/components/MyComs/Toast/index.vue +372 -0
- package/src/components/MyComs/index.ts +33 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view v-if="showDialog" class="dialog-wrapper" @click="handleMaskClick">
|
|
3
|
+
<view class="dialog-container" @click.stop>
|
|
4
|
+
<!-- 上部分内容 -->
|
|
5
|
+
<view class="dialog-content">
|
|
6
|
+
<view class="dialog-icon">
|
|
7
|
+
<image v-if="icon" :src="icon" alt="dialog-icon" />
|
|
8
|
+
<!-- 默认图标 -->
|
|
9
|
+
<view v-else class="default-icon" />
|
|
10
|
+
</view>
|
|
11
|
+
<text class="dialog-title">{{ title }}</text>
|
|
12
|
+
<text class="dialog-description">{{ description }}</text>
|
|
13
|
+
</view>
|
|
14
|
+
|
|
15
|
+
<!-- 分割线 -->
|
|
16
|
+
<view class="dialog-divider" />
|
|
17
|
+
|
|
18
|
+
<!-- 下部分按钮 -->
|
|
19
|
+
<view class="dialog-buttons">
|
|
20
|
+
<!-- 类型1: 取消和打开app -->
|
|
21
|
+
<template v-if="type === 'open-app'">
|
|
22
|
+
<view class="dialog-button dialog-button-cancel" @click="handleCancel">
|
|
23
|
+
取消
|
|
24
|
+
</view>
|
|
25
|
+
<view class="dialog-button dialog-button-primary" @click="handleConfirm">
|
|
26
|
+
打开app
|
|
27
|
+
</view>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<!-- 类型2: 知道了 -->
|
|
31
|
+
<template v-else-if="type === 'confirm'">
|
|
32
|
+
<view class="dialog-button dialog-button-full dialog-button-primary" @click="handleConfirm">
|
|
33
|
+
知道了
|
|
34
|
+
</view>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<!-- 类型3: 取消和继续访问 -->
|
|
38
|
+
<template v-else-if="type === 'continue'">
|
|
39
|
+
<view class="dialog-button dialog-button-cancel" @click="handleCancel">
|
|
40
|
+
取消
|
|
41
|
+
</view>
|
|
42
|
+
<view class="dialog-button dialog-button-primary" @click="handleConfirm">
|
|
43
|
+
继续访问
|
|
44
|
+
</view>
|
|
45
|
+
</template>
|
|
46
|
+
</view>
|
|
47
|
+
</view>
|
|
48
|
+
</view>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script setup lang="ts">
|
|
52
|
+
import { ref, watch } from 'vue'
|
|
53
|
+
|
|
54
|
+
// 弹窗类型定义
|
|
55
|
+
type DialogType = 'open-app' | 'confirm' | 'continue'
|
|
56
|
+
|
|
57
|
+
// Props定义
|
|
58
|
+
interface Props {
|
|
59
|
+
// 控制弹窗显示/隐藏(支持v-model)
|
|
60
|
+
modelValue: boolean
|
|
61
|
+
// 弹窗类型
|
|
62
|
+
type?: DialogType
|
|
63
|
+
// 弹窗标题
|
|
64
|
+
title?: string
|
|
65
|
+
// 弹窗说明文字
|
|
66
|
+
description?: string
|
|
67
|
+
// 图标路径
|
|
68
|
+
icon?: string
|
|
69
|
+
// 是否显示遮罩层
|
|
70
|
+
mask?: boolean
|
|
71
|
+
// 遮罩层透明度
|
|
72
|
+
maskOpacity?: number
|
|
73
|
+
// 点击遮罩是否关闭弹窗
|
|
74
|
+
closeOnMaskClick?: boolean
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Props默认值
|
|
78
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
79
|
+
modelValue: false,
|
|
80
|
+
type: 'confirm',
|
|
81
|
+
title: '',
|
|
82
|
+
description: '',
|
|
83
|
+
icon: '',
|
|
84
|
+
mask: true,
|
|
85
|
+
maskOpacity: 0.5,
|
|
86
|
+
closeOnMaskClick: true,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Emits定义
|
|
90
|
+
const emit = defineEmits<{
|
|
91
|
+
(e: 'update:modelValue', value: boolean): void
|
|
92
|
+
(e: 'confirm'): void
|
|
93
|
+
(e: 'cancel'): void
|
|
94
|
+
}>()
|
|
95
|
+
|
|
96
|
+
// 本地状态
|
|
97
|
+
const showDialog = ref(props.modelValue)
|
|
98
|
+
|
|
99
|
+
// 监听modelValue变化
|
|
100
|
+
watch(() => props.modelValue, (newVal) => {
|
|
101
|
+
showDialog.value = newVal
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// 监听showDialog变化
|
|
105
|
+
watch(() => showDialog.value, (newVal) => {
|
|
106
|
+
emit('update:modelValue', newVal)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// 点击遮罩层
|
|
110
|
+
function handleMaskClick() {
|
|
111
|
+
if (props.closeOnMaskClick) {
|
|
112
|
+
showDialog.value = false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 点击确认按钮
|
|
117
|
+
function handleConfirm() {
|
|
118
|
+
emit('confirm')
|
|
119
|
+
// 确认后关闭弹窗
|
|
120
|
+
showDialog.value = false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 点击取消按钮
|
|
124
|
+
function handleCancel() {
|
|
125
|
+
emit('cancel')
|
|
126
|
+
// 取消后关闭弹窗
|
|
127
|
+
showDialog.value = false
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<style scoped>
|
|
132
|
+
/* 弹窗容器 */
|
|
133
|
+
.dialog-wrapper {
|
|
134
|
+
position: fixed;
|
|
135
|
+
top: 0;
|
|
136
|
+
left: 0;
|
|
137
|
+
right: 0;
|
|
138
|
+
bottom: 0;
|
|
139
|
+
z-index: 1000;
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
background-color: rgba(0, 0, 0, v-bind('maskOpacity'));
|
|
144
|
+
animation: fadeIn 300ms ease;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* 弹窗主体 */
|
|
148
|
+
.dialog-container {
|
|
149
|
+
width: 80%;
|
|
150
|
+
max-width: 800rpx;
|
|
151
|
+
background-color: #fff;
|
|
152
|
+
border-radius: 32rpx;
|
|
153
|
+
overflow: hidden;
|
|
154
|
+
box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.15);
|
|
155
|
+
animation: scaleIn 300ms ease;
|
|
156
|
+
background-image: url('@/assets/u2.png');
|
|
157
|
+
background-size: cover;
|
|
158
|
+
background-position: center;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* 上部分内容 */
|
|
162
|
+
.dialog-content {
|
|
163
|
+
padding: 60rpx 40rpx;
|
|
164
|
+
text-align: center;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* 图标 */
|
|
168
|
+
.dialog-icon {
|
|
169
|
+
margin-bottom: 40rpx;
|
|
170
|
+
display: flex;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
align-items: center;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.dialog-icon image {
|
|
176
|
+
width: 120rpx;
|
|
177
|
+
height: 120rpx;
|
|
178
|
+
object-fit: contain;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.default-icon {
|
|
182
|
+
width: 120rpx;
|
|
183
|
+
height: 120rpx;
|
|
184
|
+
background-color: #f0f0f0;
|
|
185
|
+
border-radius: 50%;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* 标题 */
|
|
189
|
+
.dialog-title {
|
|
190
|
+
font-size: 36rpx;
|
|
191
|
+
font-weight: bold;
|
|
192
|
+
color: #333;
|
|
193
|
+
margin: 0 0 20rpx 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* 说明文字 */
|
|
197
|
+
.dialog-description {
|
|
198
|
+
font-size: 28rpx;
|
|
199
|
+
color: #666;
|
|
200
|
+
margin: 0;
|
|
201
|
+
line-height: 1.5;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* 分割线 */
|
|
205
|
+
.dialog-divider {
|
|
206
|
+
height: 2rpx;
|
|
207
|
+
background-color: #e5e5e5;
|
|
208
|
+
margin: 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* 按钮区域 */
|
|
212
|
+
.dialog-buttons {
|
|
213
|
+
display: flex;
|
|
214
|
+
flex-direction: row;
|
|
215
|
+
align-items: center;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* 按钮 */
|
|
219
|
+
.dialog-button {
|
|
220
|
+
flex: 1;
|
|
221
|
+
padding: 30rpx;
|
|
222
|
+
border: none;
|
|
223
|
+
background: none;
|
|
224
|
+
font-size: 32rpx;
|
|
225
|
+
font-weight: 500;
|
|
226
|
+
cursor: pointer;
|
|
227
|
+
transition: background-color 0.3s ease;
|
|
228
|
+
text-align: center;
|
|
229
|
+
box-sizing: border-box;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* 全宽按钮 */
|
|
233
|
+
.dialog-button-full {
|
|
234
|
+
width: 100%;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* 取消按钮 */
|
|
238
|
+
.dialog-button-cancel {
|
|
239
|
+
color: #666;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.dialog-button-cancel:hover {
|
|
243
|
+
background-color: #f5f5f5;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* 主要按钮 */
|
|
247
|
+
.dialog-button-primary {
|
|
248
|
+
color: #1989fa;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.dialog-button-primary:hover {
|
|
252
|
+
background-color: #f0f9ff;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* 动画效果 */
|
|
256
|
+
@keyframes fadeIn {
|
|
257
|
+
from {
|
|
258
|
+
opacity: 0;
|
|
259
|
+
}
|
|
260
|
+
to {
|
|
261
|
+
opacity: 1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@keyframes scaleIn {
|
|
266
|
+
from {
|
|
267
|
+
transform: scale(0.8);
|
|
268
|
+
opacity: 0;
|
|
269
|
+
}
|
|
270
|
+
to {
|
|
271
|
+
transform: scale(1);
|
|
272
|
+
opacity: 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
</style>
|
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="sw-divider"
|
|
4
|
+
:class="[
|
|
5
|
+
{ 'is-vertical': vertical },
|
|
6
|
+
{ 'is-dashed': dashed },
|
|
7
|
+
{ [`sw-divider--${position}`]: position !== 'center' },
|
|
8
|
+
]"
|
|
9
|
+
>
|
|
10
|
+
<text v-if="$slots.default" class="sw-divider__text">
|
|
11
|
+
<slot />
|
|
12
|
+
</text>
|
|
13
|
+
</view>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
interface DividerProps {
|
|
18
|
+
// 是否为垂直分隔线
|
|
19
|
+
vertical?: boolean
|
|
20
|
+
// 是否为虚线
|
|
21
|
+
dashed?: boolean
|
|
22
|
+
// 文本位置,可选值为 left、center、right
|
|
23
|
+
position?: 'left' | 'center' | 'right'
|
|
24
|
+
// 自定义类名
|
|
25
|
+
customClass?: string
|
|
26
|
+
// 自定义样式
|
|
27
|
+
customStyle?: Record<string, any>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<DividerProps>(), {
|
|
31
|
+
vertical: false,
|
|
32
|
+
dashed: false,
|
|
33
|
+
position: 'center',
|
|
34
|
+
customClass: '',
|
|
35
|
+
customStyle: () => ({}),
|
|
36
|
+
})
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<style scoped lang="less">
|
|
40
|
+
// 主题色变量
|
|
41
|
+
@primary-color: #409eff;
|
|
42
|
+
@gray-3: #c0c4cc;
|
|
43
|
+
|
|
44
|
+
// 基础样式
|
|
45
|
+
.sw-divider {
|
|
46
|
+
background-color: @gray-3;
|
|
47
|
+
position: relative;
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
|
|
50
|
+
&.is-vertical {
|
|
51
|
+
display: inline-block;
|
|
52
|
+
width: 2rpx;
|
|
53
|
+
height: 32rpx;
|
|
54
|
+
margin: 0 24rpx;
|
|
55
|
+
vertical-align: middle;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&:not(.is-vertical) {
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 2rpx;
|
|
61
|
+
margin: 32rpx 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&.is-dashed {
|
|
65
|
+
background: none;
|
|
66
|
+
border-top: 2rpx dashed @gray-3;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&__text {
|
|
70
|
+
position: absolute;
|
|
71
|
+
background-color: #fff;
|
|
72
|
+
padding: 0 32rpx;
|
|
73
|
+
font-size: 28rpx;
|
|
74
|
+
color: #606266;
|
|
75
|
+
transform: translateY(-50%);
|
|
76
|
+
|
|
77
|
+
&.is-vertical {
|
|
78
|
+
top: 50%;
|
|
79
|
+
left: 50%;
|
|
80
|
+
transform: translate(-50%, -50%);
|
|
81
|
+
padding: 32rpx 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&--left {
|
|
86
|
+
.sw-divider__text {
|
|
87
|
+
left: 0;
|
|
88
|
+
padding-left: 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&--center {
|
|
93
|
+
.sw-divider__text {
|
|
94
|
+
left: 50%;
|
|
95
|
+
transform: translateX(-50%) translateY(-50%);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&--right {
|
|
100
|
+
.sw-divider__text {
|
|
101
|
+
right: 0;
|
|
102
|
+
padding-right: 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# DropdownSelect 使用指南
|
|
2
|
+
|
|
3
|
+
**组件概述**
|
|
4
|
+
- 基于 `Vant` 的 `DropdownMenu/DropdownItem` 封装的下拉选择组件
|
|
5
|
+
- 支持双向绑定、图标自定义、插槽扩展
|
|
6
|
+
- 组件入口:`src/components/DropdownSelect/index.vue`
|
|
7
|
+
|
|
8
|
+
**快速使用**
|
|
9
|
+
- 引入组件:`import DropdownSelect from "@/components/DropdownSelect"`
|
|
10
|
+
- 基本用法
|
|
11
|
+
```vue
|
|
12
|
+
<template>
|
|
13
|
+
<DropdownSelect v-model="selectedType" :options="[
|
|
14
|
+
{ text: '全部证件', value: '' },
|
|
15
|
+
{ text: '身份证', value: 'ID_CARD' },
|
|
16
|
+
{ text: '护照', value: 'PASSPORT' }
|
|
17
|
+
]" />
|
|
18
|
+
</template>
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import DropdownSelect from "@/components/DropdownSelect";
|
|
21
|
+
import { ref } from "vue";
|
|
22
|
+
const selectedType = ref('');
|
|
23
|
+
</script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**自定义图标(属性)**
|
|
27
|
+
- 通过 `iconName` 指定 `Vant Icon` 名称
|
|
28
|
+
```vue
|
|
29
|
+
<DropdownSelect v-model="selectedType" icon-name="add-o" :options="[
|
|
30
|
+
{ text: '全部应用', value: '' },
|
|
31
|
+
{ text: '金融服务', value: 'ID_CARD' },
|
|
32
|
+
{ text: '企业管理', value: 'PASSPORT' }
|
|
33
|
+
]"/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**自定义显示(默认插槽)**
|
|
37
|
+
- 覆盖标题区域默认文本的展示
|
|
38
|
+
```vue
|
|
39
|
+
<DropdownSelect v-model="selectedType" :options="[
|
|
40
|
+
{ text: '全部应用', value: '' },
|
|
41
|
+
{ text: '金融服务', value: 'ID_CARD' },
|
|
42
|
+
{ text: '企业管理', value: 'PASSPORT' }
|
|
43
|
+
]">
|
|
44
|
+
<template #default="{ text }">
|
|
45
|
+
{{ text }}...
|
|
46
|
+
</template>
|
|
47
|
+
</DropdownSelect>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**自定义图标(插槽)**
|
|
51
|
+
- 使用插槽完全替换默认图标
|
|
52
|
+
```vue
|
|
53
|
+
<script setup lang="ts">
|
|
54
|
+
import { Icon } from "vant";
|
|
55
|
+
</script>
|
|
56
|
+
<DropdownSelect v-model="selectedType" :options="[
|
|
57
|
+
{ text: '全部应用', value: '' },
|
|
58
|
+
{ text: '金融服务', value: 'ID_CARD' },
|
|
59
|
+
{ text: '企业管理', value: 'PASSPORT' }
|
|
60
|
+
]">
|
|
61
|
+
<template #icon>
|
|
62
|
+
<icon name="close" />
|
|
63
|
+
</template>
|
|
64
|
+
</DropdownSelect>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Props**
|
|
68
|
+
- `modelValue`:`string | number`,当前选中值(双向绑定)
|
|
69
|
+
- `iconName`:`string`,标题右侧图标名称,默认 `'arrow-down'`
|
|
70
|
+
- `options`:`DropdownItemOption[]`(来自 `vant`),每项结构 `{ text: string; value: string | number }`
|
|
71
|
+
- 定义位置:`src/components/DropdownSelect/type.ts:3-7`
|
|
72
|
+
|
|
73
|
+
**Emits**
|
|
74
|
+
- `update:modelValue`:选中项变更时触发,携带新值
|
|
75
|
+
- `change`:透传 `vant` 的 `@change` 事件
|
|
76
|
+
- 定义位置:`src/components/DropdownSelect/type.ts:8-11`
|
|
77
|
+
|
|
78
|
+
**插槽**
|
|
79
|
+
- `default`:用于自定义标题区域文本展示,插槽入参 `{ text }`
|
|
80
|
+
- `icon`:用于自定义标题区域图标
|
|
81
|
+
- 实现位置:`src/components/DropdownSelect/index.vue:5-16`
|
|
82
|
+
|
|
83
|
+
**行为说明**
|
|
84
|
+
- 标题中的文案选择逻辑:`dropdownSelect.ts:10-23`
|
|
85
|
+
- 有有效 `modelValue` 时,匹配 `options` 中对应项的 `text`
|
|
86
|
+
- 无有效 `modelValue` 时,显示 `options[0].text`,若无则显示“请选择”
|
|
87
|
+
- 选中高亮绑定值:`dropdownSelect.ts:25-36`
|
|
88
|
+
- `internalValue` 用于高亮和选择,未提供 `modelValue` 时回落为第一个选项的 `value`
|
|
89
|
+
- 图标旋转状态:`isOpen` 受 `open/close` 事件控制:`dropdownSelect.ts:42-47`
|
|
90
|
+
- `change` 事件透传:`dropdownSelect.ts:38-40`
|
|
91
|
+
|
|
92
|
+
**样式**
|
|
93
|
+
- 样式文件:`src/components/DropdownSelect/dropdownSelect.less`
|
|
94
|
+
- 根类名:`.dropdown-select`,标题包裹类 `.dropdown-title`,图标包裹类 `.dropdown-icon`(激活态加 `.is-active`)
|
|
95
|
+
- 引入方式:`index.vue:35-36`
|
|
96
|
+
|
|
97
|
+
**依赖**
|
|
98
|
+
- `Vant`:`DropdownMenu`、`DropdownItem`、`Icon`
|
|
99
|
+
- 使用位置:`src/components/DropdownSelect/index.vue:21-23`
|
|
100
|
+
|
|
101
|
+
**Demo 参考**
|
|
102
|
+
- 组件演示页面:`src/views/demo/dropdownSelect/index.vue:2-58`
|
|
103
|
+
- 展示内容:
|
|
104
|
+
- 基础用法
|
|
105
|
+
- 通过 `iconName` 自定义图标
|
|
106
|
+
- 使用 `default` 插槽自定义显示文本
|
|
107
|
+
- 使用 `icon` 插槽自定义图标
|
|
108
|
+
|
|
109
|
+
**最佳实践**
|
|
110
|
+
- 始终为 `options` 提供稳定的 `value` 值,避免字符串与数字混用导致匹配失败
|
|
111
|
+
- 若希望初始高亮特定项,请显式传入 `v-model` 的初始值
|
|
112
|
+
- 使用插槽时,确保已引入并注册所需的图标组件(如 `Vant Icon`)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
.dropdown-select {
|
|
2
|
+
background-color: rgba(0, 0, 0, 0);
|
|
3
|
+
display: inline-block;
|
|
4
|
+
|
|
5
|
+
// 触发器样式
|
|
6
|
+
.dropdown-trigger {
|
|
7
|
+
cursor: pointer;
|
|
8
|
+
user-select: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 标题样式
|
|
12
|
+
.dropdown-title {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
font-size: 28rpx;
|
|
16
|
+
color: #333;
|
|
17
|
+
line-height: 1.4;
|
|
18
|
+
padding: 8rpx;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 文本样式
|
|
22
|
+
.dropdown-text {
|
|
23
|
+
flex: 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 图标容器样式
|
|
27
|
+
.dropdown-icon {
|
|
28
|
+
padding-left: 16rpx;
|
|
29
|
+
font-size: 28rpx;
|
|
30
|
+
color: #999;
|
|
31
|
+
transition: transform 0.3s;
|
|
32
|
+
|
|
33
|
+
// 展开状态
|
|
34
|
+
&.is-active {
|
|
35
|
+
transform: rotate(180deg);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 自定义箭头图标
|
|
40
|
+
.uni-icon {
|
|
41
|
+
width: 28rpx;
|
|
42
|
+
height: 28rpx;
|
|
43
|
+
position: relative;
|
|
44
|
+
|
|
45
|
+
&.arrow-down {
|
|
46
|
+
&:after {
|
|
47
|
+
content: '';
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 50%;
|
|
50
|
+
left: 50%;
|
|
51
|
+
transform: translate(-50%, -50%);
|
|
52
|
+
width: 0;
|
|
53
|
+
height: 0;
|
|
54
|
+
border-left: 8rpx solid transparent;
|
|
55
|
+
border-right: 8rpx solid transparent;
|
|
56
|
+
border-top: 8rpx solid #999;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&.arrow-up {
|
|
61
|
+
&:after {
|
|
62
|
+
content: '';
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 50%;
|
|
65
|
+
left: 50%;
|
|
66
|
+
transform: translate(-50%, -50%);
|
|
67
|
+
width: 0;
|
|
68
|
+
height: 0;
|
|
69
|
+
border-left: 8rpx solid transparent;
|
|
70
|
+
border-right: 8rpx solid transparent;
|
|
71
|
+
border-bottom: 8rpx solid #999;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DropdownSelectEmits, DropdownSelectProps } from './type'
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function useDropdownSelect<T>(
|
|
5
|
+
props: DropdownSelectProps<T>,
|
|
6
|
+
emit: DropdownSelectEmits<T>,
|
|
7
|
+
) {
|
|
8
|
+
// 控制箭头旋转状态
|
|
9
|
+
const isOpen = ref(false)
|
|
10
|
+
// 查找当前应显示的文本
|
|
11
|
+
const displayText = computed(() => {
|
|
12
|
+
if (!props.options?.length) {
|
|
13
|
+
return '请选择'
|
|
14
|
+
}
|
|
15
|
+
// 优先根据 modelValue 匹配
|
|
16
|
+
if (props.modelValue !== undefined && props.modelValue !== null) {
|
|
17
|
+
const matched = props.options.find(
|
|
18
|
+
opt => opt.value === props.modelValue,
|
|
19
|
+
)
|
|
20
|
+
if (matched)
|
|
21
|
+
return matched.text
|
|
22
|
+
}
|
|
23
|
+
// 无有效 modelValue 时,默认显示第一项文本(仅 UI 展示)
|
|
24
|
+
return props.options[0]?.text || '请选择'
|
|
25
|
+
})
|
|
26
|
+
// 内部绑定值:用于控制 Vant 选中高亮
|
|
27
|
+
const internalValue = computed({
|
|
28
|
+
get() {
|
|
29
|
+
if (props.modelValue !== undefined && props.modelValue !== null) {
|
|
30
|
+
return props.modelValue
|
|
31
|
+
}
|
|
32
|
+
// 无外部值时,返回第一个选项的 value(仅用于 UI 高亮,不 emit)
|
|
33
|
+
return props.options?.[0]?.value ?? ''
|
|
34
|
+
},
|
|
35
|
+
set(val) {
|
|
36
|
+
emit('update:modelValue', val as T)
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
// 事件处理
|
|
40
|
+
const handleChange = (value: T) => {
|
|
41
|
+
emit('change', value)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const handleOpen = () => {
|
|
45
|
+
isOpen.value = true
|
|
46
|
+
}
|
|
47
|
+
const handleClose = () => {
|
|
48
|
+
isOpen.value = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
displayText,
|
|
53
|
+
internalValue,
|
|
54
|
+
isOpen,
|
|
55
|
+
handleChange,
|
|
56
|
+
handleOpen,
|
|
57
|
+
handleClose,
|
|
58
|
+
}
|
|
59
|
+
}
|