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,88 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="dropdown-select">
|
|
3
|
+
<!-- 下拉选择触发器 -->
|
|
4
|
+
<view class="dropdown-trigger" @tap="togglePicker">
|
|
5
|
+
<slot v-bind="{ text: displayText }">
|
|
6
|
+
<view class="dropdown-title">
|
|
7
|
+
<text class="dropdown-text">{{ displayText }}</text>
|
|
8
|
+
<text class="dropdown-icon" :class="{ 'is-active': isOpen }">
|
|
9
|
+
<slot name="icon">
|
|
10
|
+
<!-- 使用 UniApp 内置图标或自定义图标 -->
|
|
11
|
+
<view class="uni-icon" :class="iconName" />
|
|
12
|
+
</slot>
|
|
13
|
+
</text>
|
|
14
|
+
</view>
|
|
15
|
+
</slot>
|
|
16
|
+
</view>
|
|
17
|
+
|
|
18
|
+
<!-- 选择器弹窗 -->
|
|
19
|
+
<picker
|
|
20
|
+
v-if="isOpen"
|
|
21
|
+
mode="selector"
|
|
22
|
+
:range="options"
|
|
23
|
+
range-key="text"
|
|
24
|
+
:value="selectedIndex"
|
|
25
|
+
class="dropdown-picker"
|
|
26
|
+
@change="onPickerChange"
|
|
27
|
+
@cancel="onPickerCancel"
|
|
28
|
+
>
|
|
29
|
+
<!-- UniApp picker 组件会自动渲染选择器UI -->
|
|
30
|
+
</picker>
|
|
31
|
+
</view>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts" generic="T">
|
|
35
|
+
import type { DropdownSelectEmits, DropdownSelectProps } from './type'
|
|
36
|
+
import { computed, ref } from 'vue'
|
|
37
|
+
import { useDropdownSelect } from './dropdownSelect'
|
|
38
|
+
|
|
39
|
+
const props = withDefaults(
|
|
40
|
+
defineProps<DropdownSelectProps<T>>(),
|
|
41
|
+
{
|
|
42
|
+
iconName: 'arrow-down',
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
const emit = defineEmits<DropdownSelectEmits<T>>()
|
|
46
|
+
|
|
47
|
+
const { displayText, internalValue, handleChange, handleOpen, handleClose } = useDropdownSelect<T>(props, emit)
|
|
48
|
+
|
|
49
|
+
// 控制选择器显示状态
|
|
50
|
+
const isOpen = ref(false)
|
|
51
|
+
|
|
52
|
+
// 计算当前选中项的索引
|
|
53
|
+
const selectedIndex = computed(() => {
|
|
54
|
+
if (!props.options || !props.options.length)
|
|
55
|
+
return 0
|
|
56
|
+
return props.options.findIndex(opt => opt.value === internalValue.value)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// 切换选择器显示/隐藏
|
|
60
|
+
function togglePicker() {
|
|
61
|
+
isOpen.value = !isOpen.value
|
|
62
|
+
if (isOpen.value) {
|
|
63
|
+
handleOpen()
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
handleClose()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 选择器确认选择
|
|
71
|
+
function onPickerChange(e: any) {
|
|
72
|
+
const index = e.detail.value
|
|
73
|
+
const selectedOption = props.options[index]
|
|
74
|
+
handleChange(selectedOption.value as T)
|
|
75
|
+
isOpen.value = false
|
|
76
|
+
handleClose()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 选择器取消选择
|
|
80
|
+
function onPickerCancel() {
|
|
81
|
+
isOpen.value = false
|
|
82
|
+
handleClose()
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<style lang="less">
|
|
87
|
+
@import './dropdownSelect.less';
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// 本地定义下拉选项类型
|
|
2
|
+
export interface DropdownItemOption {
|
|
3
|
+
text: string
|
|
4
|
+
value: any
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface DropdownSelectProps<T> {
|
|
8
|
+
modelValue?: T
|
|
9
|
+
iconName?: string
|
|
10
|
+
options: DropdownItemOption[]
|
|
11
|
+
}
|
|
12
|
+
export interface DropdownSelectEmits<T> {
|
|
13
|
+
(e: 'update:modelValue', value: T): void
|
|
14
|
+
(e: 'change', value: T): void
|
|
15
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# DropdownWithBadge 使用指南
|
|
2
|
+
|
|
3
|
+
**组件概述**
|
|
4
|
+
- 在 `DropdownSelect` 基础上增加“状态红点”提示
|
|
5
|
+
- 通过 `hideBadge` 控制是否隐藏红点,默认当 `modelValue` 为空(`'' | null | undefined`)时显示红点
|
|
6
|
+
- 入口:`src/components/DropdownWithBadge/index.vue`
|
|
7
|
+
|
|
8
|
+
**快速使用**
|
|
9
|
+
```vue
|
|
10
|
+
<template>
|
|
11
|
+
<DropdownWithBadge
|
|
12
|
+
v-model="selected"
|
|
13
|
+
:options="[
|
|
14
|
+
{ text: '全部', value: '' },
|
|
15
|
+
{ text: '待处理', value: 'todo' },
|
|
16
|
+
{ text: '已处理', value: 'done' }
|
|
17
|
+
]"
|
|
18
|
+
/>
|
|
19
|
+
</template>
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import DropdownWithBadge from "@/components/DropdownWithBadge";
|
|
22
|
+
import { ref } from "vue";
|
|
23
|
+
const selected = ref(''); // 空值时显示红点
|
|
24
|
+
</script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**自定义红点显示规则**
|
|
28
|
+
- 通过 `hideBadge` 决定是否隐藏红点;返回 `true` 表示隐藏红点,返回 `false` 表示显示红点
|
|
29
|
+
```vue
|
|
30
|
+
<DropdownWithBadge
|
|
31
|
+
v-model="selected"
|
|
32
|
+
:options="options"
|
|
33
|
+
:hide-badge="(val) => val === 'done'" <!-- 仅在选中 'done' 时隐藏红点 -->
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Props**
|
|
38
|
+
- 继承自 `DropdownSelectProps`:`modelValue`、`iconName`、`options`
|
|
39
|
+
- 额外:
|
|
40
|
+
- `hideBadge?: (value: string | number | undefined) => boolean`
|
|
41
|
+
- 默认:隐藏条件为 `value === '' || value === null || value === undefined`
|
|
42
|
+
- 定义位置:`src/components/DropdownWithBadge/type.ts:6-8`
|
|
43
|
+
- 默认图标:`iconName: 'filter-o'`(可通过属性覆盖)
|
|
44
|
+
- 设置位置:`src/components/DropdownWithBadge/index.vue:25-29`
|
|
45
|
+
|
|
46
|
+
**Emits**
|
|
47
|
+
- 与 `DropdownSelect` 保持一致:`update:modelValue`、`change`
|
|
48
|
+
- 定义位置:`src/components/DropdownWithBadge/type.ts:9-12`
|
|
49
|
+
|
|
50
|
+
**插槽**
|
|
51
|
+
- `default`:自定义标题区域文案,传入 `{ text }`
|
|
52
|
+
- `icon`:自定义标题区域图标
|
|
53
|
+
- 插槽结构位置:`src/components/DropdownWithBadge/index.vue:4-16`
|
|
54
|
+
|
|
55
|
+
**行为说明**
|
|
56
|
+
- 红点显示规则:
|
|
57
|
+
- 逻辑计算:`src/components/DropdownWithBadge/dropdownWithBadge.ts:5-7`
|
|
58
|
+
- 视图控制:`src/components/DropdownWithBadge/index.vue:14-15`
|
|
59
|
+
- 当 `hideBadge(value)` 返回 `true` 时,红点隐藏;返回 `false` 时,红点显示
|
|
60
|
+
- 与 `DropdownSelect` 的组合:
|
|
61
|
+
- 继承其选项展示、图标、双向绑定与事件透传能力
|
|
62
|
+
- 组合位置:`src/components/DropdownWithBadge/index.vue:2-18`
|
|
63
|
+
|
|
64
|
+
**样式**
|
|
65
|
+
- 文件:`src/components/DropdownWithBadge/dropdownWithBadge.less`
|
|
66
|
+
- 类名:`.dropdown-with-badge__dot`(绝对定位的 8px 红色圆点)
|
|
67
|
+
- 定义位置:`src/components/DropdownWithBadge/dropdownWithBadge.less:1-11`
|
|
68
|
+
|
|
69
|
+
**依赖**
|
|
70
|
+
- 组件基础依赖:`DropdownSelect`(`src/components/DropdownSelect`)
|
|
71
|
+
- 第三方依赖:`Vant` 的 `Icon`(图标)
|
|
72
|
+
|
|
73
|
+
**最佳实践**
|
|
74
|
+
- 使用明确的 `modelValue` 来表示当前筛选状态,避免空值语义不清
|
|
75
|
+
- 自定义 `hideBadge` 时保持返回布尔含义清晰:返回 `true` 隐藏红点,返回 `false` 显示红点
|
|
76
|
+
- 当需要更换图标时,优先通过 `iconName` 设置,复杂图标则使用 `icon` 插槽
|
|
77
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DropdownWithBadgeProps } from './type'
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function useDropdownWithBadge<T>(props: DropdownWithBadgeProps<T>) {
|
|
5
|
+
const hasBadge = computed(() => {
|
|
6
|
+
return props.hideBadge!(props.modelValue)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
return { hasBadge }
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<dropdown-select
|
|
3
|
+
v-bind="{ modelValue, options }" class="dropdown-with-badge"
|
|
4
|
+
v-on="emit"
|
|
5
|
+
>
|
|
6
|
+
<template #default="{ text }">
|
|
7
|
+
<slot v-bind="{ text }">
|
|
8
|
+
<view class="dropdown-title">
|
|
9
|
+
<text class="dropdown-text">{{ text }}</text>
|
|
10
|
+
<text class="dropdown-icon">
|
|
11
|
+
<slot name="icon">
|
|
12
|
+
<!-- 使用自定义图标类名 -->
|
|
13
|
+
<view class="uni-icon" :class="iconName" />
|
|
14
|
+
</slot>
|
|
15
|
+
</text>
|
|
16
|
+
</view>
|
|
17
|
+
<text v-show="!hasBadge" class="dropdown-with-badge__dot" />
|
|
18
|
+
</slot>
|
|
19
|
+
</template>
|
|
20
|
+
</dropdown-select>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script lang="ts" setup generic="T">
|
|
24
|
+
import type { DropdownWithBadgeEmits, DropdownWithBadgeProps } from './type'
|
|
25
|
+
import DropdownSelect from '../DropdownSelect'
|
|
26
|
+
import { useDropdownWithBadge } from './dropdownWithBadge'
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(defineProps<DropdownWithBadgeProps<T>>(), {
|
|
29
|
+
hideBadge: (modelValue?: T) => (modelValue === '' || modelValue === null || modelValue === undefined),
|
|
30
|
+
iconName: 'filter-o',
|
|
31
|
+
})
|
|
32
|
+
const emit = defineEmits<DropdownWithBadgeEmits<T>>()
|
|
33
|
+
|
|
34
|
+
const { hasBadge } = useDropdownWithBadge(props)
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<style lang="less">
|
|
38
|
+
@import './dropdownWithBadge.less';
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DropdownSelectEmits,
|
|
3
|
+
DropdownSelectProps,
|
|
4
|
+
} from '../DropdownSelect'
|
|
5
|
+
|
|
6
|
+
export interface DropdownWithBadgeProps<T> extends /* @vue-ignore */ DropdownSelectProps<T> {
|
|
7
|
+
hideBadge?: (value?: T) => boolean
|
|
8
|
+
}
|
|
9
|
+
export interface DropdownWithBadgeEmits<T> extends /* @vue-ignore */ DropdownSelectEmits<T> {
|
|
10
|
+
(e: 'update:modelValue', value: T): void
|
|
11
|
+
(e: 'change', value: T): void
|
|
12
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
.filter-drawer {
|
|
2
|
+
height: 100%;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
|
|
7
|
+
&__trigger {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 12rpx;
|
|
11
|
+
font-size: 28rpx;
|
|
12
|
+
color: #1989fa;
|
|
13
|
+
position: relative;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&__badge-dot {
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: -4rpx;
|
|
20
|
+
right: -12rpx;
|
|
21
|
+
width: 12rpx;
|
|
22
|
+
height: 12rpx;
|
|
23
|
+
background-color: #f56c6c;
|
|
24
|
+
border-radius: 50%;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&__header {
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
padding: 24rpx 32rpx;
|
|
30
|
+
background: #fff;
|
|
31
|
+
z-index: 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__title {
|
|
35
|
+
text-align: center;
|
|
36
|
+
font-size: 32rpx;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&__body {
|
|
42
|
+
flex: 1;
|
|
43
|
+
overflow-y: auto;
|
|
44
|
+
padding: 32rpx;
|
|
45
|
+
-webkit-overflow-scrolling: touch;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&__footer {
|
|
49
|
+
flex-shrink: 0;
|
|
50
|
+
display: flex;
|
|
51
|
+
background: #fff;
|
|
52
|
+
padding: 0 32rpx 24rpx;
|
|
53
|
+
z-index: 1;
|
|
54
|
+
gap: 24rpx;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&__button {
|
|
58
|
+
flex: 1;
|
|
59
|
+
height: 88rpx;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
font-size: 28rpx;
|
|
64
|
+
border-radius: 8rpx;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition: background-color 0.3s;
|
|
67
|
+
background-color: #1989fa;
|
|
68
|
+
color: #fff;
|
|
69
|
+
border: 2rpx solid #1989fa;
|
|
70
|
+
|
|
71
|
+
&--plain {
|
|
72
|
+
background-color: #fff;
|
|
73
|
+
color: #1989fa;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&__popup-mask {
|
|
78
|
+
position: fixed;
|
|
79
|
+
top: 0;
|
|
80
|
+
left: 0;
|
|
81
|
+
right: 0;
|
|
82
|
+
bottom: 0;
|
|
83
|
+
background: rgba(0, 0, 0, 0.5);
|
|
84
|
+
z-index: 999;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&__popup {
|
|
88
|
+
position: fixed;
|
|
89
|
+
top: 0;
|
|
90
|
+
right: 0;
|
|
91
|
+
height: 100vh;
|
|
92
|
+
width: 100vw;
|
|
93
|
+
background: #fff;
|
|
94
|
+
z-index: 1000;
|
|
95
|
+
transform: translateX(100%);
|
|
96
|
+
animation: slideInRight 0.3s forwards;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&__close {
|
|
100
|
+
position: absolute;
|
|
101
|
+
top: 24rpx;
|
|
102
|
+
right: 32rpx;
|
|
103
|
+
font-size: 48rpx;
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
color: #999;
|
|
106
|
+
z-index: 10;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@keyframes slideInRight {
|
|
111
|
+
from {
|
|
112
|
+
transform: translateX(100%);
|
|
113
|
+
}
|
|
114
|
+
to {
|
|
115
|
+
transform: translateX(0);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue'
|
|
2
|
+
import { computed, isRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 判断是否应显示徽章(如筛选条件已应用)
|
|
6
|
+
* @param model 表单数据或者自定义方法
|
|
7
|
+
* @returns 响应式的布尔值 Ref<boolean>
|
|
8
|
+
*/
|
|
9
|
+
export function useHasBadge<T extends object>(
|
|
10
|
+
model?: Ref<T> | T | (() => boolean),
|
|
11
|
+
): ComputedRef<boolean> {
|
|
12
|
+
if (!model) {
|
|
13
|
+
return computed(() => false)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof model === 'function') {
|
|
17
|
+
return computed(() => model())
|
|
18
|
+
}
|
|
19
|
+
if (isRef(model)) {
|
|
20
|
+
return computed(() => {
|
|
21
|
+
if (!model.value)
|
|
22
|
+
return false
|
|
23
|
+
const obj = model.value as Record<keyof T, any>
|
|
24
|
+
for (const key in obj) {
|
|
25
|
+
if (obj[key]) {
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
return computed(() => {
|
|
33
|
+
for (const key in model) {
|
|
34
|
+
const val = model[key as keyof typeof model]
|
|
35
|
+
if (val) {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false
|
|
40
|
+
})
|
|
41
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="filter-drawer__trigger" @click="openDrawer">
|
|
3
|
+
<slot name="trigger">
|
|
4
|
+
<!-- 使用自定义图标 -->
|
|
5
|
+
<view class="uni-icon icon-filter-o" />
|
|
6
|
+
</slot>
|
|
7
|
+
<text v-if="hasBadge" class="filter-drawer__badge-dot" />
|
|
8
|
+
</view>
|
|
9
|
+
|
|
10
|
+
<!-- 使用 UniApp 内置的 popup 组件 -->
|
|
11
|
+
<view v-if="visible" class="filter-drawer__popup-mask" @click="closeDrawer" />
|
|
12
|
+
<view v-if="visible" class="filter-drawer__popup" :style="{ height: '100vh', width: '100vw' }">
|
|
13
|
+
<view class="filter-drawer__close" @click="closeDrawer">
|
|
14
|
+
×
|
|
15
|
+
</view>
|
|
16
|
+
<view class="filter-drawer">
|
|
17
|
+
<view class="filter-drawer__header">
|
|
18
|
+
<slot name="header">
|
|
19
|
+
<text class="filter-drawer__title">筛选</text>
|
|
20
|
+
</slot>
|
|
21
|
+
</view>
|
|
22
|
+
<view class="filter-drawer__body">
|
|
23
|
+
<slot />
|
|
24
|
+
</view>
|
|
25
|
+
<view class="filter-drawer__footer">
|
|
26
|
+
<slot name="footer">
|
|
27
|
+
<view class="filter-drawer__button filter-drawer__button--plain" @click="resetDrawer">
|
|
28
|
+
重置条件
|
|
29
|
+
</view>
|
|
30
|
+
<view class="filter-drawer__button" @click="queryDrawer">
|
|
31
|
+
查询
|
|
32
|
+
</view>
|
|
33
|
+
</slot>
|
|
34
|
+
</view>
|
|
35
|
+
</view>
|
|
36
|
+
</view>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts" generic="T extends object">
|
|
40
|
+
import type { FilterDrawerEmits, FilterDrawerProps } from './type'
|
|
41
|
+
import { useFilterDrawer } from './useFilterDrawer'
|
|
42
|
+
|
|
43
|
+
const props = withDefaults(defineProps<FilterDrawerProps<T>>(), {
|
|
44
|
+
hasBadge: false,
|
|
45
|
+
})
|
|
46
|
+
const emit = defineEmits<FilterDrawerEmits>()
|
|
47
|
+
|
|
48
|
+
const { visible, openDrawer, closeDrawer, resetDrawer, queryDrawer, hasBadge } = useFilterDrawer(props, emit)
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<style lang="less">
|
|
52
|
+
@import './filterDrawer.less';
|
|
53
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FilterDrawerEmits, FilterDrawerProps } from './type'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { useHasBadge } from './hasBadge'
|
|
4
|
+
|
|
5
|
+
export function useFilterDrawer<T extends object>(props: FilterDrawerProps<T>, emit: FilterDrawerEmits) {
|
|
6
|
+
// 使用ref替代useToggle
|
|
7
|
+
const visible = ref(false)
|
|
8
|
+
|
|
9
|
+
// 徽章状态
|
|
10
|
+
const hasBadge = useHasBadge(props.modelValue)
|
|
11
|
+
|
|
12
|
+
const openDrawer = () => {
|
|
13
|
+
visible.value = true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const closeDrawer = () => {
|
|
17
|
+
visible.value = false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const resetDrawer = () => {
|
|
21
|
+
emit('reset')
|
|
22
|
+
closeDrawer()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const queryDrawer = () => {
|
|
26
|
+
emit('query')
|
|
27
|
+
closeDrawer()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
visible,
|
|
32
|
+
openDrawer,
|
|
33
|
+
closeDrawer,
|
|
34
|
+
resetDrawer,
|
|
35
|
+
queryDrawer,
|
|
36
|
+
hasBadge,
|
|
37
|
+
}
|
|
38
|
+
}
|