papayaui 0.2.4 → 0.2.6
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/cascader/cascader.vue +1 -1
- package/components/cascader/props.ts +8 -0
- package/components/cell/cell.vue +2 -2
- package/components/dialog/dialog.ts +14 -0
- package/components/index.ts +1 -0
- package/components/picker-popup/picker-popup.vue +5 -3
- package/components/picker-popup/props.ts +12 -0
- package/components/popup/popup.scss +1 -1
- package/components/sidebar-item/sidebar-item.vue +2 -1
- package/components/slider/demo.vue +49 -0
- package/components/slider/index.ts +4 -0
- package/components/slider/props.ts +88 -0
- package/components/slider/slider.scss +126 -0
- package/components/slider/slider.vue +254 -0
- package/components/textarea/textarea.scss +1 -1
- package/components/uploader/uploader.scss +2 -2
- package/demos/cell/demo-1.vue +1 -0
- package/demos/cell/demo-2.vue +1 -1
- package/demos/cell/demo-7.vue +1 -0
- package/demos/slider/demo-1.vue +11 -0
- package/demos/slider/demo-10.vue +22 -0
- package/demos/slider/demo-2.vue +11 -0
- package/demos/slider/demo-3.vue +11 -0
- package/demos/slider/demo-4.vue +11 -0
- package/demos/slider/demo-5.vue +11 -0
- package/demos/slider/demo-6.vue +11 -0
- package/demos/slider/demo-7.vue +11 -0
- package/demos/slider/demo-8.vue +11 -0
- package/demos/slider/demo-9.vue +25 -0
- package/global.d.ts +1 -0
- package/package.json +1 -1
- package/utils/cos.ts +10 -2
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
@closed="emit('closed')"
|
|
19
19
|
>
|
|
20
20
|
<view :class="ns.b('content')">
|
|
21
|
-
<Search v-if="showSearch" v-model="searchText" :class="ns.b('search')" />
|
|
21
|
+
<Search v-if="showSearch" v-model="searchText" v-bind="searchProps" :class="ns.b('search')" />
|
|
22
22
|
<view v-if="show" :class="ns.b('tab')">
|
|
23
23
|
<Tabs v-model="tabActive" :tabs="tabList" label-key="name" scrollable shrink />
|
|
24
24
|
</view>
|
|
@@ -3,6 +3,7 @@ import type { TreeNode, UseTreeFieldNames } from '../../core/useTree'
|
|
|
3
3
|
import { defaultFieldNames } from '../../core/useTree'
|
|
4
4
|
import { isArray, isObject } from '../../utils'
|
|
5
5
|
import { bottomPopupEmits, bottomPopupProps } from '../bottom-popup/props'
|
|
6
|
+
import type { SearchProps } from '../search'
|
|
6
7
|
|
|
7
8
|
export interface CascaderNode<T = any> {
|
|
8
9
|
props: T
|
|
@@ -50,6 +51,13 @@ export const cascaderProps = {
|
|
|
50
51
|
* 是否显示搜索
|
|
51
52
|
*/
|
|
52
53
|
showSearch: Boolean,
|
|
54
|
+
/**
|
|
55
|
+
* 搜索框的props
|
|
56
|
+
*/
|
|
57
|
+
searchProps: {
|
|
58
|
+
type: Object as PropType<Partial<SearchProps>>,
|
|
59
|
+
default: () => ({}),
|
|
60
|
+
},
|
|
53
61
|
/**
|
|
54
62
|
* 动态获取下一级节点数据
|
|
55
63
|
*/
|
package/components/cell/cell.vue
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
<view v-if="label" :class="ns.e('label')">{{ label }}</view>
|
|
23
23
|
</template>
|
|
24
24
|
</view>
|
|
25
|
-
<view :class="[ns.e('value'), valueClass]">
|
|
25
|
+
<view :class="[ns.e('value'), valueClass]" :style="{ textAlign: valueAlign }">
|
|
26
26
|
<slot v-if="$slots.default" />
|
|
27
27
|
<text v-else :selectable="selectable" :user-select="selectable">{{ value }}</text>
|
|
28
28
|
|
|
29
|
-
<view v-if="errorMessage" :class="ns.e('error-message')"
|
|
29
|
+
<view v-if="errorMessage" :class="ns.e('error-message')">
|
|
30
30
|
{{ errorMessage }}
|
|
31
31
|
</view>
|
|
32
32
|
</view>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defaultNamespace } from '../../core'
|
|
2
|
+
|
|
3
|
+
const Dialog = () => {
|
|
4
|
+
const node = uni
|
|
5
|
+
.createSelectorQuery()
|
|
6
|
+
.select(`#${defaultNamespace}-dialog`)
|
|
7
|
+
.node((result) => {
|
|
8
|
+
console.log('result', result)
|
|
9
|
+
})
|
|
10
|
+
.exec()
|
|
11
|
+
console.log(node)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default Dialog
|
package/components/index.ts
CHANGED
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
@confirm="onOk"
|
|
21
21
|
>
|
|
22
22
|
<view :class="ns.e('content')">
|
|
23
|
+
<slot name="before" />
|
|
23
24
|
<Search
|
|
24
25
|
v-if="showSearch"
|
|
25
26
|
v-model="searchText"
|
|
27
|
+
v-bind="searchProps"
|
|
26
28
|
:class="ns.e('search')"
|
|
27
29
|
@change="debounceSearchChange"
|
|
28
30
|
/>
|
|
@@ -56,7 +58,7 @@
|
|
|
56
58
|
<ButtonComponent
|
|
57
59
|
v-if="showView.create"
|
|
58
60
|
:class="ns.e('create-button')"
|
|
59
|
-
:type="
|
|
61
|
+
:type="showView.confirm ? 'default' : 'primary'"
|
|
60
62
|
ellipsis
|
|
61
63
|
block
|
|
62
64
|
@click="onCreate"
|
|
@@ -138,7 +140,7 @@ const filterOptions = computed(() => {
|
|
|
138
140
|
|
|
139
141
|
const showView = computed(() => {
|
|
140
142
|
const create = props.allowCreate && !!searchText.value
|
|
141
|
-
const confirm = props.multiple
|
|
143
|
+
const confirm = props.multiple || props.showConfirm
|
|
142
144
|
return {
|
|
143
145
|
footer: create || confirm,
|
|
144
146
|
create,
|
|
@@ -225,7 +227,7 @@ const onCreate = async () => {
|
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
const onSelect = async (value: OptionValue) => {
|
|
228
|
-
if (_onSelect(value) && !
|
|
230
|
+
if (_onSelect(value) && !showView.value.confirm) {
|
|
229
231
|
onOk()
|
|
230
232
|
}
|
|
231
233
|
}
|
|
@@ -2,6 +2,7 @@ import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'
|
|
|
2
2
|
import type { UseListProps } from '../../core/useList'
|
|
3
3
|
import { isArray, isNumber, isObject, isString } from '../../utils'
|
|
4
4
|
import { bottomPopupEmits, bottomPopupProps } from '../bottom-popup/props'
|
|
5
|
+
import type { SearchProps } from '../search'
|
|
5
6
|
|
|
6
7
|
export type Option = any
|
|
7
8
|
export type OptionValue = number | string
|
|
@@ -37,6 +38,13 @@ export const pickerPopupProps = {
|
|
|
37
38
|
* 是否显示搜索
|
|
38
39
|
*/
|
|
39
40
|
showSearch: Boolean,
|
|
41
|
+
/**
|
|
42
|
+
* 搜索框的props
|
|
43
|
+
*/
|
|
44
|
+
searchProps: {
|
|
45
|
+
type: Object as PropType<Partial<SearchProps>>,
|
|
46
|
+
default: () => ({}),
|
|
47
|
+
},
|
|
40
48
|
/**
|
|
41
49
|
* 是否多选
|
|
42
50
|
*/
|
|
@@ -84,6 +92,10 @@ export const pickerPopupProps = {
|
|
|
84
92
|
* 确定后是否重置数据
|
|
85
93
|
*/
|
|
86
94
|
resetAfterConfirm: Boolean,
|
|
95
|
+
/**
|
|
96
|
+
* 是否显示确认按钮,多选时强制开启
|
|
97
|
+
*/
|
|
98
|
+
showConfirm: Boolean,
|
|
87
99
|
/**
|
|
88
100
|
* 是否允许用户创建新条目,需配合 showSearch 使用
|
|
89
101
|
*/
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
@tap="onSelect"
|
|
11
11
|
>
|
|
12
12
|
<Badge :show="dot || !isUndefined(badge)" :dot="dot" :content="badge">
|
|
13
|
-
<
|
|
13
|
+
<slot v-if="$slots.title" name="title" />
|
|
14
|
+
<text v-else :class="ns.e('text')">{{ title }}</text>
|
|
14
15
|
</Badge>
|
|
15
16
|
</view>
|
|
16
17
|
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DocDemoBlock title="基础用法" card>
|
|
3
|
+
<Demo1 />
|
|
4
|
+
</DocDemoBlock>
|
|
5
|
+
<DocDemoBlock title="双滑块" card>
|
|
6
|
+
<Demo2 />
|
|
7
|
+
</DocDemoBlock>
|
|
8
|
+
<DocDemoBlock title="指定范围" card>
|
|
9
|
+
<Demo3 />
|
|
10
|
+
</DocDemoBlock>
|
|
11
|
+
<DocDemoBlock title="设置步长" card>
|
|
12
|
+
<Demo4 />
|
|
13
|
+
</DocDemoBlock>
|
|
14
|
+
<DocDemoBlock title="显示范围" card>
|
|
15
|
+
<Demo5 />
|
|
16
|
+
</DocDemoBlock>
|
|
17
|
+
<DocDemoBlock title="显示标签" card>
|
|
18
|
+
<Demo6 class="block mt-40" />
|
|
19
|
+
</DocDemoBlock>
|
|
20
|
+
<DocDemoBlock title="禁用" card>
|
|
21
|
+
<Demo7 />
|
|
22
|
+
</DocDemoBlock>
|
|
23
|
+
<DocDemoBlock title="自定义样式" card>
|
|
24
|
+
<Demo8 />
|
|
25
|
+
</DocDemoBlock>
|
|
26
|
+
<DocDemoBlock title="自定义按钮" card>
|
|
27
|
+
<Demo9 />
|
|
28
|
+
</DocDemoBlock>
|
|
29
|
+
<DocDemoBlock title="刻度标记" card>
|
|
30
|
+
<Demo10 />
|
|
31
|
+
</DocDemoBlock>
|
|
32
|
+
<pa-safe-bottom class="block mt-30" />
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import Demo1 from '../../demos/slider/demo-1.vue'
|
|
37
|
+
import Demo2 from '../../demos/slider/demo-2.vue'
|
|
38
|
+
import Demo3 from '../../demos/slider/demo-3.vue'
|
|
39
|
+
import Demo4 from '../../demos/slider/demo-4.vue'
|
|
40
|
+
import Demo5 from '../../demos/slider/demo-5.vue'
|
|
41
|
+
import Demo6 from '../../demos/slider/demo-6.vue'
|
|
42
|
+
import Demo7 from '../../demos/slider/demo-7.vue'
|
|
43
|
+
import Demo8 from '../../demos/slider/demo-8.vue'
|
|
44
|
+
import Demo9 from '../../demos/slider/demo-9.vue'
|
|
45
|
+
import Demo10 from '../../demos/slider/demo-10.vue'
|
|
46
|
+
import DocDemoBlock from '../../doc/doc-demo-block.vue'
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ExtractPropTypes, PropType } from 'vue'
|
|
2
|
+
import { isArray, isNumber } from '../../utils'
|
|
3
|
+
|
|
4
|
+
export type SliderValue = number | number[]
|
|
5
|
+
|
|
6
|
+
export const sliderProps = {
|
|
7
|
+
/**
|
|
8
|
+
* 当前进度百分比,在双滑块模式下为数组格式
|
|
9
|
+
*/
|
|
10
|
+
modelValue: {
|
|
11
|
+
type: [Number, Array] as PropType<SliderValue>,
|
|
12
|
+
default(rawProps: any) {
|
|
13
|
+
return rawProps.range ? [0, 100] : 0
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* 是否开启双滑块模式
|
|
18
|
+
*/
|
|
19
|
+
range: {
|
|
20
|
+
type: Boolean,
|
|
21
|
+
default: false,
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* 最小值
|
|
25
|
+
*/
|
|
26
|
+
min: {
|
|
27
|
+
type: Number,
|
|
28
|
+
default: 0,
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* 最大值
|
|
32
|
+
*/
|
|
33
|
+
max: {
|
|
34
|
+
type: Number,
|
|
35
|
+
default: 100,
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* 步长
|
|
39
|
+
*/
|
|
40
|
+
step: {
|
|
41
|
+
type: Number,
|
|
42
|
+
default: 1,
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* 是否禁用滑块
|
|
46
|
+
*/
|
|
47
|
+
disabled: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false,
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* 是否显示当前进度标签
|
|
53
|
+
*/
|
|
54
|
+
showTag: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* 是否显示范围值
|
|
60
|
+
*/
|
|
61
|
+
showRange: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
default: false,
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* 是否显示刻度标记
|
|
67
|
+
*/
|
|
68
|
+
marks: {
|
|
69
|
+
type: Object as PropType<Record<number, string>>,
|
|
70
|
+
default: false,
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* 进度条颜色
|
|
74
|
+
*/
|
|
75
|
+
activeColor: String,
|
|
76
|
+
/**
|
|
77
|
+
* 进度条非激活态颜色
|
|
78
|
+
*/
|
|
79
|
+
inactiveColor: String,
|
|
80
|
+
} as const
|
|
81
|
+
|
|
82
|
+
export const sliderEmits = {
|
|
83
|
+
'update:modelValue': (value: number | number[]) => isNumber(value) || isArray(value),
|
|
84
|
+
change: (value: number | number[]) => isNumber(value) || isArray(value),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type SliderProps = ExtractPropTypes<typeof sliderProps>
|
|
88
|
+
export type SliderEmits = typeof sliderEmits
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
@import '../../styles/vars.scss';
|
|
2
|
+
|
|
3
|
+
.#{$prefix}-slider {
|
|
4
|
+
$default-bg-color: _var(slider-background-color, #ebedf0);
|
|
5
|
+
$active-bg-color: _var(slider-active-background, _var(color-primary));
|
|
6
|
+
|
|
7
|
+
position: relative;
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: _var(slider-height, 4px);
|
|
10
|
+
background-color: $default-bg-color;
|
|
11
|
+
border-radius: 999px;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
|
|
14
|
+
&::before {
|
|
15
|
+
position: absolute;
|
|
16
|
+
top: -8px;
|
|
17
|
+
right: 0;
|
|
18
|
+
bottom: -8px;
|
|
19
|
+
left: 0;
|
|
20
|
+
content: '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&-container {
|
|
24
|
+
position: relative;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: 4px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&__min,
|
|
32
|
+
&__max {
|
|
33
|
+
color: _var(slider-tip-font-color, _var(color-black));
|
|
34
|
+
font-size: _var(slider-tip-font-size, 12px);
|
|
35
|
+
user-select: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&__bar {
|
|
39
|
+
position: relative;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
|
+
background-color: $active-bg-color;
|
|
43
|
+
border-radius: inherit;
|
|
44
|
+
transition: all _var(slider-duration, 0.2s);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&__button-wrapper {
|
|
48
|
+
position: absolute;
|
|
49
|
+
cursor: grab;
|
|
50
|
+
top: 50%;
|
|
51
|
+
|
|
52
|
+
&--left {
|
|
53
|
+
left: 0;
|
|
54
|
+
transform: translate3d(-50%, -50%, 0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&--right {
|
|
58
|
+
right: 0;
|
|
59
|
+
transform: translate3d(50%, -50%, 0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&__button {
|
|
64
|
+
width: _var(slider-button-width, 24px);
|
|
65
|
+
height: _var(slider-button-height, 24px);
|
|
66
|
+
background: _var(slider-button-background, #fff);
|
|
67
|
+
border: _var(slider-button-border, 1px solid transparent);
|
|
68
|
+
border-radius: _var(slider-button-radius, 50%);
|
|
69
|
+
box-shadow: _var(slider-button-shadow, 0 1px 2px rgba(0, 0, 0, 0.5));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&__button-number {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
width: 100%;
|
|
77
|
+
height: 100%;
|
|
78
|
+
color: _var(slider-button-tip-font-color, _var(color-black));
|
|
79
|
+
font-size: _var(slider-button-tip-font-size, 12px);
|
|
80
|
+
transform: translate3d(0, -100%, 0);
|
|
81
|
+
user-select: none;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&__mark {
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: 50%;
|
|
87
|
+
width: 100%;
|
|
88
|
+
font-size: 12px;
|
|
89
|
+
padding-top: 14px;
|
|
90
|
+
overflow: visible;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&__mark-text {
|
|
94
|
+
position: absolute;
|
|
95
|
+
display: inline-block;
|
|
96
|
+
color: #999;
|
|
97
|
+
line-height: 16px;
|
|
98
|
+
text-align: center;
|
|
99
|
+
transform: translateX(-50%);
|
|
100
|
+
user-select: none;
|
|
101
|
+
word-break: keep-all;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&__tick {
|
|
105
|
+
position: absolute;
|
|
106
|
+
top: -20px;
|
|
107
|
+
left: 0;
|
|
108
|
+
width: 11px;
|
|
109
|
+
height: 11px;
|
|
110
|
+
border-radius: 50%;
|
|
111
|
+
background-color: $default-bg-color;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&--show-range {
|
|
115
|
+
margin: _var(slider-margin, 0 15px);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
&--mark-active &__tick {
|
|
119
|
+
background-color: $active-bg-color;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
&--disabled {
|
|
123
|
+
cursor: not-allowed;
|
|
124
|
+
opacity: _var(slider-disabled-opacity, 0.5);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="ns.b('container')">
|
|
3
|
+
<text v-if="showRange" :class="ns.e('min')">{{ min }}</text>
|
|
4
|
+
<view
|
|
5
|
+
:class="[ns.b(), ns.is('show-range', showRange), ns.is('disabled', disabled)]"
|
|
6
|
+
:style="ns.style({ backgroundColor: inactiveColor })"
|
|
7
|
+
>
|
|
8
|
+
<view v-if="marks" :class="ns.e('mark')">
|
|
9
|
+
<text
|
|
10
|
+
v-for="(text, value) in marks"
|
|
11
|
+
:key="value"
|
|
12
|
+
:class="[ns.e('mark-text'), ns.is('mark-active', isWithinRange(value))]"
|
|
13
|
+
:style="ns.style({ left: value + '%' })"
|
|
14
|
+
>
|
|
15
|
+
{{ text }}
|
|
16
|
+
<text :class="ns.e('tick')"></text>
|
|
17
|
+
</text>
|
|
18
|
+
</view>
|
|
19
|
+
|
|
20
|
+
<view :class="ns.e('bar')" :style="ns.style({ backgroundColor: activeColor })">
|
|
21
|
+
<template v-for="(type, index) in ['left', 'right']" :key="type">
|
|
22
|
+
<view
|
|
23
|
+
v-if="index === 0 ? range : true"
|
|
24
|
+
:class="[ns.e('button-wrapper'), ns.em('button-wrapper', type)]"
|
|
25
|
+
:data-index="index"
|
|
26
|
+
:props="wxsProps"
|
|
27
|
+
:change:props="wxs.propsChange"
|
|
28
|
+
:value="wxsModelValue"
|
|
29
|
+
:change:value="wxs.modelValueChange"
|
|
30
|
+
@touchstart="wxs.touchstart"
|
|
31
|
+
@touchmove="wxs.touchmove"
|
|
32
|
+
@touchend="wxs.touchend"
|
|
33
|
+
>
|
|
34
|
+
<slot v-if="$slots.button" name="button" />
|
|
35
|
+
<view v-else :class="ns.e('button')">
|
|
36
|
+
<text v-if="showTag" :class="ns.e('button-number')">{{
|
|
37
|
+
Array.isArray(modelValue) ? modelValue[index] : modelValue
|
|
38
|
+
}}</text>
|
|
39
|
+
</view>
|
|
40
|
+
</view>
|
|
41
|
+
</template>
|
|
42
|
+
</view>
|
|
43
|
+
</view>
|
|
44
|
+
<text v-if="showRange" :class="ns.e('max')">{{ max }}</text>
|
|
45
|
+
</view>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script lang="ts">
|
|
49
|
+
import { computed, defineComponent, ref, watch } from 'vue'
|
|
50
|
+
import useNamespace from '../../core/useNamespace'
|
|
51
|
+
import { sliderEmits, sliderProps } from './props'
|
|
52
|
+
|
|
53
|
+
export default defineComponent({
|
|
54
|
+
props: sliderProps,
|
|
55
|
+
emits: sliderEmits,
|
|
56
|
+
setup(props, { emit }) {
|
|
57
|
+
const ns = useNamespace('slider')
|
|
58
|
+
|
|
59
|
+
let isDragging = false
|
|
60
|
+
|
|
61
|
+
const wxsProps = computed(() => {
|
|
62
|
+
return {
|
|
63
|
+
mainClassName: ns.b(),
|
|
64
|
+
barClassName: ns.e('bar'),
|
|
65
|
+
disabled: props.disabled,
|
|
66
|
+
range: props.range,
|
|
67
|
+
min: props.min,
|
|
68
|
+
max: props.max,
|
|
69
|
+
step: props.step,
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const wxsModelValue = ref(props.modelValue)
|
|
74
|
+
|
|
75
|
+
const isRange = computed(() => {
|
|
76
|
+
return props.range && Array.isArray(props.modelValue)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const isWithinRange = (value: number) => {
|
|
80
|
+
if (isRange.value) {
|
|
81
|
+
const modelValues = props.modelValue as number[]
|
|
82
|
+
return value >= modelValues[0] && value <= modelValues[1]
|
|
83
|
+
}
|
|
84
|
+
return value <= props.modelValue
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const updateModelValue = (value: number | number[]) => {
|
|
88
|
+
isDragging = true
|
|
89
|
+
// TODO: wxs的bug,为0时会变成{}, 要特殊处理
|
|
90
|
+
if (!isRange.value && typeof value !== 'number') {
|
|
91
|
+
value = 0
|
|
92
|
+
}
|
|
93
|
+
emit('update:modelValue', value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const emitChange = (value: number | number[]) => {
|
|
97
|
+
isDragging = false
|
|
98
|
+
wxsModelValue.value = value
|
|
99
|
+
emit('change', value)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
watch(
|
|
103
|
+
() => props.modelValue,
|
|
104
|
+
(newValue) => {
|
|
105
|
+
if (!isDragging) {
|
|
106
|
+
wxsModelValue.value = newValue
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
wxs: {} as any,
|
|
113
|
+
ns,
|
|
114
|
+
wxsProps,
|
|
115
|
+
wxsModelValue,
|
|
116
|
+
isWithinRange,
|
|
117
|
+
updateModelValue,
|
|
118
|
+
emitChange,
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
</script>
|
|
123
|
+
<script module="wxs" lang="wxs">
|
|
124
|
+
var MAIN_WIDTH = 0
|
|
125
|
+
var MAIN_OFFSET_LEFT = 0
|
|
126
|
+
|
|
127
|
+
function getMainWidth(ownInstance, mainClassName) {
|
|
128
|
+
if (MAIN_WIDTH) return MAIN_WIDTH
|
|
129
|
+
var rect = ownInstance.selectComponent('.' + mainClassName).getBoundingClientRect()
|
|
130
|
+
MAIN_WIDTH = rect.width
|
|
131
|
+
MAIN_OFFSET_LEFT = rect.left
|
|
132
|
+
return MAIN_WIDTH
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function touchstart(event, ownInstance) {
|
|
136
|
+
var instance = event.instance
|
|
137
|
+
var state = instance.getState()
|
|
138
|
+
if (state.disabled) return
|
|
139
|
+
getMainWidth(ownInstance, state.mainClassName)
|
|
140
|
+
state.index = instance.getDataset().index
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function touchmove(event, ownInstance) {
|
|
144
|
+
var instance = event.instance
|
|
145
|
+
var state = instance.getState()
|
|
146
|
+
if (state.disabled) return
|
|
147
|
+
var touch = event.touches[0] || event.changedTouches[0]
|
|
148
|
+
var moveX = touch.pageX - MAIN_OFFSET_LEFT
|
|
149
|
+
if (moveX < 0) {
|
|
150
|
+
moveX = 0
|
|
151
|
+
} else if (moveX > MAIN_WIDTH) {
|
|
152
|
+
moveX = MAIN_WIDTH
|
|
153
|
+
}
|
|
154
|
+
state.moveX = moveX
|
|
155
|
+
var offset = calcLeftAndRightOffsetPercent(state)
|
|
156
|
+
state._offset = offset
|
|
157
|
+
updateStyle(ownInstance, state.barClassName, offset.left, offset.right)
|
|
158
|
+
var newModelValue = state.range ? [
|
|
159
|
+
calcValueByOffsetPercent(offset.left, state.min, state.max, state.step),
|
|
160
|
+
calcValueByOffsetPercent(offset.right, state.min, state.max, state.step)
|
|
161
|
+
] : calcValueByOffsetPercent(offset.right, state.min, state.max, state.step)
|
|
162
|
+
state._modelValue = newModelValue
|
|
163
|
+
// 值没有变化不触发事件
|
|
164
|
+
if (newModelValue.toString() !== state.modelValue.toString()) {
|
|
165
|
+
ownInstance.callMethod('updateModelValue', newModelValue)
|
|
166
|
+
}
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function touchend(event, ownInstance) {
|
|
171
|
+
var instance = event.instance
|
|
172
|
+
var state = instance.getState()
|
|
173
|
+
if (state.disabled) return
|
|
174
|
+
ownInstance.callMethod('emitChange', state._modelValue)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function calcOffsetPercent(offsetX, width, step) {
|
|
178
|
+
if (offsetX <= 0) return 0
|
|
179
|
+
var percentage = Math.floor((offsetX / width) * 100)
|
|
180
|
+
percentage = percentage - (percentage % step)
|
|
181
|
+
return percentage
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function calcValueByOffsetPercent(percentage, min, max, step) {
|
|
185
|
+
var value = Math.floor(((max - min) * percentage) / 100 + min)
|
|
186
|
+
value = value - (value % step)
|
|
187
|
+
return value
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function calcLeftAndRightOffsetPercent(state) {
|
|
191
|
+
var percentage = calcOffsetPercent(state.moveX, MAIN_WIDTH, state.step)
|
|
192
|
+
// if (percentage <= 0) return { left: 0, right: 0 }
|
|
193
|
+
var left = 0
|
|
194
|
+
var right = 0
|
|
195
|
+
if (state.range) {
|
|
196
|
+
left = state.index === 0 ? percentage : state.modelValue[0]
|
|
197
|
+
right = state.index === 0 ? state.modelValue[1] : percentage
|
|
198
|
+
} else {
|
|
199
|
+
right = percentage
|
|
200
|
+
}
|
|
201
|
+
return left <= right ? { left, right } : { left: right, right: left }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function updateStyle(ownInstance, barClassName, leftOffsetPercent, rightOffsetPercent) {
|
|
205
|
+
var width = rightOffsetPercent - leftOffsetPercent
|
|
206
|
+
var left = leftOffsetPercent
|
|
207
|
+
ownInstance.selectComponent('.' + barClassName).setStyle({
|
|
208
|
+
width: width + '%',
|
|
209
|
+
left: left + '%',
|
|
210
|
+
transition: 'none 0s ease 0s',
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function propsChange(newValue, oldValue, ownInstance, instance) {
|
|
215
|
+
if (typeof newValue === 'undefined') return
|
|
216
|
+
var state = instance.getState()
|
|
217
|
+
state.mainClassName = newValue.mainClassName
|
|
218
|
+
state.barClassName = newValue.barClassName
|
|
219
|
+
state.disabled = newValue.disabled
|
|
220
|
+
state.range = newValue.range
|
|
221
|
+
state.min = newValue.min
|
|
222
|
+
state.max = newValue.max
|
|
223
|
+
state.step = newValue.step
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function modelValueChange(newValue, oldValue, ownInstance, instance) {
|
|
227
|
+
if (typeof newValue === 'undefined') return
|
|
228
|
+
var state = instance.getState()
|
|
229
|
+
state.modelValue = newValue
|
|
230
|
+
// TODO: wxs的bug,为0时会变成[object Object], 要特殊处理
|
|
231
|
+
if (!state.range && typeof state.modelValue !== 'number') {
|
|
232
|
+
state.modelValue = 0
|
|
233
|
+
}
|
|
234
|
+
var modelValues = state.range ? state.modelValue : [null, state.modelValue]
|
|
235
|
+
var offset = [0, 0]
|
|
236
|
+
for (var i = 0; i < modelValues.length; i++) {
|
|
237
|
+
if (modelValues[i] === null) continue
|
|
238
|
+
offset[i] = calcOffsetPercent(modelValues[i] - state.min, state.max - state.min, state.step)
|
|
239
|
+
}
|
|
240
|
+
updateStyle(ownInstance, state.barClassName, offset[0], offset[1])
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports = {
|
|
244
|
+
touchstart: touchstart,
|
|
245
|
+
touchmove: touchmove,
|
|
246
|
+
touchend: touchend,
|
|
247
|
+
propsChange: propsChange,
|
|
248
|
+
modelValueChange: modelValueChange,
|
|
249
|
+
}
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
<style lang="scss">
|
|
253
|
+
@import './slider.scss';
|
|
254
|
+
</style>
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
position: absolute;
|
|
29
29
|
top: 0;
|
|
30
30
|
right: 0;
|
|
31
|
-
width: _var(uploader-delete-width,
|
|
32
|
-
height: _var(uploader-delete-height,
|
|
31
|
+
width: _var(uploader-delete-width, 14px);
|
|
32
|
+
height: _var(uploader-delete-height, 14px);
|
|
33
33
|
background-color: _var(uploader-delete-bg, rgba(0, 0, 0, 0.7));
|
|
34
34
|
border-radius: 0 _var(uploader-preview-radius, 4px) 0 12px;
|
|
35
35
|
font-size: _var(uploader-delete-icon-size, 12px);
|
package/demos/cell/demo-1.vue
CHANGED
package/demos/cell/demo-2.vue
CHANGED
package/demos/cell/demo-7.vue
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<pa-slider v-model="value" :marks="marks" />
|
|
3
|
+
<pa-slider v-model="value2" :marks="marks" range class="block" style="margin-top: 60px" />
|
|
4
|
+
</template>
|
|
5
|
+
|
|
6
|
+
<script setup lang="ts">
|
|
7
|
+
import { ref } from 'vue'
|
|
8
|
+
|
|
9
|
+
const value = ref(52)
|
|
10
|
+
const value2 = ref([29, 72])
|
|
11
|
+
|
|
12
|
+
const marks = {
|
|
13
|
+
0: '0',
|
|
14
|
+
20: '20',
|
|
15
|
+
40: '40',
|
|
16
|
+
60: '60',
|
|
17
|
+
80: '80',
|
|
18
|
+
100: '100',
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<pa-slider v-model="value">
|
|
3
|
+
<template #button>
|
|
4
|
+
<div class="slider-demo-button">{{ value }}</div>
|
|
5
|
+
</template>
|
|
6
|
+
</pa-slider>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import { ref } from 'vue'
|
|
11
|
+
|
|
12
|
+
const value = ref(20)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<style lang="scss" scoped>
|
|
16
|
+
.slider-demo-button {
|
|
17
|
+
width: 26px;
|
|
18
|
+
color: #fff;
|
|
19
|
+
font-size: 10px;
|
|
20
|
+
line-height: 18px;
|
|
21
|
+
text-align: center;
|
|
22
|
+
background-color: var(--pa-color-primary);
|
|
23
|
+
border-radius: 100px;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
package/global.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ declare module '@vue/runtime-core' {
|
|
|
48
48
|
PaSidebar: typeof import('papayaui/components/sidebar/sidebar.vue')['default']
|
|
49
49
|
PaSidebarItem: typeof import('papayaui/components/sidebar-item/sidebar-item.vue')['default']
|
|
50
50
|
PaSkeleton: typeof import('papayaui/components/skeleton/skeleton.vue')['default']
|
|
51
|
+
PaSlider: typeof import('papayaui/components/slider/slider.vue')['default']
|
|
51
52
|
PaSortLabel: typeof import('papayaui/components/sort-label/sort-label.vue')['default']
|
|
52
53
|
PaSteps: typeof import('papayaui/components/steps/steps.vue')['default']
|
|
53
54
|
PaSticky: typeof import('papayaui/components/sticky/sticky.vue')['default']
|
package/package.json
CHANGED
package/utils/cos.ts
CHANGED
|
@@ -140,9 +140,9 @@ export interface GetObjectUrlParams {
|
|
|
140
140
|
/** 操作方法,例如 GET,POST,DELETE,HEAD 等 HTTP 方法,默认为 GET */
|
|
141
141
|
Method?: string
|
|
142
142
|
/** 签名中要签入的请求参数,{key: 'val'} 的格式 */
|
|
143
|
-
Query?:
|
|
143
|
+
Query?: object
|
|
144
144
|
/** 签名中要签入的请求头部,{key: 'val'} 的格式 */
|
|
145
|
-
Headers?:
|
|
145
|
+
Headers?: object
|
|
146
146
|
/** 签名几秒后失效,默认为 900 秒 */
|
|
147
147
|
Expires?: number
|
|
148
148
|
}
|
|
@@ -249,6 +249,13 @@ export class PaCOS {
|
|
|
249
249
|
const cosConfig = await this.getConfig()
|
|
250
250
|
const Key = `${cosConfig.prefix}/${params?.Key ?? filePath.replace(/^.+\/(.+)$/, '$1')}`
|
|
251
251
|
return new Promise<UploadFileResult>((resolve, reject) => {
|
|
252
|
+
console.log('uploadFile params', {
|
|
253
|
+
Bucket: cosConfig.bucket,
|
|
254
|
+
Region: cosConfig.region,
|
|
255
|
+
FilePath: filePath,
|
|
256
|
+
...params,
|
|
257
|
+
Key,
|
|
258
|
+
})
|
|
252
259
|
this.cos.uploadFile(
|
|
253
260
|
{
|
|
254
261
|
Bucket: cosConfig.bucket,
|
|
@@ -259,6 +266,7 @@ export class PaCOS {
|
|
|
259
266
|
},
|
|
260
267
|
(err: COSError | null, data: UploadFileResult) => {
|
|
261
268
|
if (err) {
|
|
269
|
+
console.error(err)
|
|
262
270
|
reject(err)
|
|
263
271
|
return
|
|
264
272
|
}
|