hy-app 0.6.4 → 0.6.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/attributes.json +1 -1
- package/components/hy-address-picker/hy-address-picker.vue +249 -249
- package/components/hy-address-picker/props.ts +103 -103
- package/components/hy-button/hy-button.vue +320 -289
- package/components/hy-button/props.ts +143 -143
- package/components/hy-button/typing.d.ts +43 -35
- package/components/hy-calendar/header.vue +58 -58
- package/components/hy-calendar/hy-calendar.vue +8 -6
- package/components/hy-calendar/month.vue +402 -402
- package/components/hy-calendar/props.ts +169 -169
- package/components/hy-calendar/typing.d.ts +47 -45
- package/components/hy-cell-item/hy-cell-item.vue +161 -161
- package/components/hy-cell-item/props.ts +59 -59
- package/components/hy-check-button/hy-check-button.vue +135 -135
- package/components/hy-code-input/hy-code-input.vue +231 -231
- package/components/hy-code-input/props.ts +90 -90
- package/components/hy-config-provider/hy-config-provider.vue +53 -53
- package/components/hy-config-provider/props.ts +30 -30
- package/components/hy-coupon/hy-coupon.vue +183 -183
- package/components/hy-coupon/props.ts +108 -108
- package/components/hy-datetime-picker/hy-datetime-picker.vue +41 -55
- package/components/hy-datetime-picker/props.ts +144 -144
- package/components/hy-datetime-picker/typing.d.ts +2 -0
- package/components/hy-divider/props.ts +83 -83
- package/components/hy-empty/icon.ts +72 -72
- package/components/hy-folding-panel/hy-folding-panel-group.vue +162 -162
- package/components/hy-form/hy-form.vue +220 -220
- package/components/hy-icon/hy-icon.vue +112 -112
- package/components/hy-index-bar/hy-index-bar.vue +185 -185
- package/components/hy-index-bar/index.scss +64 -64
- package/components/hy-index-bar/props.ts +94 -94
- package/components/hy-index-bar/typing.d.ts +36 -36
- package/components/hy-input/hy-input.vue +333 -333
- package/components/hy-input/props.ts +186 -186
- package/components/hy-modal/hy-modal.vue +211 -211
- package/components/hy-modal/props.ts +94 -94
- package/components/hy-modal/typing.d.ts +16 -16
- package/components/hy-notice-bar/hy-row-notice.vue +121 -121
- package/components/hy-notify/hy-notify.vue +174 -174
- package/components/hy-number-step/hy-number-step.vue +367 -367
- package/components/hy-overlay/hy-overlay.vue +61 -61
- package/components/hy-overlay/props.ts +38 -38
- package/components/hy-pagination/hy-pagination.vue +136 -136
- package/components/hy-pagination/props.ts +58 -58
- package/components/hy-parse/hy-parse.vue +550 -550
- package/components/hy-parse/node/node.vue +781 -781
- package/components/hy-parse/parser.js +1455 -1455
- package/components/hy-parse/props.ts +19 -19
- package/components/hy-parse/typing.d.ts +68 -68
- package/components/hy-picker/hy-picker.vue +435 -435
- package/components/hy-picker/props.ts +122 -122
- package/components/hy-picker/typing.d.ts +38 -38
- package/components/hy-qrcode/props.ts +72 -72
- package/components/hy-qrcode/qrcode.js.bak +1433 -1433
- package/components/hy-radio/props.ts +97 -97
- package/components/hy-read-more/props.ts +48 -48
- package/components/hy-search/props.ts +133 -133
- package/components/hy-signature/canvasHelper.ts +51 -51
- package/components/hy-signature/props.ts +121 -121
- package/components/hy-skeleton/hy-skeleton.vue +142 -142
- package/components/hy-skeleton/props.ts +46 -46
- package/components/hy-skeleton/typing.d.ts +31 -31
- package/components/hy-steps/hy-steps.vue +275 -275
- package/components/hy-steps/typing.d.ts +25 -25
- package/components/hy-swiper/hy-swiper.vue +3 -3
- package/components/hy-swiper/index.scss +5 -5
- package/components/hy-swiper/props.ts +0 -1
- package/components/hy-table/hy-table.vue +630 -630
- package/components/hy-table/props.ts +62 -62
- package/components/hy-table/typing.d.ts +29 -29
- package/components/hy-tabs/hy-tabs.vue +336 -335
- package/components/hy-tabs/props.ts +84 -77
- package/components/hy-tag/hy-tag.vue +173 -173
- package/components/hy-tag/props.ts +89 -89
- package/components/hy-text/hy-text.vue +237 -237
- package/components/hy-text/props.ts +115 -115
- package/components/hy-textarea/hy-textarea.vue +198 -198
- package/components/hy-toast/hy-toast.vue +200 -200
- package/components/hy-toast/props.ts +3 -3
- package/components/hy-transition/hy-transition.vue +157 -157
- package/components/hy-transition/props.ts +32 -32
- package/components/hy-upload/hy-upload.vue +384 -384
- package/components/hy-watermark/hy-watermark.vue +1058 -1058
- package/components/hy-watermark/props.ts +109 -109
- package/global.d.ts +94 -94
- package/libs/api/http.ts +119 -119
- package/libs/composables/index.ts +8 -8
- package/libs/composables/useMessage.ts +149 -149
- package/libs/composables/useToast.ts +45 -45
- package/libs/composables/useTranslate.ts +10 -10
- package/libs/css/_config.scss +5 -5
- package/libs/index.ts +8 -8
- package/libs/locale/index.ts +32 -32
- package/libs/locale/lang/en-US.ts +84 -84
- package/libs/locale/lang/zh-CN.ts +87 -87
- package/libs/typing/index.ts +2 -2
- package/libs/typing/modules/common.d.ts +139 -139
- package/libs/typing/modules/form.ts +176 -176
- package/libs/typing/modules/http.d.ts +19 -19
- package/libs/typing/modules/index.d.ts +12 -12
- package/libs/utils/inside.ts +340 -340
- package/libs/utils/inspect.ts +140 -140
- package/libs/utils/utils.ts +525 -525
- package/package.json +81 -81
- package/tags.json +1 -1
- package/web-types.json +1 -1
|
@@ -1,435 +1,435 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<view class="hy-picker">
|
|
3
|
-
<view v-if="hasInput" class="hy-picker-input cursor-pointer" @click="onShowByClickInput">
|
|
4
|
-
<slot v-if="$slots.default"></slot>
|
|
5
|
-
<template v-else>
|
|
6
|
-
<hy-input
|
|
7
|
-
v-model="inputLabelValue"
|
|
8
|
-
:disabled="input?.disabled"
|
|
9
|
-
:disabledColor="input?.disabledColor"
|
|
10
|
-
:shape="input?.shape"
|
|
11
|
-
:border="input?.border"
|
|
12
|
-
:prefixIcon="input?.prefixIcon"
|
|
13
|
-
:suffixIcon="input?.suffixIcon"
|
|
14
|
-
:color="input?.color"
|
|
15
|
-
:fontSize="input?.fontSize"
|
|
16
|
-
:inputAlign="input?.inputAlign"
|
|
17
|
-
:placeholder="input?.placeholder || t('placeholder')"
|
|
18
|
-
:placeholderStyle="input?.placeholderStyle"
|
|
19
|
-
:placeholderClass="input?.placeholderClass"
|
|
20
|
-
:customStyle="Object.assign({ 'pointer-events': 'none' }, input?.customStyle)"
|
|
21
|
-
></hy-input>
|
|
22
|
-
<view class="input-cover"></view>
|
|
23
|
-
</template>
|
|
24
|
-
</view>
|
|
25
|
-
<hy-popup
|
|
26
|
-
:show="show || (hasInput && showByClickInput)"
|
|
27
|
-
:mode="popupMode"
|
|
28
|
-
:zIndex="zIndex"
|
|
29
|
-
@close="closeHandler"
|
|
30
|
-
>
|
|
31
|
-
<view class="hy-picker">
|
|
32
|
-
<!-- 头部内容 -->
|
|
33
|
-
<view class="hy-picker__title" v-if="showToolbar">
|
|
34
|
-
<view
|
|
35
|
-
class="hy-picker__title--left"
|
|
36
|
-
:style="{ color: cancelColor }"
|
|
37
|
-
@tap="cancel"
|
|
38
|
-
>
|
|
39
|
-
{{ cancelText }}
|
|
40
|
-
</view>
|
|
41
|
-
<view class="hy-picker__title--center">
|
|
42
|
-
{{ title }}
|
|
43
|
-
</view>
|
|
44
|
-
<view
|
|
45
|
-
class="hy-picker__title--right"
|
|
46
|
-
:style="{ color: confirmColor }"
|
|
47
|
-
@tap="onConfirm"
|
|
48
|
-
>
|
|
49
|
-
<slot v-if="$slots['toolbar-right']" name="toolbar-right"></slot>
|
|
50
|
-
<text v-else>{{ confirmText }}</text>
|
|
51
|
-
</view>
|
|
52
|
-
</view>
|
|
53
|
-
<!-- 头部内容 -->
|
|
54
|
-
|
|
55
|
-
<slot name="toolbar-bottom"></slot>
|
|
56
|
-
|
|
57
|
-
<!-- 加载loading -->
|
|
58
|
-
<hy-loading
|
|
59
|
-
v-if="loading"
|
|
60
|
-
mode="circle"
|
|
61
|
-
custom-class="hy-picker__loading"
|
|
62
|
-
:custom-style="{
|
|
63
|
-
height: `${addUnit(visibleItemCount * itemHeight)}`
|
|
64
|
-
}"
|
|
65
|
-
></hy-loading>
|
|
66
|
-
<!-- 加载loading -->
|
|
67
|
-
|
|
68
|
-
<!-- 选择器内容 -->
|
|
69
|
-
<picker-view
|
|
70
|
-
v-else
|
|
71
|
-
class="hy-picker--view"
|
|
72
|
-
:indicatorStyle="`height: ${addUnit(itemHeight)}`"
|
|
73
|
-
:value="innerIndex"
|
|
74
|
-
:immediateChange="immediateChange"
|
|
75
|
-
:style="{
|
|
76
|
-
height: `${addUnit(visibleItemCount * itemHeight)}`
|
|
77
|
-
}"
|
|
78
|
-
mask-class="hy-picker--view__mask"
|
|
79
|
-
@change="changeHandler"
|
|
80
|
-
>
|
|
81
|
-
<picker-view-column
|
|
82
|
-
v-for="(item, index) in innerColumns"
|
|
83
|
-
:key="index"
|
|
84
|
-
class="hy-picker--view__column"
|
|
85
|
-
>
|
|
86
|
-
<view
|
|
87
|
-
v-if="Array.isArray(item)"
|
|
88
|
-
:class="[
|
|
89
|
-
'hy-picker--view__column__item',
|
|
90
|
-
index1 === innerIndex[index] &&
|
|
91
|
-
'hy-picker--view__column__item--selected'
|
|
92
|
-
]"
|
|
93
|
-
v-for="(item1, index1) in item"
|
|
94
|
-
:key="index1"
|
|
95
|
-
:style="{
|
|
96
|
-
height: addUnit(itemHeight),
|
|
97
|
-
lineHeight: addUnit(itemHeight),
|
|
98
|
-
fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal',
|
|
99
|
-
display: 'block'
|
|
100
|
-
}"
|
|
101
|
-
>
|
|
102
|
-
{{ getItemText(item1) }}
|
|
103
|
-
</view>
|
|
104
|
-
</picker-view-column>
|
|
105
|
-
</picker-view>
|
|
106
|
-
<!-- 选择器内容 -->
|
|
107
|
-
</view>
|
|
108
|
-
</hy-popup>
|
|
109
|
-
</view>
|
|
110
|
-
</template>
|
|
111
|
-
|
|
112
|
-
<script lang="ts">
|
|
113
|
-
export default {
|
|
114
|
-
name: 'hy-picker',
|
|
115
|
-
options: {
|
|
116
|
-
addGlobalClass: true,
|
|
117
|
-
virtualHost: true,
|
|
118
|
-
styleIsolation: 'shared'
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
</script>
|
|
122
|
-
|
|
123
|
-
<script setup lang="ts">
|
|
124
|
-
import { computed, ref, watch } from 'vue'
|
|
125
|
-
import { deepClone, sleep, addUnit, isArray, useTranslate } from '../../libs'
|
|
126
|
-
import type { IPickerEmits } from './typing'
|
|
127
|
-
import pickerProps from './props'
|
|
128
|
-
// 组件
|
|
129
|
-
import HyInput from '../hy-input/hy-input.vue'
|
|
130
|
-
import HyLoading from '../hy-loading/hy-loading.vue'
|
|
131
|
-
import HyPopup from '../hy-popup/hy-popup.vue'
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 此选择器用于单列,多列,多列联动的选择场景。
|
|
135
|
-
* @displayName hy-picker
|
|
136
|
-
*/
|
|
137
|
-
defineOptions({})
|
|
138
|
-
|
|
139
|
-
const props = defineProps(pickerProps)
|
|
140
|
-
const emit = defineEmits<IPickerEmits>()
|
|
141
|
-
|
|
142
|
-
const { t } = useTranslate('picker')
|
|
143
|
-
// 上一次选择的列索引
|
|
144
|
-
const lastIndex = ref<number[]>([])
|
|
145
|
-
// 索引值 ,对应picker-view的value
|
|
146
|
-
const innerIndex = ref<number[]>([])
|
|
147
|
-
// 各列的值
|
|
148
|
-
const innerColumns = ref<any[][]>([])
|
|
149
|
-
// 上一次的变化列索引
|
|
150
|
-
const columnIndex = ref<number>(0)
|
|
151
|
-
const showByClickInput = ref<boolean>(false)
|
|
152
|
-
// 当前用户选中,但是还没确认的值,用户没做change操作时候,点击确认可以默认选中第一个
|
|
153
|
-
const currentActiveValue = ref<number[]>([])
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* 设置整体各列的columns的值
|
|
157
|
-
* */
|
|
158
|
-
const setColumns = (columns: any[]) => {
|
|
159
|
-
innerColumns.value = columns
|
|
160
|
-
// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
|
|
161
|
-
if (innerIndex.value.length === 0) {
|
|
162
|
-
innerIndex.value = new Array(columns.length).fill(0)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 监听默认索引的变化,重新设置对应的值
|
|
168
|
-
* */
|
|
169
|
-
watch(
|
|
170
|
-
() => props.defaultIndex,
|
|
171
|
-
(newValue) => {
|
|
172
|
-
setIndexs(newValue)
|
|
173
|
-
},
|
|
174
|
-
{ immediate: true }
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* 监听默认值,给索引赋值
|
|
179
|
-
* */
|
|
180
|
-
watch(
|
|
181
|
-
() => props.modelValue,
|
|
182
|
-
(v) =>
|
|
183
|
-
setIndexs(
|
|
184
|
-
(isArray(v)
|
|
185
|
-
? v
|
|
186
|
-
: String(v).includes(props.separator)
|
|
187
|
-
? String(v).split(props.separator)
|
|
188
|
-
: [v]
|
|
189
|
-
)
|
|
190
|
-
.map((item, i) =>
|
|
191
|
-
props.columns[i]?.findIndex(
|
|
192
|
-
(val) => (typeof val === 'object' ? val[props.valueKey] : val) === item
|
|
193
|
-
)
|
|
194
|
-
)
|
|
195
|
-
.map((n) => (n < 0 ? 0 : n))
|
|
196
|
-
),
|
|
197
|
-
{ immediate: true }
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 监听columns参数的变化
|
|
202
|
-
* */
|
|
203
|
-
watch(
|
|
204
|
-
() => props.columns,
|
|
205
|
-
(newValue) => {
|
|
206
|
-
setColumns(newValue)
|
|
207
|
-
},
|
|
208
|
-
{ deep: true, immediate: true }
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* 已选&&已确认的值显示在input上面的文案
|
|
213
|
-
* */
|
|
214
|
-
const inputLabelValue = computed((): string => {
|
|
215
|
-
let firstItem = innerColumns.value[0][0]
|
|
216
|
-
// //区分是不是对象数组
|
|
217
|
-
if (firstItem && Object.prototype.toString.call(firstItem) === '[object Object]') {
|
|
218
|
-
let res: Record<string, any>[] = []
|
|
219
|
-
innerColumns.value.map((ite, i) => {
|
|
220
|
-
res.push(
|
|
221
|
-
...innerColumns.value[i]?.filter((item) => {
|
|
222
|
-
return isArray(props.modelValue)
|
|
223
|
-
? props.modelValue.includes(item[props.valueKey])
|
|
224
|
-
: props.modelValue === item[props.valueKey]
|
|
225
|
-
})
|
|
226
|
-
)
|
|
227
|
-
})
|
|
228
|
-
res = res.map((item) => item[props.labelKey])
|
|
229
|
-
return res.join(props.separator)
|
|
230
|
-
} else {
|
|
231
|
-
//用户确定的值,才显示到输入框
|
|
232
|
-
if (props.modelValue.length && isArray(props.modelValue)) {
|
|
233
|
-
return props.modelValue.join(props.separator)
|
|
234
|
-
}
|
|
235
|
-
return props.modelValue as string
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* 已选,待确认的值
|
|
241
|
-
* */
|
|
242
|
-
const inputValue = computed(() => {
|
|
243
|
-
let items = innerColumns.value.map((item, index) => item[innerIndex.value[index]])
|
|
244
|
-
let res: any[] = []
|
|
245
|
-
//区分是不是对象数组
|
|
246
|
-
if (items[0] && Object.prototype.toString.call(items[0]) === '[object Object]') {
|
|
247
|
-
//对象数组返回id集合
|
|
248
|
-
items.forEach((element) => {
|
|
249
|
-
res.push(element && element[props.valueKey])
|
|
250
|
-
})
|
|
251
|
-
} else {
|
|
252
|
-
//非对象数组返回元素集合
|
|
253
|
-
items.forEach((element) => {
|
|
254
|
-
res.push(element)
|
|
255
|
-
})
|
|
256
|
-
}
|
|
257
|
-
return res
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 显示
|
|
262
|
-
* */
|
|
263
|
-
const onShowByClickInput = () => {
|
|
264
|
-
if (!props.input?.disabled) {
|
|
265
|
-
showByClickInput.value = !showByClickInput.value
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* 获取item需要显示的文字,判别为对象还是文本
|
|
271
|
-
* */
|
|
272
|
-
const getItemText = (item: any) => {
|
|
273
|
-
if (Object.prototype.toString.call(item) === '[object Object]' && props.labelKey) {
|
|
274
|
-
return item[props.labelKey]
|
|
275
|
-
} else {
|
|
276
|
-
return item
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* 关闭选择器
|
|
282
|
-
* */
|
|
283
|
-
const closeHandler = () => {
|
|
284
|
-
if (props.closeOnClickOverlay) {
|
|
285
|
-
if (props.hasInput) {
|
|
286
|
-
showByClickInput.value = false
|
|
287
|
-
}
|
|
288
|
-
emit('update:show', false)
|
|
289
|
-
emit('close')
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* 点击工具栏的取消按钮
|
|
295
|
-
* */
|
|
296
|
-
const cancel = () => {
|
|
297
|
-
if (props.hasInput) {
|
|
298
|
-
showByClickInput.value = false
|
|
299
|
-
}
|
|
300
|
-
emit('update:show', false)
|
|
301
|
-
emit('cancel')
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* 点击工具栏的确定按钮
|
|
306
|
-
* */
|
|
307
|
-
const onConfirm = () => {
|
|
308
|
-
//如果用户还没有触发过change
|
|
309
|
-
if (!currentActiveValue.value.length) {
|
|
310
|
-
let arr = [0]
|
|
311
|
-
//如果有默认值&&默认值的数组长度是正确的,就用默认值
|
|
312
|
-
if (
|
|
313
|
-
Array.isArray(props.defaultIndex) &&
|
|
314
|
-
props.defaultIndex.length === innerColumns.value.length
|
|
315
|
-
) {
|
|
316
|
-
arr = [...props.defaultIndex]
|
|
317
|
-
} else {
|
|
318
|
-
//否则默认都选中第一个
|
|
319
|
-
arr = Array(innerColumns.value.length).fill(0)
|
|
320
|
-
}
|
|
321
|
-
setIndexs(arr, true)
|
|
322
|
-
}
|
|
323
|
-
emit('update:modelValue', inputValue.value)
|
|
324
|
-
if (props.hasInput) {
|
|
325
|
-
showByClickInput.value = false
|
|
326
|
-
}
|
|
327
|
-
emit('update:show', false)
|
|
328
|
-
emit('confirm', {
|
|
329
|
-
indexs: innerIndex.value,
|
|
330
|
-
value: innerColumns.value.map((item, index) => item[innerIndex.value[index]]),
|
|
331
|
-
values: innerColumns.value
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* 选择器某一列的数据发生变化时触发
|
|
337
|
-
* */
|
|
338
|
-
const changeHandler = (e: any) => {
|
|
339
|
-
const { value } = e.detail
|
|
340
|
-
// 优化:使用更高效的方式找出变化的列
|
|
341
|
-
let changedColumnIndex = -1
|
|
342
|
-
let changedItemIndex = 0
|
|
343
|
-
|
|
344
|
-
// 优化循环:使用for...of循环更简洁,并且在找到变化后立即退出
|
|
345
|
-
for (let [i, newValue] of value.entries()) {
|
|
346
|
-
const oldValue = lastIndex.value[i] || 0
|
|
347
|
-
if (newValue !== oldValue) {
|
|
348
|
-
changedColumnIndex = i
|
|
349
|
-
changedItemIndex = newValue
|
|
350
|
-
currentActiveValue.value = value
|
|
351
|
-
break
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// 如果有变化的列,才执行后续操作
|
|
356
|
-
if (changedColumnIndex !== -1) {
|
|
357
|
-
columnIndex.value = changedColumnIndex
|
|
358
|
-
|
|
359
|
-
// 移除无条件重置索引的代码,仅在数据实际变化时重置
|
|
360
|
-
|
|
361
|
-
// 优化:创建params对象时使用更简洁的方式
|
|
362
|
-
const params = {
|
|
363
|
-
value: innerColumns.value.map((item, idx) => item[value[idx]]),
|
|
364
|
-
index: changedItemIndex,
|
|
365
|
-
indexs: value,
|
|
366
|
-
values: innerColumns.value,
|
|
367
|
-
columnIndex: changedColumnIndex
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// 将当前的各项变化索引,设置为"上一次"的索引变化值
|
|
371
|
-
setIndexs(value, true)
|
|
372
|
-
|
|
373
|
-
//如果是非自带输入框才会在change时候触发v-model绑值的变化
|
|
374
|
-
if (!props.hasInput) {
|
|
375
|
-
emit('update:modelValue', inputValue.value)
|
|
376
|
-
}
|
|
377
|
-
emit('change', params)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* 设置index索引,此方法可被外部调用设置
|
|
383
|
-
* */
|
|
384
|
-
function setIndexs(index: number[], isSetLastIndex?: boolean) {
|
|
385
|
-
innerIndex.value = index
|
|
386
|
-
// 移除调试日志
|
|
387
|
-
if (isSetLastIndex) {
|
|
388
|
-
setLastIndex(index)
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* 记录上一次的各列索引位置
|
|
394
|
-
* */
|
|
395
|
-
const setLastIndex = (index: number[]) => {
|
|
396
|
-
// 当能进入此方法,意味着当前设置的各列默认索引,即为“上一次”的选中值,需要记录,是因为changeHandler中
|
|
397
|
-
// 需要拿前后的变化值进行对比,得出当前发生改变的是哪一列
|
|
398
|
-
lastIndex.value = index
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* 设置对应列选项的所有值
|
|
403
|
-
* */
|
|
404
|
-
const setColumnValues = (columnI: number, values: AnyObject[]) => {
|
|
405
|
-
innerColumns.value.splice(columnI, 1, values)
|
|
406
|
-
let tmpIndex = deepClone(innerIndex.value)
|
|
407
|
-
for (let i = 0; i < innerColumns.value.length; i++) {
|
|
408
|
-
if (i > columnIndex.value) {
|
|
409
|
-
tmpIndex[i] = 0
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
// 一次性赋值,不能单个修改,否则无效
|
|
413
|
-
setIndexs(tmpIndex, true)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* 获取对应列的所有选项
|
|
418
|
-
* */
|
|
419
|
-
const getColumnValues = (columnI: number) => {
|
|
420
|
-
// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
|
|
421
|
-
// 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
|
|
422
|
-
;(async () => {
|
|
423
|
-
await sleep()
|
|
424
|
-
})()
|
|
425
|
-
return innerColumns.value[columnI]
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
defineExpose({
|
|
429
|
-
setColumnValues
|
|
430
|
-
})
|
|
431
|
-
</script>
|
|
432
|
-
|
|
433
|
-
<style lang="scss" scoped>
|
|
434
|
-
@import './index.scss';
|
|
435
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<view class="hy-picker">
|
|
3
|
+
<view v-if="hasInput" class="hy-picker-input cursor-pointer" @click="onShowByClickInput">
|
|
4
|
+
<slot v-if="$slots.default"></slot>
|
|
5
|
+
<template v-else>
|
|
6
|
+
<hy-input
|
|
7
|
+
v-model="inputLabelValue"
|
|
8
|
+
:disabled="input?.disabled"
|
|
9
|
+
:disabledColor="input?.disabledColor"
|
|
10
|
+
:shape="input?.shape"
|
|
11
|
+
:border="input?.border"
|
|
12
|
+
:prefixIcon="input?.prefixIcon"
|
|
13
|
+
:suffixIcon="input?.suffixIcon"
|
|
14
|
+
:color="input?.color"
|
|
15
|
+
:fontSize="input?.fontSize"
|
|
16
|
+
:inputAlign="input?.inputAlign"
|
|
17
|
+
:placeholder="input?.placeholder || t('placeholder')"
|
|
18
|
+
:placeholderStyle="input?.placeholderStyle"
|
|
19
|
+
:placeholderClass="input?.placeholderClass"
|
|
20
|
+
:customStyle="Object.assign({ 'pointer-events': 'none' }, input?.customStyle)"
|
|
21
|
+
></hy-input>
|
|
22
|
+
<view class="input-cover"></view>
|
|
23
|
+
</template>
|
|
24
|
+
</view>
|
|
25
|
+
<hy-popup
|
|
26
|
+
:show="show || (hasInput && showByClickInput)"
|
|
27
|
+
:mode="popupMode"
|
|
28
|
+
:zIndex="zIndex"
|
|
29
|
+
@close="closeHandler"
|
|
30
|
+
>
|
|
31
|
+
<view class="hy-picker">
|
|
32
|
+
<!-- 头部内容 -->
|
|
33
|
+
<view class="hy-picker__title" v-if="showToolbar">
|
|
34
|
+
<view
|
|
35
|
+
class="hy-picker__title--left"
|
|
36
|
+
:style="{ color: cancelColor }"
|
|
37
|
+
@tap="cancel"
|
|
38
|
+
>
|
|
39
|
+
{{ cancelText }}
|
|
40
|
+
</view>
|
|
41
|
+
<view class="hy-picker__title--center">
|
|
42
|
+
{{ title }}
|
|
43
|
+
</view>
|
|
44
|
+
<view
|
|
45
|
+
class="hy-picker__title--right"
|
|
46
|
+
:style="{ color: confirmColor }"
|
|
47
|
+
@tap="onConfirm"
|
|
48
|
+
>
|
|
49
|
+
<slot v-if="$slots['toolbar-right']" name="toolbar-right"></slot>
|
|
50
|
+
<text v-else>{{ confirmText }}</text>
|
|
51
|
+
</view>
|
|
52
|
+
</view>
|
|
53
|
+
<!-- 头部内容 -->
|
|
54
|
+
|
|
55
|
+
<slot name="toolbar-bottom"></slot>
|
|
56
|
+
|
|
57
|
+
<!-- 加载loading -->
|
|
58
|
+
<hy-loading
|
|
59
|
+
v-if="loading"
|
|
60
|
+
mode="circle"
|
|
61
|
+
custom-class="hy-picker__loading"
|
|
62
|
+
:custom-style="{
|
|
63
|
+
height: `${addUnit(visibleItemCount * itemHeight)}`
|
|
64
|
+
}"
|
|
65
|
+
></hy-loading>
|
|
66
|
+
<!-- 加载loading -->
|
|
67
|
+
|
|
68
|
+
<!-- 选择器内容 -->
|
|
69
|
+
<picker-view
|
|
70
|
+
v-else
|
|
71
|
+
class="hy-picker--view"
|
|
72
|
+
:indicatorStyle="`height: ${addUnit(itemHeight)}`"
|
|
73
|
+
:value="innerIndex"
|
|
74
|
+
:immediateChange="immediateChange"
|
|
75
|
+
:style="{
|
|
76
|
+
height: `${addUnit(visibleItemCount * itemHeight)}`
|
|
77
|
+
}"
|
|
78
|
+
mask-class="hy-picker--view__mask"
|
|
79
|
+
@change="changeHandler"
|
|
80
|
+
>
|
|
81
|
+
<picker-view-column
|
|
82
|
+
v-for="(item, index) in innerColumns"
|
|
83
|
+
:key="index"
|
|
84
|
+
class="hy-picker--view__column"
|
|
85
|
+
>
|
|
86
|
+
<view
|
|
87
|
+
v-if="Array.isArray(item)"
|
|
88
|
+
:class="[
|
|
89
|
+
'hy-picker--view__column__item',
|
|
90
|
+
index1 === innerIndex[index] &&
|
|
91
|
+
'hy-picker--view__column__item--selected'
|
|
92
|
+
]"
|
|
93
|
+
v-for="(item1, index1) in item"
|
|
94
|
+
:key="index1"
|
|
95
|
+
:style="{
|
|
96
|
+
height: addUnit(itemHeight),
|
|
97
|
+
lineHeight: addUnit(itemHeight),
|
|
98
|
+
fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal',
|
|
99
|
+
display: 'block'
|
|
100
|
+
}"
|
|
101
|
+
>
|
|
102
|
+
{{ getItemText(item1) }}
|
|
103
|
+
</view>
|
|
104
|
+
</picker-view-column>
|
|
105
|
+
</picker-view>
|
|
106
|
+
<!-- 选择器内容 -->
|
|
107
|
+
</view>
|
|
108
|
+
</hy-popup>
|
|
109
|
+
</view>
|
|
110
|
+
</template>
|
|
111
|
+
|
|
112
|
+
<script lang="ts">
|
|
113
|
+
export default {
|
|
114
|
+
name: 'hy-picker',
|
|
115
|
+
options: {
|
|
116
|
+
addGlobalClass: true,
|
|
117
|
+
virtualHost: true,
|
|
118
|
+
styleIsolation: 'shared'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<script setup lang="ts">
|
|
124
|
+
import { computed, ref, watch } from 'vue'
|
|
125
|
+
import { deepClone, sleep, addUnit, isArray, useTranslate } from '../../libs'
|
|
126
|
+
import type { IPickerEmits } from './typing'
|
|
127
|
+
import pickerProps from './props'
|
|
128
|
+
// 组件
|
|
129
|
+
import HyInput from '../hy-input/hy-input.vue'
|
|
130
|
+
import HyLoading from '../hy-loading/hy-loading.vue'
|
|
131
|
+
import HyPopup from '../hy-popup/hy-popup.vue'
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 此选择器用于单列,多列,多列联动的选择场景。
|
|
135
|
+
* @displayName hy-picker
|
|
136
|
+
*/
|
|
137
|
+
defineOptions({})
|
|
138
|
+
|
|
139
|
+
const props = defineProps(pickerProps)
|
|
140
|
+
const emit = defineEmits<IPickerEmits>()
|
|
141
|
+
|
|
142
|
+
const { t } = useTranslate('picker')
|
|
143
|
+
// 上一次选择的列索引
|
|
144
|
+
const lastIndex = ref<number[]>([])
|
|
145
|
+
// 索引值 ,对应picker-view的value
|
|
146
|
+
const innerIndex = ref<number[]>([])
|
|
147
|
+
// 各列的值
|
|
148
|
+
const innerColumns = ref<any[][]>([])
|
|
149
|
+
// 上一次的变化列索引
|
|
150
|
+
const columnIndex = ref<number>(0)
|
|
151
|
+
const showByClickInput = ref<boolean>(false)
|
|
152
|
+
// 当前用户选中,但是还没确认的值,用户没做change操作时候,点击确认可以默认选中第一个
|
|
153
|
+
const currentActiveValue = ref<number[]>([])
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 设置整体各列的columns的值
|
|
157
|
+
* */
|
|
158
|
+
const setColumns = (columns: any[]) => {
|
|
159
|
+
innerColumns.value = columns
|
|
160
|
+
// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
|
|
161
|
+
if (innerIndex.value.length === 0) {
|
|
162
|
+
innerIndex.value = new Array(columns.length).fill(0)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 监听默认索引的变化,重新设置对应的值
|
|
168
|
+
* */
|
|
169
|
+
watch(
|
|
170
|
+
() => props.defaultIndex,
|
|
171
|
+
(newValue) => {
|
|
172
|
+
setIndexs(newValue)
|
|
173
|
+
},
|
|
174
|
+
{ immediate: true }
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 监听默认值,给索引赋值
|
|
179
|
+
* */
|
|
180
|
+
watch(
|
|
181
|
+
() => props.modelValue,
|
|
182
|
+
(v) =>
|
|
183
|
+
setIndexs(
|
|
184
|
+
(isArray(v)
|
|
185
|
+
? v
|
|
186
|
+
: String(v).includes(props.separator)
|
|
187
|
+
? String(v).split(props.separator)
|
|
188
|
+
: [v]
|
|
189
|
+
)
|
|
190
|
+
.map((item, i) =>
|
|
191
|
+
props.columns[i]?.findIndex(
|
|
192
|
+
(val) => (typeof val === 'object' ? val[props.valueKey] : val) === item
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
.map((n) => (n < 0 ? 0 : n))
|
|
196
|
+
),
|
|
197
|
+
{ immediate: true }
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 监听columns参数的变化
|
|
202
|
+
* */
|
|
203
|
+
watch(
|
|
204
|
+
() => props.columns,
|
|
205
|
+
(newValue) => {
|
|
206
|
+
setColumns(newValue)
|
|
207
|
+
},
|
|
208
|
+
{ deep: true, immediate: true }
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 已选&&已确认的值显示在input上面的文案
|
|
213
|
+
* */
|
|
214
|
+
const inputLabelValue = computed((): string => {
|
|
215
|
+
let firstItem = innerColumns.value[0][0]
|
|
216
|
+
// //区分是不是对象数组
|
|
217
|
+
if (firstItem && Object.prototype.toString.call(firstItem) === '[object Object]') {
|
|
218
|
+
let res: Record<string, any>[] = []
|
|
219
|
+
innerColumns.value.map((ite, i) => {
|
|
220
|
+
res.push(
|
|
221
|
+
...innerColumns.value[i]?.filter((item) => {
|
|
222
|
+
return isArray(props.modelValue)
|
|
223
|
+
? props.modelValue.includes(item[props.valueKey])
|
|
224
|
+
: props.modelValue === item[props.valueKey]
|
|
225
|
+
})
|
|
226
|
+
)
|
|
227
|
+
})
|
|
228
|
+
res = res.map((item) => item[props.labelKey])
|
|
229
|
+
return res.join(props.separator)
|
|
230
|
+
} else {
|
|
231
|
+
//用户确定的值,才显示到输入框
|
|
232
|
+
if (props.modelValue.length && isArray(props.modelValue)) {
|
|
233
|
+
return props.modelValue.join(props.separator)
|
|
234
|
+
}
|
|
235
|
+
return props.modelValue as string
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 已选,待确认的值
|
|
241
|
+
* */
|
|
242
|
+
const inputValue = computed(() => {
|
|
243
|
+
let items = innerColumns.value.map((item, index) => item[innerIndex.value[index]])
|
|
244
|
+
let res: any[] = []
|
|
245
|
+
//区分是不是对象数组
|
|
246
|
+
if (items[0] && Object.prototype.toString.call(items[0]) === '[object Object]') {
|
|
247
|
+
//对象数组返回id集合
|
|
248
|
+
items.forEach((element) => {
|
|
249
|
+
res.push(element && element[props.valueKey])
|
|
250
|
+
})
|
|
251
|
+
} else {
|
|
252
|
+
//非对象数组返回元素集合
|
|
253
|
+
items.forEach((element) => {
|
|
254
|
+
res.push(element)
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
return res
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 显示
|
|
262
|
+
* */
|
|
263
|
+
const onShowByClickInput = () => {
|
|
264
|
+
if (!props.input?.disabled) {
|
|
265
|
+
showByClickInput.value = !showByClickInput.value
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 获取item需要显示的文字,判别为对象还是文本
|
|
271
|
+
* */
|
|
272
|
+
const getItemText = (item: any) => {
|
|
273
|
+
if (Object.prototype.toString.call(item) === '[object Object]' && props.labelKey) {
|
|
274
|
+
return item[props.labelKey]
|
|
275
|
+
} else {
|
|
276
|
+
return item
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 关闭选择器
|
|
282
|
+
* */
|
|
283
|
+
const closeHandler = () => {
|
|
284
|
+
if (props.closeOnClickOverlay) {
|
|
285
|
+
if (props.hasInput) {
|
|
286
|
+
showByClickInput.value = false
|
|
287
|
+
}
|
|
288
|
+
emit('update:show', false)
|
|
289
|
+
emit('close')
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 点击工具栏的取消按钮
|
|
295
|
+
* */
|
|
296
|
+
const cancel = () => {
|
|
297
|
+
if (props.hasInput) {
|
|
298
|
+
showByClickInput.value = false
|
|
299
|
+
}
|
|
300
|
+
emit('update:show', false)
|
|
301
|
+
emit('cancel')
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 点击工具栏的确定按钮
|
|
306
|
+
* */
|
|
307
|
+
const onConfirm = () => {
|
|
308
|
+
//如果用户还没有触发过change
|
|
309
|
+
if (!currentActiveValue.value.length) {
|
|
310
|
+
let arr = [0]
|
|
311
|
+
//如果有默认值&&默认值的数组长度是正确的,就用默认值
|
|
312
|
+
if (
|
|
313
|
+
Array.isArray(props.defaultIndex) &&
|
|
314
|
+
props.defaultIndex.length === innerColumns.value.length
|
|
315
|
+
) {
|
|
316
|
+
arr = [...props.defaultIndex]
|
|
317
|
+
} else {
|
|
318
|
+
//否则默认都选中第一个
|
|
319
|
+
arr = Array(innerColumns.value.length).fill(0)
|
|
320
|
+
}
|
|
321
|
+
setIndexs(arr, true)
|
|
322
|
+
}
|
|
323
|
+
emit('update:modelValue', inputValue.value)
|
|
324
|
+
if (props.hasInput) {
|
|
325
|
+
showByClickInput.value = false
|
|
326
|
+
}
|
|
327
|
+
emit('update:show', false)
|
|
328
|
+
emit('confirm', {
|
|
329
|
+
indexs: innerIndex.value,
|
|
330
|
+
value: innerColumns.value.map((item, index) => item[innerIndex.value[index]]),
|
|
331
|
+
values: innerColumns.value
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 选择器某一列的数据发生变化时触发
|
|
337
|
+
* */
|
|
338
|
+
const changeHandler = (e: any) => {
|
|
339
|
+
const { value } = e.detail
|
|
340
|
+
// 优化:使用更高效的方式找出变化的列
|
|
341
|
+
let changedColumnIndex = -1
|
|
342
|
+
let changedItemIndex = 0
|
|
343
|
+
|
|
344
|
+
// 优化循环:使用for...of循环更简洁,并且在找到变化后立即退出
|
|
345
|
+
for (let [i, newValue] of value.entries()) {
|
|
346
|
+
const oldValue = lastIndex.value[i] || 0
|
|
347
|
+
if (newValue !== oldValue) {
|
|
348
|
+
changedColumnIndex = i
|
|
349
|
+
changedItemIndex = newValue
|
|
350
|
+
currentActiveValue.value = value
|
|
351
|
+
break
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// 如果有变化的列,才执行后续操作
|
|
356
|
+
if (changedColumnIndex !== -1) {
|
|
357
|
+
columnIndex.value = changedColumnIndex
|
|
358
|
+
|
|
359
|
+
// 移除无条件重置索引的代码,仅在数据实际变化时重置
|
|
360
|
+
|
|
361
|
+
// 优化:创建params对象时使用更简洁的方式
|
|
362
|
+
const params = {
|
|
363
|
+
value: innerColumns.value.map((item, idx) => item[value[idx]]),
|
|
364
|
+
index: changedItemIndex,
|
|
365
|
+
indexs: value,
|
|
366
|
+
values: innerColumns.value,
|
|
367
|
+
columnIndex: changedColumnIndex
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 将当前的各项变化索引,设置为"上一次"的索引变化值
|
|
371
|
+
setIndexs(value, true)
|
|
372
|
+
|
|
373
|
+
//如果是非自带输入框才会在change时候触发v-model绑值的变化
|
|
374
|
+
if (!props.hasInput) {
|
|
375
|
+
emit('update:modelValue', inputValue.value)
|
|
376
|
+
}
|
|
377
|
+
emit('change', params)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 设置index索引,此方法可被外部调用设置
|
|
383
|
+
* */
|
|
384
|
+
function setIndexs(index: number[], isSetLastIndex?: boolean) {
|
|
385
|
+
innerIndex.value = index
|
|
386
|
+
// 移除调试日志
|
|
387
|
+
if (isSetLastIndex) {
|
|
388
|
+
setLastIndex(index)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* 记录上一次的各列索引位置
|
|
394
|
+
* */
|
|
395
|
+
const setLastIndex = (index: number[]) => {
|
|
396
|
+
// 当能进入此方法,意味着当前设置的各列默认索引,即为“上一次”的选中值,需要记录,是因为changeHandler中
|
|
397
|
+
// 需要拿前后的变化值进行对比,得出当前发生改变的是哪一列
|
|
398
|
+
lastIndex.value = index
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* 设置对应列选项的所有值
|
|
403
|
+
* */
|
|
404
|
+
const setColumnValues = (columnI: number, values: AnyObject[]) => {
|
|
405
|
+
innerColumns.value.splice(columnI, 1, values)
|
|
406
|
+
let tmpIndex = deepClone(innerIndex.value)
|
|
407
|
+
for (let i = 0; i < innerColumns.value.length; i++) {
|
|
408
|
+
if (i > columnIndex.value) {
|
|
409
|
+
tmpIndex[i] = 0
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// 一次性赋值,不能单个修改,否则无效
|
|
413
|
+
setIndexs(tmpIndex, true)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 获取对应列的所有选项
|
|
418
|
+
* */
|
|
419
|
+
const getColumnValues = (columnI: number) => {
|
|
420
|
+
// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
|
|
421
|
+
// 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
|
|
422
|
+
;(async () => {
|
|
423
|
+
await sleep()
|
|
424
|
+
})()
|
|
425
|
+
return innerColumns.value[columnI]
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
defineExpose({
|
|
429
|
+
setColumnValues
|
|
430
|
+
})
|
|
431
|
+
</script>
|
|
432
|
+
|
|
433
|
+
<style lang="scss" scoped>
|
|
434
|
+
@import './index.scss';
|
|
435
|
+
</style>
|